forerunnerdb
Version:
A NoSQL document store database for browsers and Node.js.
1,050 lines (854 loc) • 30.8 kB
JavaScript
;
QUnit.module('Persist');
ForerunnerDB.moduleLoaded('Persist', function () {
/*QUnit.asyncTest('Persist.load() :: Check for saved databases', function () {
expect(1);
base.dbUp();
db.collection('test1').insert({"test": true});
db.collection('test2').insert({"test": true});
db.collection('test3').insert({"test": true});
db.load(function (err, tableStats, metaStats) {
equal(Boolean(err), false, 'Didn\'t cause an error');
base.dbDown();
start();
});
});*/
QUnit.asyncTest('Persist.load() :: Load un-saved collection', function () {
expect(1);
base.dbUp();
try {
db.collection('random112354234').load(function (err, tableStats, metaStats) {
equal(Boolean(err), false, 'Didn\'t cause an error');
base.dbDown();
start();
});
} catch (e) {
ok(false, 'Caused an error!');
start();
}
});
QUnit.asyncTest('Persist.save() :: Save data to storage and load it back', function () {
expect(13);
base.dbUp();
ok(db.persist.driver(), 'Check that there is a persistent storage driver: ' + db.persist.driver());
var coll = db.collection('test', {
changeTimestamp: true
}),
result,
lastChange;
coll.insert({
name: 'Test'
});
lastChange = coll.metaData().lastChange;
coll.save(function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Save did not produce an error');
}
base.dbDown(false);
base.dbUp();
coll = db.collection('test');
// Make sure the item does not currently exist
result = coll.find();
strictEqual(result.length, 0, 'Check that there are currently no items in the collection');
coll.load(function (err, tableStats, metaStats) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Load did not produce an error');
}
result = coll.find();
strictEqual(result.length, 1, 'Check that items were loaded correctly');
strictEqual(result[0] && result[0].name, 'Test', 'Check that the data loaded holds correct information');
strictEqual(coll.metaData().lastChange.toISOString(), lastChange.toISOString(), 'Collection lastChange flag in metadata is the same as when saved');
// Check the stats objects are correct
strictEqual(typeof tableStats, 'object', 'Table stats is an object');
strictEqual(typeof metaStats, 'object', 'Meta stats is an object');
strictEqual(tableStats.foundData, true, 'Table stats found data');
strictEqual(metaStats.foundData, true, 'Meta stats found data');
strictEqual(tableStats.rowCount, 1, 'Table stats row count correct');
strictEqual(metaStats.rowCount, 0, 'Meta stats row count correct');
base.dbDown();
start();
});
});
});
/*QUnit.asyncTest('Persist.auto(true) :: Save data to storage and load it back in auto-mode', function () {
expect(7);
base.dbUp();
ok(db.persist.driver(), 'Check that there is a persistent storage driver: ' + db.persist.driver());
var coll = db.collection('test', {
changeTimestamp: true
}),
result,
lastChange;
db.persist.auto(true);
coll.once('save', function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Save did not produce an error');
}
base.dbDown(false);
base.dbUp();
db.persist.auto(true);
coll = db.collection('test');
// Make sure the item does not currently exist
result = coll.find();
strictEqual(result.length, 0, 'Check that there are currently no items in the collection');
coll.once('load', function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Load did not produce an error');
}
result = coll.find();
strictEqual(result.length, 1, 'Check that items were loaded correctly');
strictEqual(result[0] && result[0].name, 'Test', 'Check that the data loaded holds correct information');
strictEqual(coll.metaData().lastChange.toISOString(), lastChange.toISOString(), 'Collection lastChange flag in metadata is the same as when saved');
base.dbDown();
start();
});
});
coll.insert({
name: 'Test'
});
lastChange = coll.metaData().lastChange;
});*/
QUnit.asyncTest('Persist.save() :: Save data to multiple database storage with same collection names', function () {
expect(18);
var fdb = new ForerunnerDB(),
db1 = fdb.db('db1'),
db2 = fdb.db('db2');
ok(db1.persist.driver(), 'Check that there is a persistent storage driver db1: ' + db1.persist.driver());
ok(db2.persist.driver(), 'Check that there is a persistent storage driver db2: ' + db2.persist.driver());
var coll1 = db1.collection('test'),
coll2 = db2.collection('test'),
result;
coll1.insert({
name: 'Test1'
});
coll2.insert({
name: 'Test2'
});
coll1.save(function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Save did not produce an error');
}
coll2.save(function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Save did not produce an error');
}
// Drop the databases but don't drop persistent storage
db1.drop(false);
db2.drop(false);
db1 = fdb.db('db1');
db2 = fdb.db('db2');
coll1 = db1.collection('test');
coll2 = db2.collection('test');
// Make sure the item does not currently exist
result = coll1.find();
strictEqual(result.length, 0, 'Check that there are currently no items in the test collection for db1');
result = coll2.find();
strictEqual(result.length, 0, 'Check that there are currently no items in the test collection for db2');
coll1.load(function (err, tableStats, metaStats) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Load did not produce an error');
}
coll2.load(function (err, tableStats, metaStats) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Load did not produce an error');
}
result = coll1.find();
strictEqual(result.length, 1, 'Check that items were loaded correctly');
strictEqual(result[0] && result[0].name, 'Test1', 'Check that the data loaded holds correct information');
result = coll2.find();
strictEqual(result.length, 1, 'Check that items were loaded correctly');
strictEqual(result[0] && result[0].name, 'Test2', 'Check that the data loaded holds correct information');
// Fully drop databases including persistent storage
db1.drop(true, function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Drop did not produce an error');
}
db2.drop(true, function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Drop did not produce an error');
}
// Now get them again
db1 = fdb.db('db1');
db2 = fdb.db('db2');
coll1 = db1.collection('test');
coll2 = db2.collection('test');
// Now load data again and check that it has dropped correctly
coll1.load(function (err, tableStats, metaStats) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Load did not produce an error');
}
coll2.load(function (err, tableStats, metaStats) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Load did not produce an error');
}
// Make sure the item does not currently exist
result = coll1.find();
strictEqual(result.length, 0, 'Check that there are currently no items in the test collection for db1');
result = coll2.find();
strictEqual(result.length, 0, 'Check that there are currently no items in the test collection for db2');
start();
});
});
});
});
//start();
});
});
});
});
});
QUnit.asyncTest('Persist.save() :: Select and use plugins', function () {
expect(5);
base.dbUp();
var coll = db.collection('test', {
changeTimestamp: true
}),
result;
db.persist.addStep(new db.shared.plugins.FdbCompress());
db.persist.addStep(new db.shared.plugins.FdbCrypto({
pass: 'testing'
}));
coll.insert({
name: 'Test'
});
coll.save(function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Save did not produce an error');
}
base.dbDown(false);
base.dbUp();
db.persist.addStep(new db.shared.plugins.FdbCompress());
db.persist.addStep(new db.shared.plugins.FdbCrypto({
pass: 'testing'
}));
coll = db.collection('test');
// Make sure the item does not currently exist
result = coll.find();
strictEqual(result.length, 0, 'Check that there are currently no items in the collection');
coll.load(function (err, tableStats, metaStats) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Load did not produce an error');
}
result = coll.find();
strictEqual(result.length, 1, 'Check that items were loaded correctly');
strictEqual(result[0] && result[0].name, 'Test', 'Check that the data loaded holds correct information');
base.dbDown();
start();
});
});
});
QUnit.asyncTest('Persist.save() :: Save large amount of data and ensure load callback is called AFTER it has all been inserted', function () {
expect(7);
var fdb = new ForerunnerDB(),
db = fdb.db('db1'),
items = db.collection('project'),
tmpData = [],
numRecords,
data,
i;
numRecords = 25000;
for (i = 0; i < numRecords; i++) {
data = {
_id: i,
data: 'data ' + i
};
tmpData.push(data);
}
items.insert(tmpData, function (result) {
strictEqual(result.inserted.length === numRecords, true, 'Inserted correct number of rows');
strictEqual(result.failed.length === 0, true, 'No inserted rows failed');
strictEqual(items.count(), numRecords, 'The collection contains the correct number of rows after insert');
items.save(function (err, data) {
strictEqual(Boolean(err), false, 'The save operation did not result in error');
// Drop the database from memory without dropping persistent storage (pass false)
db.drop(false);
// Now get new database reference
db = fdb.db('db1');
// Get new collection reference
items = db.collection('project');
items.load(function (err, tableStats, metaStats) {
strictEqual(Boolean(err), false, 'The load operation did not produce an error');
strictEqual(tableStats.rowCount > 0, true, 'Loaded data contains rows');
strictEqual(items.count(), numRecords, 'Loaded data contains the correct number of rows');
// Now drop the whole database, removing all persistent storage as well
db.drop(true);
start();
});
});
});
});
QUnit.asyncTest('Persist.save() :: Save with empty collection then load back empty collection check that callbacks fire', function () {
expect(7);
var fdb = new ForerunnerDB(),
db = fdb.db('db1'),
items = db.collection('project'),
tmpData = [],
numRecords,
data,
i;
numRecords = 0;
for (i = 0; i < numRecords; i++) {
data = {
_id: i,
data: 'data ' + i
};
tmpData.push(data);
}
items.insert(tmpData, function (result) {
strictEqual(result.inserted.length === numRecords, true, 'Inserted correct number of rows');
strictEqual(result.failed.length === 0, true, 'No inserted rows failed');
strictEqual(items.count(), numRecords, 'The collection contains the correct number of rows after insert');
items.save(function (err, data) {
strictEqual(Boolean(err), false, 'The save operation did not result in error');
// Drop the database from memory without dropping persistent storage (pass false)
db.drop(false);
// Now get new database reference
db = fdb.db('db1');
// Get new collection reference
items = db.collection('project');
items.load(function (err, tableStats, metaStats) {
strictEqual(Boolean(err), false, 'The load operation did not produce an error');
strictEqual(tableStats.rowCount === 0, true, 'Loaded data contains zero rows');
strictEqual(items.count(), numRecords, 'Loaded data contains the correct number of rows');
// Now drop the whole database, removing all persistent storage as well
db.drop(true);
start();
});
});
});
});
QUnit.asyncTest('Persist.save() :: Handle crypto incorrect password', function () {
expect(3);
base.dbUp();
var coll = db.collection('testPersistCrypto', {
changeTimestamp: true
}),
result;
console.log('Adding crypto step for save');
db.persist.addStep(new db.shared.plugins.FdbCrypto({
pass: 'testing'
}));
console.log('Inserting data');
coll.insert({
name: 'Test'
});
console.log('Saving data');
coll.save(function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Save did not produce an error');
}
console.log('Dropping db');
base.dbDown(false);
base.dbUp();
setTimeout(function () {
console.log('Adding crypto step for load');
db.persist.addStep(new db.shared.plugins.FdbCrypto({
pass: 'aaa'
}));
coll = db.collection('testPersistCrypto');
// Make sure the item does not currently exist
console.log('Checking for no data');
result = coll.find();
strictEqual(result.length, 0, 'Check that there are currently no items in the collection');
console.log('Loading data');
coll.load(function (err, tableStats, metaStats) {
ok(err, 'Load produced an error as expected: ' + err);
base.dbDown();
console.log('Test complete');
start();
});
}, 1);
});
});
QUnit.asyncTest('Persist.save() :: Change password after initial save', function () {
expect(12);
base.dbUp();
var coll = db.collection('testPersistCrypto', {
changeTimestamp: true
}),
result,
testObj,
crypto;
testObj = {
name: 'Test'
};
crypto = new db.shared.plugins.FdbCrypto({
pass: 'testing'
});
console.log('Adding crypto step for save');
db.persist.addStep(crypto);
console.log('Inserting data');
coll.insert(testObj);
console.log('Saving data');
coll.save(function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Save did not produce an error');
}
console.log('Dropping db');
base.dbDown(false);
base.dbUp();
db.persist.addStep(crypto);
coll = db.collection('testPersistCrypto');
ok(coll.count() === 0, 'Collection is currently empty after drop');
console.log('Loading data');
coll.load(function (err, tableStats, metaStats) {
ok(!err, 'Loaded data without error');
result = coll.find();
ok(result, 'Result exists');
ok(result[0], 'Result index zero exists');
ok(result[0].name === testObj.name, 'Data is the same and loaded correctly');
// Now change the password
crypto.pass('myNewPassword');
coll.save(function (err) {
if (err) {
console.log(err);
ok(false, err);
} else {
ok(!err, 'Save did not produce an error');
}
base.dbDown(false);
base.dbUp();
db.persist.addStep(crypto);
coll = db.collection('testPersistCrypto');
ok(coll.count() === 0, 'Collection is currently empty after drop');
console.log('Loading data');
coll.load(function (err, tableStats, metaStats) {
ok(!err, 'Loaded data without error');
result = coll.find();
ok(result, 'Result exists');
ok(result[0], 'Result index zero exists');
ok(result[0].name === testObj.name, 'Data is the same and loaded correctly');
base.dbDown();
console.log('Test complete');
start();
});
});
});
});
});
// promisify save
function asyncSave (oCollection) {
return new Promise(function (resolve, reject) {
oCollection.save(function (err) {
if (!err) {
resolve();
} else {
reject(err);
}
})
})
}
// promisify insert
function asyncInsert (oCollection, oContent) {
return new Promise(function (resolve, reject) {
oCollection.insert(oContent, function (result) {
if (result.failed.length === 0) {
resolve(oCollection);
} else {
reject(result.failed);
}
})
})
}
// promisify drop
function asyncDrop (oObject, bDropPersistent) {
return new Promise(function (resolve, reject) {
oObject.drop(bDropPersistent, function (err) {
if (!err) {
resolve();
} else {
reject(err);
}
})
})
}
// promisify find
function asyncFind (oCollection, query) {
return new Promise(function (resolve, reject) {
if (query) {
resolve(oCollection.find(query));
} else {
resolve(oCollection.find());
}
})
}
// promisify size check
function asyncSizeCheck (oDbColl) {
return new Promise(function (resolve, reject) {
oDbColl.persistedSize(function (err, resultMap) {
if (!err) {
resolve(resultMap);
} else {
reject(err);
}
})
})
}
QUnit.asyncTest('Persist.persistedSize() :: collection: save random amount (< 1MB) of data into a collection and expect exactly that size (+ persistence overhead) reported', function () {
// prepare things
var dbName = new Date().toJSON();
var db = fdb.db(dbName);
var expectedSize = 0;
var collName = "testCollPersistedSize";
var bytes = Math.floor((Math.random() * 1000000) + 1);
var char = "a";
// phantomjs doesn't know String.repeat(x)...
var content = "";
for (var i = 0; i < bytes; i++) {
content += char;
}
var doc = {_id: content};
var coll = db.collection(collName);
Promise.resolve().then(function () {
// calc expected size
// also db- and collection-prefixes count toward expected byte size of storage object
// for simplicity's sake, UTF8 names are expected here so .length can be used
var _dbPrefix = db._name + "-" + coll._name;
// also content-prefix of actual stored data count toward expected size of storage object
// shady as of now, but localforage's API doesn't provide getting the "raw storage format wrap" at runtime
var _contentWrap = "json::fdb::[" + "]";
var _metaDataWrap = "json::fdb::{}";
// total bytes to expect in persistence
expectedSize = _dbPrefix.length + _contentWrap.length
+ (_dbPrefix + "-metaData").length + _metaDataWrap.length
+ JSON.stringify(doc).length;
// insert content into collection
return asyncInsert(coll, doc);
}).then(function (coll) {
// persist collection
return asyncSave(coll);
}).then(function () {
// drop DB from mem, but not from persistence
console.log("...dropping db");
return asyncDrop(db, false);
}).then(function () {
// re-create DB in mem
console.log("...recreating db");
return fdb.db(dbName);
}).then(function (dbNew) {
// re-create collection
db = dbNew;
coll = db.collection(collName);
console.log("...recreating collection");
// make sure its empty - the persisted version is of interest, not the runtime version
return asyncFind(coll);
}).then(function (result) {
console.log("...validating empty collection: result: " + result.length);
strictEqual(result.length, 0, 'Check that there are currently no items in the collection');
return true;
}).then(function () {
// check size of persisted collection
return asyncSizeCheck(coll);
}).then(function (resultMap) {
// assertion
console.log("...counted " + resultMap.total + " bytes");
return strictEqual(resultMap.total, expectedSize, resultMap.total + ' bytes were reported correctly');
}).then(function () {
// clean up
console.log("...finally dropping db from persistence layer");
return asyncDrop(db, true);
}).then(function () {
// trigger async QUnit
start();
}).catch(function (err) {
ok(false, err);
console.log(JSON.stringify(err));
});
});
QUnit.asyncTest('Persist.persistedSize() :: DB: save random amount (< 1MB) of data into a random number of collections and expect total + persistence overhead reported', function () {
// prepare things
var dbName = new Date().toJSON();
var db = fdb.db(dbName);
var expectedSize = 0;
var collectionTemplate = "coll-";
var bytes = Math.floor((Math.random() * 1000000) + 1);
// split total bytes up into unequal parts
var aCollections = [];
while (bytes > 0) {
var byteChunk = Math.round(Math.random() * bytes);
aCollections.push(byteChunk);
bytes -= byteChunk;
}
console.info("Test.persistedSize: chunks: " + aCollections);
// this is for easier debugging purposes - content of the collections
var aChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".split("");
Promise.resolve().then(function () {
return Promise.all(aCollections.map(function (byteChunk, index) {
// create a collection for each byte chunk
var coll = db.collection(collectionTemplate + index);
// phantomjs doesn't know String.repeat(x)...
var content = "";
// insert incremented char from chars array
for (var i = 0; i < byteChunk; i++) {
content += aChars[index];
}
var doc = {_id: index, data: content};
// count 'em all
// db- and collection-prefixes count toward expected byte size of storage object
// for simplicity's sake, UTF8 names are expected here so .length can be used
var _dbPrefix = db._name + "-" + coll._name;
// also content-prefix of actual stored data count toward expected size of storage object
// shady as of now, but localforage's API doesn't provide getting the "raw storage format wrap" at runtime
var _contentWrap = "json::fdb::[]";
var _dbSize = _dbPrefix.length + _contentWrap.length + JSON.stringify(doc).length;
console.log("Test.persistedSize: " + _dbPrefix + " length: " + _dbSize);
var _metaDataPrefix = db._name + "-" + coll._name + "-metaData";
var _metaDataWrap = "json::fdb::{}";
var _dbMetaSize = _metaDataPrefix.length + _metaDataWrap.length;
console.log("Test.persistedSize: " + _metaDataPrefix + " length: " + _dbMetaSize);
expectedSize += _dbSize + _dbMetaSize;
// content -> collection
return asyncInsert(coll, doc);
}))
}).then(function (aResults) {
// persist all created collections
return Promise.all(aResults.map(function (coll, index) {
console.log("...persisting collection#" + index);
return asyncSave(coll);
}))
}).then(function () {
// drop DB from mem, but not from persistence
console.log("...dropping db");
return asyncDrop(db, false);
}).then(function () {
// re-create DB in mem
console.log("...recreating db");
return fdb.db(dbName);
}).then(function (dbNew) {
// restore all runtime collections
db = dbNew;
return Promise.all(aCollections.map(function (byteChunk, index) {
// create a collection for each byte chunk
var coll = db.collection(collectionTemplate + index);
console.log("...recreating " + collectionTemplate + index);
// make sure its empty - the persisted version is of interest, not the runtime version
return asyncFind(coll, '');
}))
}).then(function (aResults) {
aResults.map(function (result, index) {
console.log("...validating empty collection#" + index + ": result: " + result.length);
strictEqual(result.length, 0, 'Check that there are currently no items in the collection#' + index);
});
return true;
}).then(function () {
// determine entire DB size
return asyncSizeCheck(db);
}).then(function (resultMap) {
// assertion
console.log("...counted " + resultMap.total + " bytes");
return strictEqual(resultMap.total, expectedSize, resultMap.total + ' bytes were reported correctly');
}).then(function () {
// clean-up
console.log("...finally dropping db from persistence layer");
return asyncDrop(db, true);
}).then(function () {
// trigger async QUnit
start();
}).catch(function (err) {
ok(false, err);
console.log(JSON.stringify(err));
});
});
QUnit.asyncTest('Persist.persistedSize() :: DB + collection: if no data is persisted, return 0 (and not an error)', function () {
// prepare things
var dbName = new Date().toJSON();
var db = fdb.db(dbName);
var collName = "testCollNoData";
var doc = {_id: 4711};
var coll = db.collection(collName);
Promise.resolve().then(function () {
return asyncInsert(coll, doc);
}).then(function () {
// check size of persisted collection
return asyncSizeCheck(coll);
}).then(function (resultMap) {
// assertion
console.log("...counted " + resultMap + " bytes");
return strictEqual(resultMap.total, 0, 'no collection persisted -> 0 bytes were reported correctly');
}).then(function () {
// check size of DB
return asyncSizeCheck(db);
}).then(function (resultMap) {
// assertion
console.log("...counted " + resultMap + " bytes");
return strictEqual(resultMap.total, 0, 'no DB persisted -> 0 bytes were reported correctly');
}).then(function () {
// clean up
console.log("...finally dropping db");
return asyncDrop(db, false);
}).then(function () {
// trigger async QUnit
start();
}).catch(function (err) {
ok(false, err);
console.log(JSON.stringify(err));
});
});
QUnit.asyncTest('Persist.persistedSize() :: DB + collections: even when not re-attached, all persisted collections are reported with their correct size)', function () {
// prepare things
var dbName = new Date().toJSON();
var db = fdb.db(dbName);
var collNames = ["testColl1", "testColl2", "testColl3"];
var colRefs = [];
var doc = {_id: 4711};
// colNames are of same length, so we use the first one for the example calc
// also UTF8 only here, so 1 char = 1 byte -> .length property can be used
var exampleKey = db._name + "-" + collNames[0];
var exampleValue = "json::fdb::[" + JSON.stringify(doc) + "]";
var exampleMetaKey = db._name + "-" + collNames[0] + "-metaData";
var exampleMetaValue = "json::fdb::{}";
var expectedSize = collNames.length *
( (exampleKey + exampleValue).length + (exampleMetaKey + exampleMetaValue).length );
// attach all collections to DB
collNames.forEach(function (col) {
colRefs.push(db.collection(col));
});
Promise.resolve().then(function () {
// insert content for all collections
return Promise.all(colRefs.map(function (colRef) {
return asyncInsert(colRef, doc);
}));
}).then(function () {
// persist all collections
return Promise.all(colRefs.map(function (colRef) {
return asyncSave(colRef);
}));
}).then(function () {
// drop DB from mem, but not from persistence
console.log("...dropping db from memory");
return asyncDrop(db, false);
}).then(function () {
// re-create DB in mem
console.log("...recreating db");
return fdb.db(dbName);
}).then(function (dbNew) {
// restore only first collection and
// make sure its also empty
db = dbNew;
var coll1 = db.collection(collNames[0]);
return asyncFind(coll1, '');
}).then(function (result) {
strictEqual(result.length, 0, 'Check that reattached collection is empty');
return true;
}).then(function () {
// check size of DB
return asyncSizeCheck(db);
}).then(function (resultMap) {
// assertion
console.log("...counted " + resultMap.totalc + " bytes");
return strictEqual(resultMap.total, expectedSize, resultMap.total + ' bytes were reported of all persisted collections, even though only first was attached at runtime');
}).then(function () {
// clean up
console.log("...finally dropping db");
return asyncDrop(db, true);
}).then(function () {
// trigger async QUnit
start();
}).catch(function (err) {
ok(false, err);
console.log(JSON.stringify(err));
});
});
QUnit.asyncTest('Persist.persistedSize() :: collection: compress (well-suited) string when persisting -> persisted size < runtime size', function () {
// prepare things
var dbName = new Date().toJSON();
var db = fdb.db(dbName);
var collName = "testCollPersistedCompressedSize";
var bytes = Math.floor((Math.random() * 100000) + 1);
var char = "z";
// phantomjs doesn't know String.repeat(x)...
// provide an easy to compress string
var content = "";
for (var i = 0; i < bytes; i++) {
content += char;
}
var doc = {_id: 4711, data: content};
var coll = db.collection(collName);
// add compression step
db.persist.addStep(new db.shared.plugins.FdbCompress());
// calc runtime size
// also db- and collection-prefixes count toward expected byte size of storage object
// for simplicity's sake, UTF8 names are expected here so .length can be used
var _dbPrefix = db._name + "-" + coll._name;
// also content-prefix of actual stored data count toward expected size of storage object
// shady as of now, but localforage's API doesn't provide getting the "raw storage format wrap" at runtime
var _contentWrap = "json::fdb::[]";
// total bytes to expect in persistence
var runtimeSize = _dbPrefix.length + _contentWrap.length
+ JSON.stringify(doc).length;
Promise.resolve().then(function () {
// insert content into collection
return asyncInsert(coll, doc);
}).then(function (coll) {
// persist collection
return asyncSave(coll);
}).then(function () {
// check size of persisted collection
return asyncSizeCheck(coll);
}).then(function (resultMap) {
// assertion
console.log("...counted " + resultMap.total + " bytes");
var persistedSize = resultMap.collections[0][1];
return notStrictEqual(persistedSize, runtimeSize, persistedSize + ' bytes in persistence vs '
+ runtimeSize + ' bytes at runtime. Compression ratio: ' + (1 - Number((persistedSize / runtimeSize).toFixed(2))));
}).then(function () {
// clean up
console.log("...finally dropping db from persistence layer");
return asyncDrop(db, true);
}).then(function () {
// trigger async QUnit
start();
}).catch(function (err) {
ok(false, err);
console.log(JSON.stringify(err));
});
});
});