metastocle-ms
Version:
Decentralized data storage
560 lines (484 loc) • 18.9 kB
JavaScript
import { assert } from "chai";
import fse from "fs-extra";
import sizeof from "object-sizeof";
import tools from "../tools.js";
import loki from "../../src/db/transports/loki/index.js";
const DatabaseLokiMetastocle = loki();
export default function () {
describe('DatabaseLokiMetastocle', () => {
let loki;
let lastNodeDb;
describe('instance creation', function () {
it('should create an instance', function () {
assert.doesNotThrow(() => loki = new DatabaseLokiMetastocle({
filename: tools.getDbFilePath(this.node)
}));
loki.node = this.node;
lastNodeDb = this.node.db;
this.node.db = loki;
});
});
describe('.init()', function () {
it('should not throw an exception', async function () {
await loki.init();
});
it('should create the db file', async function () {
assert.isTrue(await fse.pathExists(tools.getDbFilePath(this.node)));
});
});
describe('.createCollectionName()', function () {
it('should create the right name', function () {
const name = loki.createCollectionName('test');
assert.equal(name, 'metaTest');
});
});
describe('.createDocumentPrimaryKey()', function () {
it('should create pk', function () {
assert.isString(loki.createDocumentPrimaryKey({}));
});
});
describe('.createDocumentDuplicationKey()', function () {
it('should create duplicate key', function () {
assert.isString(loki.createDocumentDuplicationKey({}));
});
});
describe('.removeDocumentSystemFields()', function () {
it('should remove all system fields', function () {
const doc = loki.removeDocumentSystemFields({ $createdAt: 1, $acceptedAt: 1 });
assert.doesNotHaveAllKeys(doc, ['$createdAt', '$acceptedAt']);
});
it('should remove not all system fields', function () {
const doc = loki.removeDocumentSystemFields({ $createdAt: 1, $acceptedAt: 1 }, ['$acceptedAt']);
assert.hasAllKeys(doc, ['$acceptedAt']);
});
});
describe('.addCollection()', function () {
it('should add the collection', async function () {
const name = 'test';
const collection = await loki.addCollection(name, {});
const fullName = loki.createCollectionName(name);
assert.isObject(collection, 'check the type');
assert.isObject(loki.loki.getCollection(fullName), 'check the loki db collection');
assert.strictEqual(loki.col[fullName], collection, 'check the equivalence');
});
});
describe('.getCollection()', function () {
it('should get the collection', async function () {
const name = 'test';
const collection = await loki.getCollection(name);
const fullName = loki.createCollectionName(name);
assert.strictEqual(loki.col[fullName], collection, loki.loki.getCollection(fullName));
});
});
describe('.emptyCollection()', function () {
it('should empty the collection', async function () {
const name = 'test';
const collection = await loki.getCollection(name);
for (let i = 0; i < 5; i++) {
await collection.insert({ x: i + 1 });
}
await loki.emptyCollection(name);
assert.equal(collection.count(), 0);
});
});
describe('.removeCollection()', function () {
it('should remove the collection', async function () {
const name = 'test';
await loki.removeCollection(name);
const fullName = loki.createCollectionName(name);
assert.isNotOk(loki.loki.getCollection(fullName), 'check the interface');
assert.isNotOk(loki.col[fullName], 'check the loki db collection');
});
});
describe('.normalizeCollections()', function () {
let col;
before(async function () {
await this.node.addCollection('test', { pk: 'id', limit: 2 });
col = loki.col[loki.createCollectionName('test')];
});
it('should normalize pk', async function () {
col.insert({ id: 1, x: 1 });
col.insert({ x: 2 });
assert.equal(col.chain().count(), 2, 'check before');
await loki.normalizeCollections();
assert.equal(col.chain().count(), 1, 'check after');
});
it('should normalize the limitation', async function () {
const count = col.chain().count();
const limit = (await this.node.getCollection('test')).limit;
const length = limit - count;
for (let i = 0; i < length + 1; i++) {
col.insert({ id: i + 2 });
}
assert.equal(col.chain().count(), limit + 1, 'check before');
await loki.normalizeCollections();
assert.equal(col.chain().count(), limit, 'check after');
});
it('should normalize the collection absence', async function () {
await this.node.removeCollection('test');
await loki.normalizeCollections();
assert.isNull(await loki.getCollection('test'));
});
});
describe('.removeCollectionExcessDocumentsByLimit()', function () {
let col;
before(async function () {
await this.node.addCollection('test', { limit: 2 });
col = loki.col[loki.createCollectionName('test')];
});
after(async function () {
await this.node.removeCollection('test');
});
it('should not change the count', async function () {
const count = col.chain().count();
await loki.removeCollectionExcessDocuments('test');
assert.equal(count, col.chain().count());
});
it('should remove the excess documents', async function () {
const limit = (await this.node.getCollection('test')).limit;
let last;
for (let i = 0; i < 4; i++) {
last = i + 2;
col.insert({ id: last, $accessedAt: Date.now() + i });
}
await loki.removeCollectionExcessDocuments('test');
assert.equal(col.chain().count(), limit, 'check the count');
assert.isNotNull(col.findOne({ id: last }), 'check the order');
});
});
describe('.removeCollectionExcessDocumentsBySize()', function () {
let col;
before(async function () {
await this.node.addCollection('test', {});
col = loki.col[loki.createCollectionName('test')];
});
after(async function () {
await this.node.removeCollection('test');
});
it('should not change the count', async function () {
const count = col.chain().count();
await loki.removeCollectionExcessDocuments('test');
assert.equal(count, col.chain().count());
});
it('should remove the excess documents', async function () {
const collection = await this.node.getCollection('test');
let lastId;
let lastSize;
for (let i = 0; i < 4; i++) {
lastId = i + 1;
const doc = { id: lastId, $accessedAt: Date.now() + i };
lastSize = sizeof(col.insert(doc));
}
const size = sizeof(col.data);
const count = col.chain().count();
collection.maxSize = size - lastSize - 1;
await loki.removeCollectionExcessDocuments('test');
assert.equal(col.chain().count(), count / 2, 'check the count');
assert.isNotNull(col.findOne({ id: lastId }), 'check the order');
});
});
describe('.getCollectionSize()', function () {
let col;
before(async function () {
await this.node.addCollection('test', {});
col = loki.col[loki.createCollectionName('test')];
});
after(async function () {
await this.node.removeCollection('test');
});
it('should get zero', async function () {
assert.equal(await loki.getCollectionSize('test'), 0);
});
it('should get one', async function () {
col.insert({ x: 1 });
assert.equal(await loki.getCollectionSize('test'), 1);
});
});
describe('.addDocument()', function () {
let col;
before(async function () {
await this.node.addCollection('test', {
schema: {
type: 'object',
props: {
id: 'number',
x: 'number',
required: ['id', 'x']
}
},
pk: 'id',
limit: 1
});
col = loki.col[loki.createCollectionName('test')];
});
after(async function () {
await this.node.removeCollection('test');
});
it('should not create document with wrong schema', async function () {
try {
await loki.addDocument('test', { id: 1, y: 2 });
await loki.addDocument('test', { id: 2 });
throw new Error('Fail');
}
catch (err) {
assert.isOk(err.message.match('Wrong'));
}
});
it('should create document with all fields', async function () {
const collection = 'test';
const document = await loki.addDocument(collection, { id: 1, x: 1 });
assert.equal(document.$collection, collection, 'check the collection');
assert.containsAllKeys(document, ['$createdAt', '$accessedAt', '$updatedAt', '$duplicate'], 'check the keys');
});
it('should not create document because of the limitation', async function () {
try {
await loki.addDocument('test', { id: 2, x: 2 });
throw new Error('Fail');
}
catch (err) {
assert.isOk(err.message.match('Too much'));
}
});
it('should remove the queue first item', async function () {
(await this.node.getCollection('test')).queue = true;
await loki.addDocument('test', { id: 2, x: 2 });
const docs = col.find();
assert.lengthOf(docs, 1, 'check the length');
assert.equal(docs[0].x, 2, 'check the content');
});
});
describe('.handleDocument()', function () {
let $collection;
let col;
before(async function () {
$collection = 'test',
await this.node.addCollection($collection, {
pk: 'id',
defaults: {
z: () => 1,
x: 1
},
setters: {
d: () => 1,
t: 1
}
});
col = loki.col[loki.createCollectionName($collection)];
await loki.addDocument($collection, { id: 1, x: 1 });
});
after(async function () {
await this.node.removeCollection($collection);
});
it('should not handle the document because of $collection', async function () {
try {
await loki.handleDocument({ id: 2 });
throw new Error('Fail');
}
catch (err) {
assert.isOk(err.message.match('\\$collection'));
}
});
it('should not handle document because of pk existence', async function () {
try {
await loki.handleDocument({ id: 1, $collection });
throw new Error('Fail');
}
catch (err) {
assert.isOk(err.message.match('already exists'));
}
});
it('should not handle document because of pk existence using options.pks', async function () {
try {
await loki.handleDocument({ id: 2, $collection }, { pks: { 2: {} } });
throw new Error('Fail');
}
catch (err) {
assert.isOk(err.message.match('already exists'));
}
});
it('should not handle document because of a dublication', async function () {
try {
const doc = col.findOne();
await loki.handleDocument({ $duplicate: doc.$duplicate, $collection });
throw new Error('Fail');
}
catch (err) {
assert.isOk(err.message.match('already exists'));
}
});
it('should not handle document because of pk type', async function () {
try {
await loki.handleDocument({ id: [], x: 1, $collection });
throw new Error('Fail');
}
catch (err) {
assert.isOk(err.message.match('a string or a number'));
}
});
it('should handle the right document', async function () {
const doc = await loki.handleDocument({ id: 2, $collection });
assert.equal(doc.id, 2);
});
it('should handle the existent document', async function () {
const doc = await loki.handleDocument(col.findOne());
assert.isObject(doc);
});
it('should handle the default pk', async function () {
const doc = await loki.handleDocument({ x: 1, $collection });
assert.isString(doc.id);
});
it('should handle the default fields', async function () {
const doc = await loki.handleDocument({ $collection });
assert.equal(doc.z, 1, 'check with a function');
assert.equal(doc.x, 1, 'check without a function');
});
it('should handle the setter fields', async function () {
const doc = await loki.handleDocument({ $collection, t: 2, d: 2 });
assert.equal(doc.d, 1, 'check with a function');
assert.equal(doc.t, 1, 'check without a function');
});
});
describe('document preparing', function () {
let $collection;
before(async function () {
$collection = 'test';
await this.node.addCollection($collection, {
pk: 'id',
setters: {
a: v => v + 1,
},
getters: {
a: v => v - 1
}
});
});
describe('.prepareDocumentToSet()', function () {
it('should create the right document', async function () {
const document = { $collection: 'test', y: 1, a: 1 };
const result = await loki.prepareDocumentToSet(Object.assign({}, document));
document.a += 1;
assert.equal(JSON.stringify(document), JSON.stringify(result));
});
});
describe('.prepareDocumentToGet()', function () {
it('should create the right document', async function () {
const document = { $collection: 'test', x: 1, y: 1, a: 2 };
const result = await loki.prepareDocumentToGet(Object.assign({}, document));
document.a -= 1;
assert.equal(JSON.stringify(document), JSON.stringify(result));
});
});
});
describe('documents playing', function () {
let col;
before(async function () {
await this.node.addCollection('test', { pk: 'id' });
col = loki.col[loki.createCollectionName('test')];
});
after(async function () {
await this.node.removeCollection('test');
});
describe('.getDocumentByPk()', function () {
it('should return the document', async function () {
await loki.addDocument('test', { id: 1 });
const doc = await loki.getDocumentByPk('test', 1);
assert.equal(doc.id, 1);
});
it('should return null', async function () {
assert.isNull(await loki.getDocumentByPk('test', 2));
});
});
describe('.getDocuments()', function () {
it('should return an empty array', async function () {
col.chain().find().remove();
assert.isEmpty(await loki.getDocuments('test'));
});
it('should return all documents', async function () {
await loki.addDocument('test', { id: 1 });
await loki.addDocument('test', { id: 2 });
const docs = await loki.getDocuments('test');
assert.lengthOf(docs, 2);
});
});
describe('.accessDocument()', function () {
it('should change $accessedAt property', async function () {
const doc = col.findOne();
const date = doc.$accessedAt;
await tools.wait(5);
await loki.accessDocument(Object.assign({}, doc));
assert.isTrue(col.findOne().$accessedAt > date);
});
});
describe('.accessDocuments()', function () {
it('should change $accessedAt property', async function () {
const docs = col.find();
const dates = docs.map(d => d.$accessedAt);
await tools.wait(5);
await loki.accessDocuments('test', docs.map(d => Object.assign({}, d)));
const newDocs = col.find();
for (let i = 0; i < newDocs.length; i++) {
assert.isTrue(newDocs[i].$accessedAt > dates[i]);
}
});
});
describe('.updateDocument()', function () {
it('should update the document', async function () {
const doc = col.findOne();
const date = doc.$updatedAt;
await tools.wait(5);
await loki.updateDocument(Object.assign({}, doc, { newField: 1 }));
const newDoc = col.findOne();
assert.isTrue(newDoc.$updatedAt > date, 'check the date');
assert.equal(newDoc.newField, 1, 'check the content');
});
});
describe('.updateDocuments()', function () {
it('should update the documents', async function () {
const docs = col.find();
const dates = docs.map(d => d.$updatedAt);
await tools.wait(5);
await loki.updateDocuments('test', docs.map(d => Object.assign({}, d, { newField: 2 })));
const newDocs = col.find();
for (let i = 0; i < newDocs.length; i++) {
assert.isTrue(newDocs[i].$accessedAt > dates[i], 'check the dates');
assert.equal(newDocs[i].newField, 2, 'check the content');
}
});
});
describe('.deleteDocument()', function () {
it('should delete the document', async function () {
const doc = col.findOne();
const count = col.chain().count();
await loki.deleteDocument(doc);
assert.equal(col.chain().count(), count - 1);
});
});
describe('.deleteDocuments()', function () {
it('should delete the document', async function () {
const docs = col.find();
await loki.deleteDocuments('test', docs);
assert.equal(col.chain().count(), 0);
});
});
});
describe('.deinit()', function () {
it('should not throw an exception', async function () {
await loki.deinit();
});
});
describe('reinitialization', () => {
it('should not throw an exception', async function () {
await loki.init();
});
});
describe('.destroy()', function () {
it('should not throw an exception', async function () {
await loki.destroy();
this.node.db = lastNodeDb;
});
it('should remove the db file', async function () {
assert.isFalse(await fse.pathExists(tools.getDbFilePath(this.node)));
});
});
});
}