treatRawData removed from Datastore

pull/2/head
Louis Chatriot 12 years ago
parent 46c4723fc5
commit 4058b23ddb
  1. 3
      lib/datastore.js
  2. 4
      lib/persistence.js
  3. 245
      test/db.test.js
  4. 280
      test/persistence.test.js

@ -241,9 +241,6 @@ Datastore.prototype.loadDatabase = function () {
}; };
Datastore.treatRawData = Persistence.treatRawData;
/** /**
* Insert a new document * Insert a new document

@ -25,7 +25,7 @@ function Persistence (options) {
if (this.filename && options.nodeWebkitAppName) { if (this.filename && options.nodeWebkitAppName) {
this.filename = Persistence.getNWAppFilename(options.nodeWebkitAppName, this.filename); this.filename = Persistence.getNWAppFilename(options.nodeWebkitAppName, this.filename);
} }
} };
/** /**
@ -37,7 +37,7 @@ Persistence.ensureDirectoryExists = function (dir, cb) {
; ;
mkdirp(dir, function (err) { return callback(err); }); mkdirp(dir, function (err) { return callback(err); });
} };
/** /**

@ -5,8 +5,8 @@ var should = require('chai').should()
, path = require('path') , path = require('path')
, _ = require('underscore') , _ = require('underscore')
, async = require('async') , async = require('async')
, Datastore = require('../lib/datastore')
, model = require('../lib/model') , model = require('../lib/model')
, Datastore = require('../lib/datastore')
, Persistence = require('../lib/persistence') , Persistence = require('../lib/persistence')
; ;
@ -38,7 +38,6 @@ describe('Database', function () {
}); });
} }
], done); ], done);
}); });
it('Constructor compatibility with v0.6-', function () { it('Constructor compatibility with v0.6-', function () {
@ -56,248 +55,6 @@ describe('Database', function () {
}); });
describe('Loading the database data from file and persistence', function () {
it('Every line represents a document', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "3", nested: { today: now } })
, treatedData = Datastore.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(3);
_.isEqual(treatedData[0], { _id: "1", a: 2, ages: [1, 5, 12] }).should.equal(true);
_.isEqual(treatedData[1], { _id: "2", hello: 'world' }).should.equal(true);
_.isEqual(treatedData[2], { _id: "3", nested: { today: now } }).should.equal(true);
});
it('Badly formatted lines have no impact on the treated data', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
'garbage\n' +
model.serialize({ _id: "3", nested: { today: now } })
, treatedData = Datastore.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(2);
_.isEqual(treatedData[0], { _id: "1", a: 2, ages: [1, 5, 12] }).should.equal(true);
_.isEqual(treatedData[1], { _id: "3", nested: { today: now } }).should.equal(true);
});
it('Well formatted lines that have no _id are not included in the data', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ nested: { today: now } })
, treatedData = Datastore.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(2);
_.isEqual(treatedData[0], { _id: "1", a: 2, ages: [1, 5, 12] }).should.equal(true);
_.isEqual(treatedData[1], { _id: "2", hello: 'world' }).should.equal(true);
});
it('If two lines concern the same doc (= same _id), the last one is the good version', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "1", nested: { today: now } })
, treatedData = Datastore.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(2);
_.isEqual(treatedData[0], { _id: "1", nested: { today: now } }).should.equal(true);
_.isEqual(treatedData[1], { _id: "2", hello: 'world' }).should.equal(true);
});
it('If a doc contains $$deleted: true, that means we need to remove it from the data', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "1", $$deleted: true }) + '\n' +
model.serialize({ _id: "3", today: now })
, treatedData = Datastore.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(2);
_.isEqual(treatedData[0], { _id: "2", hello: 'world' }).should.equal(true);
_.isEqual(treatedData[1], { _id: "3", today: now }).should.equal(true);
});
it('If a doc contains $$deleted: true, no error is thrown if the doc wasnt in the list before', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", $$deleted: true }) + '\n' +
model.serialize({ _id: "3", today: now })
, treatedData = Datastore.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(2);
_.isEqual(treatedData[0], { _id: "1", a: 2, ages: [1, 5, 12] }).should.equal(true);
_.isEqual(treatedData[1], { _id: "3", today: now }).should.equal(true);
});
it('Compact database on load', function (done) {
d.insert({ a: 2 }, function () {
d.insert({ a: 4 }, function () {
d.remove({ a: 2 }, {}, function () {
// Here, the underlying file is 3 lines long for only one document
var data = fs.readFileSync(d.filename, 'utf8').split('\n')
, filledCount = 0;
data.forEach(function (item) { if (item.length > 0) { filledCount += 1; } });
filledCount.should.equal(3);
d.loadDatabase(function (err) {
assert.isNull(err);
// Now, the file has been compacted and is only 1 line long
var data = fs.readFileSync(d.filename, 'utf8').split('\n')
, filledCount = 0;
data.forEach(function (item) { if (item.length > 0) { filledCount += 1; } });
filledCount.should.equal(1);
done();
});
})
});
});
});
it('datafileSize is the size of the dataset upon a databaseLoad', function (done) {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "3", nested: { today: now } })
;
d.getAllData().length.should.equal(0);
d.datafileSize.should.equal(0);
fs.writeFile(testDb, rawData, 'utf8', function () {
d.loadDatabase(function () {
d.getAllData().length.should.equal(3);
d.datafileSize.should.equal(3);
d.find({}, function (err, docs) {
docs.sort(function (a, b) { return a._id - b._id; });
docs.length.should.equal(3);
_.isEqual(docs[0], { _id: "1", a: 2, ages: [1, 5, 12] }).should.equal(true);
_.isEqual(docs[1], { _id: "2", hello: 'world' }).should.equal(true);
_.isEqual(docs[2], { _id: "3", nested: { today: now } }).should.equal(true);
done();
});
});
});
});
it('Calling loadDatabase after the data was modified doesnt change its contents', function (done) {
d.loadDatabase(function () {
d.insert({ a: 1 }, function (err) {
assert.isNull(err);
d.insert({ a: 2 }, function (err) {
var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
assert.isNull(err);
data.length.should.equal(2);
doc1.a.should.equal(1);
doc2.a.should.equal(2);
d.loadDatabase(function (err) {
var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
assert.isNull(err);
data.length.should.equal(2);
doc1.a.should.equal(1);
doc2.a.should.equal(2);
done();
});
});
});
});
});
it('Calling loadDatabase after the datafile was removed with will reset the database', function (done) {
d.loadDatabase(function () {
d.insert({ a: 1 }, function (err) {
assert.isNull(err);
d.insert({ a: 2 }, function (err) {
var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
assert.isNull(err);
data.length.should.equal(2);
doc1.a.should.equal(1);
doc2.a.should.equal(2);
fs.unlink(testDb, function (err) {
assert.isNull(err);
d.loadDatabase(function (err) {
assert.isNull(err);
d.getAllData().length.should.equal(0);
done();
});
});
});
});
});
});
it('Calling loadDatabase after the datafile was modified loads the new data', function (done) {
d.loadDatabase(function () {
d.insert({ a: 1 }, function (err) {
assert.isNull(err);
d.insert({ a: 2 }, function (err) {
var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
assert.isNull(err);
data.length.should.equal(2);
doc1.a.should.equal(1);
doc2.a.should.equal(2);
fs.writeFile(testDb, '{"a":3,"_id":"aaa"}', 'utf8', function (err) {
assert.isNull(err);
d.loadDatabase(function (err) {
var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
, doc3 = _.find(data, function (doc) { return doc.a === 3; })
;
assert.isNull(err);
data.length.should.equal(1);
doc3.a.should.equal(3);
assert.isUndefined(doc1);
assert.isUndefined(doc2);
done();
});
});
});
});
});
});
}); // ==== End of 'Loading the database data from file and persistence' ==== //
describe('Insert', function () { describe('Insert', function () {
it('Able to insert a document in the database, setting an _id if none provided, and retrieve it even after a reload', function (done) { it('Able to insert a document in the database, setting an _id if none provided, and retrieve it even after a reload', function (done) {

@ -0,0 +1,280 @@
var should = require('chai').should()
, assert = require('chai').assert
, testDb = 'workspace/test.db'
, fs = require('fs')
, path = require('path')
, _ = require('underscore')
, async = require('async')
, model = require('../lib/model')
, Datastore = require('../lib/datastore')
, Persistence = require('../lib/persistence')
;
describe('Persistence', function () {
var d;
beforeEach(function (done) {
d = new Datastore({ filename: testDb });
d.filename.should.equal(testDb);
d.inMemoryOnly.should.equal(false);
async.waterfall([
function (cb) {
Persistence.ensureDirectoryExists(path.dirname(testDb), function () {
fs.exists(testDb, function (exists) {
if (exists) {
fs.unlink(testDb, cb);
} else { return cb(); }
});
});
}
, function (cb) {
d.loadDatabase(function (err) {
assert.isNull(err);
d.datafileSize.should.equal(0);
d.getAllData().length.should.equal(0);
return cb();
});
}
], done);
});
it('Every line represents a document', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "3", nested: { today: now } })
, treatedData = Persistence.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(3);
_.isEqual(treatedData[0], { _id: "1", a: 2, ages: [1, 5, 12] }).should.equal(true);
_.isEqual(treatedData[1], { _id: "2", hello: 'world' }).should.equal(true);
_.isEqual(treatedData[2], { _id: "3", nested: { today: now } }).should.equal(true);
});
it('Badly formatted lines have no impact on the treated data', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
'garbage\n' +
model.serialize({ _id: "3", nested: { today: now } })
, treatedData = Persistence.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(2);
_.isEqual(treatedData[0], { _id: "1", a: 2, ages: [1, 5, 12] }).should.equal(true);
_.isEqual(treatedData[1], { _id: "3", nested: { today: now } }).should.equal(true);
});
it('Well formatted lines that have no _id are not included in the data', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ nested: { today: now } })
, treatedData = Persistence.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(2);
_.isEqual(treatedData[0], { _id: "1", a: 2, ages: [1, 5, 12] }).should.equal(true);
_.isEqual(treatedData[1], { _id: "2", hello: 'world' }).should.equal(true);
});
it('If two lines concern the same doc (= same _id), the last one is the good version', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "1", nested: { today: now } })
, treatedData = Persistence.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(2);
_.isEqual(treatedData[0], { _id: "1", nested: { today: now } }).should.equal(true);
_.isEqual(treatedData[1], { _id: "2", hello: 'world' }).should.equal(true);
});
it('If a doc contains $$deleted: true, that means we need to remove it from the data', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "1", $$deleted: true }) + '\n' +
model.serialize({ _id: "3", today: now })
, treatedData = Persistence.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(2);
_.isEqual(treatedData[0], { _id: "2", hello: 'world' }).should.equal(true);
_.isEqual(treatedData[1], { _id: "3", today: now }).should.equal(true);
});
it('If a doc contains $$deleted: true, no error is thrown if the doc wasnt in the list before', function () {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", $$deleted: true }) + '\n' +
model.serialize({ _id: "3", today: now })
, treatedData = Persistence.treatRawData(rawData)
;
treatedData.sort(function (a, b) { return a._id - b._id; });
treatedData.length.should.equal(2);
_.isEqual(treatedData[0], { _id: "1", a: 2, ages: [1, 5, 12] }).should.equal(true);
_.isEqual(treatedData[1], { _id: "3", today: now }).should.equal(true);
});
it('Compact database on load', function (done) {
d.insert({ a: 2 }, function () {
d.insert({ a: 4 }, function () {
d.remove({ a: 2 }, {}, function () {
// Here, the underlying file is 3 lines long for only one document
var data = fs.readFileSync(d.filename, 'utf8').split('\n')
, filledCount = 0;
data.forEach(function (item) { if (item.length > 0) { filledCount += 1; } });
filledCount.should.equal(3);
d.loadDatabase(function (err) {
assert.isNull(err);
// Now, the file has been compacted and is only 1 line long
var data = fs.readFileSync(d.filename, 'utf8').split('\n')
, filledCount = 0;
data.forEach(function (item) { if (item.length > 0) { filledCount += 1; } });
filledCount.should.equal(1);
done();
});
})
});
});
});
it('datafileSize is the size of the dataset upon a databaseLoad', function (done) {
var now = new Date()
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "3", nested: { today: now } })
;
d.getAllData().length.should.equal(0);
d.datafileSize.should.equal(0);
fs.writeFile(testDb, rawData, 'utf8', function () {
d.loadDatabase(function () {
d.getAllData().length.should.equal(3);
d.datafileSize.should.equal(3);
d.find({}, function (err, docs) {
docs.sort(function (a, b) { return a._id - b._id; });
docs.length.should.equal(3);
_.isEqual(docs[0], { _id: "1", a: 2, ages: [1, 5, 12] }).should.equal(true);
_.isEqual(docs[1], { _id: "2", hello: 'world' }).should.equal(true);
_.isEqual(docs[2], { _id: "3", nested: { today: now } }).should.equal(true);
done();
});
});
});
});
it('Calling loadDatabase after the data was modified doesnt change its contents', function (done) {
d.loadDatabase(function () {
d.insert({ a: 1 }, function (err) {
assert.isNull(err);
d.insert({ a: 2 }, function (err) {
var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
assert.isNull(err);
data.length.should.equal(2);
doc1.a.should.equal(1);
doc2.a.should.equal(2);
d.loadDatabase(function (err) {
var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
assert.isNull(err);
data.length.should.equal(2);
doc1.a.should.equal(1);
doc2.a.should.equal(2);
done();
});
});
});
});
});
it('Calling loadDatabase after the datafile was removed will reset the database', function (done) {
d.loadDatabase(function () {
d.insert({ a: 1 }, function (err) {
assert.isNull(err);
d.insert({ a: 2 }, function (err) {
var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
assert.isNull(err);
data.length.should.equal(2);
doc1.a.should.equal(1);
doc2.a.should.equal(2);
fs.unlink(testDb, function (err) {
assert.isNull(err);
d.loadDatabase(function (err) {
assert.isNull(err);
d.getAllData().length.should.equal(0);
done();
});
});
});
});
});
});
it('Calling loadDatabase after the datafile was modified loads the new data', function (done) {
d.loadDatabase(function () {
d.insert({ a: 1 }, function (err) {
assert.isNull(err);
d.insert({ a: 2 }, function (err) {
var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
;
assert.isNull(err);
data.length.should.equal(2);
doc1.a.should.equal(1);
doc2.a.should.equal(2);
fs.writeFile(testDb, '{"a":3,"_id":"aaa"}', 'utf8', function (err) {
assert.isNull(err);
d.loadDatabase(function (err) {
var data = d.getAllData()
, doc1 = _.find(data, function (doc) { return doc.a === 1; })
, doc2 = _.find(data, function (doc) { return doc.a === 2; })
, doc3 = _.find(data, function (doc) { return doc.a === 3; })
;
assert.isNull(err);
data.length.should.equal(1);
doc3.a.should.equal(3);
assert.isUndefined(doc1);
assert.isUndefined(doc2);
done();
});
});
});
});
});
});
});
Loading…
Cancel
Save