UNPKG

@soloseng/ganache

Version:

A library and cli to create a local blockchain for fast Ethereum development.

1,158 lines (931 loc) 35.7 kB
'use strict' const test = require('tape') const sinon = require('sinon') const inherits = require('util').inherits const AbstractLevelDOWN = require('../').AbstractLevelDOWN const AbstractIterator = require('../').AbstractIterator const AbstractChainedBatch = require('../').AbstractChainedBatch const testCommon = require('./common')({ test: test, clear: true, factory: function () { return new AbstractLevelDOWN() } }) const rangeOptions = ['gt', 'gte', 'lt', 'lte'] // Test the suite itself as well as the default implementation, // excluding noop operations that can't pass the test suite. require('./leveldown-test')(test, testCommon) require('./manifest-test')(test, testCommon) require('./open-test').all(test, testCommon) require('./open-create-if-missing-test').setUp(test, testCommon) require('./open-create-if-missing-test').tearDown(test, testCommon) require('./open-error-if-exists-test').setUp(test, testCommon) require('./open-error-if-exists-test').tearDown(test, testCommon) require('./del-test').setUp(test, testCommon) require('./del-test').args(test, testCommon) require('./get-test').setUp(test, testCommon) require('./get-test').args(test, testCommon) require('./get-many-test').setUp(test, testCommon) require('./get-many-test').args(test, testCommon) require('./put-test').setUp(test, testCommon) require('./put-test').args(test, testCommon) require('./put-get-del-test').setUp(test, testCommon) require('./put-get-del-test').errorKeys(test, testCommon) require('./put-get-del-test').tearDown(test, testCommon) require('./batch-test').setUp(test, testCommon) require('./batch-test').args(test, testCommon) require('./chained-batch-test').setUp(test, testCommon) require('./chained-batch-test').args(test, testCommon) require('./chained-batch-test').tearDown(test, testCommon) require('./close-test').all(test, testCommon) require('./iterator-test').setUp(test, testCommon) require('./iterator-test').args(test, testCommon) require('./iterator-test').sequence(test, testCommon) require('./iterator-test').tearDown(test, testCommon) require('./iterator-range-test').setUp(test, testCommon) require('./iterator-range-test').tearDown(test, testCommon) require('./async-iterator-test').setup(test, testCommon) require('./async-iterator-test').teardown(test, testCommon) require('./iterator-snapshot-test').setUp(test, testCommon) require('./iterator-snapshot-test').tearDown(test, testCommon) require('./iterator-no-snapshot-test').setUp(test, testCommon) require('./iterator-no-snapshot-test').tearDown(test, testCommon) require('./iterator-seek-test').setUp(test, testCommon) require('./iterator-seek-test').sequence(test, testCommon) require('./iterator-seek-test').tearDown(test, testCommon) require('./clear-test').setUp(test, testCommon) require('./clear-test').args(test, testCommon) require('./clear-test').tearDown(test, testCommon) require('./clear-range-test').setUp(test, testCommon) require('./clear-range-test').tearDown(test, testCommon) function implement (ctor, methods) { function Test () { ctor.apply(this, arguments) } inherits(Test, ctor) for (const k in methods) { Test.prototype[k] = methods[k] } return Test } // Temporary test for browsers // Not supported on Safari < 12 and Safari iOS < 12 test('async generator', async function (t) { let ended = false const end = async () => { await new Promise((resolve) => setTimeout(resolve, 100)) ended = true } async function * generator () { try { yield 1 yield 2 yield 3 yield 4 } finally { // Test that we're always able to cleanup resources await end() } } const res = [] for await (const x of generator()) { res.push(x) if (x === 2) break } t.same(res, [1, 2]) t.is(ended, true) ended = false try { for await (const x of generator()) { res.push(x) if (x === 2) throw new Error('userland error') } } catch (err) { t.is(err.message, 'userland error') } t.same(res, [1, 2, 1, 2]) t.is(ended, true) }) /** * Extensibility */ test('test core extensibility', function (t) { const Test = implement(AbstractLevelDOWN) const test = new Test() t.equal(test.status, 'new', 'status is new') t.end() }) test('test key/value serialization', function (t) { const Test = implement(AbstractLevelDOWN) const test = new Test() ;['', {}, null, undefined, Buffer.alloc(0)].forEach(function (v) { t.ok(test._serializeKey(v) === v, '_serializeKey is an identity function') t.ok(test._serializeValue(v) === v, '_serializeValue is an identity function') }) t.end() }) test('test open() extensibility', function (t) { const spy = sinon.spy() const expectedCb = function () {} const expectedOptions = { createIfMissing: true, errorIfExists: false } const Test = implement(AbstractLevelDOWN, { _open: spy }) const test = new Test('foobar') test.open(expectedCb) t.equal(spy.callCount, 1, 'got _open() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _open() was correct') t.equal(spy.getCall(0).args.length, 2, 'got two arguments') t.deepEqual(spy.getCall(0).args[0], expectedOptions, 'got default options argument') test.open({ options: 1 }, expectedCb) expectedOptions.options = 1 t.equal(spy.callCount, 2, 'got _open() call') t.equal(spy.getCall(1).thisValue, test, '`this` on _open() was correct') t.equal(spy.getCall(1).args.length, 2, 'got two arguments') t.deepEqual(spy.getCall(1).args[0], expectedOptions, 'got expected options argument') t.end() }) test('test close() extensibility', function (t) { const spy = sinon.spy() const expectedCb = function () {} const Test = implement(AbstractLevelDOWN, { _close: spy }) const test = new Test('foobar') test.close(expectedCb) t.equal(spy.callCount, 1, 'got _close() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _close() was correct') t.equal(spy.getCall(0).args.length, 1, 'got one arguments') t.end() }) test('test get() extensibility', function (t) { const spy = sinon.spy() const expectedCb = function () {} const expectedOptions = { asBuffer: true } const expectedKey = 'a key' const Test = implement(AbstractLevelDOWN, { _get: spy }) const test = new Test('foobar') test.get(expectedKey, expectedCb) t.equal(spy.callCount, 1, 'got _get() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _get() was correct') t.equal(spy.getCall(0).args.length, 3, 'got three arguments') t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument') t.deepEqual(spy.getCall(0).args[1], expectedOptions, 'got default options argument') t.equal(spy.getCall(0).args[2], expectedCb, 'got expected cb argument') test.get(expectedKey, { options: 1 }, expectedCb) expectedOptions.options = 1 t.equal(spy.callCount, 2, 'got _get() call') t.equal(spy.getCall(1).thisValue, test, '`this` on _get() was correct') t.equal(spy.getCall(1).args.length, 3, 'got three arguments') t.equal(spy.getCall(1).args[0], expectedKey, 'got expected key argument') t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument') t.equal(spy.getCall(1).args[2], expectedCb, 'got expected cb argument') t.end() }) test('test getMany() extensibility', function (t) { const spy = sinon.spy() const expectedCb = function () {} const expectedOptions = { asBuffer: true } const expectedKey = 'a key' const Test = implement(AbstractLevelDOWN, { _getMany: spy }) const test = new Test('foobar') test.status = 'open' test.getMany([expectedKey], expectedCb) t.equal(spy.callCount, 1, 'got _getMany() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _getMany() was correct') t.equal(spy.getCall(0).args.length, 3, 'got three arguments') t.deepEqual(spy.getCall(0).args[0], [expectedKey], 'got expected keys argument') t.deepEqual(spy.getCall(0).args[1], expectedOptions, 'got default options argument') t.equal(spy.getCall(0).args[2], expectedCb, 'got expected cb argument') test.getMany([expectedKey], { options: 1 }, expectedCb) expectedOptions.options = 1 t.equal(spy.callCount, 2, 'got _getMany() call') t.equal(spy.getCall(1).thisValue, test, '`this` on _getMany() was correct') t.equal(spy.getCall(1).args.length, 3, 'got three arguments') t.deepEqual(spy.getCall(1).args[0], [expectedKey], 'got expected key argument') t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument') t.equal(spy.getCall(1).args[2], expectedCb, 'got expected cb argument') t.end() }) test('test del() extensibility', function (t) { const spy = sinon.spy() const expectedCb = function () {} const expectedOptions = { options: 1 } const expectedKey = 'a key' const Test = implement(AbstractLevelDOWN, { _del: spy }) const test = new Test('foobar') test.del(expectedKey, expectedCb) t.equal(spy.callCount, 1, 'got _del() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _del() was correct') t.equal(spy.getCall(0).args.length, 3, 'got three arguments') t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument') t.deepEqual(spy.getCall(0).args[1], {}, 'got blank options argument') t.equal(spy.getCall(0).args[2], expectedCb, 'got expected cb argument') test.del(expectedKey, expectedOptions, expectedCb) t.equal(spy.callCount, 2, 'got _del() call') t.equal(spy.getCall(1).thisValue, test, '`this` on _del() was correct') t.equal(spy.getCall(1).args.length, 3, 'got three arguments') t.equal(spy.getCall(1).args[0], expectedKey, 'got expected key argument') t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument') t.equal(spy.getCall(1).args[2], expectedCb, 'got expected cb argument') t.end() }) test('test put() extensibility', function (t) { const spy = sinon.spy() const expectedCb = function () {} const expectedOptions = { options: 1 } const expectedKey = 'a key' const expectedValue = 'a value' const Test = implement(AbstractLevelDOWN, { _put: spy }) const test = new Test('foobar') test.put(expectedKey, expectedValue, expectedCb) t.equal(spy.callCount, 1, 'got _put() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _put() was correct') t.equal(spy.getCall(0).args.length, 4, 'got four arguments') t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument') t.equal(spy.getCall(0).args[1], expectedValue, 'got expected value argument') t.deepEqual(spy.getCall(0).args[2], {}, 'got blank options argument') t.equal(spy.getCall(0).args[3], expectedCb, 'got expected cb argument') test.put(expectedKey, expectedValue, expectedOptions, expectedCb) t.equal(spy.callCount, 2, 'got _put() call') t.equal(spy.getCall(1).thisValue, test, '`this` on _put() was correct') t.equal(spy.getCall(1).args.length, 4, 'got four arguments') t.equal(spy.getCall(1).args[0], expectedKey, 'got expected key argument') t.equal(spy.getCall(1).args[1], expectedValue, 'got expected value argument') t.deepEqual(spy.getCall(1).args[2], expectedOptions, 'got blank options argument') t.equal(spy.getCall(1).args[3], expectedCb, 'got expected cb argument') t.end() }) test('test batch([]) (array-form) extensibility', function (t) { const spy = sinon.spy() const expectedCb = function () {} const expectedOptions = { options: 1 } const expectedArray = [ { type: 'put', key: '1', value: '1' }, { type: 'del', key: '2' } ] const Test = implement(AbstractLevelDOWN, { _batch: spy }) const test = new Test('foobar') test.batch(expectedArray, expectedCb) t.equal(spy.callCount, 1, 'got _batch() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _batch() was correct') t.equal(spy.getCall(0).args.length, 3, 'got three arguments') t.deepEqual(spy.getCall(0).args[0], expectedArray, 'got expected array argument') t.deepEqual(spy.getCall(0).args[1], {}, 'got expected options argument') t.equal(spy.getCall(0).args[2], expectedCb, 'got expected callback argument') test.batch(expectedArray, expectedOptions, expectedCb) t.equal(spy.callCount, 2, 'got _batch() call') t.equal(spy.getCall(1).thisValue, test, '`this` on _batch() was correct') t.equal(spy.getCall(1).args.length, 3, 'got three arguments') t.deepEqual(spy.getCall(1).args[0], expectedArray, 'got expected array argument') t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument') t.equal(spy.getCall(1).args[2], expectedCb, 'got expected callback argument') test.batch(expectedArray, null, expectedCb) t.equal(spy.callCount, 3, 'got _batch() call') t.equal(spy.getCall(2).thisValue, test, '`this` on _batch() was correct') t.equal(spy.getCall(2).args.length, 3, 'got three arguments') t.deepEqual(spy.getCall(2).args[0], expectedArray, 'got expected array argument') t.ok(spy.getCall(2).args[1], 'options should not be null') t.equal(spy.getCall(2).args[2], expectedCb, 'got expected callback argument') t.end() }) test('test batch([]) (array-form) with empty array is asynchronous', function (t) { const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _batch: spy }) const test = new Test() let async = false test.batch([], function (err) { t.ifError(err, 'no error') t.ok(async, 'callback is asynchronous') // Assert that asynchronicity is provided by batch() rather than _batch() t.is(spy.callCount, 0, '_batch() call was bypassed') t.end() }) async = true }) test('test chained batch() extensibility', function (t) { const spy = sinon.spy() const expectedCb = function () {} const expectedOptions = { options: 1 } const Test = implement(AbstractLevelDOWN, { _batch: spy }) const test = new Test('foobar') test.batch().put('foo', 'bar').del('bang').write(expectedCb) t.equal(spy.callCount, 1, 'got _batch() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _batch() was correct') t.equal(spy.getCall(0).args.length, 3, 'got three arguments') t.equal(spy.getCall(0).args[0].length, 2, 'got expected array argument') t.deepEqual(spy.getCall(0).args[0][0], { type: 'put', key: 'foo', value: 'bar' }, 'got expected array argument[0]') t.deepEqual(spy.getCall(0).args[0][1], { type: 'del', key: 'bang' }, 'got expected array argument[1]') t.deepEqual(spy.getCall(0).args[1], {}, 'got expected options argument') t.equal(spy.getCall(0).args[2], expectedCb, 'got expected callback argument') test.batch().put('foo', 'bar', expectedOptions).del('bang', expectedOptions).write(expectedOptions, expectedCb) t.equal(spy.callCount, 2, 'got _batch() call') t.equal(spy.getCall(1).thisValue, test, '`this` on _batch() was correct') t.equal(spy.getCall(1).args.length, 3, 'got three arguments') t.equal(spy.getCall(1).args[0].length, 2, 'got expected array argument') t.deepEqual(spy.getCall(1).args[0][0], { type: 'put', key: 'foo', value: 'bar', options: 1 }, 'got expected array argument[0]') t.deepEqual(spy.getCall(1).args[0][1], { type: 'del', key: 'bang', options: 1 }, 'got expected array argument[1]') t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument') t.equal(spy.getCall(1).args[2], expectedCb, 'got expected callback argument') t.end() }) test('test chained batch() with no operations is asynchronous', function (t) { const Test = implement(AbstractLevelDOWN, {}) const test = new Test() let async = false test.batch().write(function (err) { t.ifError(err, 'no error') t.ok(async, 'callback is asynchronous') t.end() }) async = true }) test('test chained batch() (custom _chainedBatch) extensibility', function (t) { const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _chainedBatch: spy }) const test = new Test('foobar') test.batch() t.equal(spy.callCount, 1, 'got _chainedBatch() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _chainedBatch() was correct') test.batch() t.equal(spy.callCount, 2, 'got _chainedBatch() call') t.equal(spy.getCall(1).thisValue, test, '`this` on _chainedBatch() was correct') t.end() }) test('test AbstractChainedBatch extensibility', function (t) { const Test = implement(AbstractChainedBatch) const db = {} const test = new Test(db) t.ok(test.db === db, 'instance has db reference') t.end() }) test('test AbstractChainedBatch expects a db', function (t) { t.plan(1) const Test = implement(AbstractChainedBatch) try { Test() } catch (err) { t.is(err.message, 'First argument must be an abstract-leveldown compliant store') } }) test('test AbstractChainedBatch#write() extensibility', function (t) { const spy = sinon.spy() const spycb = sinon.spy() const Test = implement(AbstractChainedBatch, { _write: spy }) const test = new Test({ test: true }) test.write(spycb) t.equal(spy.callCount, 1, 'got _write() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _write() was correct') t.equal(spy.getCall(0).args.length, 2, 'got two arguments') t.same(spy.getCall(0).args[0], {}, 'got options') // awkward here cause of nextTick & an internal wrapped cb t.equal(typeof spy.getCall(0).args[1], 'function', 'got a callback function') t.equal(spycb.callCount, 0, 'spycb not called') spy.getCall(0).args[1]() t.equal(spycb.callCount, 1, 'spycb called, i.e. was our cb argument') t.end() }) test('test AbstractChainedBatch#write() extensibility with null options', function (t) { const spy = sinon.spy() const Test = implement(AbstractChainedBatch, { _write: spy }) const test = new Test({ test: true }) test.write(null, function () {}) t.equal(spy.callCount, 1, 'got _write() call') t.same(spy.getCall(0).args[0], {}, 'got options') t.end() }) test('test AbstractChainedBatch#write() extensibility with options', function (t) { const spy = sinon.spy() const Test = implement(AbstractChainedBatch, { _write: spy }) const test = new Test({ test: true }) test.write({ test: true }, function () {}) t.equal(spy.callCount, 1, 'got _write() call') t.same(spy.getCall(0).args[0], { test: true }, 'got options') t.end() }) test('test AbstractChainedBatch#put() extensibility', function (t) { const spy = sinon.spy() const expectedKey = 'key' const expectedValue = 'value' const Test = implement(AbstractChainedBatch, { _put: spy }) const test = new Test(testCommon.factory()) const returnValue = test.put(expectedKey, expectedValue) t.equal(spy.callCount, 1, 'got _put call') t.equal(spy.getCall(0).thisValue, test, '`this` on _put() was correct') t.equal(spy.getCall(0).args.length, 3, 'got 3 arguments') t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument') t.equal(spy.getCall(0).args[1], expectedValue, 'got expected value argument') t.same(spy.getCall(0).args[2], {}, 'got expected options argument') t.equal(returnValue, test, 'get expected return value') t.end() }) test('test AbstractChainedBatch#del() extensibility', function (t) { const spy = sinon.spy() const expectedKey = 'key' const Test = implement(AbstractChainedBatch, { _del: spy }) const test = new Test(testCommon.factory()) const returnValue = test.del(expectedKey) t.equal(spy.callCount, 1, 'got _del call') t.equal(spy.getCall(0).thisValue, test, '`this` on _del() was correct') t.equal(spy.getCall(0).args.length, 2, 'got 2 arguments') t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument') t.same(spy.getCall(0).args[1], {}, 'got expected options argument') t.equal(returnValue, test, 'get expected return value') t.end() }) test('test AbstractChainedBatch#clear() extensibility', function (t) { const spy = sinon.spy() const Test = implement(AbstractChainedBatch, { _clear: spy }) const test = new Test(testCommon.factory()) const returnValue = test.clear() t.equal(spy.callCount, 1, 'got _clear call') t.equal(spy.getCall(0).thisValue, test, '`this` on _clear() was correct') t.equal(spy.getCall(0).args.length, 0, 'got zero arguments') t.equal(returnValue, test, 'get expected return value') t.end() }) test('test iterator() extensibility', function (t) { const spy = sinon.spy() const expectedOptions = { options: 1, reverse: false, keys: true, values: true, limit: -1, keyAsBuffer: true, valueAsBuffer: true } const Test = implement(AbstractLevelDOWN, { _iterator: spy }) const test = new Test('foobar') test.iterator({ options: 1 }) t.equal(spy.callCount, 1, 'got _iterator() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _iterator() was correct') t.equal(spy.getCall(0).args.length, 1, 'got one arguments') t.deepEqual(spy.getCall(0).args[0], expectedOptions, 'got expected options argument') t.end() }) test('test AbstractIterator extensibility', function (t) { const Test = implement(AbstractIterator) const db = {} const test = new Test(db) t.ok(test.db === db, 'instance has db reference') t.end() }) test('test AbstractIterator#next() extensibility', function (t) { const spy = sinon.spy() const spycb = sinon.spy() const Test = implement(AbstractIterator, { _next: spy }) const test = new Test({}) test.next(spycb) t.equal(spy.callCount, 1, 'got _next() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _next() was correct') t.equal(spy.getCall(0).args.length, 1, 'got one arguments') // awkward here cause of nextTick & an internal wrapped cb t.equal(typeof spy.getCall(0).args[0], 'function', 'got a callback function') t.equal(spycb.callCount, 0, 'spycb not called') spy.getCall(0).args[0]() t.equal(spycb.callCount, 1, 'spycb called, i.e. was our cb argument') t.end() }) test('test AbstractIterator#end() extensibility', function (t) { const spy = sinon.spy() const expectedCb = function () {} const Test = implement(AbstractIterator, { _end: spy }) const test = new Test({}) test.end(expectedCb) t.equal(spy.callCount, 1, 'got _end() call') t.equal(spy.getCall(0).thisValue, test, '`this` on _end() was correct') t.equal(spy.getCall(0).args.length, 1, 'got one arguments') t.equal(spy.getCall(0).args[0], expectedCb, 'got expected cb argument') t.end() }) test('test clear() extensibility', function (t) { const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _clear: spy }) const db = new Test() const callback = function () {} call([callback], { reverse: false, limit: -1 }) call([null, callback], { reverse: false, limit: -1 }) call([undefined, callback], { reverse: false, limit: -1 }) call([{ custom: 1 }, callback], { custom: 1, reverse: false, limit: -1 }) call([{ reverse: true, limit: 0 }, callback], { reverse: true, limit: 0 }) call([{ reverse: 1 }, callback], { reverse: true, limit: -1 }) call([{ reverse: null }, callback], { reverse: false, limit: -1 }) function call (args, expectedOptions) { db.clear.apply(db, args) t.is(spy.callCount, 1, 'got _clear() call') t.is(spy.getCall(0).thisValue, db, '`this` on _clear() was correct') t.is(spy.getCall(0).args.length, 2, 'got two arguments') t.same(spy.getCall(0).args[0], expectedOptions, 'got expected options argument') t.is(spy.getCall(0).args[1], callback, 'got expected callback argument') spy.resetHistory() } t.end() }) test('test serialization extensibility (get)', function (t) { t.plan(2) const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _get: spy, _serializeKey: function (key) { return key.toUpperCase() } }) const test = new Test() test.get('foo', function () {}) t.is(spy.callCount, 1, 'got _get() call') t.is(spy.getCall(0).args[0], 'FOO', 'got expected key argument') }) test('test serialization extensibility (getMany)', function (t) { t.plan(2) const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _getMany: spy, _serializeKey: function (key) { return key.toUpperCase() } }) const test = new Test() test.status = 'open' test.getMany(['foo', 'bar'], function () {}) t.is(spy.callCount, 1, 'got _getMany() call') t.same(spy.getCall(0).args[0], ['FOO', 'BAR'], 'got expected keys argument') }) test('test serialization extensibility (put)', function (t) { t.plan(5) const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _put: spy, _serializeKey: function (key) { t.equal(key, 'no') return 'foo' }, _serializeValue: function (value) { t.equal(value, 'nope') return 'bar' } }) const test = new Test('foobar') test.put('no', 'nope', function () {}) t.equal(spy.callCount, 1, 'got _put() call') t.equal(spy.getCall(0).args[0], 'foo', 'got expected key argument') t.equal(spy.getCall(0).args[1], 'bar', 'got expected value argument') }) test('test serialization extensibility (del)', function (t) { t.plan(3) const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _del: spy, _serializeKey: function (key) { t.equal(key, 'no') return 'foo' }, _serializeValue: function (value) { t.fail('should not be called') } }) const test = new Test('foobar') test.del('no', function () {}) t.equal(spy.callCount, 1, 'got _del() call') t.equal(spy.getCall(0).args[0], 'foo', 'got expected key argument') t.end() }) test('test serialization extensibility (batch array put)', function (t) { t.plan(5) const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _batch: spy, _serializeKey: function (key) { t.equal(key, 'no') return 'foo' }, _serializeValue: function (value) { t.equal(value, 'nope') return 'bar' } }) const test = new Test('foobar') test.batch([{ type: 'put', key: 'no', value: 'nope' }], function () {}) t.equal(spy.callCount, 1, 'got _batch() call') t.equal(spy.getCall(0).args[0][0].key, 'foo', 'got expected key') t.equal(spy.getCall(0).args[0][0].value, 'bar', 'got expected value') }) test('test serialization extensibility (batch chain put)', function (t) { t.plan(5) const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _batch: spy, _serializeKey: function (key) { t.equal(key, 'no') return 'foo' }, _serializeValue: function (value) { t.equal(value, 'nope') return 'bar' } }) const test = new Test('foobar') test.batch().put('no', 'nope').write(function () {}) t.equal(spy.callCount, 1, 'got _batch() call') t.equal(spy.getCall(0).args[0][0].key, 'foo', 'got expected key') t.equal(spy.getCall(0).args[0][0].value, 'bar', 'got expected value') }) test('test serialization extensibility (batch array del)', function (t) { t.plan(3) const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _batch: spy, _serializeKey: function (key) { t.equal(key, 'no') return 'foo' }, _serializeValue: function (value) { t.fail('should not be called') } }) const test = new Test('foobar') test.batch([{ type: 'del', key: 'no' }], function () {}) t.equal(spy.callCount, 1, 'got _batch() call') t.equal(spy.getCall(0).args[0][0].key, 'foo', 'got expected key') }) test('test serialization extensibility (batch chain del)', function (t) { t.plan(3) const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _batch: spy, _serializeKey: function (key) { t.equal(key, 'no') return 'foo' }, _serializeValue: function (value) { t.fail('should not be called') } }) const test = new Test('foobar') test.batch().del('no').write(function () {}) t.equal(spy.callCount, 1, 'got _batch() call') t.equal(spy.getCall(0).args[0][0].key, 'foo', 'got expected key') }) test('test serialization extensibility (batch array is not mutated)', function (t) { t.plan(7) const spy = sinon.spy() const Test = implement(AbstractLevelDOWN, { _batch: spy, _serializeKey: function (key) { t.equal(key, 'no') return 'foo' }, _serializeValue: function (value) { t.equal(value, 'nope') return 'bar' } }) const test = new Test('foobar') const op = { type: 'put', key: 'no', value: 'nope' } test.batch([op], function () {}) t.equal(spy.callCount, 1, 'got _batch() call') t.equal(spy.getCall(0).args[0][0].key, 'foo', 'got expected key') t.equal(spy.getCall(0).args[0][0].value, 'bar', 'got expected value') t.equal(op.key, 'no', 'did not mutate input key') t.equal(op.value, 'nope', 'did not mutate input value') }) test('test serialization extensibility (iterator range options)', function (t) { t.plan(2) function Test () { AbstractLevelDOWN.call(this) } inherits(Test, AbstractLevelDOWN) Test.prototype._serializeKey = function (key) { t.is(key, 'input') return 'output' } Test.prototype._iterator = function (options) { return new Iterator(this, options) } function Iterator (db, options) { AbstractIterator.call(this, db) t.is(options.gt, 'output') } inherits(Iterator, AbstractIterator) const test = new Test() test.iterator({ gt: 'input' }) }) test('test serialization extensibility (iterator seek)', function (t) { t.plan(3) const spy = sinon.spy() const TestIterator = implement(AbstractIterator, { _seek: spy }) const Test = implement(AbstractLevelDOWN, { _iterator: function () { return new TestIterator(this) }, _serializeKey: function (key) { t.equal(key, 'target') return 'serialized' } }) const test = new Test('foobar') const it = test.iterator() it.seek('target') t.equal(spy.callCount, 1, 'got _seek() call') t.equal(spy.getCall(0).args[0], 'serialized', 'got expected target argument') }) test('test serialization extensibility (clear range options)', function (t) { t.plan(rangeOptions.length * 2) rangeOptions.forEach(function (key) { const Test = implement(AbstractLevelDOWN, { _serializeKey: function (key) { t.is(key, 'input') return 'output' }, _clear: function (options, callback) { t.is(options[key], 'output') } }) const db = new Test() const options = {} options[key] = 'input' db.clear(options, function () {}) }) }) test('clear() does not delete empty or nullish range options', function (t) { const rangeValues = [Buffer.alloc(0), '', null, undefined] t.plan(rangeOptions.length * rangeValues.length) rangeValues.forEach(function (value) { const Test = implement(AbstractLevelDOWN, { _clear: function (options, callback) { rangeOptions.forEach(function (key) { t.ok(key in options, key + ' option should not be deleted') }) } }) const db = new Test() const options = {} rangeOptions.forEach(function (key) { options[key] = value }) db.clear(options, function () {}) }) }) test('.status', function (t) { t.plan(5) t.test('empty prototype', function (t) { const Test = implement(AbstractLevelDOWN) const test = new Test('foobar') t.equal(test.status, 'new') test.open(function (err) { t.error(err) t.equal(test.status, 'open') test.close(function (err) { t.error(err) t.equal(test.status, 'closed') t.end() }) }) t.equal(test.status, 'opening') }) t.test('open error', function (t) { const Test = implement(AbstractLevelDOWN, { _open: function (options, cb) { cb(new Error()) } }) const test = new Test('foobar') test.open(function (err) { t.ok(err) t.equal(test.status, 'new') t.end() }) }) t.test('close error', function (t) { const Test = implement(AbstractLevelDOWN, { _close: function (cb) { cb(new Error()) } }) const test = new Test('foobar') test.open(function () { test.close(function (err) { t.ok(err) t.equal(test.status, 'open') t.end() }) }) }) t.test('open', function (t) { const Test = implement(AbstractLevelDOWN, { _open: function (options, cb) { this._nextTick(cb) } }) const test = new Test('foobar') test.open(function (err) { t.error(err) t.equal(test.status, 'open') t.end() }) t.equal(test.status, 'opening') }) t.test('close', function (t) { const Test = implement(AbstractLevelDOWN, { _close: function (cb) { this._nextTick(cb) } }) const test = new Test('foobar') test.open(function (err) { t.error(err) test.close(function (err) { t.error(err) t.equal(test.status, 'closed') t.end() }) t.equal(test.status, 'closing') }) }) }) test('_setupIteratorOptions', function (t) { const keys = rangeOptions.slice() const db = new AbstractLevelDOWN() function setupOptions (constrFn) { const options = {} keys.forEach(function (key) { options[key] = constrFn() }) return options } function verifyOptions (t, options) { keys.forEach(function (key) { t.ok(key in options, key + ' option should not be deleted') }) t.end() } t.plan(7) t.test('default options', function (t) { t.same(db._setupIteratorOptions(), { reverse: false, keys: true, values: true, limit: -1, keyAsBuffer: true, valueAsBuffer: true }, 'correct defaults') t.end() }) t.test('set options', function (t) { t.same(db._setupIteratorOptions({ reverse: false, keys: false, values: false, limit: 20, keyAsBuffer: false, valueAsBuffer: false }), { reverse: false, keys: false, values: false, limit: 20, keyAsBuffer: false, valueAsBuffer: false }, 'options set correctly') t.end() }) t.test('does not delete empty buffers', function (t) { const options = setupOptions(function () { return Buffer.from('') }) keys.forEach(function (key) { t.is(Buffer.isBuffer(options[key]), true, 'should be buffer') t.is(options[key].length, 0, 'should be empty') }) verifyOptions(t, db._setupIteratorOptions(options)) }) t.test('does not delete empty strings', function (t) { const options = setupOptions(function () { return '' }) keys.forEach(function (key) { t.is(typeof options[key], 'string', 'should be string') t.is(options[key].length, 0, 'should be empty') }) verifyOptions(t, db._setupIteratorOptions(options)) }) t.test('does not delete null', function (t) { const options = setupOptions(function () { return null }) keys.forEach(function (key) { t.is(options[key], null, 'should be null') }) verifyOptions(t, db._setupIteratorOptions(options)) }) t.test('does not delete undefined', function (t) { const options = setupOptions(function () { return undefined }) keys.forEach(function (key) { t.is(options[key], undefined, 'should be undefined') }) verifyOptions(t, db._setupIteratorOptions(options)) }) t.test('rejects legacy range options', function (t) { t.plan(2) for (const key of ['start', 'end']) { const options = {} options[key] = 'x' try { db._setupIteratorOptions(options) } catch (err) { t.is(err.message, 'Legacy range options ("start" and "end") have been removed') } } }) })