slouch/test/spec/doc.js
Andreas Siegel 6e2676b09b Add doc.find() (#102)
* Add find to doc functions

* Replace const by var
2019-01-04 11:40:31 -06:00

656 lines
18 KiB
JavaScript

'use strict';
var Slouch = require('../../scripts'),
utils = require('../utils'),
sporks = require('sporks'),
Promise = require('sporks/scripts/promise'),
config = require('../config.json'),
Backoff = require('backoff-promise'),
crypto = require("crypto"),
assert = require('chai').assert;
describe('doc', function () {
var slouch = null,
db = null,
defaultGet = null,
defaultUpdate = null,
conflictDoc = null,
updates = null;
beforeEach(function () {
slouch = new Slouch(utils.couchDBURL());
db = slouch.db;
updates = [];
// Shorten backoff
slouch.doc._newBackoff = function () {
return new Backoff(1);
};
return utils.createDB();
});
afterEach(function () {
return utils.destroyDB();
});
var createDocs = function () {
return slouch.doc.create(utils.createdDB, {
_id: '1',
thing: 'jam'
}).then(function () {
return slouch.doc.create(utils.createdDB, {
thing: 'clean',
fun: false
});
}).then(function () {
return slouch.doc.create(utils.createdDB, {
thing: 'code'
});
});
};
var fakeConflict = function (numConflicts) {
defaultUpdate = slouch.doc.update;
var i = 0;
// Fake resolution of conflict
slouch.doc.update = function () {
if (numConflicts && i++ > numConflicts) {
// Resolve after a few attempts
slouch.doc.get = defaultGet;
}
return defaultUpdate.apply(this, arguments);
};
return createDocs().then(function () {
return slouch.doc.get(utils.createdDB, '1');
}).then(function (doc) {
conflictDoc = doc;
return slouch.doc.createOrUpdate(utils.createdDB, {
_id: '1',
thing: 'dance'
}).then(function () {
defaultGet = slouch.doc.get;
// Fake conflict
slouch.doc.get = function () {
return Promise.resolve({
_id: '1',
_rev: doc._rev,
thing: 'dance'
});
};
});
});
};
var spyOnUpdates = function () {
defaultUpdate = slouch.doc.update;
slouch.doc.update = function () {
return defaultUpdate.apply(this, arguments).then(function (update) {
updates.push(update);
return update;
});
};
};
it('should create, update and get doc', function () {
var doc = {
thing: 'play'
};
var newRev = null;
return slouch.doc.create(utils.createdDB, doc).then(function (_doc) {
doc._id = _doc.id;
return slouch.doc.get(utils.createdDB, doc._id);
}).then(function (body) {
doc._rev = body._rev;
doc.priority = 'medium';
return slouch.doc.update(utils.createdDB, doc);
}).then(function (updatedDoc) {
var clonedDoc = sporks.clone(doc);
newRev = updatedDoc._rev;
delete clonedDoc._rev;
delete updatedDoc._rev;
updatedDoc.should.eql(clonedDoc);
return slouch.doc.get(utils.createdDB, doc._id);
}).then(function (body) {
doc._rev = body._rev;
body.should.eql(doc);
// Make sure we have the new revision number from the update response
newRev.should.eql(body._rev);
});
});
it('should destroy all non-design docs', function () {
return slouch.doc.create(utils.createdDB, {
thing: 'play'
}).then(function () {
return slouch.doc.create(utils.createdDB, {
thing: 'write'
});
}).then(function () {
return slouch.doc.create(utils.createdDB, {
_id: '_design/mydesign',
foo: 'bar'
});
}).then(function () {
return slouch.doc.destroyAllNonDesign(utils.createdDB);
}).then(function () {
return slouch.doc.allArray(utils.createdDB);
}).then(function (body) {
body.total_rows.should.eql(1);
});
});
it('should throw when conflict', function () {
return sporks.shouldThrow(function () {
var doc = {
thing: 'play'
};
return slouch.doc.create(utils.createdDB, doc).then(function (_doc) {
doc._id = _doc.id;
doc.priority = 'medium';
// Generates conflict as no rev provided
return slouch.doc.update(utils.createdDB, doc);
});
});
});
it('should ignore conflict when updating', function () {
return slouch.doc.create(utils.createdDB, {
_id: '1',
thing: 'jam'
}).then(function () {
return slouch.doc.updateIgnoreConflict(utils.createdDB, {
_id: '1',
thing: 'clean'
});
});
});
it('should only ignore conflicts', function () {
return sporks.shouldThrow(function () {
return slouch.doc.updateIgnoreConflict('missingdb', {
thing: 'clean'
});
});
});
it('should ignore missing docs', function () {
return slouch.doc.getIgnoreMissing(utils.createdDB, 'missingid');
});
it('should check if doc exists', function () {
return createDocs().then(function () {
return slouch.doc.exists(utils.createdDB, '1');
}).then(function (exists) {
exists.should.eql(true);
});
});
it('should throw if not missing', function () {
return sporks.shouldThrow(function () {
return slouch.doc.ignoreMissing(function () {
return sporks.promiseError(new Error());
});
});
});
it('should create and ignore conflict', function () {
return fakeConflict().then(function () {
return slouch.doc.createAndIgnoreConflict(utils.createdDB, {
_id: '1',
thing: 'dance'
});
});
});
it('should create when creating or updating', function () {
return slouch.doc.createOrUpdate(utils.createdDB, {
_id: '1',
thing: 'jam'
}).then(function () {
return slouch.doc.get(utils.createdDB, '1');
}).then(function (doc) {
doc._id.should.eql('1');
doc.thing.should.eql('jam');
});
});
it('should update when creating or updating', function () {
return createDocs().then(function () {
return slouch.doc.createOrUpdate(utils.createdDB, {
_id: '1',
thing: 'dance'
});
}).then(function () {
return slouch.doc.get(utils.createdDB, '1');
}).then(function (doc) {
doc._id.should.eql('1');
doc.thing.should.eql('dance');
});
});
it('should throw when creating or updating', function () {
return fakeConflict().then(function () {
return sporks.shouldThrow(function () {
return slouch.doc.createOrUpdate(utils.createdDB, {
_id: '1',
thing: 'jam'
});
});
});
});
it('should ignore conflict when creating or updating', function () {
return fakeConflict().then(function () {
return slouch.doc.createOrUpdateIgnoreConflict(utils.createdDB, {
_id: '1',
thing: 'sing'
});
});
});
it('should upsert when conflict', function () {
return fakeConflict(3).then(function () {
return slouch.doc.upsert(utils.createdDB, {
_id: '1',
thing: 'dance'
});
}).then(function () {
return slouch.doc.get(utils.createdDB, '1');
}).then(function (doc) {
doc._id.should.eql('1');
doc.thing.should.eql('dance');
});
});
it('upsert should fail after max retries', function () {
// Disable for conflict faking
slouch.doc.ignoreDuplicateUpdates = false;
return fakeConflict().then(function () {
return sporks.shouldThrow(function () {
return slouch.doc.upsert(utils.createdDB, {
_id: '1',
thing: 'dance'
});
});
});
});
it('should get, merge and update', function () {
return createDocs().then(function () {
return slouch.doc.getMergeUpdate(utils.createdDB, {
_id: '1',
priority: 'high'
});
}).then(function () {
return slouch.doc.get(utils.createdDB, '1');
}).then(function (doc) {
doc._id.should.eql('1');
doc.thing.should.eql('jam');
doc.priority.should.eql('high');
});
});
it('should get, merge, create or update when doc existing', function () {
return createDocs().then(function () {
return slouch.doc.getMergeCreateOrUpdate(utils.createdDB, {
_id: '1',
priority: 'high'
});
}).then(function () {
return slouch.doc.get(utils.createdDB, '1');
}).then(function (doc) {
doc._id.should.eql('1');
doc.thing.should.eql('jam');
doc.priority.should.eql('high');
});
});
it('should get, merge, create or update when missing', function () {
return slouch.doc.getMergeCreateOrUpdate(utils.createdDB, {
_id: '1',
priority: 'high'
}).then(function () {
return slouch.doc.get(utils.createdDB, '1');
}).then(function (doc) {
doc._id.should.eql('1');
doc.priority.should.eql('high');
});
});
it('should ignore conflict when getting, merging and updating', function () {
return fakeConflict().then(function () {
return slouch.doc.getMergeUpdateIgnoreConflict(utils.createdDB, {
_id: '1',
thing: 'sing'
});
});
});
it('should get, merge and upsert when conflict', function () {
return fakeConflict(3).then(function () {
return slouch.doc.getMergeUpsert(utils.createdDB, {
_id: '1',
priority: 'high'
});
}).then(function () {
return slouch.doc.get(utils.createdDB, '1');
}).then(function (doc) {
doc._id.should.eql('1');
doc.thing.should.eql('dance');
doc.priority.should.eql('high');
});
});
it('get, merge and upsert should fail after max retries', function () {
// Disable for conflict faking
slouch.doc.ignoreDuplicateUpdates = false;
return fakeConflict().then(function () {
return sporks.shouldThrow(function () {
return slouch.doc.getMergeUpsert(utils.createdDB, {
_id: '1',
thing: 'dance'
});
});
});
});
it('should get, modify and upsert when conflict', function () {
return fakeConflict(3).then(function () {
return slouch.doc.getModifyUpsert(utils.createdDB, '1', function (doc) {
return Promise.resolve({
_id: '1',
_rev: doc._rev,
thing: doc.thing,
priority: 'high'
});
});
}).then(function () {
return slouch.doc.get(utils.createdDB, '1');
}).then(function (doc) {
doc._id.should.eql('1');
doc.thing.should.eql('dance');
doc.priority.should.eql('high');
});
});
it('get, modify and upsert should fail after max retries', function () {
return fakeConflict().then(function () {
return sporks.shouldThrow(function () {
return slouch.doc.getModifyUpsert(utils.createdDB, '1', function (doc) {
return Promise.resolve({
_id: '1',
_rev: doc._rev,
thing: doc.thing,
priority: 'high'
});
});
});
});
});
it('should ignore conflicts when destroying', function () {
return fakeConflict().then(function () {
return slouch.doc.destroyIgnoreConflict(utils.createdDB, '1', conflictDoc._rev);
});
});
it('should get and destroy', function () {
return createDocs().then(function () {
return slouch.doc.getAndDestroy(utils.createdDB, '1');
}).then(function () {
return slouch.doc.exists();
}).then(function (exists) {
exists.should.eql(false);
});
});
it('should mark as destroyed', function () {
return createDocs().then(function () {
return slouch.doc.markAsDestroyed(utils.createdDB, '1');
}).then(function () {
return slouch.doc.exists();
}).then(function (exists) {
exists.should.eql(false);
});
});
it('should set destroyed', function () {
var doc = {};
slouch.doc.setDestroyed(doc);
doc._deleted.should.eql(true);
});
it('all should throw when permissions error', function () {
var badAuthURL = config.couchdb.scheme + '://baduser:badpassord@' + config.couchdb.host +
':' + config.couchdb.port,
slouch2 = new Slouch(badAuthURL),
readItem = false;
return sporks.shouldThrow(function () {
return slouch2.doc.all(utils.createdDB).each(function ( /* doc */ ) {
readItem = true;
});
}).then(function () {
readItem.should.eql(false);
});
});
it('should update when ignoreDuplicateUpdates is true', function () {
spyOnUpdates();
var doc = {
_id: '1',
thing: 'play'
};
return slouch.doc.create(utils.createdDB, doc).then(function () {
doc.thing = 'write';
return slouch.doc.createOrUpdate(utils.createdDB, doc);
}).then(function (_doc) {
updates.should.eql([_doc]);
});
});
it('should ignore when ignoreDuplicateUpdates is true', function () {
spyOnUpdates();
var doc = {
_id: '1',
thing: 'play'
};
return slouch.doc.create(utils.createdDB, doc).then(function () {
return slouch.doc.createOrUpdate(utils.createdDB, doc);
}).then(function (_doc) {
// Remove the rev as we don't have it stored in doc
delete _doc._rev;
// Update should still return doc
_doc.should.eql(doc);
// Spy should show that nothing was actually updated
updates.length.should.eql(0);
});
});
it('should update when ignoreDuplicateUpdates is false', function () {
slouch.doc.ignoreDuplicateUpdates = false;
spyOnUpdates();
var doc = {
_id: '1',
thing: 'play'
};
return slouch.doc.create(utils.createdDB, doc).then(function () {
return slouch.doc.createOrUpdate(utils.createdDB, doc);
}).then(function (_doc) {
updates.should.eql([_doc]);
});
});
it('should bulk create or update', function () {
// Bulk create with a single doc
return slouch.doc.bulkCreateOrUpdate(utils.createdDB, [{
_id: '1',
thing: 'read'
}, {
_id: '2',
thing: 'jam'
}]).then(function (docs) {
docs.length.should.eql(2);
docs[0].id.should.eql('1');
docs[0].ok.should.eql(true);
(docs[0].rev === undefined).should.eql(false);
docs[1].id.should.eql('2');
docs[1].ok.should.eql(true);
(docs[1].rev === undefined).should.eql(false);
// Edit doc so that we can prepare for conflict
return slouch.doc.update(utils.createdDB, {
_id: '1',
_rev: docs[0].rev,
thing: 'read books'
}).then(function () {
// Create 1 doc and update 2 docs. The first update should result in a conflict
return slouch.doc.bulkCreateOrUpdate(utils.createdDB, [{
_id: '1',
_rev: docs[0].rev, // outdated rev
thing: 'read many books'
}, {
thing: 'play'
}, {
_id: '2',
_rev: docs[1].rev,
thing: 'jam on guitar'
}]);
}).then(function (docs) {
docs.length.should.eql(3);
// 1st operation should have resulted in conflict
docs[0].id.should.eql('1');
docs[0].error.should.eql('conflict');
// 2nd operation should have succeeded
(docs[1].id === undefined).should.eql(false);
docs[1].ok.should.eql(true);
(docs[1].rev === undefined).should.eql(false);
// 3rd operation should have succeeded and rev should have changed
(docs[2].id === undefined).should.eql(false);
docs[2].ok.should.eql(true);
docs[2].rev.should.not.eql(docs[1].rev);
});
});
});
it('should create, update and get doc with slash in _id', function () {
var doc = {
_id: 'spiegel_cl_user/user_name',
thing: 'play'
};
var newRev = null;
return slouch.doc.create(utils.createdDB, doc).then(function (_doc) {
doc._id = _doc.id;
return slouch.doc.get(utils.createdDB, doc._id);
}).then(function (body) {
doc._rev = body._rev;
doc.priority = 'medium';
return slouch.doc.update(utils.createdDB, doc);
}).then(function (updatedDoc) {
var clonedDoc = sporks.clone(doc);
newRev = updatedDoc._rev;
delete clonedDoc._rev;
delete updatedDoc._rev;
updatedDoc.should.eql(clonedDoc);
return slouch.doc.get(utils.createdDB, doc._id);
}).then(function (body) {
doc._rev = body._rev;
body.should.eql(doc);
// Make sure we have the new revision number from the update response
newRev.should.eql(body._rev);
});
});
it('should create, update and get doc with slash in _id in a db with slash in name', function () {
var doc = {
_id: crypto.randomBytes(16).toString("hex") + '/user_name',
thing: 'play'
};
var newRev = null;
var dbName = utils.createdDB + '/test';
return db.create(dbName)
.then(function () {
return slouch.doc.create(dbName, doc);
})
.then(function (_doc) {
doc._id = _doc.id;
return slouch.doc.get(dbName, doc._id);
}).then(function (body) {
doc._rev = body._rev;
doc.priority = 'medium';
return slouch.doc.update(dbName, doc);
}).then(function (updatedDoc) {
var clonedDoc = sporks.clone(doc);
newRev = updatedDoc._rev;
delete clonedDoc._rev;
delete updatedDoc._rev;
updatedDoc.should.eql(clonedDoc);
return slouch.doc.get(dbName, doc._id);
}).then(function (body) {
doc._rev = body._rev;
body.should.eql(doc);
// Make sure we have the new revision number from the update response
newRev.should.eql(body._rev);
return db.destroy(dbName);
});
});
it('should find a document by selector', function () {
var requestBody = {
selector: {
thing: 'findme',
},
};
return slouch.doc.create(utils.createdDB, {
thing: 'play'
}).then(function () {
return slouch.doc.create(utils.createdDB, {
thing: 'findme'
});
}).then(function () {
return slouch.doc.find(utils.createdDB, requestBody);
}).then(function (body) {
assert.lengthOf(body.docs, 1, 'body.docs has a length of 1');
assert.equal(body.docs[0].thing, 'findme', '`thing` field has value `findme`');
});
});
});