1056 lines
31 KiB
JavaScript
1056 lines
31 KiB
JavaScript
'use strict';
|
|
|
|
var PouchDB = require('./pouchdb');
|
|
var should = require('chai').should();
|
|
var testUtils = require('./test.utils.js');
|
|
var adapters = ['local'];
|
|
|
|
function makeDocs(start, end, templateDoc) {
|
|
var templateDocSrc = templateDoc ? JSON.stringify(templateDoc) : '{}';
|
|
if (end === undefined) {
|
|
end = start;
|
|
start = 0;
|
|
}
|
|
var docs = [];
|
|
for (var i = start; i < end; i++) {
|
|
/*jshint evil:true */
|
|
var newDoc = eval('(' + templateDocSrc + ')');
|
|
newDoc._id = i.toString();
|
|
newDoc.integer = i;
|
|
newDoc.string = i.toString();
|
|
docs.push(newDoc);
|
|
}
|
|
return docs;
|
|
}
|
|
|
|
adapters.forEach(function (adapter) {
|
|
describe('test.bulk_docs.js-' + adapter, function () {
|
|
|
|
var dbs = {};
|
|
|
|
beforeEach(function (done) {
|
|
dbs.name = testUtils.adapterUrl(adapter, 'testdb');
|
|
testUtils.cleanup([dbs.name], done);
|
|
});
|
|
|
|
after(function (done) {
|
|
testUtils.cleanup([dbs.name], done);
|
|
});
|
|
|
|
|
|
var authors = [
|
|
{name: 'Dale Harvey', commits: 253},
|
|
{name: 'Mikeal Rogers', commits: 42},
|
|
{name: 'Johannes J. Schmidt', commits: 13},
|
|
{name: 'Randall Leeds', commits: 9}
|
|
];
|
|
|
|
it('Testing bulk docs', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = makeDocs(5);
|
|
db.bulkDocs({ docs: docs }, function (err, results) {
|
|
results.should.have.length(5, 'results length matches');
|
|
for (var i = 0; i < 5; i++) {
|
|
results[i].id.should.equal(docs[i]._id, 'id matches');
|
|
should.exist(results[i].rev, 'rev is set');
|
|
// Update the doc
|
|
docs[i]._rev = results[i].rev;
|
|
docs[i].string = docs[i].string + '.00';
|
|
}
|
|
db.bulkDocs({ docs: docs }, function (err, results) {
|
|
results.should.have.length(5, 'results length matches');
|
|
for (i = 0; i < 5; i++) {
|
|
results[i].id.should.equal(i.toString(), 'id matches again');
|
|
// set the delete flag to delete the docs in the next step
|
|
docs[i]._rev = results[i].rev;
|
|
docs[i]._deleted = true;
|
|
}
|
|
db.put(docs[0], function () {
|
|
db.bulkDocs({ docs: docs }, function (err, results) {
|
|
results[0].name.should.equal(
|
|
'conflict', 'First doc should be in conflict');
|
|
should.not.exist(results[0].rev, 'no rev in conflict');
|
|
for (i = 1; i < 5; i++) {
|
|
results[i].id.should.equal(i.toString());
|
|
should.exist(results[i].rev);
|
|
}
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('No id in bulk docs', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var newdoc = {
|
|
'_id': 'foobar',
|
|
'body': 'baz'
|
|
};
|
|
db.put(newdoc, function (err, doc) {
|
|
should.exist(doc.ok);
|
|
var docs = [
|
|
{
|
|
'_id': newdoc._id,
|
|
'_rev': newdoc._rev,
|
|
'body': 'blam'
|
|
},
|
|
{
|
|
'_id': newdoc._id,
|
|
'_rev': newdoc._rev,
|
|
'_deleted': true
|
|
}
|
|
];
|
|
db.bulkDocs({ docs: docs }, function (err, results) {
|
|
results[0].should.have.property('name', 'conflict');
|
|
results[1].should.have.property('name', 'conflict');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('No _rev and new_edits=false', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [{
|
|
_id: 'foo',
|
|
integer: 1
|
|
}];
|
|
db.bulkDocs({ docs: docs }, { new_edits: false }, function (err) {
|
|
should.exist(err, 'error reported');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Test empty bulkDocs', function () {
|
|
var db = new PouchDB(dbs.name);
|
|
return db.bulkDocs([]);
|
|
});
|
|
|
|
it('Test many bulkDocs', function () {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [];
|
|
for (var i = 0; i < 201; i++) {
|
|
docs.push({_id: i.toString()});
|
|
}
|
|
return db.bulkDocs(docs);
|
|
});
|
|
|
|
it('Test errors on invalid doc id', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [{
|
|
'_id': '_invalid',
|
|
foo: 'bar'
|
|
}];
|
|
db.bulkDocs({ docs: docs }, function (err, info) {
|
|
err.status.should.equal(PouchDB.Errors.RESERVED_ID.status,
|
|
'correct error status returned');
|
|
should.not.exist(info, 'info is empty');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Test two errors on invalid doc id', function (done) {
|
|
var docs = [
|
|
{'_id': '_invalid', foo: 'bar'},
|
|
{'_id': 123, foo: 'bar'}
|
|
];
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: docs }, function (err, info) {
|
|
err.status.should.equal(PouchDB.Errors.RESERVED_ID.status,
|
|
'correct error returned');
|
|
should.not.exist(info, 'info is empty');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('No docs', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ 'doc': [{ 'foo': 'bar' }] }, function (err) {
|
|
err.status.should.equal(PouchDB.Errors.MISSING_BULK_DOCS.status,
|
|
'correct error returned');
|
|
err.message.should.equal(PouchDB.Errors.MISSING_BULK_DOCS.message,
|
|
'correct error message returned');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Jira 911', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [
|
|
{'_id': '0', 'a': 0},
|
|
{'_id': '1', 'a': 1},
|
|
{'_id': '1', 'a': 1},
|
|
{'_id': '3', 'a': 3}
|
|
];
|
|
db.bulkDocs({ docs: docs }, function (err, results) {
|
|
results[1].id.should.equal('1', 'check ordering');
|
|
should.not.exist(results[1].name, 'first id succeded');
|
|
results[2].name.should.equal('conflict', 'second conflicted');
|
|
results.should.have.length(4, 'got right amount of results');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Test multiple bulkdocs', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: authors }, function () {
|
|
db.bulkDocs({ docs: authors }, function () {
|
|
db.allDocs(function (err, result) {
|
|
result.total_rows.should.equal(8, 'correct number of results');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('#2935 new_edits=false correct number', function () {
|
|
var docs = [
|
|
{
|
|
"_id": "EE35E",
|
|
"_rev": "4-70b26",
|
|
"_deleted": true,
|
|
"_revisions": {
|
|
"start": 4,
|
|
"ids": ["70b26", "9f454", "914bf", "7fdf8"]
|
|
}
|
|
}, {
|
|
"_id": "EE35E",
|
|
"_rev": "3-f6d28",
|
|
"_revisions": {"start": 3, "ids": ["f6d28", "914bf", "7fdf8"]}
|
|
}
|
|
];
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
return db.bulkDocs({docs: docs, new_edits: false}).then(function (res) {
|
|
res.should.deep.equal([]);
|
|
return db.allDocs();
|
|
}).then(function (res) {
|
|
res.total_rows.should.equal(1);
|
|
return db.info();
|
|
}).then(function (info) {
|
|
info.doc_count.should.equal(1);
|
|
});
|
|
});
|
|
|
|
it('#2935 new_edits=false correct number 2', function () {
|
|
var docs = [
|
|
{
|
|
"_id": "EE35E",
|
|
"_rev": "3-f6d28",
|
|
"_revisions": {"start": 3, "ids": ["f6d28", "914bf", "7fdf8"]}
|
|
}, {
|
|
"_id": "EE35E",
|
|
"_rev": "4-70b26",
|
|
"_deleted": true,
|
|
"_revisions": {
|
|
"start": 4,
|
|
"ids": ["70b26", "9f454", "914bf", "7fdf8"]
|
|
}
|
|
}
|
|
];
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
return db.bulkDocs({docs: docs, new_edits: false}).then(function (res) {
|
|
res.should.deep.equal([]);
|
|
return db.allDocs();
|
|
}).then(function (res) {
|
|
res.total_rows.should.equal(1);
|
|
return db.info();
|
|
}).then(function (info) {
|
|
info.doc_count.should.equal(1);
|
|
});
|
|
});
|
|
|
|
it('#2935 new_edits=false with single unauthorized', function (done) {
|
|
|
|
testUtils.isCouchDB(function (isCouchDB) {
|
|
if (adapter !== 'http' || !isCouchDB) {
|
|
return done();
|
|
}
|
|
|
|
var ddoc = {
|
|
"_id": "_design/validate",
|
|
"validate_doc_update": function (newDoc) {
|
|
if (newDoc.foo === undefined) {
|
|
throw {unauthorized: 'Document must have a foo.'};
|
|
}
|
|
}.toString()
|
|
};
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
db.put(ddoc).then(function () {
|
|
return db.bulkDocs({
|
|
docs: [
|
|
{
|
|
'_id': 'doc0',
|
|
'_rev': '1-x',
|
|
'foo': 'bar',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
}, {
|
|
'_id': 'doc1',
|
|
'_rev': '1-x',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
}, {
|
|
'_id': 'doc2',
|
|
'_rev': '1-x',
|
|
'foo': 'bar',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
}
|
|
]
|
|
}, {new_edits: false});
|
|
}).then(function (res) {
|
|
res.should.have.length(1);
|
|
should.exist(res[0].error);
|
|
res[0].id.should.equal('doc1');
|
|
}).then(done);
|
|
});
|
|
});
|
|
|
|
it('Deleting _local docs with bulkDocs' , function () {
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
var rev1;
|
|
var rev2;
|
|
var rev3;
|
|
return db.put({_id: '_local/godzilla'}).then(function (info) {
|
|
rev1 = info.rev;
|
|
return db.put({_id: 'mothra'});
|
|
}).then(function (info) {
|
|
rev2 = info.rev;
|
|
return db.put({_id: 'rodan'});
|
|
}).then(function (info) {
|
|
rev3 = info.rev;
|
|
return db.bulkDocs([
|
|
{_id: 'mothra', _rev: rev2, _deleted: true},
|
|
{_id: '_local/godzilla', _rev: rev1, _deleted: true},
|
|
{_id: 'rodan', _rev: rev3, _deleted: true}
|
|
]);
|
|
}).then(function () {
|
|
return db.allDocs();
|
|
}).then(function (info) {
|
|
info.rows.should.have.length(0);
|
|
return db.get('_local/godzilla').then(function () {
|
|
throw new Error('expected 404');
|
|
}, function (err) {
|
|
should.exist(err);
|
|
});
|
|
});
|
|
});
|
|
|
|
if (adapter === 'local') {
|
|
// these tests crash CouchDB with a 500, neat
|
|
// https://issues.apache.org/jira/browse/COUCHDB-2758
|
|
|
|
it('Deleting _local docs with bulkDocs, not found', function () {
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
var rev2;
|
|
var rev3;
|
|
return db.put({_id: 'mothra'}).then(function (info) {
|
|
rev2 = info.rev;
|
|
return db.put({_id: 'rodan'});
|
|
}).then(function (info) {
|
|
rev3 = info.rev;
|
|
return db.bulkDocs([
|
|
{_id: 'mothra', _rev: rev2, _deleted: true},
|
|
{_id: '_local/godzilla', _rev: '1-fake', _deleted: true},
|
|
{_id: 'rodan', _rev: rev3, _deleted: true}
|
|
]);
|
|
}).then(function (res) {
|
|
should.not.exist(res[0].error);
|
|
should.exist(res[1].error);
|
|
should.not.exist(res[2].error);
|
|
});
|
|
});
|
|
|
|
it('Deleting _local docs with bulkDocs, wrong rev', function () {
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
var rev2;
|
|
var rev3;
|
|
return db.put({_id: '_local/godzilla'}).then(function () {
|
|
return db.put({_id: 'mothra'});
|
|
}).then(function (info) {
|
|
rev2 = info.rev;
|
|
return db.put({_id: 'rodan'});
|
|
}).then(function (info) {
|
|
rev3 = info.rev;
|
|
return db.bulkDocs([
|
|
{_id: 'mothra', _rev: rev2, _deleted: true},
|
|
{_id: '_local/godzilla', _rev: '1-fake', _deleted: true},
|
|
{_id: 'rodan', _rev: rev3, _deleted: true}
|
|
]);
|
|
}).then(function (res) {
|
|
should.not.exist(res[0].error);
|
|
should.exist(res[1].error);
|
|
should.not.exist(res[2].error);
|
|
});
|
|
});
|
|
}
|
|
|
|
it('Bulk with new_edits=false', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [{
|
|
'_id': 'foo',
|
|
'_rev': '2-x',
|
|
'_revisions': {
|
|
'start': 2,
|
|
'ids': ['x', 'a']
|
|
}
|
|
}, {
|
|
'_id': 'foo',
|
|
'_rev': '2-y',
|
|
'_revisions': {
|
|
'start': 2,
|
|
'ids': ['y', 'a']
|
|
}
|
|
}];
|
|
db.bulkDocs({docs: docs}, {new_edits: false}, function () {
|
|
db.get('foo', {open_revs: 'all'}, function (err, res) {
|
|
res.sort(function (a, b) {
|
|
return a.ok._rev < b.ok._rev ? -1 :
|
|
a.ok._rev > b.ok._rev ? 1 : 0;
|
|
});
|
|
res.length.should.equal(2);
|
|
res[0].ok._rev.should.equal('2-x', 'doc1 ok');
|
|
res[1].ok._rev.should.equal('2-y', 'doc2 ok');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Testing successive new_edits to the same doc', function (done) {
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [{
|
|
'_id': 'foobar123',
|
|
'_rev': '1-x',
|
|
'bar': 'huzzah',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
}];
|
|
|
|
db.bulkDocs({docs: docs, new_edits: false}, function (err) {
|
|
should.not.exist(err);
|
|
db.bulkDocs({docs: docs, new_edits: false}, function (err) {
|
|
should.not.exist(err);
|
|
db.get('foobar123', function (err, res) {
|
|
res._rev.should.equal('1-x');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('#3062 bulkDocs with staggered seqs', function () {
|
|
return new PouchDB(dbs.name).then(function (db) {
|
|
var docs = [];
|
|
for (var i = 10; i <= 20; i++) {
|
|
docs.push({ _id: 'doc-' + i});
|
|
}
|
|
return db.bulkDocs({docs: docs}).then(function (infos) {
|
|
docs.forEach(function (doc, i) {
|
|
doc._rev = infos[i].rev;
|
|
});
|
|
var docsToUpdate = docs.filter(function (doc, i) {
|
|
return i % 2 === 1;
|
|
});
|
|
docsToUpdate.reverse();
|
|
return db.bulkDocs({docs: docsToUpdate});
|
|
}).then(function (infos) {
|
|
infos.map(function (x) {
|
|
return {id: x.id, error: !!x.error, rev: (typeof x.rev)};
|
|
}).should.deep.equal([
|
|
{ error: false, id: 'doc-19', rev: 'string'},
|
|
{ error: false, id: 'doc-17', rev: 'string'},
|
|
{ error: false, id: 'doc-15', rev: 'string'},
|
|
{ error: false, id: 'doc-13', rev: 'string'},
|
|
{ error: false, id: 'doc-11', rev: 'string'}
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Testing successive new_edits to the same doc, different content',
|
|
function (done) {
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var docsA = [{
|
|
'_id': 'foo321',
|
|
'_rev': '1-x',
|
|
'bar' : 'baz',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
}, {
|
|
'_id' : 'fee321',
|
|
'bar': 'quux',
|
|
'_rev': '1-x',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
}];
|
|
|
|
var docsB = [{
|
|
'_id': 'foo321',
|
|
'_rev': '1-x',
|
|
'bar' : 'zam', // this update should be rejected
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
}, {
|
|
'_id' : 'faa321',
|
|
'_rev': '1-x',
|
|
'bar': 'zul',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
}];
|
|
|
|
db.bulkDocs({docs: docsA, new_edits: false}, function (err) {
|
|
should.not.exist(err);
|
|
db.changes().on('complete', function (result) {
|
|
var ids = result.results.map(function (row) {
|
|
return row.id;
|
|
});
|
|
ids.should.include("foo321");
|
|
ids.should.include("fee321");
|
|
ids.should.not.include("faa321");
|
|
|
|
var update_seq = result.last_seq;
|
|
db.bulkDocs({docs: docsB, new_edits: false}, function (err) {
|
|
should.not.exist(err);
|
|
db.changes({
|
|
since: update_seq
|
|
}).on('complete', function (result) {
|
|
var ids = result.results.map(function (row) {
|
|
return row.id;
|
|
});
|
|
ids.should.not.include("foo321");
|
|
ids.should.not.include("fee321");
|
|
ids.should.include("faa321");
|
|
|
|
db.get('foo321', function (err, res) {
|
|
res._rev.should.equal('1-x');
|
|
res.bar.should.equal("baz");
|
|
db.info(function (err, info) {
|
|
info.doc_count.should.equal(3);
|
|
done();
|
|
});
|
|
});
|
|
}).on('error', done);
|
|
});
|
|
}).on('error', done);
|
|
});
|
|
});
|
|
|
|
it('Testing successive new_edits to two doc', function () {
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var doc1 = {
|
|
'_id': 'foo',
|
|
'_rev': '1-x',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
};
|
|
var doc2 = {
|
|
'_id': 'bar',
|
|
'_rev': '1-x',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
};
|
|
|
|
return db.put(doc1, {new_edits: false}).then(function () {
|
|
return db.put(doc2, {new_edits: false});
|
|
}).then(function () {
|
|
return db.put(doc1, {new_edits: false});
|
|
}).then(function () {
|
|
return db.get('foo');
|
|
}).then(function () {
|
|
return db.get('bar');
|
|
});
|
|
});
|
|
|
|
it('Deletion with new_edits=false', function () {
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var doc1 = {
|
|
'_id': 'foo',
|
|
'_rev': '1-x',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
};
|
|
var doc2 = {
|
|
'_deleted': true,
|
|
'_id': 'foo',
|
|
'_rev': '2-y',
|
|
'_revisions': {
|
|
'start': 2,
|
|
'ids': ['y', 'x']
|
|
}
|
|
};
|
|
|
|
return db.put(doc1, {new_edits: false}).then(function () {
|
|
return db.put(doc2, {new_edits: false});
|
|
}).then(function () {
|
|
return db.allDocs({keys: ['foo']});
|
|
}).then(function (res) {
|
|
res.rows[0].value.rev.should.equal('2-y');
|
|
res.rows[0].value.deleted.should.equal(true);
|
|
});
|
|
});
|
|
|
|
it('Deletion with new_edits=false, no history', function () {
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var doc1 = {
|
|
'_id': 'foo',
|
|
'_rev': '1-x',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
};
|
|
var doc2 = {
|
|
'_deleted': true,
|
|
'_id': 'foo',
|
|
'_rev': '2-y'
|
|
};
|
|
|
|
return db.put(doc1, {new_edits: false}).then(function () {
|
|
return db.put(doc2, {new_edits: false});
|
|
}).then(function () {
|
|
return db.allDocs({keys: ['foo']});
|
|
}).then(function (res) {
|
|
res.rows[0].value.rev.should.equal('1-x');
|
|
should.equal(!!res.rows[0].value.deleted, false);
|
|
});
|
|
});
|
|
|
|
it('Modification with new_edits=false, no history', function () {
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var doc1 = {
|
|
'_id': 'foo',
|
|
'_rev': '1-x',
|
|
'_revisions': {
|
|
'start': 1,
|
|
'ids': ['x']
|
|
}
|
|
};
|
|
var doc2 = {
|
|
'_id': 'foo',
|
|
'_rev': '2-y'
|
|
};
|
|
|
|
return db.put(doc1, {new_edits: false}).then(function () {
|
|
return db.put(doc2, {new_edits: false});
|
|
}).then(function () {
|
|
return db.allDocs({keys: ['foo']});
|
|
}).then(function (res) {
|
|
res.rows[0].value.rev.should.equal('2-y');
|
|
});
|
|
});
|
|
|
|
it('Deletion with new_edits=false, no history, no revisions', function () {
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var doc = {
|
|
'_deleted': true,
|
|
'_id': 'foo',
|
|
'_rev': '2-y'
|
|
};
|
|
|
|
return db.put(doc, {new_edits: false}).then(function () {
|
|
return db.allDocs({keys: ['foo']});
|
|
}).then(function (res) {
|
|
res.rows[0].value.rev.should.equal('2-y');
|
|
res.rows[0].value.deleted.should.equal(true);
|
|
});
|
|
});
|
|
|
|
it('Testing new_edits=false in req body', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [{
|
|
'_id': 'foo',
|
|
'_rev': '2-x',
|
|
'_revisions': {
|
|
'start': 2,
|
|
'ids': ['x', 'a']
|
|
}
|
|
}, {
|
|
'_id': 'foo',
|
|
'_rev': '2-y',
|
|
'_revisions': {
|
|
'start': 2,
|
|
'ids': ['y', 'a']
|
|
}
|
|
}];
|
|
db.bulkDocs({docs: docs, new_edits: false}, function () {
|
|
db.get('foo', {open_revs: 'all'}, function (err, res) {
|
|
res.sort(function (a, b) {
|
|
return a.ok._rev < b.ok._rev ? -1 :
|
|
a.ok._rev > b.ok._rev ? 1 : 0;
|
|
});
|
|
res.length.should.equal(2);
|
|
res[0].ok._rev.should.equal('2-x', 'doc1 ok');
|
|
res[1].ok._rev.should.equal('2-y', 'doc2 ok');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('656 regression in handling deleted docs', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({
|
|
docs: [{
|
|
_id: 'foo',
|
|
_rev: '1-a',
|
|
_deleted: true
|
|
}]
|
|
}, { new_edits: false }, function () {
|
|
db.get('foo', function (err) {
|
|
should.exist(err, 'deleted');
|
|
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('deleted',
|
|
// 'correct error reason returned');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Test quotes in doc ids', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = [{ _id: '\'your_sql_injection_script_here\'' }];
|
|
db.bulkDocs({docs: docs}, function (err) {
|
|
should.not.exist(err, 'got error: ' + JSON.stringify(err));
|
|
db.get('foo', function (err) {
|
|
should.exist(err, 'deleted');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Bulk docs empty list', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: [] }, function (err) {
|
|
done(err);
|
|
});
|
|
});
|
|
|
|
it('handles simultaneous writes', function (done) {
|
|
var db1 = new PouchDB(dbs.name);
|
|
var db2 = new PouchDB(dbs.name);
|
|
var id = 'fooId';
|
|
var errorNames = [];
|
|
var ids = [];
|
|
var numDone = 0;
|
|
function callback(err, res) {
|
|
should.not.exist(err);
|
|
if (res[0].error) {
|
|
errorNames.push(res[0].name);
|
|
} else {
|
|
ids.push(res[0].id);
|
|
}
|
|
if (++numDone === 2) {
|
|
errorNames.should.deep.equal(['conflict']);
|
|
ids.should.deep.equal([id]);
|
|
done();
|
|
}
|
|
}
|
|
db1.bulkDocs({docs : [{_id : id}]}, callback);
|
|
db2.bulkDocs({docs : [{_id : id}]}, callback);
|
|
});
|
|
|
|
it('bulk docs input by array', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docs = makeDocs(5);
|
|
db.bulkDocs(docs, function (err, results) {
|
|
results.should.have.length(5, 'results length matches');
|
|
for (var i = 0; i < 5; i++) {
|
|
results[i].id.should.equal(docs[i]._id, 'id matches');
|
|
should.exist(results[i].rev, 'rev is set');
|
|
// Update the doc
|
|
docs[i]._rev = results[i].rev;
|
|
docs[i].string = docs[i].string + '.00';
|
|
}
|
|
db.bulkDocs(docs, function (err, results) {
|
|
results.should.have.length(5, 'results length matches');
|
|
for (i = 0; i < 5; i++) {
|
|
results[i].id.should.equal(i.toString(), 'id matches again');
|
|
// set the delete flag to delete the docs in the next step
|
|
docs[i]._rev = results[i].rev;
|
|
docs[i]._deleted = true;
|
|
}
|
|
db.put(docs[0], function () {
|
|
db.bulkDocs(docs, function (err, results) {
|
|
results[0].name.should.equal(
|
|
'conflict', 'First doc should be in conflict');
|
|
should.not.exist(results[0].rev, 'no rev in conflict');
|
|
for (i = 1; i < 5; i++) {
|
|
results[i].id.should.equal(i.toString());
|
|
should.exist(results[i].rev);
|
|
}
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Bulk empty list', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs([], function (err) {
|
|
done(err);
|
|
});
|
|
});
|
|
|
|
it('Bulk docs not an array', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: 'foo' }, function (err) {
|
|
should.exist(err, 'error reported');
|
|
err.status.should.equal(PouchDB.Errors.MISSING_BULK_DOCS.status,
|
|
'correct error status returned');
|
|
err.message.should.equal(PouchDB.Errors.MISSING_BULK_DOCS.message,
|
|
'correct error message returned');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Bulk docs not an object', function (done) {
|
|
var db = new PouchDB(dbs.name);
|
|
db.bulkDocs({ docs: ['foo'] }, function (err) {
|
|
should.exist(err, 'error reported');
|
|
err.status.should.equal(PouchDB.Errors.NOT_AN_OBJECT.status,
|
|
'correct error status returned');
|
|
err.message.should.equal(PouchDB.Errors.NOT_AN_OBJECT.message,
|
|
'correct error message returned');
|
|
});
|
|
db.bulkDocs({ docs: [[]] }, function (err) {
|
|
should.exist(err, 'error reported');
|
|
err.status.should.equal(PouchDB.Errors.NOT_AN_OBJECT.status,
|
|
'correct error status returned');
|
|
err.message.should.equal(PouchDB.Errors.NOT_AN_OBJECT.message,
|
|
'correct error message returned');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Bulk docs two different revisions to same document id', function(done) {
|
|
var db = new PouchDB(dbs.name);
|
|
var docid = "mydoc";
|
|
|
|
function uuid() {
|
|
return PouchDB.utils.uuid(32, 16).toLowerCase();
|
|
}
|
|
|
|
// create a few of rando, good revisions
|
|
var numRevs = 3;
|
|
var uuids = [];
|
|
for (var i = 0; i < numRevs - 1; i++) {
|
|
uuids.push(uuid());
|
|
}
|
|
|
|
// branch 1
|
|
var a_conflict = uuid();
|
|
var a_doc = {
|
|
_id: docid,
|
|
_rev: numRevs + '-' + a_conflict,
|
|
_revisions: {
|
|
start: numRevs,
|
|
ids: [ a_conflict ].concat(uuids)
|
|
}
|
|
};
|
|
|
|
// branch 2
|
|
var b_conflict = uuid();
|
|
var b_doc = {
|
|
_id: docid,
|
|
_rev: numRevs + '-' + b_conflict,
|
|
_revisions: {
|
|
start: numRevs,
|
|
ids: [ b_conflict ].concat(uuids)
|
|
}
|
|
};
|
|
|
|
// push the conflicted documents
|
|
return db.bulkDocs([ a_doc, b_doc ], { new_edits: false })
|
|
|
|
.then(function() {
|
|
return db.get(docid, { open_revs: "all" }).then(function(resp) {
|
|
resp.length.should.equal(2, 'correct number of open revisions');
|
|
resp[0].ok._id.should.equal(docid, 'rev 1, correct document id');
|
|
resp[1].ok._id.should.equal(docid, 'rev 2, correct document id');
|
|
|
|
// order of revisions is not specified
|
|
((resp[0].ok._rev === a_doc._rev &&
|
|
resp[1].ok._rev === b_doc._rev) ||
|
|
(resp[0].ok._rev === b_doc._rev &&
|
|
resp[1].ok._rev === a_doc._rev)).should.equal(true);
|
|
});
|
|
})
|
|
|
|
.then(function() { done(); }, done);
|
|
});
|
|
|
|
it('4204 respect revs_limit', function () {
|
|
var db = new PouchDB(dbs.name);
|
|
|
|
// simulate 5000 normal commits with two conflicts at the very end
|
|
function uuid() {
|
|
return PouchDB.utils.uuid(32, 16).toLowerCase();
|
|
}
|
|
|
|
var isSafari = (typeof process === 'undefined' || process.browser) &&
|
|
/Safari/.test(window.navigator.userAgent) &&
|
|
!/Chrome/.test(window.navigator.userAgent);
|
|
|
|
var numRevs = isSafari ? 10 : 5000;
|
|
var expected = isSafari ? 10 : 1000;
|
|
var uuids = [];
|
|
|
|
for (var i = 0; i < numRevs - 1; i++) {
|
|
uuids.push(uuid());
|
|
}
|
|
var conflict1 = 'a' + uuid();
|
|
|
|
var doc1 = {
|
|
_id: 'doc',
|
|
_rev: numRevs + '-' + conflict1,
|
|
_revisions: {
|
|
start: numRevs,
|
|
ids: [conflict1].concat(uuids)
|
|
}
|
|
};
|
|
|
|
return db.bulkDocs([doc1], {new_edits: false}).then(function () {
|
|
return db.get('doc', {revs: true});
|
|
}).then(function (doc) {
|
|
doc._revisions.ids.length.should.equal(expected);
|
|
});
|
|
});
|
|
|
|
it('2839 implement revs_limit', function (done) {
|
|
|
|
// We only implement revs_limit locally
|
|
if (adapter === 'http') {
|
|
return done();
|
|
}
|
|
|
|
var LIMIT = 50;
|
|
var db = new PouchDB(dbs.name, {revs_limit: LIMIT});
|
|
|
|
// simulate 5000 normal commits with two conflicts at the very end
|
|
function uuid() {
|
|
return PouchDB.utils.uuid(32, 16).toLowerCase();
|
|
}
|
|
|
|
var numRevs = 5000;
|
|
var uuids = [];
|
|
for (var i = 0; i < numRevs - 1; i++) {
|
|
uuids.push(uuid());
|
|
}
|
|
var conflict1 = 'a' + uuid();
|
|
var doc1 = {
|
|
_id: 'doc',
|
|
_rev: numRevs + '-' + conflict1,
|
|
_revisions: {
|
|
start: numRevs,
|
|
ids: [conflict1].concat(uuids)
|
|
}
|
|
};
|
|
|
|
db.bulkDocs([doc1], {new_edits: false}).then(function () {
|
|
return db.get('doc', {revs: true});
|
|
}).then(function (doc) {
|
|
doc._revisions.ids.length.should.equal(LIMIT);
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
|
|
it('4372 revs_limit deletes old revisions of the doc', function (done) {
|
|
|
|
// We only implement revs_limit locally
|
|
if (adapter === 'http') {
|
|
return done();
|
|
}
|
|
|
|
var db = new PouchDB(dbs.name, {revs_limit: 2});
|
|
|
|
// old revisions are always deleted with auto compaction
|
|
if (db.auto_compaction) {
|
|
return done();
|
|
}
|
|
|
|
var revs = [];
|
|
db.put({v: 1}, 'doc').then(function (v1) {
|
|
revs.push(v1.rev);
|
|
return db.put({v: 2}, 'doc', revs[0]);
|
|
}).then(function (v2) {
|
|
revs.push(v2.rev);
|
|
return db.put({v: 3}, 'doc', revs[1]);
|
|
}).then(function () {
|
|
// the v2 revision is still in the db
|
|
return db.get('doc', {rev: revs[1]});
|
|
}).then(function (v2) {
|
|
v2.v.should.equal(2);
|
|
|
|
return db.get('doc', {rev: revs[0]}).then(function () {
|
|
// the v1 revision is not in the db anymore
|
|
done(new Error('v1 should be missing'));
|
|
}).catch(function (error) {
|
|
error.message.should.equal('missing');
|
|
done();
|
|
});
|
|
}).catch(done);
|
|
});
|
|
|
|
it('4712 invalid rev for new doc generates conflict', function () {
|
|
// CouchDB 1.X has a bug which allows this insertion via bulk_docs
|
|
// (which PouchDB uses for all document insertions)
|
|
if (adapter === 'http' && !testUtils.isCouchMaster()) {
|
|
return;
|
|
}
|
|
|
|
var db = new PouchDB(dbs.name);
|
|
var newdoc = {
|
|
'_id': 'foobar',
|
|
'_rev': '1-123'
|
|
};
|
|
|
|
return db.bulkDocs({ docs: [newdoc] }).then (function (results) {
|
|
results[0].should.have.property('status', 409);
|
|
});
|
|
});
|
|
});
|
|
});
|