| 'use strict'; |
| |
| var adapters = [ |
| ['local', 'http'], |
| ['http', 'http'], |
| ['http', 'local'], |
| ['local', 'local'] |
| ]; |
| |
| if ('saucelabs' in testUtils.params()) { |
| adapters = [['local', 'http'], ['http', 'local']]; |
| } |
| |
| |
| adapters.forEach(function (adapters) { |
| var title = 'test.replication_events.js-' + adapters[0] + '-' + adapters[1]; |
| describe('suite2 ' + title, function () { |
| |
| var dbs = {}; |
| |
| beforeEach(function () { |
| dbs.name = testUtils.adapterUrl(adapters[0], 'testdb'); |
| dbs.remote = testUtils.adapterUrl(adapters[1], 'test_repl_remote'); |
| }); |
| |
| afterEach(function (done) { |
| testUtils.cleanup([dbs.name, dbs.remote], done); |
| }); |
| |
| it('#3852 Test basic starting empty', function (done) { |
| |
| var db = new PouchDB(dbs.name); |
| var repl = db.replicate.to(dbs.remote, {retry: true, live: true}); |
| var counter = 0; |
| |
| repl.on('complete', function () { done(); }); |
| |
| repl.on('active', function () { |
| counter++; |
| if (!(counter === 2 || counter === 4)) { |
| done('active fired incorrectly'); |
| } |
| }); |
| |
| repl.on('paused', function () { |
| counter++; |
| // We should receive a paused event when replication |
| // starts because there is nothing to replicate |
| if (counter === 1) { |
| db.bulkDocs([{_id: 'a'}, {_id: 'b'}]); |
| } else if (counter === 3) { |
| db.bulkDocs([{_id: 'c'}, {_id: 'd'}]); |
| } else if (counter === 5) { |
| repl.cancel(); |
| } else { |
| done('paused fired incorrectly'); |
| } |
| }); |
| }); |
| |
| |
| it('#3852 Test basic starting with docs', function (done) { |
| |
| var db = new PouchDB(dbs.name); |
| |
| db.bulkDocs([{_id: 'a'}, {_id: 'b'}]).then(function () { |
| |
| var repl = db.replicate.to(dbs.remote, {retry: true, live: true}); |
| |
| var counter = 0; |
| |
| repl.on('complete', function () { done(); }); |
| |
| repl.on('active', function () { |
| counter++; |
| if (!(counter === 1 || counter === 3 || counter === 5)) { |
| done('active fired incorrectly:' + counter); |
| } |
| }); |
| |
| repl.on('paused', function () { |
| counter++; |
| // We should receive a paused event when replication |
| // starts because there is nothing to replicate |
| if (counter === 2) { |
| db.bulkDocs([{_id: 'c'}, {_id: 'd'}]); |
| } else if (counter === 4) { |
| db.bulkDocs([{_id: 'e'}, {_id: 'f'}]); |
| } else if (counter === 6) { |
| repl.cancel(); |
| } else { |
| done('paused fired incorrectly'); |
| } |
| }); |
| }); |
| }); |
| |
| it('#5710 Test pending property support', function (done) { |
| |
| var db = new PouchDB(dbs.name); |
| var remote = new PouchDB(dbs.remote); |
| var docId = 0; |
| var numDocs = 10; |
| |
| function generateDocs(n) { |
| return Array.apply(null, new Array(n)).map(function () { |
| docId += 1; |
| return { |
| _id: docId.toString(), |
| foo: Math.random().toString() |
| }; |
| }); |
| } |
| remote.bulkDocs(generateDocs(numDocs)).then(function () { |
| var repl = db.replicate.from(dbs.remote, { retry: true, live: false, batch_size: 4 }); |
| var pendingSum = 0; |
| |
| repl.on('change', function (info) { |
| if (typeof info.pending === 'number') { |
| pendingSum += info.pending; |
| if (info.pending === 0) { |
| pendingSum += info.docs.length; |
| } |
| } |
| }); |
| |
| repl.on('complete', function () { |
| if (pendingSum > 0) { |
| pendingSum.should.equal(numDocs); |
| } |
| done(); |
| }); |
| }); |
| }); |
| |
| it('#3852 Test errors', function (done) { |
| |
| if (!(/http/.test(dbs.remote) && !/http/.test(dbs.name))) { |
| // Only run test when remote is http and local is local |
| return done(); |
| } |
| |
| var db = new PouchDB(dbs.name); |
| var remote = new PouchDB(dbs.remote, { |
| fetch: function (url, opts) { |
| if (rejectAjax) { |
| throw new Error('flunking you'); |
| } else { |
| return PouchDB.fetch(url, opts); |
| } |
| } |
| }); |
| var rejectAjax = true; |
| |
| db.bulkDocs([{_id: 'a'}, {_id: 'b'}]).then(function () { |
| |
| var repl = db.replicate.to(remote, { |
| retry: true, |
| live: true, |
| back_off_function: function () { return 0; } |
| }); |
| |
| var counter = 0; |
| |
| repl.on('complete', function () { |
| done(); |
| }); |
| |
| repl.on('active', function () { |
| counter++; |
| if (counter === 2) { |
| // All good, wait for pause |
| } else if (counter === 4) { |
| // Lets start failing while active |
| rejectAjax = true; |
| db.bulkDocs([{_id: 'e'}, {_id: 'f'}]); |
| } else if (counter === 6) { |
| // All good, wait for pause |
| } else { |
| done('active fired incorrectly'); |
| } |
| }); |
| |
| repl.on('paused', function (err) { |
| counter++; |
| // Replication starts with a paused(err) because ajax is |
| // failing |
| if (counter === 1) { |
| should.exist(err); |
| // Lets let the repliation start |
| rejectAjax = false; |
| } else if (counter === 3) { |
| db.bulkDocs([{_id: 'c'}, {_id: 'd'}]); |
| } else if (counter === 5) { |
| // We started failing while active, should have an error |
| // then we stop rejecting and should become active again |
| should.exist(err); |
| rejectAjax = false; |
| } else if (counter === 7) { |
| repl.cancel(); |
| } else { |
| done('paused fired incorrectly'); |
| } |
| }); |
| }).catch(done); |
| }); |
| |
| |
| // this test sets up a 2 way replication which initially transfers |
| // documents from a remote to a local database. |
| // At the same time, we insert documents locally - the changes |
| // should propagate to the remote database and then back to the |
| // local database via the live replications. |
| // Previously, this test resulted in 'change' events being |
| // generated for already-replicated documents. When PouchDB is working |
| // as expected, each remote document should be passed to a |
| // change event exactly once (though a change might contain multiple docs) |
| it('#4627 Test no duplicate changes in live replication', function (done) { |
| if (testUtils.isIE()) { |
| return done(); |
| } |
| var db = new PouchDB(dbs.name); |
| var remote = new PouchDB(dbs.remote); |
| var docId = -1; |
| var docsToGenerate = 10; |
| var lastChange = -1; |
| var firstReplication; |
| var secondReplication; |
| var completeCalls = 0; |
| |
| function generateDocs(n) { |
| return Array.apply(null, new Array(n)).map(function () { |
| docId += 1; |
| return { |
| _id: docId.toString(), |
| foo: Math.random().toString() |
| }; |
| }); |
| } |
| |
| function complete() { |
| completeCalls++; |
| if (completeCalls === 2) { |
| done(); |
| } |
| } |
| |
| remote.bulkDocs(generateDocs(docsToGenerate)).then(function () { |
| firstReplication = db.replicate.to(remote, { |
| live: true, |
| retry: true, |
| since: 0 |
| }) |
| .on('error', done) |
| .on('complete', complete); |
| |
| secondReplication = remote.replicate.to(db, { |
| live: true, |
| retry: true, |
| since: 0 |
| }) |
| .on('error', done) |
| .on('complete', complete) |
| .on('change', function (feed) { |
| // attempt to detect changes loop |
| var ids = feed.docs.map(function (d) { |
| return parseInt(d._id, 10); |
| }).sort(); |
| |
| var firstChange = ids[0]; |
| if (firstChange <= lastChange) { |
| done(new Error("Duplicate change events detected")); |
| } |
| |
| lastChange = ids[ids.length - 1]; |
| |
| if (lastChange === docsToGenerate - 1) { |
| // if a change loop doesn't occur within 2 seconds, assume success |
| setTimeout(function () { |
| // success! |
| // cancelling the replications to clean up and trigger |
| // the 'complete' event, which in turn ends the test |
| firstReplication.cancel(); |
| secondReplication.cancel(); |
| }, 2000); |
| } |
| |
| // write doc to local db - should round trip in _changes |
| // but not generate a change event |
| db.bulkDocs(generateDocs(1)); |
| }); |
| }).catch(done); |
| }); |
| |
| describe('#5172 triggering error when replicating', replication401TestFunction('unauthorized')); |
| |
| function replication401TestFunction(eventName) { |
| return function () { |
| var securedDbs = [], source, dest; |
| beforeEach(function () { |
| var err = { |
| 'status': 401, |
| 'name': eventName, |
| 'message': 'You are not authorized to access this db.' |
| }; |
| |
| if (adapters[0] === 'http') { |
| source = new PouchDB(dbs.name, { |
| fetch: function () { |
| throw err; |
| } |
| }); |
| dest = new PouchDB(dbs.remote); |
| securedDbs.push(source); |
| } |
| |
| if (adapters[1] === 'http') { |
| source = new PouchDB(dbs.name); |
| dest = new PouchDB(dbs.remote, { |
| fetch: function () { |
| throw err; |
| } |
| }); |
| securedDbs.push(dest); |
| } |
| }); |
| |
| function attachHandlers(replication) { |
| var invokedHandlers = []; |
| ['change', 'complete', 'paused', 'active', 'denied', 'error'].forEach(function (type) { |
| replication.on(type, function () { |
| invokedHandlers.push(type); |
| }); |
| }); |
| return invokedHandlers; |
| } |
| |
| it('from or to a secured database, using live replication', function () { |
| if (adapters[0] === 'local' && adapters[1] === 'local') { |
| return; |
| } |
| |
| var replication = source.replicate.to(dest, {live: true}); |
| var invokedHandlers = attachHandlers(replication); |
| |
| return replication.then(function () { |
| throw new Error('Resulting promise should be rejected'); |
| }, function () { |
| invokedHandlers.should.be.eql(['error'], 'incorrect handler was invoked'); |
| }); |
| }); |
| |
| it('from or to a secured database, using live replication with checkpoint', function () { |
| if (adapters[0] === 'local' && adapters[1] === 'local') { |
| return; |
| } |
| |
| var replication = source.replicate.to(dest, {live: true, since: 1234}); |
| var invokedHandlers = attachHandlers(replication); |
| |
| return replication.then(function () { |
| throw new Error('Resulting promise should be rejected'); |
| }, function () { |
| invokedHandlers.should.be.eql(['error'], 'incorrect handler was invoked'); |
| }); |
| }); |
| |
| it('from or to a secured database, using live replication with retrying', function () { |
| if (adapters[0] === 'local' && adapters[1] === 'local') { |
| return; |
| } |
| |
| var replication = source.replicate.to(dest, {live: true, retry: true}); |
| var invokedHandlers = attachHandlers(replication); |
| |
| return replication.then(function () { |
| throw new Error('Resulting promise should be rejected'); |
| }, function () { |
| invokedHandlers.should.be.eql(['error'], 'incorrect handler was invoked'); |
| }); |
| }); |
| |
| it('from or to a secured database, using one-shot replication', function () { |
| if (adapters[0] === 'local' && adapters[1] === 'local') { |
| return; |
| } |
| |
| var replication = source.replicate.to(dest); |
| var invokedHandlers = attachHandlers(replication); |
| |
| return replication.then(function () { |
| throw new Error('Resulting promise should be rejected'); |
| }, function () { |
| invokedHandlers.should.be.eql(['error'], 'incorrect handler was invoked'); |
| }); |
| }); |
| }; |
| } |
| |
| // #5607 https://github.com/pouchdb/pouchdb/issues/5607#issuecomment-346078688 |
| // Note: Uppercase 'Unauthorized' response |
| describe('#5607 handle uppercase third-party error responses', replication401TestFunction('Unauthorized')); |
| }); |
| }); |