From 64a1dbadc8fbe3cdae2916ab30b6a58afec69cd3 Mon Sep 17 00:00:00 2001 From: Pradnya Baviskar Date: Mon, 20 Jul 2015 17:30:05 +0530 Subject: [PATCH] Promisify 'PersistedModel - replication' --- lib/persisted-model.js | 6 +- test/replication.test.js | 141 ++++++++++++++++++++++++++++++++------- 2 files changed, 121 insertions(+), 26 deletions(-) diff --git a/lib/persisted-model.js b/lib/persisted-model.js index 67585a9d..f823a320 100644 --- a/lib/persisted-model.js +++ b/lib/persisted-model.js @@ -8,6 +8,7 @@ var async = require('async'); var deprecated = require('depd')('loopback'); var debug = require('debug')('loopback:persisted-model'); var PassThrough = require('stream').PassThrough; +var utils = require('./utils'); module.exports = function(registry) { var Model = registry.getModel('Model'); @@ -916,9 +917,7 @@ module.exports = function(registry) { options = options || {}; var sourceModel = this; - callback = callback || function defaultReplicationCallback(err) { - if (err) throw err; - }; + callback = callback || utils.createPromiseCallback(); debug('replicating %s since %s to %s since %s', sourceModel.modelName, @@ -944,6 +943,7 @@ module.exports = function(registry) { var MAX_ATTEMPTS = 3; run(1, since); + return callback.promise; function run(attempt, since) { debug('\titeration #%s', attempt); diff --git a/test/replication.test.js b/test/replication.test.js index cd3515bb..bc9c4c5c 100644 --- a/test/replication.test.js +++ b/test/replication.test.js @@ -82,43 +82,70 @@ describe('Replication / Change APIs', function() { }); describe('Model.replicate(since, targetModel, options, callback)', function() { + + function assertTargetModelEqualsSourceModel(conflicts, sourceModel, + targetModel, done) { + var sourceData; + var targetData; + + assert(conflicts.length === 0); + async.parallel([ + function(cb) { + sourceModel.find(function(err, result) { + if (err) return cb(err); + sourceData = result; + cb(); + }); + }, + function(cb) { + targetModel.find(function(err, result) { + if (err) return cb(err); + targetData = result; + cb(); + }); + } + ], function(err) { + if (err) return done(err); + + assert.deepEqual(sourceData, targetData); + done(); + }); + } + it('Replicate data using the target model', function(done) { var test = this; var options = {}; - var sourceData; - var targetData; this.SourceModel.create({name: 'foo'}, function(err) { if (err) return done(err); test.SourceModel.replicate(test.startingCheckpoint, test.TargetModel, options, function(err, conflicts) { if (err) return done(err); - assert(conflicts.length === 0); - async.parallel([ - function(cb) { - test.SourceModel.find(function(err, result) { - if (err) return cb(err); - sourceData = result; - cb(); - }); - }, - function(cb) { - test.TargetModel.find(function(err, result) { - if (err) return cb(err); - targetData = result; - cb(); - }); - } - ], function(err) { - if (err) return done(err); - assert.deepEqual(sourceData, targetData); - done(); - }); + assertTargetModelEqualsSourceModel(conflicts, test.SourceModel, + test.TargetModel, done); }); }); }); + it('Replicate data using the target model - promise variant', function(done) { + var test = this; + var options = {}; + + this.SourceModel.create({name: 'foo'}, function(err) { + if (err) return done(err); + test.SourceModel.replicate(test.startingCheckpoint, test.TargetModel, + options) + .then(function(conflicts) { + assertTargetModelEqualsSourceModel(conflicts, test.SourceModel, + test.TargetModel, done); + }) + .catch(function(err) { + done(err); + }); + }); + }); + it('applies "since" filter on source changes', function(done) { async.series([ function createModelInSourceCp1(next) { @@ -147,6 +174,38 @@ describe('Replication / Change APIs', function() { ], done); }); + it('applies "since" filter on source changes - promise variant', function(done) { + async.series([ + function createModelInSourceCp1(next) { + SourceModel.create({ id: '1' }, next); + }, + function checkpoint(next) { + SourceModel.checkpoint(next); + }, + function createModelInSourceCp2(next) { + SourceModel.create({ id: '2' }, next); + }, + function replicateLastChangeOnly(next) { + SourceModel.currentCheckpoint(function(err, cp) { + if (err) return done(err); + SourceModel.replicate(cp, TargetModel, {}) + .then(function(next) { + done(); + }) + .catch(err); + }); + }, + function verify(next) { + TargetModel.find(function(err, list) { + if (err) return done(err); + // '1' should be skipped by replication + expect(getIds(list)).to.eql(['2']); + next(); + }); + } + ], done); + }); + it('applies "since" filter on target changes', function(done) { // Because the "since" filter is just an optimization, // there isn't really any observable behaviour we could @@ -161,6 +220,23 @@ describe('Replication / Change APIs', function() { }); }); + it('applies "since" filter on target changes - promise variant', function(done) { + // Because the "since" filter is just an optimization, + // there isn't really any observable behaviour we could + // check to assert correct implementation. + var diffSince = []; + spyAndStoreSinceArg(TargetModel, 'diff', diffSince); + + SourceModel.replicate(10, TargetModel, {}) + .then(function() { + expect(diffSince).to.eql([10]); + done(); + }) + .catch(function(err) { + done(err); + }); + }); + it('uses different "since" value for source and target', function(done) { var sourceSince = []; var targetSince = []; @@ -177,6 +253,25 @@ describe('Replication / Change APIs', function() { }); }); + it('uses different "since" value for source and target - promise variant', function(done) { + var sourceSince = []; + var targetSince = []; + + spyAndStoreSinceArg(SourceModel, 'changes', sourceSince); + spyAndStoreSinceArg(TargetModel, 'diff', targetSince); + + var since = { source: 1, target: 2 }; + SourceModel.replicate(since, TargetModel, {}) + .then(function() { + expect(sourceSince).to.eql([1]); + expect(targetSince).to.eql([2]); + done(); + }) + .catch(function(err) { + done(err); + }); + }); + it('picks up changes made during replication', function(done) { setupRaceConditionInReplication(function(cb) { // simulate the situation when another model is created