blob: c6e850c66972cebaa9a25dda3f01a46d9787e795 [file] [log] [blame]
'use strict';
var adapters = [
['http', 'http'],
['http', 'local'],
['local', 'http'],
['local', 'local']
];
if ('saucelabs' in testUtils.params()) {
adapters = [['local', 'http'], ['http', 'local']];
}
adapters.forEach(function (adapters) {
describe('test.issue3179.js-' + adapters[0] + '-' + adapters[1], 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('#3179 conflicts synced, non-live replication', function () {
var local = new PouchDB(dbs.name);
var remote = new PouchDB(dbs.remote);
return local.put({ _id: '1'}).then(function () {
return local.replicate.to(remote).then(function () {
return remote.replicate.to(local);
});
}).then(function () {
return local.get('1').then(function (doc) {
doc.foo = Math.random();
return local.put(doc);
});
}).then(function () {
return remote.get('1').then(function (doc) {
doc.foo = Math.random();
return remote.put(doc);
});
}).then(function () {
return local.replicate.to(remote).then(function () {
return remote.replicate.to(local);
});
}).then(function () {
return local.get('1', {conflicts: true}).then(function (doc) {
return local.remove(doc._id, doc._conflicts[0]);
});
}).then(function () {
return local.replicate.to(remote).then(function () {
return remote.replicate.to(local);
});
}).then(function () {
return local.get('1', {conflicts: true, revs: true});
}).then(function (localDoc) {
return remote.get('1', {
conflicts: true,
revs: true
}).then(function (remoteDoc) {
remoteDoc.should.deep.equal(localDoc);
});
});
});
it('#3179 conflicts synced, non-live sync', function () {
var local = new PouchDB(dbs.name);
var remote = new PouchDB(dbs.remote);
return local.put({ _id: '1'}).then(function () {
return local.sync(remote);
}).then(function () {
return local.get('1').then(function (doc) {
doc.foo = Math.random();
return local.put(doc);
});
}).then(function () {
return remote.get('1').then(function (doc) {
doc.foo = Math.random();
return remote.put(doc);
});
}).then(function () {
return local.sync(remote);
}).then(function () {
return local.get('1', {conflicts: true}).then(function (doc) {
return local.remove(doc._id, doc._conflicts[0]);
});
}).then(function () {
return local.sync(remote);
}).then(function () {
return local.get('1', {conflicts: true, revs: true});
}).then(function (localDoc) {
return remote.get('1', {
conflicts: true,
revs: true
}).then(function (remoteDoc) {
remoteDoc.should.deep.equal(localDoc);
});
});
});
it('#3179 conflicts synced, live sync', function () {
var local = new PouchDB(dbs.name);
var remote = new PouchDB(dbs.remote);
var sync = local.sync(remote, { live: true });
function waitForUptodate() {
function defaultToEmpty(promise) {
return promise.catch(function (err) {
if (err.status !== 404) {
throw err;
}
return {_revisions: []};
});
}
return defaultToEmpty(local.get('1', {
revs: true,
conflicts: true
})).then(function (localDoc) {
return defaultToEmpty(remote.get('1', {
revs: true,
conflicts: true
})).then(function (remoteDoc) {
var revsEqual = JSON.stringify(localDoc._revisions) ===
JSON.stringify(remoteDoc._revisions);
var conflictsEqual = JSON.stringify(localDoc._conflicts || []) ===
JSON.stringify(remoteDoc._conflicts || []);
if (!revsEqual || !conflictsEqual) {
// we can get caught in an infinite loop here when using adapters based
// on microtasks, e.g. memdown, so use setTimeout() to get a macrotask
return new testUtils.Promise(function (resolve) {
setTimeout(resolve, 0);
}).then(waitForUptodate);
}
});
});
}
function waitForConflictsResolved() {
return new testUtils.Promise(function (resolve) {
var changes = remote.changes({
live: true,
include_docs: true,
conflicts: true
}).on('change', function (change) {
if (!('_conflicts' in change.doc)) {
changes.cancel();
}
});
changes.on('complete', resolve);
});
}
function cleanup() {
return new testUtils.Promise(function (resolve, reject) {
sync.on('complete', resolve);
sync.on('error', reject);
sync.cancel();
sync = null;
});
}
return local.put({ _id: '1'}).then(function () {
return waitForUptodate();
}).then(function () {
sync.cancel();
return waitForUptodate();
}).then(function () {
return local.get('1').then(function (doc) {
doc.foo = Math.random();
return local.put(doc);
});
}).then(function () {
return remote.get('1').then(function (doc) {
doc.foo = Math.random();
return remote.put(doc);
});
}).then(function () {
sync = local.sync(remote, { live: true });
return waitForUptodate();
}).then(function () {
return local.get('1', {conflicts: true}).then(function (doc) {
return local.remove(doc._id, doc._conflicts[0]);
});
}).then(function () {
return waitForConflictsResolved();
}).then(function () {
return local.get('1', {conflicts: true, revs: true});
}).then(function (localDoc) {
return remote.get('1', {
conflicts: true,
revs: true
}).then(function (remoteDoc) {
remoteDoc.should.deep.equal(localDoc);
});
}).then(function () {
return cleanup();
}, function (err) {
return cleanup().then(function () {
throw err;
});
});
});
it('#3179 conflicts synced, live repl', function () {
var local = new PouchDB(dbs.name);
var remote = new PouchDB(dbs.remote);
var repl1 = local.replicate.to(remote, { live: true });
var repl2 = local.replicate.from(remote, { live: true });
function waitForConflictsResolved() {
return new testUtils.Promise(function (resolve) {
var changes = remote.changes({
live: true,
include_docs: true,
conflicts: true
}).on('change', function (change) {
if (!('_conflicts' in change.doc)) {
changes.cancel();
}
});
changes.on('complete', resolve);
});
}
function waitForUptodate() {
function defaultToEmpty(promise) {
return promise.catch(function (err) {
if (err.status !== 404) {
throw err;
}
return {_revisions: []};
});
}
return defaultToEmpty(local.get('1', {
revs: true,
conflicts: true
})).then(function (localDoc) {
return defaultToEmpty(remote.get('1', {
revs: true,
conflicts: true
})).then(function (remoteDoc) {
var revsEqual = JSON.stringify(localDoc._revisions) ===
JSON.stringify(remoteDoc._revisions);
var conflictsEqual = JSON.stringify(localDoc._conflicts || []) ===
JSON.stringify(remoteDoc._conflicts || []);
if (!revsEqual || !conflictsEqual) {
// we can get caught in an infinite loop here when using adapters based
// on microtasks, e.g. memdown, so use setTimeout() to get a macrotask
return new testUtils.Promise(function (resolve) {
setTimeout(resolve, 0);
}).then(waitForUptodate);
}
});
});
}
function cleanup() {
return new testUtils.Promise(function (resolve, reject) {
var numDone = 0;
function checkDone() {
if (++numDone === 2) {
resolve();
}
}
repl1.on('complete', checkDone);
repl2.on('complete', checkDone);
repl1.on('error', reject);
repl2.on('error', reject);
repl1.cancel();
repl2.cancel();
repl1 = null;
repl2 = null;
});
}
return local.put({ _id: '1'}).then(function () {
return waitForUptodate();
}).then(function () {
repl1.cancel();
repl2.cancel();
return waitForUptodate();
}).then(function () {
return local.get('1').then(function (doc) {
doc.foo = Math.random();
return local.put(doc);
});
}).then(function () {
return remote.get('1').then(function (doc) {
doc.foo = Math.random();
return remote.put(doc);
});
}).then(function () {
repl1 = local.replicate.to(remote, { live: true });
repl2 = local.replicate.from(remote, { live: true });
return waitForUptodate();
}).then(function () {
return local.get('1', {conflicts: true}).then(function (doc) {
return local.remove(doc._id, doc._conflicts[0]);
});
}).then(function () {
return waitForConflictsResolved();
}).then(function () {
return local.get('1', {conflicts: true, revs: true});
}).then(function (localDoc) {
return remote.get('1', {
conflicts: true,
revs: true
}).then(function (remoteDoc) {
remoteDoc.should.deep.equal(localDoc);
});
}).then(function () {
return cleanup();
}, function (err) {
return cleanup().then(function () {
throw err;
});
});
});
});
});