Son CV dans un terminal web en Javascript! https://terminal-cv.gregandev.fr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1542 lines
46 KiB

1 year ago
'use strict';
var path = require('path');
var rimraf = require('rimraf');
var chai = require('chai');
var fs = require('fs');
var should = chai.should();
var spawn = require('child_process').spawn;
var inspector = require('inspector')
//inspector.open(9330, null, true)
var lmdb = require('..');
const MAX_DB_SIZE = 256 * 1024 * 1024;
describe.skip('Node.js LMDB Bindings', function() {
var testDirPath = path.resolve(__dirname, './testdata');
var testBackupDirPath = path.resolve(__dirname, './testdata/backup');
// 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);
}
// setup clean directory
fs.mkdirSync(testBackupDirPath, { recursive: true })
done();
});
});
it('will construct, open and close an environment', function() {
this.timeout(10000);
var env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10
});
env.close.should.be.a('function');
env.beginTxn.should.be.a('function');
env.openDbi.should.be.a('function');
env.sync.should.be.a('function');
env.resize.should.be.a('function');
env.stat.should.be.a('function');
env.info.should.be.a('function');
env.close();
});
it.skip('will open multiple environments and begin transactions', function() {
this.timeout(10000);
for (let i =0; i < 12; i++) {
var env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath + '/data' + i + '.mdb',
noSubdir: true
});
env.beginTxn();
}
});
describe('Basics', function() {
this.timeout(10000);
var env;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
maxReaders: 422,
mapSize: MAX_DB_SIZE
});
});
after(function() {
env.close();
});
it('will attempt to create two write transactions', function () {
var wtxn1 = env.beginTxn();
(function() {
var wtxn2 = env.beginTxn();
}).should.throw("You have already opened a write transaction in the current process, can't open a second one.");
wtxn1.abort();
});
it('will open a database, begin a transaction and get/put/delete data', function() {
var dbi = env.openDbi({
name: 'mydb1',
create: true
});
var txn = env.beginTxn();
var data = txn.getUtf8(dbi, 'hello');
should.equal(data, undefined);
txn.putUtf8(dbi, 'hello', 'Hello world!');
var data2 = txn.getUtf8(dbi, 'hello');
data2.should.equal('Hello world!');
txn.del(dbi, 'hello');
var data3 = txn.getUtf8(dbi, 'hello');
should.equal(data3, undefined);
txn.commit();
dbi.close();
});
it('env.openDbi should throw an error when invalid parameters are passed', function() {
chai.assert.throw(function () {
env.openDbi();
});
chai.assert.throw(function () {
env.openDbi(null);
});
chai.assert.throw(function () {
env.openDbi(1);
});
});
it('will open a database and empty the database without closing it', function() {
var dbi = env.openDbi({
name: 'mydb1',
create: true
});
dbi.drop({
justFreePages: true
});
dbi.close();
});
it('will open a database, begin a transaction and get/put/delete string data containing zeros', function() {
var dbi = env.openDbi({
name: 'mydb1x',
create: true
});
var txn = env.beginTxn();
var data = txn.getUtf8(dbi, 'hello');
should.equal(data, undefined);
txn.putUtf8(dbi, 'hello', 'Hello \0 world!');
var data2 = txn.getUtf8(dbi, 'hello');
data2.should.equal('Hello \0 world!');
txn.del(dbi, 'hello');
var data3 = txn.getUtf8(dbi, 'hello');
should.equal(data3, undefined);
txn.commit();
dbi.close();
});
it.skip('will check if UTF-16 Buffers can be read as strings', function() {
// The string we want to store using a buffer
var expectedString = 'Hello \0 world!';
// node-lmdb internally stores a terminating zero, so we need to manually emulate that here
// NOTE: this would NEVER work without 'utf16le'!
var buf = Buffer.from(expectedString + '\0', 'utf16le');
var key = 'hello';
// Open dbi and create cursor
var dbi = env.openDbi({
name: 'mydb1xx',
create: true
});
var txn = env.beginTxn();
// Check non-existence of the key
var data1 = txn.getBinary(dbi, key);
should.equal(data1, undefined);
// Store data as binary
txn.putBinary(dbi, key, buf);
// Retrieve data as binary and check
var data2 = txn.getBinary(dbi, key);
should.equal(buf.compare(data2), 0);
// Retrieve same data as string and check
var data3 = txn.getUtf8(dbi, key);
should.equal(data3, expectedString);
// Delete data
txn.del(dbi, key);
// Put new binary data without zero termination
txn.putBinary(dbi, key, Buffer.from(expectedString));
// Verify that you can't read it back as a string
(function () {
var data = txn.getUtf8(dbi, key);
}).should.throw('Invalid zero-terminated UTF-16 string');
// Delete data
txn.del(dbi, key);
// Verify non-existence of data
var data3 = txn.getBinary(dbi, key);
should.equal(data3, undefined);
txn.commit();
dbi.close();
});
it('will throw Javascript error if named database cannot be found', function () {
try {
env.openDbi({
name: 'does-not-exist',
create: false
});
} catch (err) {
err.should.be.an.instanceof(Error);
}
});
it('will get information about an environment', function() {
var info = env.info();
info.mapAddress.should.be.a('number');
info.mapSize.should.be.a('number');
info.lastPageNumber.should.be.a('number');
info.lastTxnId.should.be.a('number');
info.maxReaders.should.be.a('number');
info.numReaders.should.be.a('number');
should.equal(info.mapSize, MAX_DB_SIZE);
should.equal(info.maxReaders, 422);
});
it('will check for open transactions before resizing the mapSize', function() {
var dbi = env.openDbi({
name: 'mydb1',
create: true
});
var info = env.info();
should.equal(info.mapSize, MAX_DB_SIZE);
// Open write transaction
var txn = env.beginTxn();
try {
env.resize(info.mapSize * 2);
} catch (err) {
err.should.be.an.instanceof(Error);
}
txn.abort();
info = env.info();
should.equal(info.mapSize, MAX_DB_SIZE);
dbi.close();
});
it('will resize the mapSize', function() {
var dbi = env.openDbi({
name: 'mydb1',
create: true
});
var info = env.info();
should.equal(info.mapSize, MAX_DB_SIZE);
env.resize(info.mapSize * 2);
info = env.info();
should.equal(info.mapSize, 2 * MAX_DB_SIZE);
dbi.close();
});
it('will get statistics about an environment', function() {
var stat = env.stat();
stat.pageSize.should.be.a('number');
stat.treeDepth.should.be.a('number');
stat.treeBranchPageCount.should.be.a('number');
stat.treeLeafPageCount.should.be.a('number');
stat.entryCount.should.be.a('number');
});
it('will get statistics about a database', function() {
var dbi = env.openDbi({
name: 'mydb2',
create: true
});
var txn = env.beginTxn();
var stat = dbi.stat(txn);
stat.pageSize.should.be.a('number');
stat.treeDepth.should.be.a('number');
stat.treeBranchPageCount.should.be.a('number');
stat.treeLeafPageCount.should.be.a('number');
stat.entryCount.should.be.a('number');
txn.abort();
dbi.close();
});
it('will create a database with a user-supplied transaction', function () {
var txn = env.beginTxn();
var dbi = env.openDbi({
name: 'dbUsingUserSuppliedTxn',
create: true,
txn: txn
});
txn.putUtf8(dbi, 'hello', 'world');
txn.commit();
var txn = env.beginTxn({ readOnly: true });
var str = txn.getUtf8(dbi, 'hello');
should.equal(str, 'world');
txn.abort();
dbi.close();
});
it('will create a database and back it up', function (done) {
var txn = env.beginTxn();
var dbi = env.openDbi({
name: 'backup',
create: true,
txn: txn
});
txn.putUtf8(dbi, 'hello', 'world');
txn.commit();
env.copy(testBackupDirPath, true, (error) => {
done(error)
});
// console.log('sent copy')
});
});
describe('Data types', function() {
this.timeout(10000);
var env;
var dbi;
var txn;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10
});
dbi = env.openDbi({
name: 'mydb3',
create: true,
useVersions: true,
compression: new lmdb.Compression({
threshold: 100,
dictionary: fs.readFileSync(require.resolve('../dict/dict.txt')),
})
});
txn = env.beginTxn();
});
after(function() {
txn.commit();
dbi.close();
env.close();
});
it('string', function() {
txn.putUtf8(dbi, 'key1', 'Hello world!');
var data = txn.getUtf8(dbi, 'key1');
data.should.equal('Hello world!');
txn.del(dbi, 'key1');
var data2 = txn.getUtf8(dbi, 'key1');
should.equal(data2, undefined);
});
it('string with non-latin characters', function() {
txn.putUtf8(dbi, 'k:中华人民共和', '中华人民共和');
var data = txn.getUtf8(dbi, 'k:中华人民共和');
data.should.equal('中华人民共和');
txn.del(dbi, 'k:中华人民共和');
var data2 = txn.getUtf8(dbi, 'k:中华人民共和');
should.equal(data2, undefined);
});
it.skip('string with version', function() {
txn.putUtf8(dbi, 'key1', 'Hello world!', 334);
var data = txn.getUtf8(dbi, 'key1');
data.should.equal('Hello world!');
var lastVersion = lmdb.getLastVersion();
lastVersion.should.equal(334);
txn.del(dbi, 'key1');
var data2 = txn.getUtf8(dbi, 'key1');
should.equal(data2, undefined);
});
it('string with compression', function() {
let value = '{"id":34,"enabled":true,"title":"this is a test that should be using common words of our language and seeing if it is well compressed","children":[{"data":"some more"}]}'
for (let i = 0; i < 0; i++)
value += value;
txn.putUtf8(dbi, 'key1', value);
var data = txn.getUtf8(dbi, 'key1');
data.should.equal(value);
txn.del(dbi, 'key1');
var data2 = txn.getUtf8(dbi, 'key1');
should.equal(data2, undefined);
});
it.skip('string with compression with version', function() {
let value = 'Hello world!'
for (let i = 0; i < 7; i++)
value += value;
txn.putUtf8(dbi, 'key1', value, 5555);
var data = txn.getUtf8(dbi, 'key1');
data.should.equal(value);
var lastVersion = lmdb.getLastVersion();
lastVersion.should.equal(5555);
txn.del(dbi, 'key1');
var data2 = txn.getUtf8(dbi, 'key1');
should.equal(data2, undefined);
});
it.skip('string (zero copy)', function() {
txn.putUtf8(dbi, 'key1', 'Hello world!');
var data = txn.getUtf8Unsafe(dbi, 'key1');
data.should.equal('Hello world!');
txn.del(dbi, 'key1');
var data2 = txn.getUtf8Unsafe(dbi, 'key1');
should.equal(data2, undefined);
});
it('binary', function() {
var buffer = new Buffer('48656c6c6f2c20776f726c6421', 'hex');
txn.putBinary(dbi, ['key2', 2], buffer);
var data = txn.getBinary(dbi, ['key2', 2]);
data.should.deep.equal(buffer);
txn.del(dbi, ['key2', 2]);
var data2 = txn.getBinary(dbi, ['key2', 2]);
should.equal(data2, undefined);
});
it('binary (zero copy)', function() {
var buffer = new Buffer('48656c6c6f2c20776f726c6421', 'hex');
txn.putBinary(dbi, 'key2', buffer);
var buffer2 = new Buffer('58636c6c6f2c20779f324c5414', 'hex');
txn.putBinary(dbi, 'key3', buffer2);
var length = txn.getBinaryUnsafe(dbi, 'key2');
var data = dbi.unsafeBuffer.slice(0, length);
data.toString()
//data.slice(0, buffer.length).should.deep.equal(buffer);
//env.detachBuffer(data.buffer);
var length = txn.getBinaryUnsafe(dbi, 'key3');
var data2 = dbi.unsafeBuffer.slice(0, length);
var byte = data[0]; // make sure we can access it
data.slice(0, buffer2.length).should.deep.equal(buffer2);
//env.detachBuffer(data.buffer);
txn.del(dbi, 'key2');
var data2 = txn.getBinaryUnsafe(dbi, 'key2');
should.equal(data2, undefined);
});
it('binary key', function() {
var buffer = new Buffer('48656c6c6f2c20776f726c6421', 'hex');
var key = new Buffer('key2');
txn.putBinary(dbi, key, buffer);
var data = txn.getBinary(dbi, key);
data.should.deep.equal(buffer);
txn.del(dbi, key, { keyIsBuffer: true });
var data2 = txn.getBinary(dbi, key);
should.equal(data2, undefined);
});
it.skip('number', function() {
txn.putNumber(dbi, 'key3', 900719925474099);
var data = txn.getNumber(dbi, 'key3');
data.should.equal(900719925474099);
txn.del(dbi, 'key3');
var data2 = txn.getNumber(dbi, 'key3');
should.equal(data2, undefined);
});
it('string and number key', function() {
txn.putUtf8(dbi, -2.4, 'Hello world!');
var data = txn.getUtf8(dbi, -2.4);
data.should.equal('Hello world!');
txn.del(dbi, -2.4);
var data2 = txn.getUtf8(dbi, -2.4);
should.equal(data2, undefined);
});
it('boolean', function() {
txn.putBoolean(dbi, 'key4', true);
var data = txn.getBoolean(dbi, 'key4');
data.should.equal(true);
txn.putBoolean(dbi, 'key5', false);
var data2 = txn.getBoolean(dbi, 'key5');
data2.should.equal(false);
txn.del(dbi, 'key4');
txn.del(dbi, 'key5');
var data3 = txn.getBoolean(dbi, 'key4');
var data4 = txn.getBoolean(dbi, 'key5');
should.equal(data3, undefined);
should.equal(data4, undefined);
});
});
describe('Multiple transactions', function() {
this.timeout(10000);
var env;
var dbi;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10
});
dbi = env.openDbi({
name: 'mydb4',
create: true,
keyIsUint32: true
});
var txn = env.beginTxn();
txn.putUtf8(dbi, 1, 'Hello1');
txn.putUtf8(dbi, 2, 'Hello2');
txn.commit();
});
after(function() {
dbi.close();
env.close();
});
it('readonly transaction should not see uncommited changes', function() {
var readTxn = env.beginTxn({readOnly: true});
var data = readTxn.getUtf8(dbi, 1);
should.equal(data, 'Hello1');
var writeTxn = env.beginTxn();
writeTxn.putUtf8(dbi, 1, 'Ha ha ha');
var data2 = writeTxn.getUtf8(dbi, 1);
data2.should.equal('Ha ha ha');
var data3 = readTxn.getUtf8(dbi, 1);
should.equal(data3, 'Hello1');
writeTxn.commit();
var data4 = readTxn.getUtf8(dbi, 1);
should.equal(data4, 'Hello1');
readTxn.reset();
readTxn.renew();
var data5 = readTxn.getUtf8(dbi, 1);
should.equal(data5, 'Ha ha ha');
readTxn.abort();
});
it('readonly transaction will throw if tries to write', function() {
var readTxn = env.beginTxn({readOnly: true});
(function() {
readTxn.putUtf8(dbi, 2, 'hööhh');
}).should.throw('Permission denied');
readTxn.abort();
});
});
describe('Cursors, basic operation', function() {
this.timeout(10000);
var env;
var dbi;
var total = 5;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
mapSize: MAX_DB_SIZE
});
dbi = env.openDbi({
name: 'cursor_verybasic',
create: true
});
const txn = env.beginTxn();
let count = 0;
while (count < total) {
let key = "hello\x1E_" + count.toString(16);
let data = key + "_data";
txn.putUtf8(dbi, key, data);
count++;
}
count = 0;
while (count < total) {
let key = count - 2
let data = key + "_data";
txn.putUtf8(dbi, key, data);
count++;
}
count = 0;
while (count < total) {
let key = ["hello_\x1B" + count.toString(16) + '\x1E', count];
let data = key + "_data";
txn.putUtf8(dbi, key, data);
count++;
}
count = 0;
while (count < total) {
let key = [count, "hello_" + count.toString(16)];
let data = key + "_data";
txn.putUtf8(dbi, key, data);
count++;
}
txn.commit();
});
it('will move cursor over values, expects to get correct key', function (done) {
var txn = env.beginTxn({ readOnly: true });
var cursor = new lmdb.Cursor(txn, dbi);
var count;
for (count = 0; count < total; count ++) {
var expectedKey = "hello\x1E_" + count.toString(16);
var key = cursor.goToKey(expectedKey);
should.equal(expectedKey, key);
}
for (count = 0; count < total; count ++) {
var expectedKey = count - 2;
var key = cursor.goToKey(expectedKey);
should.equal(expectedKey, key);
}
for (count = 0; count < total; count ++) {
var expectedKey = ["hello_\x1B" + count.toString(16) + '\x1E', count];
var key = cursor.goToKey(expectedKey);
should.equal(JSON.stringify(expectedKey), JSON.stringify(key));
}
for (count = 0; count < total; count ++) {
var expectedKey = [count, "hello_" + count.toString(16)];
var key = cursor.goToKey(expectedKey);
should.equal(JSON.stringify(expectedKey), JSON.stringify(key));
}
should.equal(count, total);
count = 0;
for (var key = cursor.goToFirst(); key != null; key = cursor.goToNext()) {
var key2 = cursor.goToKey(key);
should.equal(JSON.stringify(key), JSON.stringify(key2));
count ++;
}
should.equal(count, total * 4);
cursor.close();
txn.abort();
done();
});
it.skip('will move cursor over values, expects to get correct key even if key is binary', function (done) {
var txn = env.beginTxn({ readOnly: true });
var cursor = new lmdb.Cursor(txn, dbi);
var count;
for (count = 0; count < total; count ++) {
var expectedKey = "hello_" + count.toString(16);
var binaryKey = new Buffer(expectedKey + "\0", "utf16le");
var key = cursor.goToKey(binaryKey);
should.equal(expectedKey, key);
should.equal(binaryKey.toString("utf16le"), key + "\0");
}
should.equal(count, total);
count = 0;
for (var key = cursor.goToFirst(); key; key = cursor.goToNext()) {
var key2 = cursor.goToKey(new Buffer(key + "\0", "utf16le"), { keyIsBuffer: true });
should.equal(key, key2);
count ++;
}
should.equal(count, total);
cursor.close();
txn.abort();
done();
});
after(function () {
dbi.close();
env.close();
});
});
describe('Cursors', function() {
this.timeout(10000);
var env;
var dbi;
var total = 1000;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
mapSize: MAX_DB_SIZE
});
dbi = env.openDbi({
name: 'mydb5',
create: true,
dupSort: false,
keyIsUint32: true
});
var txn = env.beginTxn();
var c = 0;
while(c < total) {
var buffer = new Buffer(new Array(8));
buffer.writeDoubleBE(c);
txn.putBinary(dbi, c, buffer);
c++;
}
txn.commit();
});
after(function() {
dbi.close();
env.close();
});
it('will move cursor over key/values', function(done) {
var txn = env.beginTxn();
var cursor = new lmdb.Cursor(txn, dbi);
cursor.goToKey(40);
cursor.getCurrentBinary(function(key, value) {
key.should.equal(40);
value.readDoubleBE().should.equal(40);
});
var values = [];
cursor.goToKey(0);
function iterator() {
cursor.getCurrentBinary(function(key, value) {
value.readDoubleBE().should.equal(values.length);
values.push(value);
});
cursor.goToNext();
if (values.length < total) {
// prevent maximum call stack errors
if (values.length % 1000 === 0) {
setImmediate(iterator);
} else {
iterator();
}
} else {
cursor.close();
txn.abort();
done();
}
}
iterator();
});
it.skip('will move cursor over key/values (zero copy)', function(done) {
var txn = env.beginTxn();
var cursor = new lmdb.Cursor(txn, dbi);
cursor.goToKey(40);
cursor.getCurrentBinaryUnsafe(function(key, value) {
key.should.equal(40);
value.readDoubleBE().should.equal(40);
});
var values = [];
cursor.goToKey(0);
function iterator() {
cursor.getCurrentBinaryUnsafe(function(key, value) {
value.readDoubleBE().should.equal(values.length);
values.push(value);
});
cursor.goToNext();
if (values.length < total) {
// prevent maximum call stack errors
if (values.length % 1000 === 0) {
setImmediate(iterator);
} else {
iterator();
}
} else {
cursor.close();
txn.abort();
done();
}
}
iterator();
});
it('will first/last key', function() {
var txn = env.beginTxn();
var cursor = new lmdb.Cursor(txn, dbi);
cursor.goToFirst();
cursor.getCurrentBinary(function(key, value) {
key.should.equal(0);
value.readDoubleBE().should.equal(0);
});
cursor.goToLast();
cursor.getCurrentBinary(function(key, value) {
key.should.equal(total - 1);
value.readDoubleBE().should.equal(total - 1);
});
cursor.close();
txn.abort();
});
});
describe.skip('Cursors, dupsort', function() {
this.timeout(10000);
var env;
var dbi;
var total = 50;
var dataCount = {};
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
mapSize: MAX_DB_SIZE
});
dbi = env.openDbi({
name: 'cursor_dupsort',
create: true,
dupSort: true
});
const txn = env.beginTxn();
var count;
for (count = 0; count < total; count ++) {
let key = "hello_" + count.toString(16);
let data = key + "_data";
dataCount[key] = (count % 4) + 1;
for (var j = 0; j < dataCount[key]; j++) {
txn.putUtf8(dbi, key, data + String(j));
}
}
txn.commit();
});
it('will move cursor over values, expects to get correct key and data', function (done) {
var txn = env.beginTxn({ readOnly: true });
var cursor = new lmdb.Cursor(txn, dbi);
var count;
for (count = 0; count < total; count ++) {
var expectedKey = "hello_" + count.toString(16);
var expectedDataX = expectedKey + "_data";
var key = cursor.goToRange(expectedKey);
should.equal(expectedKey, key);
var data = cursor.getCurrentUtf8();
should.equal(expectedDataX + "0", data);
var dc;
// Iterate over dup keys
dc = 0;
for (var k = cursor.goToFirstDup(); k; k = cursor.goToNextDup()) {
var data = cursor.getCurrentUtf8();
should.equal(expectedKey, k);
should.equal(expectedDataX + String(dc), data);
dc ++;
}
should.equal(dataCount[key], dc);
// Iterate over dup keys by using goToDup first
dc = 0;
for (var k = cursor.goToDup(expectedKey, expectedDataX + "0"); k; k = cursor.goToNextDup()) {
var data = cursor.getCurrentUtf8();
should.equal(expectedKey, k);
should.equal(expectedDataX + String(dc), data);
dc ++;
}
should.equal(dataCount[key], dc);
}
should.equal(count, total);
count = 0;
cursor.close();
txn.abort();
done();
});
after(function () {
dbi.close();
env.close();
});
});
describe('Cursors (with strings)', function() {
this.timeout(10000);
var env;
var dbi;
var total = 1000;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
mapSize: MAX_DB_SIZE
});
dbi = env.openDbi({
name: 'cursorstrings',
create: true,
dupSort: true,
//keyIsUint32: true
});
var txn = env.beginTxn();
var c = 0;
while(c < total) {
txn.putUtf8(dbi, c, c.toString());
c++;
}
txn.commit();
});
after(function() {
dbi.close();
env.close();
});
it.skip('will move cursor over key/values (zero copy)', function(done) {
var txn = env.beginTxn();
var cursor = new lmdb.Cursor(txn, dbi);
cursor.goToKey(40);
cursor.getCurrentStringUnsafe(function(key, value) {
key.should.equal(40);
value.should.equal('40');
});
var values = [];
cursor.goToKey(0);
function iterator() {
cursor.getCurrentStringUnsafe(function(key, value) {
value.should.equal(values.length.toString());
values.push(value);
});
cursor.goToNext();
if (values.length < total) {
// prevent maximum call stack errors
if (values.length % 1000 === 0) {
setImmediate(iterator);
} else {
iterator();
}
} else {
cursor.close();
txn.abort();
done();
}
}
iterator();
});
it('will move cursor over key/values', function(done) {
var txn = env.beginTxn();
var cursor = new lmdb.Cursor(txn, dbi);
cursor.goToKey(40);
cursor.getCurrentUtf8(function(key, value) {
key.should.equal(40);
value.should.equal('40');
});
var values = [];
cursor.goToKey(0);
function iterator() {
cursor.getCurrentUtf8(function(key, value) {
value.should.equal(values.length.toString());
values.push(value);
});
cursor.goToNext();
if (values.length < total) {
// prevent maximum call stack errors
if (values.length % 1000 === 0) {
setImmediate(iterator);
} else {
iterator();
}
} else {
cursor.close();
txn.abort();
done();
}
}
iterator();
});
it('will first/last key', function(done) {
var txn = env.beginTxn();
var cursor = new lmdb.Cursor(txn, dbi);
cursor.goToFirst();
cursor.getCurrentUtf8(function(key, value) {
key.should.equal(0);
value.should.equal('0');
});
cursor.goToLast();
cursor.getCurrentUtf8(function(key, value) {
key.should.equal(total - 1);
value.should.equal((total - 1).toString());
});
cursor.close();
txn.abort();
done();
});
});
describe('Remove last item', function() {
this.timeout(10000);
var env;
var dbi;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
mapSize: MAX_DB_SIZE
});
dbi = env.openDbi({
name: 'removelast',
create: true,
keyIsBuffer: true
});
});
after(function() {
dbi.close();
env.close();
});
it('remove last entry', function() {
for (let i = 0; i < 10; i++) {
var readTxn = env.beginTxn({readOnly: true});
var txn = env.beginTxn();
txn.putBinary(dbi, Buffer.from([1]), Buffer.from([1]));
txn.putBinary(dbi, Buffer.from([2]), Buffer.from([1]));
txn.commit();
var txn = env.beginTxn();
txn.del(dbi, Buffer.from([1]));
txn.del(dbi, Buffer.from([2]));
txn.commit();
readTxn.commit();
}
})
});
describe('Cursors with binary key and data', function() {
this.timeout(10000);
var env;
var dbi;
var total = 1000;
let padding = '000000000';
let keyEnc = 'utf8';
let valueEnc = 'utf8';
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
mapSize: MAX_DB_SIZE
});
dbi = env.openDbi({
name: 'cursorbinkeydata',
create: true,
keyIsBuffer: true
});
const txn = env.beginTxn();
let count = 0;
while (count < total) {
let keyStr = (padding + count).slice(-padding.length);
let dataStr = expand(keyStr);
let key = Buffer.from(keyStr,keyEnc);
let data = Buffer.from(dataStr,valueEnc) // just for kicks.
txn.putBinary(dbi, key, data);
count++;
}
txn.commit();
});
after(function() {
dbi.close();
env.close();
});
it('will move cursor over key/values', function(done) {
var txn = env.beginTxn();
var cursor = new lmdb.Cursor(txn, dbi);
let expectedKey = (padding + 40).slice(-padding.length);
let key = Buffer.from(expectedKey,keyEnc);
cursor.goToKey(key);
cursor.getCurrentBinary(function(key, value) {
let readKey = key.toString(keyEnc);
let readValue = value.toString(valueEnc);
readKey.should.equal(expectedKey);
readValue.should.equal(expand(expectedKey));
});
let count = 0;
key = cursor.goToFirst();
// key is a string... bug...
//(typeof key).should.not.equal('string');
while (key && count < total+1) { //+1 to run off end if fails to return null
let expectedKey = (padding + count).slice(-padding.length);
cursor.getCurrentBinary(function(key, value) {
(typeof key).should.not.equal('string');
let readKey = key.toString(keyEnc);
let readValue = value.toString(valueEnc);
readKey.should.equal(expectedKey);
readValue.should.equal(expand(expectedKey));
});
key = cursor.goToNext();
(typeof key).should.not.equal('string');
count++;
}
cursor.close();
txn.commit();
count.should.equal(total);
done();
});
});
describe('Cursors reading existing binary key and data', function() {
this.timeout(10000);
var env;
var dbi;
var total = 1000;
let padding = '000000000';
let keyEnc = 'utf8';
let valueEnc = 'utf8';
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
mapSize: MAX_DB_SIZE
});
dbi = env.openDbi({
name: 'cursorbinkeydata',
create: false,
keyIsBuffer: true
});
});
after(function() {
dbi.close();
env.close();
});
it('will move cursor over existing key/values', function(done) {
var txn = env.beginTxn();
var cursor = new lmdb.Cursor(txn, dbi);
let expectedKey = (padding + 40).slice(-padding.length);
let key = Buffer.from(expectedKey,keyEnc);
cursor.goToKey(key);
let rvalue = cursor.getCurrentBinary(function(key, value) {
(typeof key).should.not.equal('string');
let readKey = key.toString(keyEnc);
let readValue = value.toString(valueEnc);
readKey.should.equal(expectedKey);
readValue.should.equal(expand(expectedKey));
});
rvalue.toString(valueEnc).should.equal(expand(expectedKey));
let count = 0;
key = cursor.goToFirst();
(typeof key).should.not.equal('string');
while (key && count < total+1) { //+1 to run off end if fails to return null
let expectedKey = (padding + count).slice(-padding.length);
cursor.getCurrentBinary(function(key, value) {
let readKey = key.toString(keyEnc);
let readValue = value.toString(valueEnc);
readKey.should.equal(expectedKey);
readValue.should.equal(expand(expectedKey));
});
key = cursor.goToNext();
(typeof key).should.not.equal('string');
count++;
}
cursor.close();
txn.commit();
count.should.equal(total);
done();
});
});
describe('Cluster', function() {
this.timeout(10000);
it('will run a cluster of processes with read-only transactions', function(done) {
var child = spawn('node', [path.resolve(__dirname, './cluster')]);
child.stdout.on('data', function(data) {
console.log(data.toString());
});
child.stderr.on('data', function(data) {
console.error(data.toString());
});
child.on('close', function(code) {
code.should.equal(0);
done();
});
});
});
describe('Threads', function() {
this.timeout(10000);
it('will run a group of threads with read-only transactions', function(done) {
var child = spawn('node', [path.resolve(__dirname, './threads')]);
child.stdout.on('data', function(data) {
console.log(data.toString());
});
child.stderr.on('data', function(data) {
console.error(data.toString());
});
child.on('close', function(code) {
code.should.equal(0);
done();
});
});
});
describe.skip('Dupsort', function () {
this.timeout(10000);
var env;
var dbi;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
mapSize: MAX_DB_SIZE
});
});
after(function () {
env.close();
});
beforeEach(function () {
dbi = env.openDbi({
name: 'testdb_dupsort',
create: true,
dupSort: true,
dupFixed: false,
keyIsBuffer: true
});
});
afterEach(function () {
dbi.drop();
});
it('will insert values with different lengths', function(done) {
var txn = env.beginTxn();
var value1 = new Buffer(new Array(8));
var value2 = new Buffer(new Array(4));
txn.putBinary(dbi, new Buffer('id'), value1);
txn.putBinary(dbi, new Buffer('id'), value2);
txn.commit();
var txn2 = env.beginTxn({readonly: true});
var cursor = new lmdb.Cursor(txn2, dbi);
var found = cursor.goToKey(new Buffer('id'));
should.exist(found);
cursor.getCurrentBinary(function(key, value) {
key.toString().should.equal('id');
value.length.should.equal(4);
cursor.goToNext();
cursor.getCurrentBinary(function(key, value) {
key.toString().should.equal('id');
value.length.should.equal(8);
cursor.close();
txn2.abort();
done();
});
});
});
it('will delete dupsort data correctly', function (done) {
var txn;
var cursor;
var key;
// Add test data to database
txn = env.beginTxn();
txn.putNumber(dbi, 100, 1);
txn.putNumber(dbi, 100, 2);
txn.putNumber(dbi, 100, 3);
txn.putNumber(dbi, 100, 4);
txn.putNumber(dbi, 101, 1);
txn.putNumber(dbi, 101, 2);
txn.putNumber(dbi, 101, 3);
txn.putNumber(dbi, 101, 4);
txn.putNumber(dbi, 102, 1);
txn.putNumber(dbi, 102, 2);
txn.putNumber(dbi, 102, 3);
txn.putNumber(dbi, 102, 4);
txn.commit();
// Now delete some data
txn = env.beginTxn();
txn.del(dbi, 101, 2);
txn.del(dbi, 101, 4);
txn.del(dbi, 102, 1);
txn.del(dbi, 102, 3);
txn.commit();
// Verify data
txn = env.beginTxn({ readOnly: true });
cursor = new lmdb.Cursor(txn, dbi);
cursor.goToFirst().readUInt32LE().should.equal(100);
cursor.goToNext().readUInt32LE().should.equal(100);
cursor.goToNext().readUInt32LE().should.equal(100);
cursor.goToNext().readUInt32LE().should.equal(100);
cursor.goToNext().readUInt32LE().should.equal(101);
cursor.goToNext().readUInt32LE().should.equal(101);
cursor.goToNext().readUInt32LE().should.equal(102);
cursor.goToNext().readUInt32LE().should.equal(102);
should.equal(cursor.goToNext(), undefined);
txn.abort();
done();
});
});
describe('Dupfixed', function() {
this.timeout(10000);
var env;
var dbi;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
mapSize: MAX_DB_SIZE
});
dbi = env.openDbi({
name: 'mydb7',
create: true,
dupSort: true,
dupFixed: true,
keyIsBuffer: true
});
});
after(function() {
dbi.close();
env.close();
});
it('will insert values with the same length (inserted with different lengths)', function(done) {
var txn = env.beginTxn();
var value1 = new Buffer(new Array(4));
value1.writeUInt32BE(100);
var value2 = new Buffer(new Array(8));
value2.writeUInt32BE(200);
txn.putBinary(dbi, new Buffer('id'), value1);
txn.putBinary(dbi, new Buffer('id'), value2);
txn.commit();
var txn2 = env.beginTxn({readonly: true});
var cursor = new lmdb.Cursor(txn2, dbi);
var found = cursor.goToKey(new Buffer('id'));
should.exist(found);
cursor.getCurrentBinary(function(key, value) {
key.toString().should.equal('id');
value.length.should.equal(8);
cursor.goToNext();
cursor.getCurrentBinary(function(key, value) {
key.toString().should.equal('id');
value.length.should.equal(8);
cursor.close();
txn2.abort();
done();
});
});
});
});
describe('Memory Freeing / Garbage Collection', function() {
it('should not cause a segment fault', function(done) {
var expectedKey = new Buffer('822285ee315d2b04');
var expectedValue = new Buffer('ec65d632d9168c33350ed31a30848d01e95172931e90984c218ef6b08c1fa90a', 'hex');
var env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 12,
mapSize: MAX_DB_SIZE
});
var dbi = env.openDbi({
name: 'testfree',
create: true,
keyIsBuffer: true
});
var txn = env.beginTxn();
txn.putBinary(dbi, expectedKey, expectedValue);
txn.commit();
var txn2 = env.beginTxn();
var cursor = new lmdb.Cursor(txn2, dbi);
var key;
var value;
cursor.goToFirst();
cursor.getCurrentBinary(function(returnKey, returnValue) {
key = returnKey;
value = returnValue;
});
cursor.close();
txn2.abort();
dbi.close();
env.close();
key.should.deep.equal(expectedKey);
value.compare(expectedValue).should.equal(0);
done();
});
});
describe('Type Conversion', function() {
var env;
var dbi;
var expectedKey = new Buffer('822285ee315d2b04', 'hex');
var expectedValue = new Buffer('ec65d632d9168c33350ed31a30848d01e95172931e90984c218ef6b08c1fa90a', 'hex');
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 12,
mapSize: MAX_DB_SIZE
});
dbi = env.openDbi({
name: 'testkeys',
create: true,
keyIsBuffer: true
});
var txn = env.beginTxn();
txn.putBinary(dbi, expectedKey, expectedValue);
txn.commit();
});
after(function() {
dbi.close();
env.close();
});
it('will be able to convert key to buffer', function(done) {
var txn = env.beginTxn();
var cursor = new lmdb.Cursor(txn, dbi);
cursor.goToFirst();
cursor.getCurrentBinary(function(key, value) {
var keyBuffer = new Buffer(key);
cursor.close();
txn.abort();
keyBuffer.compare(expectedKey).should.equal(0);
value.compare(expectedValue).should.equal(0);
done();
});
});
});
describe('Sync', function() {
var env;
var dbi;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 12,
mapSize: MAX_DB_SIZE
});
dbi = env.openDbi({
name: 'testsync',
create: true,
keyIsBuffer: true
});
});
after(function() {
dbi.close();
env.close();
});
it('should not block promise callbacks', function(done) {
var timeoutResult
new Promise(resolve => {
env.sync(() => {
resolve();
})
}).then(() => {
// this should execute immediately after it is synced, before the timeout, so timeoutResult should be undefined
done(timeoutResult)
});
setTimeout(() => {
timeoutResult = 'Timeout occurred'
}, 100);
});
});
describe('batch', function() {
this.timeout(10000);
var env;
before(function() {
env = new lmdb.Env();
env.open({
keyBuffer: Buffer.alloc(2048),
path: testDirPath,
maxDbs: 10,
maxReaders: 422,
mapSize: MAX_DB_SIZE
});
});
after(function() {
env.close();
});
it.skip('will batchWrite binary data and read it', function(done) {
var dbi = env.openDbi({
name: 'mydb8',
create: true
});
var data = [
[ dbi, Buffer.from([47]), Buffer.from([1, 2]), Buffer.from([5, 2]) ],
[ dbi, Buffer.from([4]), Buffer.from([1, 2]) ],
[ dbi, Buffer.from([5]), Buffer.from([3, 4]) ],
[ dbi, Buffer.from([6]), Buffer.from([5, 6]) ],
[ dbi, Buffer.from([7]) ],
[ dbi, Buffer.from([6]), Buffer.from([7, 8]), Buffer.from([1, 1]) ],
[ dbi, Buffer.from([6]), Buffer.from([7, 8]), Buffer.from([5]) ],
{
db: dbi,
key: Buffer.from([5]),
value: Buffer.from([8, 9]),
ifValue: Buffer.from([7]),
ifKey: Buffer.from([6]),
ifExactMatch: false,
}
];
env.batchWrite(data, { keyIsBuffer: true, progress(results) {
//console.log('progress', results)
} }, function(error, results) {
if (error) {
should.fail(error);
return done();
}
results.should.deep.equal([ 1, 0, 0, 0, 2, 1, 0, 0 ]);
var txn = env.beginTxn();
var expectedData = [
[ Buffer.from([4]), Buffer.from([1, 2]) ],
[ Buffer.from([5]), Buffer.from([8, 9]) ],
[ Buffer.from([7]) ],
[ Buffer.from([6]), Buffer.from([7, 8]) ],
];
for (var i = 0; i < expectedData.length; i++) {
var key = expectedData[i][0];
var value = expectedData[i][1];
if (value) {
should.equal(value.equals(txn.getBinary(dbi, key)), true);
}
else
should.equal(txn.getBinary(dbi, key), undefined);
}
txn.commit();
dbi.close();
done();
});
});
it('will batchWrite strings and read it', function(done) {
var dbi = env.openDbi({
name: 'mydb8',
create: true,
useVersions: true,
});
var data = [
dbi,
[ 'key 1', 'this is a test 1', 546 ],
[ 'key 2', 'this is a test 2', 444 ],
[ 'key 3', 'this is a test 3', 643 ]
];
var results = Buffer.alloc(3)
env.batchWrite(data, results, function(error) {
if (error) {
should.fail(error);
return done();
}
var txn = env.beginTxn();
for (var i = 0; i < data.length; i++) {
if (!data[i].length)
continue
var key = data[i][0];
var value = data[i][1];
var version = data[i][2];
if (value)
value.should.equal(txn.getUtf8(dbi, key));
else
should.equal(txn.getUtf8(dbi, key), undefined);
//if (version)
//version.should.equal(lmdb.getLastVersion())
}
txn.commit();
dbi.close();
done();
});
console.log('submitted batch')
});
});
});