Housekeeping!

pull/2/head
James M. Greene 9 years ago
parent 5d190f17c5
commit aefe40d0a5
  1. 15
      lib/datastore.js
  2. 21
      lib/indexes.js
  3. 8
      lib/storage.js
  4. 12
      test/db.test.js
  5. 30
      test/executor.test.js
  6. 201
      test/indexes.test.js
  7. 8
      test/persistence.test.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)

@ -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;
};

@ -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);
});
});
});

@ -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'));
}
});
});

@ -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

@ -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' ==== //

@ -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();
});

Loading…
Cancel
Save