@rmw/mdbx
Version:
Simple, efficient, scalable data store wrapper for libmdbx
342 lines (336 loc) • 10.8 kB
JavaScript
;
//var inspector = require('inspector'); inspector.open(9330, null, true)
let path = require('path');
let mkdirp = require('mkdirp');
let rimraf = require('rimraf');
let chai = require('chai');
let should = chai.should();
let expect = chai.expect;
let spawn = require('child_process').spawn;
let { open, getLastVersion } = require('..');
const { ArrayLikeIterable } = require('../util/ArrayLikeIterable')
describe('lmdbx-store', function() {
let testDirPath = path.resolve(__dirname, './testdata-ls');
// just to make a reasonable sized chunk of data...
function expand(str) {
str = '(' + str + ')';
str = str + str;
str = str + str;
str = str + str;
str = str + str;
str = str + str;
return str;
}
before(function(done) {
// cleanup previous test directory
rimraf(testDirPath, function(err) {
if (err) {
return done(err);
}
done();
});
});
let testIteration = 1
describe('Basic use', basicTests({ compression: false }));
describe('Basic use with encryption', basicTests({ compression: false, encryptionKey: 'Use this key to encrypt the data' }));
//describe('Check encrypted data', basicTests({ compression: false, checkLast: true }));
describe('Basic use with JSON', basicTests({ encoding: 'json' }));
describe('Basic use with caching', basicTests({ cache: true }));
function basicTests(options) { return function() {
this.timeout(10000);
let db, db2;
before(function() {
db = open(testDirPath + '/test-' + testIteration + '.mdb', Object.assign({
name: 'mydb3',
create: true,
useVersions: true,
compression: {
threshold: 256,
},
}, options));
testIteration++;
if (!options.checkLast)
db.clear();
db2 = db.openDB(Object.assign({
name: 'mydb4',
create: true,
dupSort: true,
}));
if (!options.checkLast)
db2.clear();
});
if (options.checkLast) {
it('encrypted data can not be accessed', function() {
let data = db.get('key1');
console.log({data})
data.should.deep.equal({foo: 1, bar: true})
})
return
}
it('query of keys', async function() {
let keys = [
Symbol.for('test'),
false,
true,
-33,
-1.1,
3.3,
5,
[5,4],
[5,55],
'hello',
['hello', 3],
['hello', 'world'],
[ 'uid', 'I-7l9ySkD-wAOULIjOEnb', 'Rwsu6gqOw8cqdCZG5_YNF' ],
'z'
]
for (let key of keys)
await db.put(key, 3);
let returnedKeys = []
for (let { key, value } of db.getRange({
start: Symbol.for('A')
})) {
returnedKeys.push(key)
}
keys.should.deep.equal(returnedKeys)
});
it('string', async function() {
await db.put('key1', 'Hello world!');
let data = db.get('key1');
data.should.equal('Hello world!');
await db.remove('key1')
let data2 = db.get('key1');
should.equal(data2, undefined);
});
it('string with version', async function() {
await db.put('key1', 'Hello world!', 53252);
let entry = db.getEntry('key1');
entry.value.should.equal('Hello world!');
entry.version.should.equal(53252);
(await db.remove('key1', 33)).should.equal(false);
entry = db.getEntry('key1');
entry.value.should.equal('Hello world!');
entry.version.should.equal(53252);
(await db.remove('key1', 53252)).should.equal(true);
entry = db.getEntry('key1');
should.equal(entry, undefined);
});
it('string with version branching', async function() {
await db.put('key1', 'Hello world!', 53252);
let entry = db.getEntry('key1');
entry.value.should.equal('Hello world!');
entry.version.should.equal(53252);
(await db.ifVersion('key1', 777, () => {
db.put('newKey', 'test', 6);
db2.put('keyB', 'test', 6);
})).should.equal(false);
should.equal(db.get('newKey'), undefined);
should.equal(db2.get('keyB'), undefined);
let result = (await db.ifVersion('key1', 53252, () => {
db.put('newKey', 'test', 6);
db2.put('keyB', 'test', 6);
}))
should.equal(db.get('newKey'), 'test')
should.equal(db2.get('keyB'), 'test')
should.equal(result, true);
result = await db.ifNoExists('key1', () => {
db.put('newKey', 'changed', 7);
})
should.equal(db.get('newKey'), 'test');
should.equal(result, false);
result = await db.ifNoExists('key-no-exist', () => {
db.put('newKey', 'changed', 7);
})
should.equal(db.get('newKey'), 'changed')
should.equal(result, true);
});
it('string with compression and versions', async function() {
let str = expand('Hello world!')
await db.put('key1', str, 53252);
let entry = db.getEntry('key1');
entry.value.should.equal(str);
entry.version.should.equal(53252);
(await db.remove('key1', 33)).should.equal(false);
let data = db.get('key1');
data.should.equal(str);
(await db.remove('key1', 53252)).should.equal(true);
data = db.get('key1');
should.equal(data, undefined);
});
it('store objects', async function() {
let dataIn = {foo: 3, bar: true}
await db.put('key1', dataIn);
let dataOut = db.get('key1');
dataOut.should.deep.equal(dataIn);
db.removeSync('not-there').should.equal(false);
});
it.skip('trigger sync commit', async function() {
let dataIn = {foo: 4, bar: false}
db.immediateBatchThreshold = 1
db.syncBatchThreshold = 1
await db.put('key1', dataIn);
await db.put('key2', dataIn);
db.immediateBatchThreshold = 100000
db.syncBatchThreshold = 1000000
let dataOut = db.get('key1');
dataOut.should.deep.equal(dataIn);
});
it('should iterate over query', async function() {
let data1 = {foo: 1, bar: true}
let data2 = {foo: 2, bar: false}
db.put('key1', data1);
await db.put('key2', data2);
let count = 0
for (let { key, value } of db.getRange({start:'key', end:'keyz'})) {
count++
switch(key) {
case 'key1': data1.should.deep.equal(value); break;
case 'key2': data2.should.deep.equal(value); break;
}
}
count.should.equal(2)
});
it('should iterate over query with offset/limit', async function() {
let data1 = {foo: 1, bar: true}
let data2 = {foo: 2, bar: false}
let data3 = {foo: 3, bar: false}
db.put('key1', data1);
db.put('key2', data2);
await db.put('key3', data3);
let count = 0
for (let { key, value } of db.getRange({start:'key', end:'keyz', offset: 1, limit: 1})) {
count++
switch(key) {
case 'key2': data2.should.deep.equal(value); break;
}
}
count.should.equal(1)
count = 0
for (let { key, value } of db.getRange({start:'key', end:'keyz', offset: 3, limit: 3})) {
count++
}
count.should.equal(0)
for (let { key, value } of db.getRange({start:'key', end:'keyz', offset: 10, limit: 3})) {
count++
}
count.should.equal(0)
for (let { key, value } of db.getRange({start:'key', end:'keyz', offset: 2, limit: 3})) {
count++
switch(key) {
case 'key3': data3.should.deep.equal(value); break;
}
}
count.should.equal(1)
});
it('should iterate over dupsort query, with removal', async function() {
let data1 = {foo: 1, bar: true}
let data2 = {foo: 2, bar: false}
let data3 = {foo: 3, bar: true}
db2.put('key1', data1);
db2.put('key1', data2);
db2.put('key1', data3);
await db2.put('key2', data3);
let count = 0;
for (let value of db2.getValues('key1')) {
count++
switch(count) {
case 1: data1.should.deep.equal(value); break;
case 2: data2.should.deep.equal(value); break;
case 3: data3.should.deep.equal(value); break;
}
}
count.should.equal(3);
await db2.remove('key1', data2);
count = 0;
for (let value of db2.getValues('key1')) {
count++;
switch(count) {
case 1: data1.should.deep.equal(value); break;
case 2: data3.should.deep.equal(value); break;
}
}
count.should.equal(2)
count = 0;
for (let value of db2.getValues('key1', { reverse: true })) {
count++;
switch(count) {
case 1: data3.should.deep.equal(value); break;
case 2: data1.should.deep.equal(value); break;
}
}
count.should.equal(2);
count = 0;
for (let value of db2.getValues('key0')) {
count++;
}
count.should.equal(0);
});
it('should iterate over keys without duplicates', async function() {
let lastKey
for (let key of db2.getKeys({ start: 'k' })) {
if (key == lastKey)
throw new Error('duplicate key returned')
lastKey = key
}
})
it('invalid key', async function() {
expect(() => db.get({ foo: 'bar' })).to.throw();
//expect(() => db.put({ foo: 'bar' }, 'hello')).to.throw();
});
after(function(done) {
db.get('key1');
let iterator = db.getRange({})[Symbol.iterator]()
setTimeout(() => {
db.get('key1');
// should have open read and cursor transactions
db2.close();
db.close();
done();
},10);
});
}}
describe('uint32 keys', function() {
this.timeout(10000);
let db, db2;
before(function() {
db = open(testDirPath, {
name: 'uint32',
keyIsUint32: true,
compression: true,
});
});
it('write and read range', async function() {
let lastPromise
for (let i = 0; i < 10; i++) {
lastPromise = db.put(i, 'value' + i);
}
await lastPromise
let i = 0
for (let { key, value } of db.getRange()) {
key.should.equal(i);
value.should.equal('value' + i);
i++;
}
i = 0
for (let { key, value } of db.getRange({ start: 0 })) {
key.should.equal(i);
value.should.equal('value' + i);
i++;
}
});
after(function() {
db.close();
});
});
describe('ArrayLikeIterable', function() {
it('concat and iterate', async function() {
let a = new ArrayLikeIterable([1, 2, 3])
let b = new ArrayLikeIterable([4, 5, 6])
let all = []
for (let v of a.concat(b)) {
all.push(v)
}
all.should.deep.equal([1, 2, 3, 4, 5, 6])
});
});
});