From aefe40d0a5a930db885af5a13283b77638aff352 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Thu, 7 Jan 2016 07:34:51 -0600 Subject: [PATCH] Housekeeping! --- lib/datastore.js | 15 ++- lib/indexes.js | 21 ++-- lib/storage.js | 8 +- test/db.test.js | 12 ++- test/executor.test.js | 30 +++--- test/indexes.test.js | 201 ++++++++++++++++++++------------------- test/persistence.test.js | 8 +- 7 files changed, 151 insertions(+), 144 deletions(-) diff --git a/lib/datastore.js b/lib/datastore.js index d2a1f7d..17eb73f 100755 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -109,11 +109,16 @@ Datastore.prototype.resetIndexes = function (newData) { * @param {Function} cb Optional callback, signature: err */ Datastore.prototype.ensureIndex = function (options, cb) { - var callback = cb || function () {}; + var err + , callback = cb || function () {}; options = options || {}; - if (!options.fieldName) { return callback({ missingFieldName: true }); } + if (!options.fieldName) { + err = new Error("Cannot create an index without a fieldName"); + err.missingFieldName = true; + return callback(err); + } if (this.indexes[options.fieldName]) { return callback(null); } this.indexes[options.fieldName] = new Index(options); @@ -535,9 +540,9 @@ Datastore.prototype._update = function (query, updateQuery, options, cb) { } , function () { // Perform the update var modifiedDoc - , candidates = self.getCandidates(query) - , modifications = [] - ; + , candidates = self.getCandidates(query) + , modifications = [] + ; // Preparing update (if an error is thrown here neither the datafile nor // the in-memory indexes are affected) diff --git a/lib/indexes.js b/lib/indexes.js index 79227ce..1cc6244 100755 --- a/lib/indexes.js +++ b/lib/indexes.js @@ -230,28 +230,20 @@ Index.prototype.revertUpdate = function (oldDoc, newDoc) { }; -// Append all elements in toAppend to array -function append (array, toAppend) { - var i; - - for (i = 0; i < toAppend.length; i += 1) { - array.push(toAppend[i]); - } -} - - /** * Get all documents in index whose key match value (if it is a Thing) or one of the elements of value (if it is an array of Things) * @param {Thing} value Value to match the key against * @return {Array of documents} */ Index.prototype.getMatching = function (value) { - var self = this; + var _res, res + , self = this; if (!util.isArray(value)) { - return this.tree.search(value); + res = self.tree.search(value); } else { - var _res = {}, res = []; + _res = {}; + res = []; value.forEach(function (v) { self.getMatching(v).forEach(function (doc) { @@ -262,9 +254,8 @@ Index.prototype.getMatching = function (value) { Object.keys(_res).forEach(function (_id) { res.push(_res[_id]); }); - - return res; } + return res; }; diff --git a/lib/storage.js b/lib/storage.js index 24f3455..28b8416 100755 --- a/lib/storage.js +++ b/lib/storage.js @@ -59,11 +59,13 @@ storage.flushToStorage = function (options, callback) { if (err) { return callback(err); } fs.fsync(fd, function (errFS) { fs.close(fd, function (errC) { + var e = null; if (errFS || errC) { - return callback({ errorOnFsync: errFS, errorOnClose: errC }); - } else { - return callback(null); + e = new Error('Failed to flush to storage'); + e.errorOnFsync = errFS; + e.errorOnClose = errC; } + return callback(e); }); }); }); diff --git a/test/db.test.js b/test/db.test.js index 496e71f..da646af 100755 --- a/test/db.test.js +++ b/test/db.test.js @@ -89,7 +89,7 @@ describe('Database', function () { db = new Datastore({ filename: autoDb, autoload: true, onload: onload }) db.find({}, function (err, docs) { - done("Find should not be executed since autoload failed"); + done(new Error("Find should not be executed since autoload failed")); }); }); @@ -390,7 +390,7 @@ describe('Database', function () { * * Note: maybe using an in-memory only NeDB would give us an easier solution */ - it('If the callback throws an uncaught execption, dont catch it inside findOne, this is userspace concern', function (done) { + it('If the callback throws an uncaught exception, do not catch it inside findOne, this is userspace concern', function (done) { var tryCount = 0 , currentUncaughtExceptionHandlers = process.listeners('uncaughtException') , i @@ -399,11 +399,13 @@ describe('Database', function () { process.removeAllListeners('uncaughtException'); process.on('uncaughtException', function MINE (ex) { + process.removeAllListeners('uncaughtException'); + for (i = 0; i < currentUncaughtExceptionHandlers.length; i += 1) { process.on('uncaughtException', currentUncaughtExceptionHandlers[i]); } - ex.should.equal('SOME EXCEPTION'); + ex.message.should.equal('SOME EXCEPTION'); done(); }); @@ -411,9 +413,9 @@ describe('Database', function () { d.findOne({ a : 5}, function (err, doc) { if (tryCount === 0) { tryCount += 1; - throw 'SOME EXCEPTION'; + throw new Error('SOME EXCEPTION'); } else { - done('Callback was called twice'); + done(new Error('Callback was called twice')); } }); }); diff --git a/test/executor.test.js b/test/executor.test.js index 0c911bd..6bd1d23 100755 --- a/test/executor.test.js +++ b/test/executor.test.js @@ -15,25 +15,26 @@ var should = require('chai').should() // We prevent Mocha from catching the exception we throw on purpose by remembering all current handlers, remove them and register them back after test ends function testThrowInCallback (d, done) { var currentUncaughtExceptionHandlers = process.listeners('uncaughtException'); - + process.removeAllListeners('uncaughtException'); process.on('uncaughtException', function (err) { // Do nothing with the error which is only there to test we stay on track }); - d.find({}, function (err) { + d.find({}, function (err) { process.nextTick(function () { d.insert({ bar: 1 }, function (err) { + process.removeAllListeners('uncaughtException'); for (var i = 0; i < currentUncaughtExceptionHandlers.length; i += 1) { process.on('uncaughtException', currentUncaughtExceptionHandlers[i]); } - + done(); }); }); - - throw 'Some error'; + + throw new Error('Some error'); }); } @@ -51,29 +52,34 @@ function testRightOrder (d, done) { d.find({}, function (err, docs) { docs.length.should.equal(0); - + d.insert({ a: 1 }, function () { d.update({ a: 1 }, { a: 2 }, {}, function () { d.find({}, function (err, docs) { docs[0].a.should.equal(2); - + process.nextTick(function () { d.update({ a: 2 }, { a: 3 }, {}, function () { d.find({}, function (err, docs) { docs[0].a.should.equal(3); - done(); + process.removeAllListeners('uncaughtException'); + for (var i = 0; i < currentUncaughtExceptionHandlers.length; i += 1) { + process.on('uncaughtException', currentUncaughtExceptionHandlers[i]); + } + + done(); }); }); }); - - throw 'Some error'; + + throw new Error('Some error'); }); }); }); }); -} - +} + // Note: The following test does not have any assertion because it diff --git a/test/indexes.test.js b/test/indexes.test.js index f0722ff..020c396 100755 --- a/test/indexes.test.js +++ b/test/indexes.test.js @@ -120,107 +120,108 @@ describe('Indexes', function () { assert.deepEqual(idx.tree.search('world'), []); assert.deepEqual(idx.tree.search('bloup'), []); }); - - describe('Array fields', function () { - - it('Inserts one entry per array element in the index', function () { - var obj = { tf: ['aa', 'bb'], really: 'yeah' } - , obj2 = { tf: 'normal', yes: 'indeed' } - , idx = new Index({ fieldName: 'tf' }) - ; - - idx.insert(obj); - idx.getAll().length.should.equal(2); - idx.getAll()[0].should.equal(obj); - idx.getAll()[1].should.equal(obj); - - idx.insert(obj2); - idx.getAll().length.should.equal(3); - }); - - it('Inserts one entry per array element in the index, type-checked', function () { - var obj = { tf: ['42', 42, new Date(42), 42], really: 'yeah' } - , idx = new Index({ fieldName: 'tf' }) - ; - - idx.insert(obj); - idx.getAll().length.should.equal(3); - idx.getAll()[0].should.equal(obj); - idx.getAll()[1].should.equal(obj); - idx.getAll()[2].should.equal(obj); - }); - - it('Inserts one entry per unique array element in the index, the unique constraint only holds across documents', function () { - var obj = { tf: ['aa', 'aa'], really: 'yeah' } - , obj2 = { tf: ['cc', 'yy', 'cc'], yes: 'indeed' } - , idx = new Index({ fieldName: 'tf', unique: true }) - ; - - idx.insert(obj); - idx.getAll().length.should.equal(1); - idx.getAll()[0].should.equal(obj); - - idx.insert(obj2); - idx.getAll().length.should.equal(3); - }); - - it('The unique constraint holds across documents', function () { - var obj = { tf: ['aa', 'aa'], really: 'yeah' } - , obj2 = { tf: ['cc', 'aa', 'cc'], yes: 'indeed' } - , idx = new Index({ fieldName: 'tf', unique: true }) - ; - - idx.insert(obj); - idx.getAll().length.should.equal(1); - idx.getAll()[0].should.equal(obj); - - (function () { idx.insert(obj2); }).should.throw(); - }); - - it('When removing a document, remove it from the index at all unique array elements', function () { - var obj = { tf: ['aa', 'aa'], really: 'yeah' } - , obj2 = { tf: ['cc', 'aa', 'cc'], yes: 'indeed' } - , idx = new Index({ fieldName: 'tf' }) - ; - - idx.insert(obj); - idx.insert(obj2); - idx.getMatching('aa').length.should.equal(2); - idx.getMatching('aa').indexOf(obj).should.not.equal(-1); - idx.getMatching('aa').indexOf(obj2).should.not.equal(-1); - idx.getMatching('cc').length.should.equal(1); - - idx.remove(obj2); - idx.getMatching('aa').length.should.equal(1); - idx.getMatching('aa').indexOf(obj).should.not.equal(-1); - idx.getMatching('aa').indexOf(obj2).should.equal(-1); - idx.getMatching('cc').length.should.equal(0); - }); - - it('If a unique constraint is violated when inserting an array key, roll back all inserts before the key', function () { - var obj = { tf: ['aa', 'bb'], really: 'yeah' } - , obj2 = { tf: ['cc', 'dd', 'aa', 'ee'], yes: 'indeed' } - , idx = new Index({ fieldName: 'tf', unique: true }) - ; - idx.insert(obj); - idx.getAll().length.should.equal(2); - idx.getMatching('aa').length.should.equal(1); - idx.getMatching('bb').length.should.equal(1); - idx.getMatching('cc').length.should.equal(0); - idx.getMatching('dd').length.should.equal(0); - idx.getMatching('ee').length.should.equal(0); - - (function () { idx.insert(obj2); }).should.throw(); - idx.getAll().length.should.equal(2); - idx.getMatching('aa').length.should.equal(1); - idx.getMatching('bb').length.should.equal(1); - idx.getMatching('cc').length.should.equal(0); - idx.getMatching('dd').length.should.equal(0); - idx.getMatching('ee').length.should.equal(0); - }); - - }); // ==== End of 'Array fields' ==== // + + describe('Array fields', function () { + + it('Inserts one entry per array element in the index', function () { + var obj = { tf: ['aa', 'bb'], really: 'yeah' } + , obj2 = { tf: 'normal', yes: 'indeed' } + , idx = new Index({ fieldName: 'tf' }) + ; + + idx.insert(obj); + idx.getAll().length.should.equal(2); + idx.getAll()[0].should.equal(obj); + idx.getAll()[1].should.equal(obj); + + idx.insert(obj2); + idx.getAll().length.should.equal(3); + }); + + it('Inserts one entry per array element in the index, type-checked', function () { + var obj = { tf: ['42', 42, new Date(42), 42], really: 'yeah' } + , idx = new Index({ fieldName: 'tf' }) + ; + + idx.insert(obj); + idx.getAll().length.should.equal(3); + idx.getAll()[0].should.equal(obj); + idx.getAll()[1].should.equal(obj); + idx.getAll()[2].should.equal(obj); + }); + + it('Inserts one entry per unique array element in the index, the unique constraint only holds across documents', function () { + var obj = { tf: ['aa', 'aa'], really: 'yeah' } + , obj2 = { tf: ['cc', 'yy', 'cc'], yes: 'indeed' } + , idx = new Index({ fieldName: 'tf', unique: true }) + ; + + idx.insert(obj); + idx.getAll().length.should.equal(1); + idx.getAll()[0].should.equal(obj); + + idx.insert(obj2); + idx.getAll().length.should.equal(3); + }); + + it('The unique constraint holds across documents', function () { + var obj = { tf: ['aa', 'aa'], really: 'yeah' } + , obj2 = { tf: ['cc', 'aa', 'cc'], yes: 'indeed' } + , idx = new Index({ fieldName: 'tf', unique: true }) + ; + + idx.insert(obj); + idx.getAll().length.should.equal(1); + idx.getAll()[0].should.equal(obj); + + (function () { idx.insert(obj2); }).should.throw(); + }); + + it('When removing a document, remove it from the index at all unique array elements', function () { + var obj = { tf: ['aa', 'aa'], really: 'yeah' } + , obj2 = { tf: ['cc', 'aa', 'cc'], yes: 'indeed' } + , idx = new Index({ fieldName: 'tf' }) + ; + + idx.insert(obj); + idx.insert(obj2); + idx.getMatching('aa').length.should.equal(2); + idx.getMatching('aa').indexOf(obj).should.not.equal(-1); + idx.getMatching('aa').indexOf(obj2).should.not.equal(-1); + idx.getMatching('cc').length.should.equal(1); + + idx.remove(obj2); + idx.getMatching('aa').length.should.equal(1); + idx.getMatching('aa').indexOf(obj).should.not.equal(-1); + idx.getMatching('aa').indexOf(obj2).should.equal(-1); + idx.getMatching('cc').length.should.equal(0); + }); + + it('If a unique constraint is violated when inserting an array key, roll back all inserts before the key', function () { + var obj = { tf: ['aa', 'bb'], really: 'yeah' } + , obj2 = { tf: ['cc', 'dd', 'aa', 'ee'], yes: 'indeed' } + , idx = new Index({ fieldName: 'tf', unique: true }) + ; + + idx.insert(obj); + idx.getAll().length.should.equal(2); + idx.getMatching('aa').length.should.equal(1); + idx.getMatching('bb').length.should.equal(1); + idx.getMatching('cc').length.should.equal(0); + idx.getMatching('dd').length.should.equal(0); + idx.getMatching('ee').length.should.equal(0); + + (function () { idx.insert(obj2); }).should.throw(); + idx.getAll().length.should.equal(2); + idx.getMatching('aa').length.should.equal(1); + idx.getMatching('bb').length.should.equal(1); + idx.getMatching('cc').length.should.equal(0); + idx.getMatching('dd').length.should.equal(0); + idx.getMatching('ee').length.should.equal(0); + }); + + }); // ==== End of 'Array fields' ==== // }); // ==== End of 'Insertion' ==== // diff --git a/test/persistence.test.js b/test/persistence.test.js index 7618b41..e7d77ed 100755 --- a/test/persistence.test.js +++ b/test/persistence.test.js @@ -670,7 +670,7 @@ describe('Persistence', function () { fs.existsSync(testDb).should.equal(true); fs.existsSync(testDb + '~').should.equal(false); if (!contents.match(/^{"hello":"world","_id":"[0-9a-zA-Z]{16}"}\n$/)) { - throw "Datafile contents not as expected"; + throw new Error("Datafile contents not as expected"); } done(); }); @@ -697,7 +697,7 @@ describe('Persistence', function () { fs.existsSync(testDb).should.equal(true); fs.existsSync(testDb + '~').should.equal(false); if (!contents.match(/^{"hello":"world","_id":"[0-9a-zA-Z]{16}"}\n$/)) { - throw "Datafile contents not as expected"; + throw new Error("Datafile contents not as expected"); } done(); }); @@ -721,7 +721,7 @@ describe('Persistence', function () { fs.existsSync(testDb).should.equal(true); fs.existsSync(testDb + '~').should.equal(false); if (!contents.match(/^{"hello":"world","_id":"[0-9a-zA-Z]{16}"}\n$/)) { - throw "Datafile contents not as expected"; + throw new Error("Datafile contents not as expected"); } done(); }); @@ -743,7 +743,7 @@ describe('Persistence', function () { fs.existsSync(dbFile).should.equal(true); fs.existsSync(dbFile + '~').should.equal(false); if (contents != "") { - throw "Datafile contents not as expected"; + throw new Error("Datafile contents not as expected"); } done(); });