From e609be27de2197c5d4d689455e0b8958e6b39d24 Mon Sep 17 00:00:00 2001 From: Louis Chatriot Date: Sat, 23 Nov 2013 10:38:10 +0100 Subject: [PATCH] ensureDatafileIntegrity completely tested --- lib/datastore.js | 7 --- lib/persistence.js | 14 ++--- test/persistence.test.js | 130 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 129 insertions(+), 22 deletions(-) diff --git a/lib/datastore.js b/lib/datastore.js index 60a719c..863508d 100644 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -542,11 +542,4 @@ Datastore.prototype.remove = function () { }; -var fs = require('fs'); - -console.log(fs.statSync('workspace/test.db')); - - - - module.exports = Datastore; \ No newline at end of file diff --git a/lib/persistence.js b/lib/persistence.js index b428851..ef4705f 100644 --- a/lib/persistence.js +++ b/lib/persistence.js @@ -25,11 +25,11 @@ function Persistence (options) { this.filename = this.db.filename; if (!this.inMemoryOnly && this.filename) { - if (this.filename.charAt(this.filename.length - 1) === '~') { - throw "The datafile name can't end with a ~, which is reserved for automatic backup files"; - } else { - this.tempFilename = this.filename + '~'; - } + if (this.filename.charAt(this.filename.length - 1) === '~') { + throw "The datafile name can't end with a ~, which is reserved for automatic backup files"; + } else { + this.tempFilename = this.filename + '~'; + } } // For NW apps, store data in the same directory where NW stores application data @@ -233,7 +233,7 @@ Persistence.prototype.ensureDatafileIntegrity = function (callback) { fs.exists(self.tempFilename, function (tempFilenameExists) { // Normal case if (filenameExists && !tempFilenameExists) { - return callback(); + return callback(null); } // Process crashed right after renaming filename @@ -256,7 +256,7 @@ Persistence.prototype.ensureDatafileIntegrity = function (callback) { if (stats.size > 0) { fs.unlink(self.tempFilename, function (err) { return callback(err); }); } else { - fs.unlink(self.tempFilename, function (err) { + fs.unlink(self.filename, function (err) { if (err) { return callback(err); } fs.rename(self.tempFilename, self.filename, function (err) { return callback(err); }); }); diff --git a/test/persistence.test.js b/test/persistence.test.js index 7b9ef59..4a71da1 100644 --- a/test/persistence.test.js +++ b/test/persistence.test.js @@ -256,15 +256,129 @@ describe('Persistence', function () { // That's why it is skipped, but all versions of nedb pass this test describe.only('Prevent dataloss when persisting data', function () { - it('Creating a datastore with in memory as true and a bad filename wont cause an error', function () { - new Datastore({ filename: 'workspace/bad.db~', inMemoryOnly: true }); - }) - - it('Creating a persistent datastore with a bad filename will cause an error', function () { - (function () { new Datastore({ filename: 'workspace/bad.db~' }); }).should.throw(); - }) + it('Creating a datastore with in memory as true and a bad filename wont cause an error', function () { + new Datastore({ filename: 'workspace/bad.db~', inMemoryOnly: true }); + }) + + it('Creating a persistent datastore with a bad filename will cause an error', function () { + (function () { new Datastore({ filename: 'workspace/bad.db~' }); }).should.throw(); + }) - it('If system crashes during a loadDatabase, the former version is not lost', function (done) { + it('If no file exists, ensureDatafileIntegrity creates an empty datafile', function (done) { + var p = new Persistence({ db: { inMemoryOnly: false, filename: 'workspace/it.db' } }); + + if (fs.existsSync('workspace/it.db')) { fs.unlinkSync('workspace/it.db'); } + if (fs.existsSync('workspace/it.db~')) { fs.unlinkSync('workspace/it.db~'); } + + fs.existsSync('workspace/it.db').should.equal(false); + fs.existsSync('workspace/it.db~').should.equal(false); + + p.ensureDatafileIntegrity(function (err) { + assert.isNull(err); + fs.existsSync('workspace/it.db').should.equal(true); + fs.existsSync('workspace/it.db~').should.equal(false); + + fs.readFileSync('workspace/it.db', 'utf8').should.equal(''); + + done(); + }); + }); + + it('If only datafile exists, ensureDatafileIntegrity will use it', function (done) { + var p = new Persistence({ db: { inMemoryOnly: false, filename: 'workspace/it.db' } }); + + if (fs.existsSync('workspace/it.db')) { fs.unlinkSync('workspace/it.db'); } + if (fs.existsSync('workspace/it.db~')) { fs.unlinkSync('workspace/it.db~'); } + + fs.writeFileSync('workspace/it.db', 'something', 'utf8'); + + fs.existsSync('workspace/it.db').should.equal(true); + fs.existsSync('workspace/it.db~').should.equal(false); + + p.ensureDatafileIntegrity(function (err) { + assert.isNull(err); + + fs.existsSync('workspace/it.db').should.equal(true); + fs.existsSync('workspace/it.db~').should.equal(false); + + fs.readFileSync('workspace/it.db', 'utf8').should.equal('something'); + + done(); + }); + }); + + it('If only temp datafile exists, ensureDatafileIntegrity will use it', function (done) { + var p = new Persistence({ db: { inMemoryOnly: false, filename: 'workspace/it.db' } }); + + if (fs.existsSync('workspace/it.db')) { fs.unlinkSync('workspace/it.db'); } + if (fs.existsSync('workspace/it.db~')) { fs.unlinkSync('workspace/it.db~'); } + + fs.writeFileSync('workspace/it.db~', 'something', 'utf8'); + + fs.existsSync('workspace/it.db').should.equal(false); + fs.existsSync('workspace/it.db~').should.equal(true); + + p.ensureDatafileIntegrity(function (err) { + assert.isNull(err); + + fs.existsSync('workspace/it.db').should.equal(true); + fs.existsSync('workspace/it.db~').should.equal(false); + + fs.readFileSync('workspace/it.db', 'utf8').should.equal('something'); + + done(); + }); + }); + + it('If both files exist and datafile is not empty, ensureDatafileIntegrity will use the datafile', function (done) { + var p = new Persistence({ db: { inMemoryOnly: false, filename: 'workspace/it.db' } }); + + if (fs.existsSync('workspace/it.db')) { fs.unlinkSync('workspace/it.db'); } + if (fs.existsSync('workspace/it.db~')) { fs.unlinkSync('workspace/it.db~'); } + + fs.writeFileSync('workspace/it.db', 'something', 'utf8'); + fs.writeFileSync('workspace/it.db~', 'other', 'utf8'); + + fs.existsSync('workspace/it.db').should.equal(true); + fs.existsSync('workspace/it.db~').should.equal(true); + + p.ensureDatafileIntegrity(function (err) { + assert.isNull(err); + + fs.existsSync('workspace/it.db').should.equal(true); + fs.existsSync('workspace/it.db~').should.equal(false); + + fs.readFileSync('workspace/it.db', 'utf8').should.equal('something'); + + done(); + }); + }); + + it('If both files exist and datafile is empty, ensureDatafileIntegrity will use the datafile', function (done) { + var p = new Persistence({ db: { inMemoryOnly: false, filename: 'workspace/it.db' } }); + + if (fs.existsSync('workspace/it.db')) { fs.unlinkSync('workspace/it.db'); } + if (fs.existsSync('workspace/it.db~')) { fs.unlinkSync('workspace/it.db~'); } + + fs.writeFileSync('workspace/it.db', '', 'utf8'); + fs.writeFileSync('workspace/it.db~', 'other', 'utf8'); + + fs.existsSync('workspace/it.db').should.equal(true); + fs.existsSync('workspace/it.db~').should.equal(true); + + p.ensureDatafileIntegrity(function (err) { + assert.isNull(err); + + fs.existsSync('workspace/it.db').should.equal(true); + fs.existsSync('workspace/it.db~').should.equal(false); + + fs.readFileSync('workspace/it.db', 'utf8').should.equal('other'); + + done(); + }); + }); + + it.skip('If system crashes during a loadDatabase, the former version is not lost', function (done) { var cp, N = 150000, toWrite = "", i; // Creating a db file with 150k records (a bit long to load)