2511 lines
73 KiB
JavaScript
2511 lines
73 KiB
JavaScript
|
|
'use strict';
|
|
|
|
var PouchDB = require('./pouchdb');
|
|
var should = require('chai').should();
|
|
var testUtils = require('./test.utils.js');
|
|
var adapters = ['local'];
|
|
|
|
adapters.forEach(function (adapter) {
|
|
|
|
describe('test.changes.js-' + adapter, function () {
|
|
|
|
var dbs = {};
|
|
|
|
// if it exists, return the single element
|
|
// which has the specific id. Else retun null.
|
|
// useful for finding elements within a _changes feed
|
|
function findById(array, id) {
|
|
var result = array.filter(function (i) {
|
|
return i.id === id;
|
|
});
|
|
|
|
//
|
|
if (result.length === 1) {
|
|
return result[0];
|
|
}
|
|
}
|
|
|
|
beforeEach(function (done) {
|
|
dbs.name = testUtils.adapterUrl(adapter, 'testdb');
|
|
dbs.remote = testUtils.adapterUrl(adapter, 'test_repl_remote');
|
|
testUtils.cleanup([dbs.name, dbs.remote], done);
|
|
});
|
|
|
|
after(function (done) {
|
|
testUtils.cleanup([dbs.name, dbs.remote], done);
|
|
});
|
|
|
|
it('All changes', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.post({ test: 'somestuff' }, function () {
|
|
var promise = db.changes({
|
|
}).on('change', function (change) {
|
|
change.should.not.have.property('doc');
|
|
change.should.have.property('seq');
|
|
done();
|
|
});
|
|
should.exist(promise);
|
|
promise.cancel.should.be.a('function');
|
|
});
|
|
});
|
|
|
|
it('Promise resolved when changes cancelled', function (done) {
|
|
var docs = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3},
|
|
{_id: '4', integer: 4},
|
|
{_id: '5', integer: 5},
|
|
{_id: '6', integer: 6},
|
|
{_id: '7', integer: 7},
|
|
{_id: '8', integer: 9},
|
|
{_id: '9', integer: 9},
|
|
{_id: '10', integer: 10}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs }, function () {
|
|
var changeCount = 0;
|
|
var promise = db.changes().on('change', function handler() {
|
|
changeCount++;
|
|
if (changeCount === 5) {
|
|
promise.cancel();
|
|
promise.removeListener('change', handler);
|
|
}
|
|
});
|
|
should.exist(promise);
|
|
should.exist(promise.then);
|
|
promise.then.should.be.a('function');
|
|
promise.then(
|
|
function (result) {
|
|
changeCount.should.equal(5, 'changeCount');
|
|
should.exist(result);
|
|
result.should.deep.equal({status: 'cancelled'});
|
|
done();
|
|
}, function (err) {
|
|
changeCount.should.equal(5, 'changeCount');
|
|
should.exist(err);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes Since', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3},
|
|
{_id: '4', integer: 4},
|
|
{_id: '5', integer: 5},
|
|
{_id: '6', integer: 6},
|
|
{_id: '7', integer: 7},
|
|
{_id: '8', integer: 9},
|
|
{_id: '9', integer: 9},
|
|
{_id: '10', integer: 10},
|
|
{_id: '11', integer: 11}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
db.info(function (err, info) {
|
|
var update_seq = info.update_seq;
|
|
|
|
var docs2 = [
|
|
{_id: '12', integer: 12},
|
|
{_id: '13', integer: 13}
|
|
];
|
|
|
|
db.bulkDocs({ docs: docs2 }, function () {
|
|
var promise = db.changes({
|
|
since: update_seq
|
|
}).on('complete', function (results) {
|
|
results.results.length.should.be.at.least(2);
|
|
done();
|
|
});
|
|
should.exist(promise);
|
|
promise.cancel.should.be.a('function');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes Since and limit limit 1', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
db.info(function (err, info) {
|
|
var update_seq = info.update_seq;
|
|
|
|
var docs2 = [
|
|
{_id: '3', integer: 3},
|
|
{_id: '4', integer: 4}
|
|
];
|
|
|
|
db.bulkDocs({ docs: docs2 }, function () {
|
|
db.changes({
|
|
since: update_seq,
|
|
limit: 1
|
|
}).on('complete', function (results) {
|
|
results.results.length.should.equal(1);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes Since and limit limit 0', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
db.info(function (err, info) {
|
|
var update_seq = info.update_seq;
|
|
|
|
var docs2 = [
|
|
{_id: '3', integer: 3},
|
|
{_id: '4', integer: 4}
|
|
];
|
|
|
|
db.bulkDocs({ docs: docs2 }, function () {
|
|
db.changes({
|
|
since: update_seq,
|
|
limit: 0
|
|
}).on('complete', function (results) {
|
|
results.results.length.should.equal(1);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes limit', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3}
|
|
];
|
|
var docs2 = [
|
|
{_id: '2', integer: 11},
|
|
{_id: '3', integer: 12}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
// we use writeDocs since bulkDocs looks to have undefined
|
|
// order of doing insertions
|
|
testUtils.writeDocs(db, docs1, function (err, info) {
|
|
docs2[0]._rev = info[2].rev;
|
|
docs2[1]._rev = info[3].rev;
|
|
|
|
db.info(function (err, info) {
|
|
var update_seq = info.update_seq;
|
|
|
|
db.put(docs2[0], function (err, info) {
|
|
docs2[0]._rev = info.rev;
|
|
db.put(docs2[1], function (err, info) {
|
|
docs2[1]._rev = info.rev;
|
|
db.changes({
|
|
limit: 2,
|
|
since: update_seq,
|
|
include_docs: true
|
|
}).on('complete', function (results) {
|
|
results = results.results;
|
|
results.length.should.equal(2);
|
|
|
|
// order is not guaranteed
|
|
var first = results[0];
|
|
var second = results[1];
|
|
if (first.id === '3') {
|
|
second = first;
|
|
first = results[1];
|
|
}
|
|
first.id.should.equal('2');
|
|
first.doc.integer.should.equal(docs2[0].integer);
|
|
first.doc._rev.should.equal(docs2[0]._rev);
|
|
second.id.should.equal('3');
|
|
second.doc.integer.should.equal(docs2[1].integer);
|
|
second.doc._rev.should.equal(docs2[1]._rev);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes with filter not present in ddoc', function (done) {
|
|
var docs = [
|
|
{_id: '1', integer: 1},
|
|
{ _id: '_design/foo',
|
|
integer: 4,
|
|
filters: { even: 'function (doc) { return doc.integer % 2 === 1; }' }
|
|
}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
testUtils.writeDocs(db, docs, function () {
|
|
db.changes({
|
|
filter: 'foo/odd',
|
|
limit: 2,
|
|
include_docs: true
|
|
}).on('error', function (err) {
|
|
err.status.should.equal(PouchDB.Errors.MISSING_DOC.status,
|
|
'correct error status returned');
|
|
err.message.should.equal(PouchDB.Errors.MISSING_DOC.message,
|
|
'correct error message returned');
|
|
// todo: does not work in pouchdb-server.
|
|
// err.reason.should.equal('missing json key: odd');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes with `filters` key not present in ddoc', function (done) {
|
|
var docs = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{
|
|
_id: '_design/foo',
|
|
integer: 4,
|
|
views: {
|
|
even: {
|
|
map: 'function (doc) { if (doc.integer % 2 === 1)' +
|
|
' { emit(doc._id, null) }; }'
|
|
}
|
|
}
|
|
}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
testUtils.writeDocs(db, docs, function () {
|
|
db.changes({
|
|
filter: 'foo/even',
|
|
limit: 2,
|
|
include_docs: true
|
|
}).on('error', function (err) {
|
|
err.status.should.equal(PouchDB.Errors.MISSING_DOC.status,
|
|
'correct error status returned');
|
|
err.message.should.equal(PouchDB.Errors.MISSING_DOC.message,
|
|
'correct error message returned');
|
|
// todo: does not work in pouchdb-server.
|
|
// err.reason.should.equal('missing json key: filters');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes limit and filter', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
var docs2 = [
|
|
{_id: '3', integer: 3},
|
|
{_id: '4', integer: 4},
|
|
{_id: '5', integer: 5},
|
|
{
|
|
_id: '_design/foo',
|
|
integer: 4,
|
|
filters: { even: 'function (doc) { return doc.integer % 2 === 1; }' }
|
|
}
|
|
];
|
|
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
db.info(function (err, info) {
|
|
var update_seq = info.update_seq;
|
|
|
|
testUtils.writeDocs(db, docs2, function () {
|
|
var promise = db.changes({
|
|
filter: 'foo/even',
|
|
limit: 2,
|
|
since: update_seq,
|
|
include_docs: true
|
|
}).on('complete', function (results) {
|
|
results.results.length.should.equal(2);
|
|
var three = findById(results.results, '3');
|
|
three.doc.integer.should.equal(3);
|
|
var five = findById(results.results, '5');
|
|
five.doc.integer.should.equal(5);
|
|
done();
|
|
}).on('error', done);
|
|
should.exist(promise);
|
|
promise.cancel.should.be.a('function');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes with shorthand function name', function (done) {
|
|
var docs = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{
|
|
_id: '_design/even',
|
|
integer: 3,
|
|
filters: { even: 'function (doc) { return doc.integer % 2 === 0; }' }
|
|
}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
db.bulkDocs({ docs: docs }, function () {
|
|
var promise = db.changes({
|
|
filter: 'even',
|
|
include_docs: true
|
|
}).on('complete', function (results) {
|
|
results.results.length.should.equal(2);
|
|
var zero = findById(results.results, '0');
|
|
zero.doc.integer.should.equal(0);
|
|
var two = findById(results.results, '2');
|
|
two.doc.integer.should.equal(2);
|
|
done();
|
|
}).on('error', done);
|
|
should.exist(promise);
|
|
promise.cancel.should.be.a('function');
|
|
});
|
|
});
|
|
|
|
it('Changes with filter from nonexistent ddoc', function (done) {
|
|
var docs = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
testUtils.writeDocs(db, docs, function () {
|
|
db.changes({
|
|
filter: 'foobar/odd'
|
|
}).on('error', function (err) {
|
|
should.exist(err);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes with view not present in ddoc', function (done) {
|
|
var docs = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{
|
|
_id: '_design/foo',
|
|
integer: 4,
|
|
views:
|
|
{ even:
|
|
{ map: 'function (doc) { if (doc.integer % 2 === 1) { ' +
|
|
'emit(doc._id, null) }; }' } }
|
|
}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
testUtils.writeDocs(db, docs, function () {
|
|
db.changes({
|
|
filter: '_view',
|
|
view: 'foo/odd'
|
|
}).on('error', function (err) {
|
|
err.status.should.equal(PouchDB.Errors.MISSING_DOC.status,
|
|
'correct error status returned');
|
|
err.message.should.equal(PouchDB.Errors.MISSING_DOC.message,
|
|
'correct error message returned');
|
|
// todo: does not work in pouchdb-server.
|
|
// err.reason.should.equal('missing json key: odd');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes with `views` key not present in ddoc', function (done) {
|
|
var docs = [
|
|
{_id: '1', integer: 1},
|
|
{
|
|
_id: '_design/foo',
|
|
integer: 4,
|
|
filters: { even: 'function (doc) { return doc.integer % 2 === 1; }' }
|
|
}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
testUtils.writeDocs(db, docs, function () {
|
|
db.changes({
|
|
filter: '_view',
|
|
view: 'foo/even'
|
|
}).on('error', function (err) {
|
|
err.status.should.equal(PouchDB.Errors.MISSING_DOC.status,
|
|
'correct error status returned');
|
|
err.message.should.equal(PouchDB.Errors.MISSING_DOC.message,
|
|
'correct error message returned');
|
|
// todo: does not work in pouchdb-server.
|
|
// err.reason.should.equal('missing json key: views',
|
|
// 'correct error reason returned');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('#4451 Changes with invalid view filter', function (done) {
|
|
var docs = [
|
|
{_id: '1', integer: 1},
|
|
{
|
|
_id: '_design/foo',
|
|
filters: { even: 'function (doc) { return doc.integer % 2 === 1; }' }
|
|
}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs(docs).then(function() {
|
|
db.changes({filter: 'a/b/c'}).on('error', function () {
|
|
done('should not be called');
|
|
}).on('complete', function() {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('3356 throw inside a filter', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.put({
|
|
_id: "_design/test",
|
|
filters: {
|
|
test: function () {
|
|
throw new Error(); // syntaxerrors can't be caught either.
|
|
}.toString()
|
|
}
|
|
}).then(function() {
|
|
db.changes({filter: 'test/test'}).then(function() {
|
|
done('should have thrown');
|
|
}).catch(function () {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes with missing param `view` in request', function (done) {
|
|
var docs = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{
|
|
_id: '_design/foo',
|
|
integer: 4,
|
|
views: { even: { map: 'function (doc) { if (doc.integer % 2 === 1) ' +
|
|
'{ emit(doc._id, null) }; }' } }
|
|
}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
testUtils.writeDocs(db, docs, function () {
|
|
db.changes({
|
|
filter: '_view'
|
|
}).on('error', function (err) {
|
|
err.status.should.equal(PouchDB.Errors.BAD_REQUEST.status,
|
|
'correct error status returned');
|
|
err.message.should.equal(PouchDB.Errors.BAD_REQUEST.message,
|
|
'correct error message returned');
|
|
// todo: does not work in pouchdb-server.
|
|
// err.reason.should
|
|
// .equal('`view` filter parameter is not provided.',
|
|
// 'correct error reason returned');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes limit and view instead of filter', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
db.info(function (err, info) {
|
|
var update_seq = info.update_seq;
|
|
|
|
var docs2 = [
|
|
{_id: '3', integer: 3},
|
|
{_id: '4', integer: 4},
|
|
{_id: '5', integer: 5},
|
|
{
|
|
_id: '_design/foo',
|
|
integer: 4,
|
|
views: { even: { map: 'function (doc) ' +
|
|
'{ if (doc.integer % 2 === 1) ' +
|
|
'{ emit(doc._id, null) }; }'
|
|
}
|
|
}
|
|
}
|
|
];
|
|
|
|
db.bulkDocs({ docs: docs2 }, function () {
|
|
|
|
db.changes({
|
|
filter: '_view',
|
|
view: 'foo/even',
|
|
limit: 2,
|
|
since: update_seq,
|
|
include_docs: true
|
|
}).on('complete', function (results) {
|
|
var changes = results.results;
|
|
changes.length.should.equal(2);
|
|
|
|
findById(changes, '3')
|
|
.doc.integer.should.equal(3);
|
|
|
|
findById(changes, '5')
|
|
.doc.integer.should.equal(5);
|
|
|
|
done();
|
|
}).on('error', done);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes last_seq', function (done) {
|
|
// this test doesn't really make sense for clustered
|
|
// CouchDB because changes is unordered and last_seq might
|
|
// not equal the last seq in the _changes feed (although it
|
|
// should evaluate to the same thing on the server).
|
|
if (testUtils.isCouchMaster()) {
|
|
return done();
|
|
}
|
|
|
|
var docs = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3},
|
|
{
|
|
_id: '_design/foo',
|
|
integer: 4,
|
|
filters: { even: 'function (doc) { return doc.integer % 2 === 1; }' }
|
|
}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.changes().on('complete', function (results) {
|
|
results.last_seq.should.equal(0);
|
|
db.bulkDocs({ docs: docs }, function () {
|
|
db.changes().on('complete', function (results) {
|
|
results.last_seq.should.equal(5);
|
|
db.changes({
|
|
filter: 'foo/even'
|
|
}).on('complete', function (results) {
|
|
results.last_seq.should.equal(5);
|
|
results.results.length.should.equal(2);
|
|
done();
|
|
}).on('error', done);
|
|
}).on('error', done);
|
|
});
|
|
}).on('error', done);
|
|
});
|
|
|
|
it('Immediately cancel changes', function () {
|
|
// fixes code coverage by ensuring the changes() listener
|
|
// emits 'complete' even if the db's task queue isn't
|
|
// ready yet
|
|
return new PouchDB.utils.Promise(function (resolve, reject) {
|
|
var db = new PouchDB(dbs.name);
|
|
var changes = db.changes({live: true});
|
|
changes.on('error', reject);
|
|
changes.on('complete', resolve);
|
|
changes.cancel();
|
|
});
|
|
});
|
|
|
|
it('Changes with invalid ddoc view name', function () {
|
|
return new PouchDB.utils.Promise(function (resolve, reject) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.post({});
|
|
var changes = db.changes({live: true, filter: '_view', view: ''});
|
|
changes.on('error', resolve);
|
|
changes.on('change', reject);
|
|
});
|
|
});
|
|
|
|
it('Changes with invalid ddoc view name 2', function () {
|
|
return new PouchDB.utils.Promise(function (resolve, reject) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.post({});
|
|
var changes = db.changes({live: true, filter: '_view', view: 'a/b/c'});
|
|
changes.on('error', resolve);
|
|
changes.on('change', reject);
|
|
});
|
|
});
|
|
|
|
if (adapter === 'local') {
|
|
// This test crashes due to an invalid JSON response from CouchDB:
|
|
// https://issues.apache.org/jira/browse/COUCHDB-2765
|
|
// We could remove the "if" check and put a try/catch in our
|
|
// JSON parsing, but since this is a super-rare bug it may not be
|
|
// worth our time. This test does increase code coverage for our
|
|
// own local code, though.
|
|
it('Changes with invalid ddoc with no map function', function () {
|
|
// CouchDB 2.X does not allow saving of invalid design docs,
|
|
// so this test is not valid
|
|
if (testUtils.isCouchMaster()) {
|
|
return PouchDB.utils.Promise.resolve();
|
|
}
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
return db.put({
|
|
_id: '_design/name',
|
|
views: {
|
|
name: {
|
|
empty: 'sad face'
|
|
}
|
|
}
|
|
}).then(function () {
|
|
return new PouchDB.utils.Promise(function (resolve, reject) {
|
|
var changes = db.changes({
|
|
live: true,
|
|
filter: '_view',
|
|
view: 'name/name'
|
|
});
|
|
changes.on('error', resolve);
|
|
changes.on('change', reject);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
it('Changes with invalid ddoc with no filter function', function () {
|
|
// CouchDB 2.X does not allow saving of invalid design docs,
|
|
// so this test is not valid
|
|
if (testUtils.isCouchMaster()) {
|
|
return PouchDB.utils.Promise.resolve();
|
|
}
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
return db.put({
|
|
_id: '_design/name',
|
|
views: {
|
|
name: {
|
|
empty: 'sad face'
|
|
}
|
|
}
|
|
}).then(function () {
|
|
return new PouchDB.utils.Promise(function (resolve, reject) {
|
|
var changes = db.changes({
|
|
live: true,
|
|
filter: 'name/name'
|
|
});
|
|
changes.on('error', resolve);
|
|
changes.on('change', reject);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes last_seq with view instead of filter', function (done) {
|
|
// this test doesn't really make sense for clustered
|
|
// CouchDB because changes is unordered and last_seq might
|
|
// not equal the last seq in the _changes feed (although it
|
|
// should evaluate to the same thing on the server).
|
|
if (testUtils.isCouchMaster()) {
|
|
return done();
|
|
}
|
|
|
|
var docs = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3},
|
|
{
|
|
_id: '_design/foo',
|
|
integer: 4,
|
|
views:
|
|
{ even:
|
|
{ map: 'function (doc) { if (doc.integer % 2 === 1) { ' +
|
|
'emit(doc._id, null) }; }' } }
|
|
}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.changes().on('complete', function (results) {
|
|
results.last_seq.should.equal(0);
|
|
db.bulkDocs({ docs: docs }, function () {
|
|
db.changes().on('complete', function (results) {
|
|
results.last_seq.should.equal(5);
|
|
db.changes({
|
|
filter: '_view',
|
|
view: 'foo/even'
|
|
}).on('complete', function (results) {
|
|
results.last_seq.should.equal(5);
|
|
results.results.length.should.equal(2);
|
|
done();
|
|
}).on('error', done);
|
|
}).on('error', done);
|
|
});
|
|
}).on('error', done);
|
|
});
|
|
|
|
it('Changes with style = all_docs', function (done) {
|
|
var simpleTree = [
|
|
[{_id: 'foo', _rev: '1-a', value: 'foo a'},
|
|
{_id: 'foo', _rev: '2-b', value: 'foo b'},
|
|
{_id: 'foo', _rev: '3-c', value: 'foo c'}],
|
|
[{_id: 'foo', _rev: '1-a', value: 'foo a'},
|
|
{_id: 'foo', _rev: '2-d', value: 'foo d'},
|
|
{_id: 'foo', _rev: '3-e', value: 'foo e'},
|
|
{_id: 'foo', _rev: '4-f', value: 'foo f'}],
|
|
[{_id: 'foo', _rev: '1-a', value: 'foo a'},
|
|
{_id: 'foo', _rev: '2-g', value: 'foo g', _deleted: true}]
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
testUtils.putTree(db, simpleTree, function () {
|
|
db.changes().on('complete', function (res) {
|
|
res.results[0].changes.length.should.equal(1);
|
|
res.results[0].changes[0].rev.should.equal('4-f');
|
|
db.changes({
|
|
style: 'all_docs'
|
|
}).on('complete', function (res) {
|
|
res.results[0].changes.length.should.equal(3);
|
|
var changes = res.results[0].changes;
|
|
changes.sort(function (a, b) {
|
|
return a.rev < b.rev;
|
|
});
|
|
changes[0].rev.should.equal('4-f');
|
|
changes[1].rev.should.equal('3-c');
|
|
changes[2].rev.should.equal('2-g');
|
|
done();
|
|
}).on('error', done);
|
|
}).on('error', done);
|
|
});
|
|
});
|
|
|
|
it('Changes with style = all_docs and a callback for complete',
|
|
function (done) {
|
|
var simpleTree = [
|
|
[{_id: 'foo', _rev: '1-a', value: 'foo a'},
|
|
{_id: 'foo', _rev: '2-b', value: 'foo b'},
|
|
{_id: 'foo', _rev: '3-c', value: 'foo c'}],
|
|
[{_id: 'foo', _rev: '1-a', value: 'foo a'},
|
|
{_id: 'foo', _rev: '2-d', value: 'foo d'},
|
|
{_id: 'foo', _rev: '3-e', value: 'foo e'},
|
|
{_id: 'foo', _rev: '4-f', value: 'foo f'}],
|
|
[{_id: 'foo', _rev: '1-a', value: 'foo a'},
|
|
{_id: 'foo', _rev: '2-g', value: 'foo g', _deleted: true}]
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
testUtils.putTree(db, simpleTree, function () {
|
|
db.changes(function (err, res) {
|
|
res.results[0].changes.length.should.equal(1);
|
|
res.results[0].changes[0].rev.should.equal('4-f');
|
|
db.changes({
|
|
style: 'all_docs'
|
|
}, function (err, res) {
|
|
should.not.exist(err);
|
|
res.results[0].changes.length.should.equal(3);
|
|
var changes = res.results[0].changes;
|
|
changes.sort(function (a, b) {
|
|
return a.rev < b.rev;
|
|
});
|
|
changes[0].rev.should.equal('4-f');
|
|
changes[1].rev.should.equal('3-c');
|
|
changes[2].rev.should.equal('2-g');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes limit = 0', function (done) {
|
|
var docs = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs }, function () {
|
|
db.changes({
|
|
limit: 0
|
|
}).on('complete', function (results) {
|
|
results.results.length.should.equal(1);
|
|
done();
|
|
}).on('error', done);
|
|
});
|
|
});
|
|
|
|
// Note for the following test that CouchDB's implementation of /_changes
|
|
// with `descending=true` ignores any `since` parameter.
|
|
it('Descending changes', function (done) {
|
|
// _changes in CouchDB 2.0 does not guarantee order
|
|
// so skip this test
|
|
if (testUtils.isCouchMaster()) {
|
|
return done();
|
|
}
|
|
var db = new PouchDB(dbs.name);
|
|
db.post({_id: '0', test: 'ing'}, function () {
|
|
db.post({_id: '1', test: 'ing'}, function () {
|
|
db.post({_id: '2', test: 'ing'}, function () {
|
|
db.changes({
|
|
descending: true,
|
|
since: 1
|
|
}).on('complete', function (results) {
|
|
results.results.length.should.equal(3);
|
|
var ids = ['2', '1', '0'];
|
|
results.results.forEach(function (row, i) {
|
|
row.id.should.equal(ids[i]);
|
|
});
|
|
done();
|
|
}).on('error', done);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes doc', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.post({ test: 'somestuff' }, function () {
|
|
db.changes({
|
|
include_docs: true
|
|
}).on('change', function (change) {
|
|
change.doc._id.should.equal(change.id);
|
|
change.doc._rev.should
|
|
.equal(change.changes[change.changes.length - 1].rev);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
// Note for the following test that CouchDB's implementation of /_changes
|
|
// with `descending=true` ignores any `since` parameter.
|
|
it('Descending many changes', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [];
|
|
var num = 100;
|
|
for (var i = 0; i < num; i++) {
|
|
docs.push({
|
|
_id: 'doc_' + i,
|
|
foo: 'bar_' + i
|
|
});
|
|
}
|
|
var changes = 0;
|
|
db.bulkDocs({ docs: docs }, function (err) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
db.changes({
|
|
descending: true
|
|
}).on('change', function () {
|
|
changes++;
|
|
}).on('complete', function () {
|
|
changes.should.equal(num, 'correct number of changes');
|
|
done();
|
|
}).on('error', function (err) {
|
|
done(err);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('live-changes', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var count = 0;
|
|
var changes = db.changes({
|
|
live: true
|
|
}).on('complete', function () {
|
|
count.should.equal(1);
|
|
done();
|
|
}).on('change', function (change) {
|
|
count += 1;
|
|
change.should.not.have.property('doc');
|
|
count.should.equal(1);
|
|
changes.cancel();
|
|
});
|
|
db.post({ test: 'adoc' });
|
|
});
|
|
|
|
it('Multiple watchers', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var count = 0;
|
|
var changes1Complete = false;
|
|
var changes2Complete = false;
|
|
function checkCount() {
|
|
if (changes1Complete && changes2Complete) {
|
|
count.should.equal(2);
|
|
done();
|
|
}
|
|
}
|
|
var changes1 = db.changes({
|
|
live: true
|
|
}).on('complete', function () {
|
|
changes1Complete = true;
|
|
checkCount();
|
|
}).on('change', function () {
|
|
count += 1;
|
|
changes1.cancel();
|
|
changes1 = null;
|
|
}).on('error', done);
|
|
var changes2 = db.changes({
|
|
live: true
|
|
}).on('complete', function () {
|
|
changes2Complete = true;
|
|
checkCount();
|
|
}).on('change', function () {
|
|
count += 1;
|
|
changes2.cancel();
|
|
changes2 = null;
|
|
}).on('error', done);
|
|
db.post({test: 'adoc'});
|
|
});
|
|
|
|
it('Continuous changes doc', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var changes = db.changes({
|
|
live: true,
|
|
include_docs: true
|
|
}).on('complete', function (result) {
|
|
result.status.should.equal('cancelled');
|
|
done();
|
|
}).on('change', function (change) {
|
|
change.should.have.property('doc');
|
|
change.doc.should.have.property('_rev');
|
|
changes.cancel();
|
|
}).on('error', done);
|
|
db.post({ test: 'adoc' });
|
|
});
|
|
|
|
it('Cancel changes', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var count = 0;
|
|
var interval;
|
|
var docPosted = false;
|
|
|
|
// We want to wait for a period of time after the final
|
|
// document was posted to ensure we didnt see another
|
|
// change
|
|
function waitForDocPosted() {
|
|
if (!docPosted) {
|
|
return;
|
|
}
|
|
clearInterval(interval);
|
|
setTimeout(function () {
|
|
count.should.equal(1);
|
|
done();
|
|
}, 200);
|
|
}
|
|
|
|
var changes = db.changes({
|
|
live: true
|
|
}).on('complete', function (result) {
|
|
result.status.should.equal('cancelled');
|
|
// This setTimeout ensures that once we cancel a change we dont recieve
|
|
// subsequent callbacks, so it is needed
|
|
interval = setInterval(waitForDocPosted, 100);
|
|
}).on('change', function () {
|
|
count += 1;
|
|
if (count === 1) {
|
|
changes.cancel();
|
|
db.post({ test: 'another doc' }, function (err) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
docPosted = true;
|
|
});
|
|
}
|
|
});
|
|
db.post({ test: 'adoc' });
|
|
});
|
|
|
|
|
|
it("#3579 changes firing 1 too many times", function () {
|
|
var db = new PouchDB(dbs.name);
|
|
var Promise = PouchDB.utils.Promise;
|
|
return db.bulkDocs([{}, {}, {}]).then(function () {
|
|
var changes = db.changes({
|
|
since: 'now',
|
|
live: true,
|
|
include_docs: true
|
|
});
|
|
return Promise.all([
|
|
new Promise(function (resolve, reject) {
|
|
changes.on('error', reject);
|
|
changes.on('change', function (change) {
|
|
changes.cancel();
|
|
resolve(change);
|
|
});
|
|
}),
|
|
new Promise(function (resolve) {
|
|
setTimeout(resolve, 50);
|
|
}).then(function () {
|
|
return db.put({_id: 'foobar'});
|
|
})
|
|
]);
|
|
}).then(function (result) {
|
|
var change = result[0];
|
|
change.id.should.equal('foobar');
|
|
change.doc._id.should.equal('foobar');
|
|
});
|
|
});
|
|
|
|
it('Kill database while listening to live changes', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
db.changes({live: true})
|
|
.on('error', function () { done(); })
|
|
.on('complete', function () { done(); })
|
|
.on('change', function () { db.destroy().catch(done); });
|
|
|
|
db.post({ test: 'adoc' });
|
|
});
|
|
|
|
it('#3136 style=all_docs', function () {
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
var chain = PouchDB.utils.Promise.resolve();
|
|
|
|
var docIds = ['b', 'c', 'a', 'z', 'd', 'e'];
|
|
|
|
docIds.forEach(function (docId) {
|
|
chain = chain.then(function () {
|
|
return db.put({_id: docId});
|
|
});
|
|
});
|
|
|
|
return chain.then(function () {
|
|
return db.changes({style: 'all_docs'});
|
|
}).then(function (res) {
|
|
var ids = res.results.map(function (x) {
|
|
return x.id;
|
|
});
|
|
ids.should.include.members(docIds);
|
|
});
|
|
});
|
|
|
|
it('#4191 revs_diff causes endless loop', function () {
|
|
var db = new PouchDB(dbs.name, {auto_compaction: false});
|
|
return db.bulkDocs({
|
|
"new_edits": false,
|
|
"docs": [{"_id": "799","_rev":"1-d22"}
|
|
]}).then(function () {
|
|
return db.bulkDocs({
|
|
"new_edits": false,
|
|
"docs": [{"_id": "3E1", "_rev": "1-ab5"}]
|
|
});
|
|
}).then(function () {
|
|
return db.bulkDocs(
|
|
{ new_edits: false,
|
|
docs:
|
|
[ { _id: 'FB3', _rev: '1-363' },
|
|
{ _id: '27C', _rev: '1-4c3' },
|
|
{ _id: 'BD6', _rev: '1-de0' },
|
|
{ _id: '1E9', _rev: '1-451' } ] }
|
|
);
|
|
}).then(function () {
|
|
return db.changes({style: 'all_docs', limit: 100});
|
|
}).then(function (res) {
|
|
var lastSeq = res.last_seq;
|
|
return db.changes({since: lastSeq, style: 'all_docs', limit: 100});
|
|
}).then(function (res) {
|
|
res.results.should.have.length(0);
|
|
});
|
|
});
|
|
|
|
it('#3136 style=all_docs & include_docs', function () {
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
var chain = PouchDB.utils.Promise.resolve();
|
|
|
|
var docIds = ['b', 'c', 'a', 'z', 'd', 'e'];
|
|
|
|
docIds.forEach(function (docId) {
|
|
chain = chain.then(function () {
|
|
return db.put({_id: docId});
|
|
});
|
|
});
|
|
|
|
return chain.then(function () {
|
|
return db.changes({
|
|
style: 'all_docs',
|
|
include_docs: true
|
|
});
|
|
}).then(function (res) {
|
|
var ids = res.results.map(function (x) {
|
|
return x.id;
|
|
});
|
|
ids.should.include.members(docIds);
|
|
});
|
|
});
|
|
|
|
it('#3136 tricky changes, limit/descending', function () {
|
|
if (testUtils.isCouchMaster()) {
|
|
return true;
|
|
}
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
var docs = [
|
|
{
|
|
_id: 'alpha',
|
|
_rev: '1-a',
|
|
_revisions: {
|
|
start: 1,
|
|
ids: ['a']
|
|
}
|
|
}, {
|
|
_id: 'beta',
|
|
_rev: '1-b',
|
|
_revisions: {
|
|
start: 1,
|
|
ids: ['b']
|
|
}
|
|
}, {
|
|
_id: 'gamma',
|
|
_rev: '1-b',
|
|
_revisions: {
|
|
start: 1,
|
|
ids: ['b']
|
|
}
|
|
}, {
|
|
_id: 'alpha',
|
|
_rev: '2-d',
|
|
_revisions: {
|
|
start: 2,
|
|
ids: ['d', 'a']
|
|
}
|
|
}, {
|
|
_id: 'beta',
|
|
_rev: '2-e',
|
|
_revisions: {
|
|
start: 2,
|
|
ids: ['e', 'b']
|
|
}
|
|
}, {
|
|
_id: 'beta',
|
|
_rev: '3-f',
|
|
_deleted: true,
|
|
_revisions: {
|
|
start: 3,
|
|
ids: ['f', 'e', 'b']
|
|
}
|
|
}
|
|
];
|
|
|
|
var chain = PouchDB.utils.Promise.resolve();
|
|
var seqs = [];
|
|
|
|
docs.forEach(function (doc) {
|
|
chain = chain.then(function () {
|
|
return db.bulkDocs([doc], {new_edits: false}).then(function () {
|
|
return db.changes({doc_ids: [doc._id]});
|
|
}).then(function (res) {
|
|
seqs.push(res.results[0].seq);
|
|
});
|
|
});
|
|
});
|
|
|
|
function normalizeResult(result) {
|
|
// order of changes doesn't matter
|
|
result.results.forEach(function (ch) {
|
|
ch.changes = ch.changes.sort(function (a, b) {
|
|
return a.rev < b.rev ? -1 : 1;
|
|
});
|
|
});
|
|
}
|
|
|
|
return chain.then(function () {
|
|
return db.changes();
|
|
}).then(function (result) {
|
|
normalizeResult(result);
|
|
result.should.deep.equal({
|
|
"results": [
|
|
{
|
|
"seq": seqs[2],
|
|
"id": "gamma",
|
|
"changes": [{ "rev": "1-b"}
|
|
]
|
|
},
|
|
{
|
|
"seq": seqs[3],
|
|
"id": "alpha",
|
|
"changes": [{ "rev": "2-d"}
|
|
]
|
|
},
|
|
{
|
|
"seq": seqs[5],
|
|
"id": "beta",
|
|
"deleted": true,
|
|
"changes": [{ "rev": "3-f"}
|
|
]
|
|
}
|
|
],
|
|
"last_seq": seqs[5]
|
|
});
|
|
return db.changes({limit: 0});
|
|
}).then(function (result) {
|
|
normalizeResult(result);
|
|
result.should.deep.equal({
|
|
"results": [{
|
|
"seq": seqs[2],
|
|
"id": "gamma",
|
|
"changes": [{"rev": "1-b"}]
|
|
}],
|
|
"last_seq": seqs[2]
|
|
}, '1:' + JSON.stringify(result));
|
|
return db.changes({limit: 1});
|
|
}).then(function (result) {
|
|
normalizeResult(result);
|
|
result.should.deep.equal({
|
|
"results": [{
|
|
"seq": seqs[2],
|
|
"id": "gamma",
|
|
"changes": [{"rev": "1-b"}]
|
|
}],
|
|
"last_seq": seqs[2]
|
|
}, '2:' + JSON.stringify(result));
|
|
return db.changes({limit: 2});
|
|
}).then(function (result) {
|
|
normalizeResult(result);
|
|
result.should.deep.equal({
|
|
"results": [{
|
|
"seq": seqs[2],
|
|
"id": "gamma",
|
|
"changes": [{"rev": "1-b"}]
|
|
}, {"seq": seqs[3], "id": "alpha", "changes": [{"rev": "2-d"}]}],
|
|
"last_seq": seqs[3]
|
|
}, '3:' + JSON.stringify(result));
|
|
return db.changes({limit: 1, descending: true});
|
|
}).then(function (result) {
|
|
normalizeResult(result);
|
|
result.should.deep.equal({
|
|
"results": [{
|
|
"seq": seqs[5],
|
|
"id": "beta",
|
|
"changes": [{"rev": "3-f"}],
|
|
"deleted": true
|
|
}],
|
|
"last_seq": seqs[5]
|
|
}, '4:' + JSON.stringify(result));
|
|
return db.changes({limit: 2, descending: true});
|
|
}).then(function (result) {
|
|
normalizeResult(result);
|
|
var expected = {
|
|
"results": [{
|
|
"seq": seqs[5],
|
|
"id": "beta",
|
|
"changes": [{"rev": "3-f"}],
|
|
"deleted": true
|
|
}, {"seq": seqs[3], "id": "alpha", "changes": [{"rev": "2-d"}]}],
|
|
"last_seq": seqs[3]
|
|
};
|
|
result.should.deep.equal(expected, '5:' + JSON.stringify(result) +
|
|
', shoulda got: ' + JSON.stringify(expected));
|
|
return db.changes({descending: true});
|
|
}).then(function (result) {
|
|
normalizeResult(result);
|
|
var expected = {
|
|
"results": [{
|
|
"seq": seqs[5],
|
|
"id": "beta",
|
|
"changes": [{"rev": "3-f"}],
|
|
"deleted": true
|
|
}, {"seq": seqs[3], "id": "alpha", "changes": [{"rev": "2-d"}]}, {
|
|
"seq": seqs[2],
|
|
"id": "gamma",
|
|
"changes": [{"rev": "1-b"}]
|
|
}],
|
|
"last_seq": seqs[2]
|
|
};
|
|
result.should.deep.equal(expected, '6:' + JSON.stringify(result) +
|
|
', shoulda got: ' + JSON.stringify(expected));
|
|
});
|
|
});
|
|
|
|
it('#3176 winningRev has a lower seq, descending', function () {
|
|
if (testUtils.isCouchMaster()) {
|
|
return true;
|
|
}
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var tree = [
|
|
[
|
|
{
|
|
_id: 'foo',
|
|
_rev: '1-a',
|
|
_revisions: {start: 1, ids: ['a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '2-e',
|
|
_deleted: true,
|
|
_revisions: {start: 2, ids: ['e', 'a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '3-g',
|
|
_revisions: {start: 3, ids: ['g', 'e', 'a']}
|
|
}
|
|
],
|
|
[
|
|
{
|
|
_id: 'foo',
|
|
_rev: '1-a',
|
|
_revisions: {start: 1, ids: ['a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '2-b',
|
|
_revisions: {start: 2, ids: ['b', 'a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '3-c',
|
|
_revisions: {start: 3, ids: ['c', 'b', 'a']}
|
|
}
|
|
]
|
|
];
|
|
|
|
var chain = PouchDB.utils.Promise.resolve();
|
|
var seqs = [0];
|
|
|
|
function getExpected(i) {
|
|
var expecteds = [
|
|
{
|
|
"results": [
|
|
{
|
|
"seq": seqs[1],
|
|
"id": "foo",
|
|
"changes": [{"rev": "3-g"}]
|
|
}
|
|
],
|
|
"last_seq" : seqs[1]
|
|
},
|
|
{
|
|
"results": [
|
|
{
|
|
"seq": seqs[2],
|
|
"id": "foo",
|
|
"changes": [{"rev": "3-g"}]
|
|
}
|
|
],
|
|
"last_seq" : seqs[2]
|
|
}
|
|
];
|
|
return expecteds[i];
|
|
}
|
|
|
|
function normalizeResult(result) {
|
|
// order of changes doesn't matter
|
|
result.results.forEach(function (ch) {
|
|
ch.changes = ch.changes.sort(function (a, b) {
|
|
return a.rev < b.rev ? -1 : 1;
|
|
});
|
|
});
|
|
}
|
|
|
|
tree.forEach(function (docs, i) {
|
|
chain = chain.then(function () {
|
|
return db.bulkDocs(docs, {new_edits: false}).then(function () {
|
|
return db.changes({
|
|
descending: true
|
|
});
|
|
}).then(function (result) {
|
|
seqs.push(result.last_seq);
|
|
var expected = getExpected(i);
|
|
normalizeResult(result);
|
|
result.should.deep.equal(expected,
|
|
i + ': should get: ' + JSON.stringify(expected) +
|
|
', but got: ' + JSON.stringify(result));
|
|
});
|
|
});
|
|
});
|
|
return chain;
|
|
});
|
|
|
|
it('#3136 winningRev has a lower seq, style=all_docs', function () {
|
|
if (testUtils.isCouchMaster()) {
|
|
return true;
|
|
}
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var tree = [
|
|
[
|
|
{
|
|
_id: 'foo',
|
|
_rev: '1-a',
|
|
_revisions: {start: 1, ids: ['a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '2-e',
|
|
_deleted: true,
|
|
_revisions: {start: 2, ids: ['e', 'a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '3-g',
|
|
_revisions: {start: 3, ids: ['g', 'e', 'a']}
|
|
}
|
|
],
|
|
[
|
|
{
|
|
_id: 'foo',
|
|
_rev: '1-a',
|
|
_revisions: {start: 1, ids: ['a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '2-b',
|
|
_revisions: {start: 2, ids: ['b', 'a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '3-c',
|
|
_revisions: {start: 3, ids: ['c', 'b', 'a']}
|
|
}
|
|
],
|
|
[
|
|
{
|
|
_id: 'foo',
|
|
_rev: '1-a',
|
|
_revisions: {start: 1, ids: ['a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '2-d',
|
|
_revisions: {start: 2, ids: ['d', 'a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '3-h',
|
|
_revisions: {start: 3, ids: ['h', 'd', 'a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '4-f',
|
|
_revisions: {start: 4, ids: ['f', 'h', 'd', 'a']}
|
|
}
|
|
]
|
|
];
|
|
|
|
var chain = PouchDB.utils.Promise.resolve();
|
|
var seqs = [0];
|
|
|
|
function getExpected(i) {
|
|
var expecteds = [
|
|
{
|
|
"results": [
|
|
{
|
|
"seq": seqs[1],
|
|
"id": "foo",
|
|
"changes": [{"rev": "3-g"}],
|
|
"doc": {"_id": "foo", "_rev": "3-g"}
|
|
}
|
|
],
|
|
"last_seq" : seqs[1]
|
|
},
|
|
{
|
|
"results": [
|
|
{
|
|
"seq": seqs[2],
|
|
"id": "foo",
|
|
"changes": [{"rev": "3-c"}, {"rev": "3-g"}],
|
|
"doc": {"_id": "foo", "_rev": "3-g"}
|
|
}
|
|
],
|
|
"last_seq" : seqs[2]
|
|
},
|
|
{
|
|
"results": [
|
|
{
|
|
"seq": seqs[3],
|
|
"id": "foo",
|
|
"changes": [{"rev": "3-c"}, {"rev": "3-g"}, {"rev": "4-f"}],
|
|
"doc": {"_id": "foo", "_rev": "4-f"}
|
|
}
|
|
],
|
|
"last_seq" : seqs[3]
|
|
}
|
|
];
|
|
return expecteds[i];
|
|
}
|
|
|
|
function normalizeResult(result) {
|
|
// order of changes doesn't matter
|
|
result.results.forEach(function (ch) {
|
|
ch.changes = ch.changes.sort(function (a, b) {
|
|
return a.rev < b.rev ? -1 : 1;
|
|
});
|
|
});
|
|
}
|
|
|
|
tree.forEach(function (docs, i) {
|
|
chain = chain.then(function () {
|
|
return db.bulkDocs(docs, {new_edits: false}).then(function () {
|
|
return db.changes({
|
|
style: 'all_docs',
|
|
since: seqs[seqs.length - 1],
|
|
include_docs: true
|
|
});
|
|
}).then(function (result) {
|
|
seqs.push(result.last_seq);
|
|
var expected = getExpected(i);
|
|
normalizeResult(result);
|
|
result.should.deep.equal(expected,
|
|
i + ': should get: ' + JSON.stringify(expected) +
|
|
', but got: ' + JSON.stringify(result));
|
|
});
|
|
});
|
|
});
|
|
return chain;
|
|
});
|
|
|
|
it('#3136 winningRev has a lower seq, style=all_docs 2', function () {
|
|
if (testUtils.isCouchMaster()) {
|
|
return true;
|
|
}
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var tree = [
|
|
[
|
|
{
|
|
_id: 'foo',
|
|
_rev: '1-a',
|
|
_revisions: {start: 1, ids: ['a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '2-e',
|
|
_deleted: true,
|
|
_revisions: {start: 2, ids: ['e', 'a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '3-g',
|
|
_revisions: {start: 3, ids: ['g', 'e', 'a']}
|
|
}
|
|
], [
|
|
{
|
|
_id: 'foo',
|
|
_rev: '1-a',
|
|
_revisions: {start: 1, ids: ['a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '2-b',
|
|
_revisions: {start: 2, ids: ['b', 'a']}
|
|
},
|
|
{
|
|
_id: 'foo',
|
|
_rev: '3-c',
|
|
_revisions: {start: 3, ids: ['c', 'b', 'a']}
|
|
}
|
|
], [
|
|
{
|
|
_id: 'bar',
|
|
_rev: '1-z',
|
|
_revisions: {start: 1, ids: ['z']}
|
|
}
|
|
]
|
|
];
|
|
|
|
var chain = PouchDB.utils.Promise.resolve();
|
|
var seqs = [0];
|
|
|
|
tree.forEach(function (docs) {
|
|
chain = chain.then(function () {
|
|
return db.bulkDocs(docs, {new_edits: false}).then(function () {
|
|
return db.changes();
|
|
}).then(function (result) {
|
|
seqs.push(result.last_seq);
|
|
});
|
|
});
|
|
});
|
|
|
|
return chain.then(function () {
|
|
|
|
var expecteds = [
|
|
{
|
|
"results": [{
|
|
"seq": seqs[2],
|
|
"id": "foo",
|
|
"changes": [{"rev": "3-c"}, {"rev": "3-g"}]
|
|
}, {"seq": seqs[3], "id": "bar", "changes": [{"rev": "1-z"}]}],
|
|
"last_seq": seqs[3]
|
|
},
|
|
{
|
|
"results": [{
|
|
"seq": seqs[2],
|
|
"id": "foo",
|
|
"changes": [{"rev": "3-c"}, {"rev": "3-g"}]
|
|
}, {"seq": seqs[3], "id": "bar", "changes": [{"rev": "1-z"}]}],
|
|
"last_seq": seqs[3]
|
|
},
|
|
{
|
|
"results": [{"seq": seqs[3], "id": "bar",
|
|
"changes": [{"rev": "1-z"}]}],
|
|
"last_seq": seqs[3]
|
|
},
|
|
{"results": [], "last_seq": seqs[3]}
|
|
];
|
|
|
|
var chain2 = PouchDB.utils.Promise.resolve();
|
|
|
|
function normalizeResult(result) {
|
|
// order of changes doesn't matter
|
|
result.results.forEach(function (ch) {
|
|
ch.changes = ch.changes.sort(function (a, b) {
|
|
return a.rev < b.rev ? -1 : 1;
|
|
});
|
|
});
|
|
}
|
|
|
|
seqs.forEach(function (seq, i) {
|
|
chain2 = chain2.then(function () {
|
|
return db.changes({
|
|
since: seq,
|
|
style: 'all_docs'
|
|
}).then(function (res) {
|
|
normalizeResult(res);
|
|
res.should.deep.equal(expecteds[i], 'since=' + seq +
|
|
': got: ' +
|
|
JSON.stringify(res) +
|
|
', shoulda got: ' +
|
|
JSON.stringify(expecteds[i]));
|
|
});
|
|
});
|
|
});
|
|
return chain2;
|
|
});
|
|
});
|
|
|
|
it('#3136 winningRev has a higher seq, using limit', function () {
|
|
if (testUtils.isCouchMaster()) {
|
|
return true;
|
|
}
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var tree = [
|
|
[
|
|
{
|
|
_id: 'foo',
|
|
_rev: '1-a',
|
|
_revisions: {start: 1, ids: ['a']}
|
|
}
|
|
], [
|
|
{
|
|
_id: 'foo',
|
|
_rev: '2-b',
|
|
_revisions: {start: 2, ids: ['b', 'a']}
|
|
}
|
|
], [
|
|
{
|
|
_id: 'bar',
|
|
_rev: '1-x',
|
|
_revisions: {start: 1, ids: ['x']}
|
|
}
|
|
], [
|
|
{
|
|
_id: 'foo',
|
|
_rev: '2-c',
|
|
_deleted: true,
|
|
_revisions: {start: 2, ids: ['c', 'a']}
|
|
}
|
|
]
|
|
];
|
|
|
|
var chain = PouchDB.utils.Promise.resolve();
|
|
var seqs = [0];
|
|
|
|
tree.forEach(function (docs) {
|
|
chain = chain.then(function () {
|
|
return db.bulkDocs(docs, {new_edits: false}).then(function () {
|
|
return db.changes().then(function (result) {
|
|
seqs.push(result.last_seq);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
return chain.then(function () {
|
|
|
|
var expecteds = [{
|
|
"results": [{
|
|
"seq": seqs[3],
|
|
"id": "bar",
|
|
"changes": [{"rev": "1-x"}],
|
|
"doc": {"_id": "bar", "_rev": "1-x"}
|
|
}],
|
|
"last_seq": seqs[3]
|
|
},
|
|
{
|
|
"results": [{
|
|
"seq": seqs[3],
|
|
"id": "bar",
|
|
"changes": [{"rev": "1-x"}],
|
|
"doc": {"_id": "bar", "_rev": "1-x"}
|
|
}],
|
|
"last_seq": seqs[3]
|
|
},
|
|
{
|
|
"results": [{
|
|
"seq": seqs[3],
|
|
"id": "bar",
|
|
"changes": [{"rev": "1-x"}],
|
|
"doc": {"_id": "bar", "_rev": "1-x"}
|
|
}],
|
|
"last_seq": seqs[3]
|
|
},
|
|
{
|
|
"results": [{
|
|
"seq": seqs[4],
|
|
"id": "foo",
|
|
"changes": [{"rev": "2-b"}, {"rev": "2-c"}],
|
|
"doc": {"_id": "foo", "_rev": "2-b"}
|
|
}],
|
|
"last_seq": seqs[4]
|
|
},
|
|
{"results": [], "last_seq": seqs[4]}
|
|
];
|
|
|
|
var chain2 = PouchDB.utils.Promise.resolve();
|
|
|
|
function normalizeResult(result) {
|
|
// order of changes doesn't matter
|
|
result.results.forEach(function (ch) {
|
|
ch.changes = ch.changes.sort(function (a, b) {
|
|
return a.rev < b.rev ? -1 : 1;
|
|
});
|
|
});
|
|
}
|
|
|
|
seqs.forEach(function (seq, i) {
|
|
chain2 = chain2.then(function () {
|
|
return db.changes({
|
|
style: 'all_docs',
|
|
since: seq,
|
|
limit: 1,
|
|
include_docs: true
|
|
});
|
|
}).then(function (result) {
|
|
normalizeResult(result);
|
|
result.should.deep.equal(expecteds[i],
|
|
i + ': got: ' + JSON.stringify(result) +
|
|
', shoulda got: ' + JSON.stringify(expecteds[i]));
|
|
});
|
|
});
|
|
return chain2;
|
|
});
|
|
});
|
|
|
|
it('changes-filter', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3}
|
|
];
|
|
var docs2 = [
|
|
{_id: '4', integer: 4},
|
|
{_id: '5', integer: 5},
|
|
{_id: '6', integer: 6},
|
|
{_id: '7', integer: 7}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
var count = 0;
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
var changes = db.changes({
|
|
filter: function (doc) {
|
|
return doc.integer % 2 === 0;
|
|
},
|
|
live: true
|
|
}).on('complete', function (result) {
|
|
result.status.should.equal('cancelled');
|
|
done();
|
|
}).on('change', function () {
|
|
count += 1;
|
|
if (count === 4) {
|
|
changes.cancel();
|
|
}
|
|
}).on('error', done);
|
|
db.bulkDocs({ docs: docs2 });
|
|
});
|
|
});
|
|
|
|
it('changes-filter with query params', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3}
|
|
];
|
|
var docs2 = [
|
|
{_id: '4', integer: 4},
|
|
{_id: '5', integer: 5},
|
|
{_id: '6', integer: 6},
|
|
{_id: '7', integer: 7}
|
|
];
|
|
var params = { 'abc': true };
|
|
var db = new PouchDB(dbs.name);
|
|
var count = 0;
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
var changes = db.changes({
|
|
filter: function (doc, req) {
|
|
if (req.query.abc) {
|
|
return doc.integer % 2 === 0;
|
|
}
|
|
},
|
|
query_params: params,
|
|
live: true
|
|
}).on('complete', function (result) {
|
|
result.status.should.equal('cancelled');
|
|
done();
|
|
}).on('change', function () {
|
|
count += 1;
|
|
if (count === 4) {
|
|
changes.cancel();
|
|
}
|
|
}).on('error', done);
|
|
db.bulkDocs({ docs: docs2 });
|
|
});
|
|
});
|
|
|
|
it('Non-live changes filter', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
db.changes().on('complete', function (allChanges) {
|
|
db.changes({
|
|
filter: function (doc) {
|
|
return doc.integer % 2 === 0;
|
|
}
|
|
}).on('complete', function (filteredChanges) {
|
|
// Should get docs 0 and 2 if the filter
|
|
// has been applied correctly.
|
|
filteredChanges.results.length.should.equal(2);
|
|
filteredChanges.last_seq.should.deep.equal(allChanges.last_seq);
|
|
done();
|
|
}).on('error', done);
|
|
}).on('error', done);
|
|
});
|
|
});
|
|
|
|
it('Non-live changes filter, descending', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
db.changes({
|
|
descending: true
|
|
}).on('complete', function (allChanges) {
|
|
db.changes({
|
|
descending: true,
|
|
filter: function (doc) {
|
|
return doc.integer > 2;
|
|
}
|
|
}).on('complete', function (filteredChanges) {
|
|
// Should get docs 2 and 3 if the filter
|
|
// has been applied correctly.
|
|
filteredChanges.results.length.should.equal(1);
|
|
filteredChanges.last_seq.should.deep.equal(allChanges.last_seq);
|
|
done();
|
|
}).on('error', done);
|
|
}).on('error', done);
|
|
});
|
|
});
|
|
|
|
it('#2569 Non-live doc_ids filter', function () {
|
|
var docs = [
|
|
{_id: '0'},
|
|
{_id: '1'},
|
|
{_id: '2'},
|
|
{_id: '3'}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
return db.bulkDocs(docs).then(function () {
|
|
return db.changes({
|
|
doc_ids: ['1', '3']
|
|
});
|
|
}).then(function (changes) {
|
|
var ids = changes.results.map(function (x) {
|
|
return x.id;
|
|
});
|
|
ids.sort().should.deep.equal(['1', '3']);
|
|
});
|
|
});
|
|
|
|
it('#2569 Big non-live doc_ids filter', function () {
|
|
var docs = [];
|
|
for (var i = 0; i < 5; i++) {
|
|
var id = '';
|
|
for (var j = 0; j < 50; j++) {
|
|
// make a huge id
|
|
id += PouchDB.utils.btoa(Math.random().toString());
|
|
}
|
|
docs.push({_id: id});
|
|
}
|
|
var db = new PouchDB(dbs.name);
|
|
return db.bulkDocs(docs).then(function () {
|
|
return db.changes({
|
|
doc_ids: [docs[1]._id, docs[3]._id]
|
|
});
|
|
}).then(function (changes) {
|
|
var ids = changes.results.map(function (x) {
|
|
return x.id;
|
|
});
|
|
var expectedIds = [docs[1]._id, docs[3]._id];
|
|
ids.sort().should.deep.equal(expectedIds.sort());
|
|
});
|
|
});
|
|
|
|
it('#2569 Live doc_ids filter', function () {
|
|
var docs = [
|
|
{_id: '0'},
|
|
{_id: '1'},
|
|
{_id: '2'},
|
|
{_id: '3'}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
return db.bulkDocs(docs).then(function () {
|
|
return new PouchDB.utils.Promise(function (resolve, reject) {
|
|
var retChanges = [];
|
|
var changes = db.changes({
|
|
doc_ids: ['1', '3'],
|
|
live: true
|
|
}).on('change', function (change) {
|
|
retChanges.push(change);
|
|
if (retChanges.length === 2) {
|
|
changes.cancel();
|
|
resolve(retChanges);
|
|
}
|
|
}).on('error', reject);
|
|
});
|
|
}).then(function (changes) {
|
|
var ids = changes.map(function (x) {
|
|
return x.id;
|
|
});
|
|
var expectedIds = ['1', '3'];
|
|
ids.sort().should.deep.equal(expectedIds);
|
|
});
|
|
});
|
|
|
|
it('#2569 Big live doc_ids filter', function () {
|
|
var docs = [];
|
|
for (var i = 0; i < 5; i++) {
|
|
var id = '';
|
|
for (var j = 0; j < 50; j++) {
|
|
// make a huge id
|
|
id += PouchDB.utils.btoa(Math.random().toString());
|
|
}
|
|
docs.push({_id: id});
|
|
}
|
|
var db = new PouchDB(dbs.name);
|
|
return db.bulkDocs(docs).then(function () {
|
|
return new PouchDB.utils.Promise(function (resolve, reject) {
|
|
var retChanges = [];
|
|
var changes = db.changes({
|
|
doc_ids: [docs[1]._id, docs[3]._id],
|
|
live: true
|
|
}).on('change', function (change) {
|
|
retChanges.push(change);
|
|
if (retChanges.length === 2) {
|
|
changes.cancel();
|
|
resolve(retChanges);
|
|
}
|
|
}).on('error', reject);
|
|
});
|
|
}).then(function (changes) {
|
|
var ids = changes.map(function (x) {
|
|
return x.id;
|
|
});
|
|
var expectedIds = [docs[1]._id, docs[3]._id];
|
|
ids.sort().should.deep.equal(expectedIds.sort());
|
|
});
|
|
});
|
|
|
|
it('#2569 Non-live doc_ids filter with filter=_doc_ids', function () {
|
|
var docs = [
|
|
{_id: '0'},
|
|
{_id: '1'},
|
|
{_id: '2'},
|
|
{_id: '3'}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
return db.bulkDocs(docs).then(function () {
|
|
return db.changes({
|
|
filter: '_doc_ids',
|
|
doc_ids: ['1', '3']
|
|
});
|
|
}).then(function (changes) {
|
|
var ids = changes.results.map(function (x) {
|
|
return x.id;
|
|
});
|
|
ids.sort().should.deep.equal(['1', '3']);
|
|
});
|
|
});
|
|
|
|
it('#2569 Live doc_ids filter with filter=_doc_ids', function () {
|
|
var docs = [
|
|
{_id: '0'},
|
|
{_id: '1'},
|
|
{_id: '2'},
|
|
{_id: '3'}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
return db.bulkDocs(docs).then(function () {
|
|
return db.changes({
|
|
filter: '_doc_ids',
|
|
doc_ids: ['1', '3']
|
|
});
|
|
}).then(function (changes) {
|
|
var ids = changes.results.map(function (x) {
|
|
return x.id;
|
|
});
|
|
ids.sort().should.deep.equal(['1', '3']);
|
|
});
|
|
});
|
|
|
|
it('Changes to same doc are grouped', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3}
|
|
];
|
|
var docs2 = [
|
|
{_id: '2', integer: 11},
|
|
{_id: '3', integer: 12}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs1 }, function (err, info) {
|
|
docs2[0]._rev = info[2].rev;
|
|
docs2[1]._rev = info[3].rev;
|
|
db.put(docs2[0], function () {
|
|
db.put(docs2[1], function () {
|
|
db.changes({
|
|
include_docs: true
|
|
}).on('complete', function (changes) {
|
|
changes.results.length.should.equal(4);
|
|
|
|
var second = findById(changes.results, '2');
|
|
second.changes.length.should.equal(1);
|
|
second.doc.integer.should.equal(11);
|
|
done();
|
|
}).on('error', done);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Changes with conflicts are handled correctly', function (testDone) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3}
|
|
];
|
|
var docs2 = [
|
|
{_id: '2', integer: 11},
|
|
{_id: '3', integer: 12}
|
|
];
|
|
new PouchDB(dbs.name).then(function (localdb) {
|
|
var remotedb = new PouchDB(dbs.remote);
|
|
return localdb.bulkDocs({ docs: docs1 }).then(function (info) {
|
|
docs2[0]._rev = info[2].rev;
|
|
docs2[1]._rev = info[3].rev;
|
|
return localdb.put(docs2[0]).then(function () {
|
|
return localdb.put(docs2[1]).then(function (info) {
|
|
var rev2 = info.rev;
|
|
return PouchDB.replicate(localdb, remotedb).then(function () {
|
|
// update remote once, local twice, then replicate from
|
|
// remote to local so the remote losing conflict is later in
|
|
// the tree
|
|
return localdb.put({
|
|
_id: '3',
|
|
_rev: rev2,
|
|
integer: 20
|
|
}).then(function (resp) {
|
|
var rev3Doc = {
|
|
_id: '3',
|
|
_rev: resp.rev,
|
|
integer: 30
|
|
};
|
|
return localdb.put(rev3Doc).then(function (resp) {
|
|
var rev4local = resp.rev;
|
|
var rev4Doc = {
|
|
_id: '3',
|
|
_rev: rev2,
|
|
integer: 100
|
|
};
|
|
return remotedb.put(rev4Doc).then(function (resp) {
|
|
var remoterev = resp.rev;
|
|
return PouchDB.replicate(remotedb, localdb).then(
|
|
function () {
|
|
return localdb.changes({
|
|
include_docs: true,
|
|
style: 'all_docs',
|
|
conflicts: true
|
|
}).on('error', testDone)
|
|
.then(function (changes) {
|
|
changes.results.length.should.equal(4);
|
|
var ch = findById(changes.results, '3');
|
|
ch.changes.length.should.equal(2);
|
|
ch.doc.integer.should.equal(30);
|
|
ch.doc._rev.should.equal(rev4local);
|
|
ch.changes.should.deep.equal([
|
|
{ rev: rev4local },
|
|
{ rev: remoterev }
|
|
]);
|
|
|
|
ch.doc.should.have.property('_conflicts');
|
|
ch.doc._conflicts.length.should.equal(1);
|
|
ch.doc._conflicts[0].should.equal(remoterev);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}).then(function () {
|
|
testDone();
|
|
}, testDone);
|
|
});
|
|
});
|
|
|
|
it('Change entry for a deleted doc', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs1 }, function (err, info) {
|
|
var rev = info[3].rev;
|
|
db.remove({
|
|
_id: '3',
|
|
_rev: rev
|
|
}, function () {
|
|
db.changes({
|
|
include_docs: true
|
|
}).on('complete', function (changes) {
|
|
changes.results.length.should.equal(4);
|
|
var ch = findById(changes.results, '3');
|
|
// sequence numbers are not incremental in CouchDB 2.0
|
|
if (!testUtils.isCouchMaster()) {
|
|
ch.seq.should.equal(5);
|
|
}
|
|
ch.deleted.should.equal(true);
|
|
done();
|
|
}).on('error', done);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('changes large number of docs', function (done) {
|
|
var docs = [];
|
|
var num = 30;
|
|
for (var i = 0; i < num; i++) {
|
|
docs.push({
|
|
_id: 'doc_' + i,
|
|
foo: 'bar_' + i
|
|
});
|
|
}
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs }, function () {
|
|
db.changes().on('complete', function (res) {
|
|
res.results.length.should.equal(num);
|
|
done();
|
|
}).on('error', done);
|
|
});
|
|
});
|
|
|
|
it('Calling db.changes({since: \'now\'})', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: [{ foo: 'bar' }] }, function () {
|
|
db.info(function (err, info) {
|
|
var api = db.changes({
|
|
since: 'now'
|
|
}).on('complete', function (res) {
|
|
// last_seq and update_seq might be encoded differently
|
|
// in clustered CouchDB - they cannot be reliably compared.
|
|
if (!testUtils.isCouchMaster()) {
|
|
res.last_seq.should.equal(info.update_seq);
|
|
}
|
|
done();
|
|
}).on('error', done);
|
|
api.should.be.an('object');
|
|
api.cancel.should.be.an('function');
|
|
});
|
|
});
|
|
});
|
|
|
|
//Duplicate to make sure both api options work.
|
|
it('Calling db.changes({since: \'latest\'})', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: [{ foo: 'bar' }] }, function () {
|
|
db.info(function (err, info) {
|
|
var api = db.changes({
|
|
since: 'latest'
|
|
}).on('complete', function (res) {
|
|
// last_seq and update_seq might be encoded differently
|
|
// in clustered CouchDB - they cannot be reliably compared.
|
|
if (!testUtils.isCouchMaster()) {
|
|
res.last_seq.should.equal(info.update_seq);
|
|
}
|
|
done();
|
|
}).on('error', done);
|
|
api.should.be.an('object');
|
|
api.cancel.should.be.an('function');
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Closing db does not cause a crash if changes cancelled',
|
|
function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var called = 0;
|
|
function checkDone() {
|
|
called++;
|
|
if (called === 2) {
|
|
done();
|
|
}
|
|
}
|
|
db.bulkDocs({ docs: [{ foo: 'bar' }] }, function () {
|
|
var changes = db.changes({
|
|
live: true
|
|
}).on('complete', function (result) {
|
|
result.status.should.equal('cancelled');
|
|
checkDone();
|
|
});
|
|
should.exist(changes);
|
|
changes.cancel.should.be.a('function');
|
|
changes.cancel();
|
|
db.close(function (error) {
|
|
should.not.exist(error);
|
|
checkDone();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('fire-complete-on-cancel', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var cancelled = false;
|
|
var changes = db.changes({
|
|
live: true
|
|
}).on('complete', function (result) {
|
|
cancelled.should.equal(true);
|
|
should.exist(result);
|
|
if (result) {
|
|
result.status.should.equal('cancelled');
|
|
}
|
|
done();
|
|
}).on('error', done);
|
|
should.exist(changes);
|
|
changes.cancel.should.be.a('function');
|
|
setTimeout(function () {
|
|
cancelled = true;
|
|
changes.cancel();
|
|
}, 100);
|
|
});
|
|
|
|
it('changes are not duplicated', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var called = 0;
|
|
var changes = db.changes({
|
|
live: true
|
|
}).on('change', function () {
|
|
called++;
|
|
if (called === 1) {
|
|
setTimeout(function () {
|
|
changes.cancel();
|
|
}, 1000);
|
|
}
|
|
}).on('complete', function () {
|
|
called.should.equal(1);
|
|
done();
|
|
});
|
|
db.post({key: 'value'});
|
|
});
|
|
|
|
it('supports return_docs=false', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [];
|
|
var num = 10;
|
|
for (var i = 0; i < num; i++) {
|
|
docs.push({ _id: 'doc_' + i});
|
|
}
|
|
var changes = 0;
|
|
db.bulkDocs({ docs: docs }, function (err) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
db.changes({
|
|
descending: true,
|
|
return_docs: false
|
|
}).on('change', function () {
|
|
changes++;
|
|
}).on('complete', function (results) {
|
|
results.results.should.have.length(0, '0 results returned');
|
|
changes.should.equal(num, 'correct number of changes');
|
|
done();
|
|
}).on('error', function (err) {
|
|
done(err);
|
|
});
|
|
});
|
|
});
|
|
|
|
// TODO: Remove 'returnDocs' in favor of 'return_docs' in a future release
|
|
it('supports returnDocs=false', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [];
|
|
var num = 10;
|
|
for (var i = 0; i < num; i++) {
|
|
docs.push({ _id: 'doc_' + i});
|
|
}
|
|
var changes = 0;
|
|
db.bulkDocs({ docs: docs }, function (err) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
db.changes({
|
|
descending: true,
|
|
returnDocs: false
|
|
}).on('change', function () {
|
|
changes++;
|
|
}).on('complete', function (results) {
|
|
results.results.should.have.length(0, '0 results returned');
|
|
changes.should.equal(num, 'correct number of changes');
|
|
done();
|
|
}).on('error', function (err) {
|
|
done(err);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should respects limit', function (done) {
|
|
var docs1 = [
|
|
{_id: '_local/foo'},
|
|
{_id: 'a', integer: 0},
|
|
{_id: 'b', integer: 1},
|
|
{_id: 'c', integer: 2},
|
|
{_id: 'd', integer: 3}
|
|
];
|
|
var called = 0;
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
db.changes({
|
|
limit: 1
|
|
}).on('change', function () {
|
|
(called++).should.equal(0);
|
|
}).on('complete', function () {
|
|
setTimeout(function () {
|
|
done();
|
|
}, 50);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('doesn\'t throw if opts.complete is undefined', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.put({_id: 'foo'}).then(function () {
|
|
db.changes().on('change', function () {
|
|
done();
|
|
}).on('error', function (err) {
|
|
done(err);
|
|
});
|
|
}, done);
|
|
});
|
|
|
|
it('it handles a bunch of individual changes in live replication',
|
|
function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var len = 80;
|
|
var called = 0;
|
|
var changesDone = false;
|
|
var changesWritten = 0;
|
|
var changes = db.changes({live: true});
|
|
|
|
changes.on('change', function () {
|
|
called++;
|
|
if (called === len) {
|
|
changes.cancel();
|
|
}
|
|
}).on('error', done).on('complete', function () {
|
|
changesDone = true;
|
|
maybeDone();
|
|
});
|
|
|
|
var i = -1;
|
|
|
|
function maybeDone() {
|
|
if (changesDone && changesWritten === len) {
|
|
done();
|
|
}
|
|
}
|
|
|
|
function after() {
|
|
changesWritten++;
|
|
db.listeners('destroyed').should.have.length.lessThan(5);
|
|
maybeDone();
|
|
}
|
|
|
|
while (++i < len) {
|
|
db.post({}).then(after).catch(done);
|
|
}
|
|
|
|
});
|
|
|
|
it('changes-filter without filter', function (done) {
|
|
var docs1 = [
|
|
{_id: '0', integer: 0},
|
|
{_id: '1', integer: 1},
|
|
{_id: '2', integer: 2},
|
|
{_id: '3', integer: 3}
|
|
];
|
|
var docs2 = [
|
|
{_id: '4', integer: 4},
|
|
{_id: '5', integer: 5},
|
|
{_id: '6', integer: 6},
|
|
{_id: '7', integer: 7}
|
|
];
|
|
var db = new PouchDB(dbs.name);
|
|
var count = 0;
|
|
db.bulkDocs({ docs: docs1 }, function () {
|
|
var changes = db.changes({
|
|
live: true
|
|
}).on('complete', function (result) {
|
|
result.status.should.equal('cancelled');
|
|
done();
|
|
}).on('change', function () {
|
|
count += 1;
|
|
if (count === 8) {
|
|
changes.cancel();
|
|
}
|
|
}).on('error', done);
|
|
db.bulkDocs({ docs: docs2 });
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('changes-standalone', function () {
|
|
|
|
it('Changes reports errors', function (done) {
|
|
var db = new PouchDB('http://infiniterequest.com', { skipSetup: true });
|
|
db.changes({
|
|
timeout: 1000
|
|
}).on('error', function (err) {
|
|
should.exist(err);
|
|
done();
|
|
});
|
|
});
|
|
|
|
});
|