From 4058b23ddb30fd8b276e332b99399e89534eb84c Mon Sep 17 00:00:00 2001 From: Louis Chatriot Date: Fri, 12 Jul 2013 15:36:26 +0200 Subject: [PATCH] treatRawData removed from Datastore --- lib/datastore.js | 3 - lib/persistence.js | 4 +- test/db.test.js | 245 +--------------------------------- test/persistence.test.js | 280 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 283 insertions(+), 249 deletions(-) create mode 100644 test/persistence.test.js diff --git a/lib/datastore.js b/lib/datastore.js index 44450d3..139ec2d 100644 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -241,9 +241,6 @@ Datastore.prototype.loadDatabase = function () { }; -Datastore.treatRawData = Persistence.treatRawData; - - /** * Insert a new document diff --git a/lib/persistence.js b/lib/persistence.js index a09331e..149507d 100644 --- a/lib/persistence.js +++ b/lib/persistence.js @@ -25,7 +25,7 @@ function Persistence (options) { if (this.filename && options.nodeWebkitAppName) { this.filename = Persistence.getNWAppFilename(options.nodeWebkitAppName, this.filename); } -} +}; /** @@ -37,7 +37,7 @@ Persistence.ensureDirectoryExists = function (dir, cb) { ; mkdirp(dir, function (err) { return callback(err); }); -} +}; /** diff --git a/test/db.test.js b/test/db.test.js index a66bbb9..c37bfb1 100644 --- a/test/db.test.js +++ b/test/db.test.js @@ -5,8 +5,8 @@ var should = require('chai').should() , path = require('path') , _ = require('underscore') , async = require('async') - , Datastore = require('../lib/datastore') , model = require('../lib/model') + , Datastore = require('../lib/datastore') , Persistence = require('../lib/persistence') ; @@ -38,7 +38,6 @@ describe('Database', function () { }); } ], done); - }); 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 () { it('Able to insert a document in the database, setting an _id if none provided, and retrieve it even after a reload', function (done) { diff --git a/test/persistence.test.js b/test/persistence.test.js new file mode 100644 index 0000000..1f683f3 --- /dev/null +++ b/test/persistence.test.js @@ -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(); + }); + }); + }); + }); + }); + }); + +});