Initial import with skill sheet working

This commit is contained in:
2024-12-04 00:11:23 +01:00
commit 9050c80ab4
4488 changed files with 671048 additions and 0 deletions

View File

@@ -0,0 +1,263 @@
'use strict'
const test = require('tape')
const { AbstractLevel, AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('../..')
const testCommon = require('../common')({
test,
factory: function () {
return new AbstractLevel({ encodings: { utf8: true } })
}
})
for (const Ctor of [AbstractIterator, AbstractKeyIterator, AbstractValueIterator]) {
test(`test ${Ctor.name} extensibility`, function (t) {
const Test = class TestIterator extends Ctor {}
const db = testCommon.factory()
const test = new Test(db, {})
t.ok(test.db === db, 'instance has db reference')
t.end()
})
test(`${Ctor.name} throws on invalid db argument`, function (t) {
t.plan(4 * 2)
for (const args of [[], [null], [undefined], 'foo']) {
const hint = args[0] === null ? 'null' : typeof args[0]
try {
// eslint-disable-next-line no-new
new Ctor(...args)
} catch (err) {
t.is(err.name, 'TypeError')
t.is(err.message, 'The first argument must be an abstract-level database, received ' + hint)
}
}
})
test(`${Ctor.name} throws on invalid options argument`, function (t) {
t.plan(4 * 2)
for (const args of [[], [null], [undefined], 'foo']) {
try {
// eslint-disable-next-line no-new
new Ctor({}, ...args)
} catch (err) {
t.is(err.name, 'TypeError')
t.is(err.message, 'The second argument must be an options object')
}
}
})
test(`${Ctor.name}.next() extensibility`, async function (t) {
t.plan(3)
class TestIterator extends Ctor {
_next (callback) {
t.is(this, it, 'thisArg on _next() was correct')
t.is(arguments.length, 1, 'got one argument')
t.is(typeof callback, 'function', 'got a callback function')
this.nextTick(callback)
}
}
const db = testCommon.factory()
await db.open()
const it = new TestIterator(db, {})
await it.next()
await db.close()
})
test(`${Ctor.name}.next() throws on invalid callback argument`, async function (t) {
t.plan(3 * 2)
const db = testCommon.factory()
await db.open()
for (const invalid of [{}, null, 'foo']) {
const it = new Ctor(db, {})
try {
it.next(invalid)
} catch (err) {
t.is(err.name, 'TypeError')
t.is(err.message, 'Callback must be a function')
}
}
})
test(`${Ctor.name}.nextv() extensibility`, async function (t) {
t.plan(5 * 2)
class TestIterator extends Ctor {
_nextv (size, options, callback) {
t.is(this, it, 'thisArg on _nextv() was correct')
t.is(arguments.length, 3, 'got 3 arguments')
t.is(size, 100)
t.same(options, {}, 'empty options')
t.is(typeof callback, 'function', 'got a callback function')
this.nextTick(callback, null, [])
}
}
const db = testCommon.factory()
await db.open()
const it = new TestIterator(db, {})
await it.nextv(100)
await it.nextv(100, {})
await db.close()
})
test(`${Ctor.name}.nextv() extensibility (options)`, async function (t) {
t.plan(2)
class TestIterator extends Ctor {
_nextv (size, options, callback) {
t.is(size, 100)
t.same(options, { foo: 123 }, 'got options')
this.nextTick(callback, null, [])
}
}
const db = testCommon.factory()
await db.open()
const it = new TestIterator(db, {})
await it.nextv(100, { foo: 123 })
await db.close()
})
test(`${Ctor.name}.nextv() throws on invalid callback argument`, async function (t) {
t.plan(3 * 2)
const db = testCommon.factory()
await db.open()
for (const invalid of [{}, null, 'foo']) {
const it = new Ctor(db, {})
try {
it.nextv(100, {}, invalid)
} catch (err) {
t.is(err.name, 'TypeError')
t.is(err.message, 'Callback must be a function')
}
}
})
test(`${Ctor.name}.all() extensibility`, async function (t) {
t.plan(2 * 4)
for (const args of [[], [{}]]) {
class TestIterator extends Ctor {
_all (options, callback) {
t.is(this, it, 'thisArg on _all() was correct')
t.is(arguments.length, 2, 'got 2 arguments')
t.same(options, {}, 'empty options')
t.is(typeof callback, 'function', 'got a callback function')
this.nextTick(callback, null, [])
}
}
const db = testCommon.factory()
await db.open()
const it = new TestIterator(db, {})
await it.all(...args)
await db.close()
}
})
test(`${Ctor.name}.all() extensibility (options)`, async function (t) {
t.plan(1)
class TestIterator extends Ctor {
_all (options, callback) {
t.same(options, { foo: 123 }, 'got options')
this.nextTick(callback, null, [])
}
}
const db = testCommon.factory()
await db.open()
const it = new TestIterator(db, {})
await it.all({ foo: 123 })
await db.close()
})
test(`${Ctor.name}.all() throws on invalid callback argument`, async function (t) {
t.plan(3 * 2)
const db = testCommon.factory()
await db.open()
for (const invalid of [{}, null, 'foo']) {
const it = new Ctor(db, {})
try {
it.all({}, invalid)
} catch (err) {
t.is(err.name, 'TypeError')
t.is(err.message, 'Callback must be a function')
}
}
})
test(`${Ctor.name}.close() extensibility`, async function (t) {
t.plan(3)
class TestIterator extends Ctor {
_close (callback) {
t.is(this, it, 'thisArg on _close() was correct')
t.is(arguments.length, 1, 'got one argument')
t.is(typeof callback, 'function', 'got a callback function')
this.nextTick(callback)
}
}
const db = testCommon.factory()
await db.open()
const it = new TestIterator(db, {})
await it.close()
await db.close()
})
test(`${Ctor.name}.close() throws on invalid callback argument`, async function (t) {
t.plan(3 * 2)
const db = testCommon.factory()
await db.open()
for (const invalid of [{}, null, 'foo']) {
const it = new Ctor(db, {})
try {
it.close(invalid)
} catch (err) {
t.is(err.name, 'TypeError')
t.is(err.message, 'Callback must be a function')
}
}
})
}
test('AbstractIterator throws when accessing legacy properties', async function (t) {
t.plan(3 * 2)
const db = testCommon.factory()
await db.open()
const it = new AbstractIterator(db, {})
for (const k of ['_ended property', '_nexting property', '_end method']) {
try {
// eslint-disable-next-line no-unused-expressions
it[k.split(' ')[0]]
} catch (err) {
t.is(err.code, 'LEVEL_LEGACY')
}
try {
it[k.split(' ')[0]] = 123
} catch (err) {
t.is(err.code, 'LEVEL_LEGACY')
}
}
})

View File

@@ -0,0 +1,213 @@
'use strict'
const test = require('tape')
const { AbstractLevel, AbstractIterator } = require('../..')
const { DeferredIterator, DeferredKeyIterator, DeferredValueIterator } = require('../../lib/deferred-iterator')
function withIterator (methods) {
class TestIterator extends AbstractIterator { }
for (const k in methods) {
TestIterator.prototype[k] = methods[k]
}
class Test extends AbstractLevel {
_iterator (options) {
return new TestIterator(this, options)
}
}
return new Test({ encodings: { utf8: true } })
}
for (const mode of ['iterator', 'keys', 'values']) {
for (const type of ['explicit', 'deferred']) {
const verify = function (t, db, it) {
t.is(db.status, type === 'explicit' ? 'open' : 'opening')
if (type === 'explicit') {
t.is(
it.constructor.name,
mode === 'iterator' ? 'TestIterator' : mode === 'keys' ? 'DefaultKeyIterator' : 'DefaultValueIterator'
)
} else {
t.is(
it.constructor,
mode === 'iterator' ? DeferredIterator : mode === 'keys' ? DeferredKeyIterator : DeferredValueIterator
)
}
}
test(`for await...of ${mode}() (${type} open)`, async function (t) {
t.plan(4)
const input = [{ key: '1', value: '1' }, { key: '2', value: '2' }]
const output = []
const db = withIterator({
_next (callback) {
const { key, value } = input[n++] || []
this.nextTick(callback, null, key, value)
},
_close (callback) {
this.nextTick(function () {
closed = true
callback()
})
}
})
if (type === 'explicit') await db.open()
const it = db[mode]({ keyEncoding: 'utf8', valueEncoding: 'utf8' })
verify(t, db, it)
let n = 0
let closed = false
for await (const item of it) {
output.push(item)
}
t.same(output, input.map(x => mode === 'iterator' ? [x.key, x.value] : mode === 'keys' ? x.key : x.value))
t.ok(closed, 'closed')
})
test(`for await...of ${mode}() closes on user error (${type} open)`, async function (t) {
t.plan(4)
const db = withIterator({
_next (callback) {
this.nextTick(callback, null, n.toString(), n.toString())
if (n++ > 10) throw new Error('Infinite loop')
},
_close (callback) {
this.nextTick(function () {
closed = true
callback(new Error('close error'))
})
}
})
if (type === 'explicit') await db.open()
const it = db[mode]()
verify(t, db, it)
let n = 0
let closed = false
try {
// eslint-disable-next-line no-unused-vars, no-unreachable-loop
for await (const kv of it) {
throw new Error('user error')
}
} catch (err) {
t.is(err.message, 'user error')
t.ok(closed, 'closed')
}
})
test(`for await...of ${mode}() closes on iterator error (${type} open)`, async function (t) {
t.plan(5)
const db = withIterator({
_next (callback) {
t.pass('nexted')
this.nextTick(callback, new Error('iterator error'))
},
_close (callback) {
this.nextTick(function () {
closed = true
callback()
})
}
})
if (type === 'explicit') await db.open()
const it = db[mode]()
verify(t, db, it)
let closed = false
try {
// eslint-disable-next-line no-unused-vars
for await (const kv of it) {
t.fail('should not yield items')
}
} catch (err) {
t.is(err.message, 'iterator error')
t.ok(closed, 'closed')
}
})
test(`for await...of ${mode}() closes on user break (${type} open)`, async function (t) {
t.plan(4)
const db = withIterator({
_next (callback) {
this.nextTick(callback, null, n.toString(), n.toString())
if (n++ > 10) throw new Error('Infinite loop')
},
_close (callback) {
this.nextTick(function () {
closed = true
callback()
})
}
})
if (type === 'explicit') await db.open()
const it = db[mode]()
verify(t, db, it)
let n = 0
let closed = false
// eslint-disable-next-line no-unused-vars, no-unreachable-loop
for await (const kv of it) {
t.pass('got a chance to break')
break
}
t.ok(closed, 'closed')
})
test(`for await...of ${mode}() closes on user return (${type} open)`, async function (t) {
t.plan(4)
const db = withIterator({
_next (callback) {
this.nextTick(callback, null, n.toString(), n.toString())
if (n++ > 10) throw new Error('Infinite loop')
},
_close (callback) {
this.nextTick(function () {
closed = true
callback()
})
}
})
if (type === 'explicit') await db.open()
const it = db[mode]()
verify(t, db, it)
let n = 0
let closed = false
await (async () => {
// eslint-disable-next-line no-unused-vars, no-unreachable-loop
for await (const kv of it) {
t.pass('got a chance to return')
return
}
})()
t.ok(closed, 'closed')
})
}
}

View File

@@ -0,0 +1,75 @@
'use strict'
const test = require('tape')
const { mockLevel } = require('../util')
const nextTick = require('../../lib/next-tick')
test('resource must be an object with a close() method', function (t) {
t.plan(5)
const db = mockLevel()
for (const invalid of [null, undefined, {}, { close: 123 }]) {
try {
db.attachResource(invalid)
} catch (err) {
t.is(err && err.message, 'The first argument must be a resource object')
}
}
db.close(t.ifError.bind(t))
})
test('resource is closed on failed open', function (t) {
t.plan(2)
const db = mockLevel({
_open: function (options, callback) {
t.pass('opened')
this.nextTick(callback, new Error('_open error'))
}
})
const resource = {
close: function (cb) {
// Note: resource shouldn't care about db.status
t.pass('closed')
nextTick(cb)
}
}
db.attachResource(resource)
})
test('resource is closed on db.close()', function (t) {
t.plan(2)
const db = mockLevel()
const resource = {
close: function (cb) {
// Note: resource shouldn't care about db.status
t.pass('closed')
nextTick(cb)
}
}
db.attachResource(resource)
db.close(t.ifError.bind(t))
})
test('resource is not closed on db.close() if detached', function (t) {
t.plan(1)
const db = mockLevel()
const resource = {
close: function (cb) {
t.fail('should not be called')
}
}
db.attachResource(resource)
db.detachResource(resource)
db.close(t.ifError.bind(t))
})

90
node_modules/abstract-level/test/self/defer-test.js generated vendored Normal file
View File

@@ -0,0 +1,90 @@
'use strict'
const test = require('tape')
const { mockLevel } = require('../util')
test('defer() requires valid function argument', function (t) {
t.plan(7)
const db = mockLevel()
for (const invalid of [123, true, false, null, undefined, {}]) {
try {
db.defer(invalid)
} catch (err) {
t.is(err.message, 'The first argument must be a function')
}
}
db.close(t.ifError.bind(t))
})
test('defer() custom operation', function (t) {
t.plan(6)
const db = mockLevel({
custom (arg, callback) {
if (this.status === 'opening') {
t.is(arg, 123)
this.defer(() => this.custom(456, callback))
} else {
t.is(db.status, 'open')
t.is(arg, 456)
this.nextTick(callback, null, 987)
}
}
})
db.custom(123, function (err, result) {
t.ifError(err, 'no custom() error')
t.is(result, 987, 'result ok')
db.close(t.ifError.bind(t))
})
})
test('defer() custom operation with failed open', function (t) {
t.plan(4)
const db = mockLevel({
_open (options, callback) {
t.pass('opened')
this.nextTick(callback, new Error('_open error'))
},
custom (arg, callback) {
if (this.status === 'opening') {
this.defer(() => this.custom(arg, callback))
} else {
t.is(db.status, 'closed')
this.nextTick(callback, new Error('Database is not open (x)'))
}
}
})
db.custom(123, function (err, result) {
t.is(err && err.message, 'Database is not open (x)')
t.is(result, undefined, 'result ok')
})
})
test('defer() can drop custom synchronous operation', function (t) {
t.plan(3)
const db = mockLevel({
_open (options, callback) {
t.pass('opened')
this.nextTick(callback, new Error('_open error'))
},
custom (arg) {
if (this.status === 'opening') {
this.defer(() => this.custom(arg * 2))
} else {
// Handling other states is a userland responsibility
t.is(db.status, 'closed')
t.is(arg, 246)
}
}
})
db.custom(123)
})

View File

@@ -0,0 +1,98 @@
'use strict'
const test = require('tape')
const { mockLevel } = require('../util')
const { DefaultChainedBatch } = require('../../lib/default-chained-batch')
const identity = (v) => v
// NOTE: adapted from deferred-leveldown
test('deferred chained batch encodes once', function (t) {
t.plan(9)
let called = false
const keyEncoding = {
format: 'utf8',
encode (key) {
t.is(called, false, 'not yet called')
t.is(key, 'foo')
return key.toUpperCase()
},
decode: identity
}
const valueEncoding = {
format: 'utf8',
encode (value) {
t.is(called, false, 'not yet called')
t.is(value, 'bar')
return value.toUpperCase()
},
decode: identity
}
const db = mockLevel({
_batch: function (array, options, callback) {
called = true
t.is(array[0] && array[0].key, 'FOO')
t.is(array[0] && array[0].value, 'BAR')
this.nextTick(callback)
},
_open: function (options, callback) {
t.is(called, false, 'not yet called')
this.nextTick(callback)
}
}, { encodings: { utf8: true } }, {
keyEncoding,
valueEncoding
})
db.once('open', function () {
t.is(called, true, 'called')
})
db.batch().put('foo', 'bar').write(function (err) {
t.ifError(err, 'no write() error')
})
})
test('deferred chained batch is closed upon failed open', function (t) {
t.plan(6)
const db = mockLevel({
_open (options, callback) {
t.pass('opening')
this.nextTick(callback, new Error('_open error'))
},
_batch () {
t.fail('should not be called')
}
})
const batch = db.batch()
t.ok(batch instanceof DefaultChainedBatch)
batch.put('foo', 'bar')
batch.del('123')
batch.write(function (err) {
t.is(err && err.code, 'LEVEL_BATCH_NOT_OPEN')
// Should account for userland code that ignores errors
try {
batch.put('beep', 'boop')
} catch (err) {
t.is(err && err.code, 'LEVEL_BATCH_NOT_OPEN')
}
try {
batch.del('456')
} catch (err) {
t.is(err && err.code, 'LEVEL_BATCH_NOT_OPEN')
}
batch.write(function (err) {
t.is(err && err.code, 'LEVEL_BATCH_NOT_OPEN')
})
})
})

View File

@@ -0,0 +1,298 @@
'use strict'
const test = require('tape')
const { DeferredIterator, DeferredKeyIterator, DeferredValueIterator } = require('../../lib/deferred-iterator')
const { AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('../..')
const { mockLevel } = require('../util')
const noop = () => {}
const identity = (v) => v
for (const mode of ['iterator', 'keys', 'values']) {
const RealCtor = mode === 'iterator' ? AbstractIterator : mode === 'keys' ? AbstractKeyIterator : AbstractValueIterator
const DeferredCtor = mode === 'iterator' ? DeferredIterator : mode === 'keys' ? DeferredKeyIterator : DeferredValueIterator
const nextArgs = mode === 'iterator' ? ['key', 'value'] : mode === 'keys' ? ['key'] : ['value']
const privateMethod = '_' + mode
const publicMethod = mode
// NOTE: adapted from deferred-leveldown
test(`deferred ${mode}()`, function (t) {
t.plan(8)
const keyEncoding = {
format: 'utf8',
encode (key) {
t.is(key, 'foo', 'encoding got key')
return key.toUpperCase()
},
decode: identity
}
class MockIterator extends RealCtor {
_next (cb) {
this.nextTick(cb, null, ...nextArgs)
}
_close (cb) {
this.nextTick(cb)
}
}
const db = mockLevel({
[privateMethod]: function (options) {
t.is(options.gt, 'FOO', 'got encoded range option')
return new MockIterator(this, options)
},
_open: function (options, callback) {
t.pass('opened')
this.nextTick(callback)
}
}, { encodings: { utf8: true } }, {
keyEncoding
})
const it = db[publicMethod]({ gt: 'foo' })
t.ok(it instanceof DeferredCtor, 'is deferred')
let nextFirst = false
it.next(function (err, ...rest) {
nextFirst = true
t.error(err, 'no next() error')
t.same(rest, nextArgs)
})
it.close(function (err) {
t.error(err, 'no close() error')
t.ok(nextFirst)
})
})
// NOTE: adapted from deferred-leveldown
test(`deferred ${mode}(): non-deferred operations`, function (t) {
t.plan(6)
class MockIterator extends RealCtor {
_seek (target) {
t.is(target, '123')
}
_next (cb) {
this.nextTick(cb, null, ...nextArgs)
}
}
const db = mockLevel({
[privateMethod]: function (options) {
return new MockIterator(this, options)
}
})
db.open(function (err) {
t.error(err, 'no open() error')
it.seek(123)
it.next(function (err, ...rest) {
t.error(err, 'no next() error')
t.same(rest, nextArgs)
it.close(function (err) {
t.error(err, 'no close() error')
})
})
})
const it = db[publicMethod]({ gt: 'foo' })
t.ok(it instanceof DeferredCtor)
})
// NOTE: adapted from deferred-leveldown
test(`deferred ${mode}(): iterators are created in order`, function (t) {
t.plan(6)
const order1 = []
const order2 = []
class MockIterator extends RealCtor {}
function db (order) {
return mockLevel({
[privateMethod]: function (options) {
order.push('iterator created')
return new MockIterator(this, options)
},
_put: function (key, value, options, callback) {
order.push('put')
},
_open: function (options, callback) {
this.nextTick(callback)
}
})
}
const db1 = db(order1)
const db2 = db(order2)
db1.open(function (err) {
t.error(err, 'no error')
t.same(order1, ['iterator created', 'put'])
})
db2.open(function (err) {
t.error(err, 'no error')
t.same(order2, ['put', 'iterator created'])
})
t.ok(db1[publicMethod]() instanceof DeferredCtor)
db1.put('key', 'value', noop)
db2.put('key', 'value', noop)
t.ok(db2[publicMethod]() instanceof DeferredCtor)
})
for (const method of ['next', 'nextv', 'all']) {
test(`deferred ${mode}(): closed upon failed open, verified by ${method}()`, function (t) {
t.plan(5)
const db = mockLevel({
_open (options, callback) {
t.pass('opening')
this.nextTick(callback, new Error('_open error'))
},
_iterator () {
t.fail('should not be called')
},
[privateMethod] () {
t.fail('should not be called')
}
})
const it = db[publicMethod]()
t.ok(it instanceof DeferredCtor)
const original = it._close
it._close = function (...args) {
t.pass('closed')
return original.call(this, ...args)
}
verifyClosed(t, it, method, () => {})
})
test(`deferred ${mode}(): deferred and real iterators are closed on db.close(), verified by ${method}()`, function (t) {
t.plan(10)
class MockIterator extends RealCtor {
_close (callback) {
t.pass('closed')
this.nextTick(callback)
}
}
const db = mockLevel({
[privateMethod] (options) {
return new MockIterator(this, options)
}
})
const it = db[publicMethod]()
t.ok(it instanceof DeferredCtor)
const original = it._close
it._close = function (...args) {
t.pass('closed')
return original.call(this, ...args)
}
db.close(function (err) {
t.ifError(err, 'no close() error')
verifyClosed(t, it, method, function () {
db.open(function (err) {
t.ifError(err, 'no open() error')
// Should still be closed
verifyClosed(t, it, method, function () {
db.close(t.ifError.bind(t))
})
})
})
})
})
}
test(`deferred ${mode}(): deferred and real iterators are detached on db.close()`, function (t) {
t.plan(4)
class MockIterator extends RealCtor {}
let real
const db = mockLevel({
[privateMethod] (options) {
real = new MockIterator(this, options)
return real
}
})
const it = db[publicMethod]()
t.ok(it instanceof DeferredCtor)
db.close(function (err) {
t.ifError(err, 'no close() error')
db.open(function (err) {
t.ifError(err, 'no open() error')
it.close = real.close = it._close = real._close = function () {
t.fail('should not be called')
}
db.close(t.ifError.bind(t))
})
})
})
test(`deferred ${mode}(): defers underlying close()`, function (t) {
t.plan(3)
class MockIterator extends RealCtor {
_close (callback) {
order.push('_close')
this.nextTick(callback)
}
}
const order = []
const db = mockLevel({
_open (options, callback) {
order.push('_open')
this.nextTick(callback)
},
[privateMethod] (options) {
order.push(privateMethod)
return new MockIterator(this, options)
}
})
const it = db[publicMethod]()
t.ok(it instanceof DeferredCtor)
it.close(function (err) {
t.ifError(err, 'no close() error')
t.same(order, ['_open', privateMethod, '_close'])
})
})
const verifyClosed = function (t, it, method, cb) {
const requiredArgs = method === 'nextv' ? [10] : []
it[method](...requiredArgs, function (err) {
t.is(err && err.code, 'LEVEL_ITERATOR_NOT_OPEN', `correct error on first ${method}()`)
// Should account for userland code that ignores errors
it[method](...requiredArgs, function (err) {
t.is(err && err.code, 'LEVEL_ITERATOR_NOT_OPEN', `correct error on second ${method}()`)
cb()
})
})
}
}

View File

@@ -0,0 +1,93 @@
'use strict'
const test = require('tape')
const { mockLevel, mockIterator } = require('../util')
// NOTE: copied from deferred-leveldown
test('deferred operations are called in order', function (t) {
t.plan(15)
const calls = []
const db = mockLevel({
_put: function (key, value, options, callback) {
this.nextTick(callback)
calls.push({ type: 'put', key, value, options })
},
_get: function (key, options, callback) {
this.nextTick(callback)
calls.push({ type: 'get', key, options })
},
_del: function (key, options, callback) {
this.nextTick(callback)
calls.push({ type: 'del', key, options })
},
_batch: function (arr, options, callback) {
this.nextTick(callback)
calls.push({ type: 'batch', keys: arr.map(op => op.key).join(',') })
},
_clear: function (options, callback) {
this.nextTick(callback)
calls.push({ ...options, type: 'clear' })
},
_iterator (options) {
calls.push({ type: 'iterator' })
return mockIterator(this, options, {
_next (callback) {
this.nextTick(callback)
calls.push({ type: 'iterator.next' })
}
})
},
_open: function (options, callback) {
this.nextTick(callback)
t.is(calls.length, 0, 'not yet called')
}
}, {
encodings: {
utf8: true,
buffer: true
}
}, {
keyEncoding: 'utf8',
valueEncoding: 'utf8'
})
db.open(function (err) {
t.ifError(err, 'no open() error')
t.same(calls, [
{ type: 'put', key: '001', value: 'bar1', options: { keyEncoding: 'utf8', valueEncoding: 'utf8' } },
{ type: 'get', key: '002', options: { keyEncoding: 'utf8', valueEncoding: 'utf8' } },
{ type: 'clear', reverse: false, limit: -1, keyEncoding: 'utf8' },
{ type: 'put', key: '010', value: 'bar2', options: { keyEncoding: 'utf8', valueEncoding: 'utf8' } },
{ type: 'get', key: Buffer.from('011'), options: { keyEncoding: 'buffer', valueEncoding: 'utf8' } },
{ type: 'del', key: '020', options: { customOption: 123, keyEncoding: 'utf8' } },
{ type: 'del', key: '021', options: { keyEncoding: 'utf8' } },
{ type: 'batch', keys: '040,041' },
{ type: 'iterator' },
{ type: 'batch', keys: '050,051' },
{ type: 'iterator.next' },
{ type: 'clear', gt: '060', reverse: false, limit: -1, keyEncoding: 'utf8' }
], 'calls correctly behaved')
})
db.put('001', 'bar1', t.ifError.bind(t))
db.get('002', t.ifError.bind(t))
db.clear(t.ifError.bind(t))
db.put('010', 'bar2', t.ifError.bind(t))
db.get('011', { keyEncoding: 'buffer' }, t.ifError.bind(t))
db.del('020', { customOption: 123 }, t.ifError.bind(t))
db.del('021', t.ifError.bind(t))
db.batch([
{ type: 'put', key: '040', value: 'a' },
{ type: 'put', key: '041', value: 'b' }
], t.ifError.bind(t))
const it = db.iterator()
db.batch()
.put('050', 'c')
.put('051', 'd')
.write(t.ifError.bind(t))
it.next(t.ifError.bind(t))
db.clear({ gt: '060' }, t.ifError.bind(t))
t.is(calls.length, 0, 'not yet called')
})

426
node_modules/abstract-level/test/self/encoding-test.js generated vendored Normal file
View File

@@ -0,0 +1,426 @@
'use strict'
// TODO: move to per-method test files
const test = require('tape')
const { Buffer } = require('buffer')
const { mockLevel, mockChainedBatch, nullishEncoding } = require('../util')
const identity = (v) => v
const utf8Manifest = { encodings: { utf8: true } }
const dualManifest = { encodings: { utf8: true, buffer: true } }
const hasOwnProperty = Object.prototype.hasOwnProperty
for (const deferred of [false, true]) {
// NOTE: adapted from encoding-down
test(`get() encodes utf8 key (deferred: ${deferred})`, async function (t) {
t.plan(4)
const db = mockLevel({
_get (key, options, callback) {
t.is(key, '8')
t.is(options.keyEncoding, 'utf8')
t.is(options.valueEncoding, 'utf8')
this.nextTick(callback, null, 'foo')
}
}, utf8Manifest)
if (!deferred) await db.open()
t.same(await db.get(8), 'foo')
})
// NOTE: adapted from encoding-down
test(`get() takes encoding options (deferred: ${deferred})`, async function (t) {
t.plan(4)
const db = mockLevel({
_get (key, options, callback) {
t.is(key, '[1,"2"]')
t.is(options.keyEncoding, 'utf8')
t.is(options.valueEncoding, 'utf8')
this.nextTick(callback, null, '123')
}
}, utf8Manifest)
if (!deferred) await db.open()
t.same(await db.get([1, '2'], { keyEncoding: 'json', valueEncoding: 'json' }), 123)
})
// NOTE: adapted from encoding-down
test(`get() with custom value encoding that wants a buffer (deferred: ${deferred})`, async function (t) {
t.plan(3)
const db = mockLevel({
_get (key, options, callback) {
t.same(key, 'key')
t.same(options, { keyEncoding: 'utf8', valueEncoding: 'buffer' })
this.nextTick(callback, null, Buffer.alloc(1))
}
}, dualManifest, {
keyEncoding: 'utf8',
valueEncoding: { encode: identity, decode: identity, format: 'buffer' }
})
if (!deferred) await db.open()
t.same(await db.get('key'), Buffer.alloc(1))
})
// NOTE: adapted from encoding-down
test(`get() with custom value encoding that wants a string (deferred: ${deferred})`, async function (t) {
t.plan(3)
const db = mockLevel({
_get (key, options, callback) {
t.same(key, Buffer.from('key'))
t.same(options, { keyEncoding: 'buffer', valueEncoding: 'utf8' })
this.nextTick(callback, null, 'x')
}
}, dualManifest, {
keyEncoding: 'buffer',
valueEncoding: { encode: identity, decode: identity, format: 'utf8' }
})
if (!deferred) await db.open()
t.same(await db.get(Buffer.from('key')), 'x')
})
// NOTE: adapted from encoding-down
test(`put() encodes utf8 key and value (deferred: ${deferred})`, async function (t) {
t.plan(4)
const db = mockLevel({
_put (key, value, options, callback) {
t.is(key, '8')
t.is(value, '4')
t.is(options.keyEncoding, 'utf8')
t.is(options.valueEncoding, 'utf8')
this.nextTick(callback)
}
}, utf8Manifest)
if (!deferred) await db.open()
await db.put(8, 4)
})
// NOTE: adapted from encoding-down
test(`put() takes encoding options (deferred: ${deferred})`, async function (t) {
t.plan(4)
const db = mockLevel({
_put (key, value, options, callback) {
t.is(key, '[1,"2"]')
t.is(value, '{"x":3}')
t.is(options.keyEncoding, 'utf8')
t.is(options.valueEncoding, 'utf8')
this.nextTick(callback)
}
}, utf8Manifest)
if (!deferred) await db.open()
await db.put([1, '2'], { x: 3 }, { keyEncoding: 'json', valueEncoding: 'json' })
})
// NOTE: adapted from encoding-down
test(`del() encodes utf8 key (deferred: ${deferred})`, async function (t) {
t.plan(2)
const db = mockLevel({
_del (key, options, callback) {
t.is(key, '2')
t.is(options.keyEncoding, 'utf8')
this.nextTick(callback)
}
}, utf8Manifest)
if (!deferred) await db.open()
await db.del(2)
})
// NOTE: adapted from encoding-down
test(`del() takes keyEncoding option (deferred: ${deferred})`, async function (t) {
t.plan(2)
const db = mockLevel({
_del (key, options, callback) {
t.is(key, '[1,"2"]')
t.is(options.keyEncoding, 'utf8')
this.nextTick(callback)
}
}, utf8Manifest)
if (!deferred) await db.open()
await db.del([1, '2'], { keyEncoding: 'json' })
})
test(`getMany() encodes utf8 key (deferred: ${deferred})`, async function (t) {
t.plan(4)
const db = mockLevel({
_getMany (keys, options, callback) {
t.same(keys, ['8', '29'])
t.is(options.keyEncoding, 'utf8')
t.is(options.valueEncoding, 'utf8')
this.nextTick(callback, null, ['foo', 'bar'])
}
}, utf8Manifest)
if (!deferred) await db.open()
t.same(await db.getMany([8, 29]), ['foo', 'bar'])
})
test(`getMany() takes encoding options (deferred: ${deferred})`, async function (t) {
t.plan(4)
const db = mockLevel({
_getMany (keys, options, callback) {
t.same(keys, ['[1,"2"]', '"x"'])
t.is(options.keyEncoding, 'utf8')
t.is(options.valueEncoding, 'utf8')
this.nextTick(callback, null, ['123', '"hi"'])
}
}, utf8Manifest)
if (!deferred) await db.open()
t.same(await db.getMany([[1, '2'], 'x'], { keyEncoding: 'json', valueEncoding: 'json' }), [123, 'hi'])
})
test(`getMany() with custom value encoding that wants a buffer (deferred: ${deferred})`, async function (t) {
t.plan(3)
const db = mockLevel({
_getMany (keys, options, callback) {
t.same(keys, ['key'])
t.same(options, { keyEncoding: 'utf8', valueEncoding: 'buffer' })
this.nextTick(callback, null, [Buffer.alloc(1)])
}
}, dualManifest, {
keyEncoding: 'utf8',
valueEncoding: { encode: identity, decode: identity, format: 'buffer' }
})
if (!deferred) await db.open()
t.same(await db.getMany(['key']), [Buffer.alloc(1)])
})
test(`getMany() with custom value encoding that wants a string (deferred: ${deferred})`, async function (t) {
t.plan(3)
const db = mockLevel({
_getMany (keys, options, callback) {
t.same(keys, [Buffer.from('key')])
t.same(options, { keyEncoding: 'buffer', valueEncoding: 'utf8' })
this.nextTick(callback, null, ['x'])
}
}, dualManifest, {
keyEncoding: 'buffer',
valueEncoding: { encode: identity, decode: identity, format: 'utf8' }
})
if (!deferred) await db.open()
t.same(await db.getMany([Buffer.from('key')]), ['x'])
})
// NOTE: adapted from encoding-down
test(`chainedBatch.put() and del() encode utf8 key and value (deferred: ${deferred})`, async function (t) {
t.plan(deferred ? 2 : 4)
let db
if (deferred) {
db = mockLevel({
_batch (array, options, callback) {
t.same(array, [
{ type: 'put', key: '1', value: '2', keyEncoding: 'utf8', valueEncoding: 'utf8' },
{ type: 'del', key: '3', keyEncoding: 'utf8' }
])
t.same(options, {})
this.nextTick(callback)
}
}, utf8Manifest)
} else {
db = mockLevel({
_chainedBatch () {
return mockChainedBatch(this, {
_put: function (key, value, options) {
t.same({ key, value }, { key: '1', value: '2' })
t.same(options, { keyEncoding: 'utf8', valueEncoding: 'utf8' })
},
_del: function (key, options) {
t.is(key, '3')
t.same(options, { keyEncoding: 'utf8' })
}
})
}
}, utf8Manifest)
}
if (!deferred) await db.open()
await db.batch().put(1, 2).del(3).write()
})
// NOTE: adapted from encoding-down
test(`chainedBatch.put() and del() take encoding options (deferred: ${deferred})`, async function (t) {
t.plan(deferred ? 2 : 4)
let db
const putOptions = { keyEncoding: 'json', valueEncoding: 'json' }
const delOptions = { keyEncoding: 'json' }
if (deferred) {
db = mockLevel({
_batch (array, options, callback) {
t.same(array, [
{ type: 'put', key: '"1"', value: '{"x":[2]}', keyEncoding: 'utf8', valueEncoding: 'utf8' },
{ type: 'del', key: '"3"', keyEncoding: 'utf8' }
])
t.same(options, {})
this.nextTick(callback)
}
}, utf8Manifest)
} else {
db = mockLevel({
_chainedBatch () {
return mockChainedBatch(this, {
_put: function (key, value, options) {
t.same({ key, value }, { key: '"1"', value: '{"x":[2]}' })
t.same(options, { keyEncoding: 'utf8', valueEncoding: 'utf8' })
},
_del: function (key, options) {
t.is(key, '"3"')
t.same(options, { keyEncoding: 'utf8' })
}
})
}
}, utf8Manifest)
}
if (!deferred) await db.open()
await db.batch().put('1', { x: [2] }, putOptions).del('3', delOptions).write()
})
// NOTE: adapted from encoding-down
test(`clear() receives keyEncoding option (deferred: ${deferred})`, async function (t) {
t.plan(1)
const db = mockLevel({
_clear: function (options, callback) {
t.same(options, { keyEncoding: 'utf8', reverse: false, limit: -1 })
this.nextTick(callback)
}
}, utf8Manifest)
if (!deferred) await db.open()
await db.clear()
})
test(`clear() takes keyEncoding option (deferred: ${deferred})`, async function (t) {
t.plan(1)
const db = mockLevel({
_clear: function (options, callback) {
t.same(options, { keyEncoding: 'utf8', gt: '"a"', reverse: false, limit: -1 })
this.nextTick(callback)
}
}, utf8Manifest)
if (!deferred) await db.open()
await db.clear({ keyEncoding: 'json', gt: 'a' })
})
// NOTE: adapted from encoding-down
test(`clear() encodes range options (deferred: ${deferred})`, async function (t) {
t.plan(5)
const keyEncoding = {
format: 'utf8',
encode: function (key) {
return 'encoded_' + key
},
decode: identity
}
const db = mockLevel({
_clear: function (options, callback) {
t.is(options.gt, 'encoded_1')
t.is(options.gte, 'encoded_2')
t.is(options.lt, 'encoded_3')
t.is(options.lte, 'encoded_4')
t.is(options.foo, 5)
this.nextTick(callback)
}
}, utf8Manifest, { keyEncoding })
if (!deferred) await db.open()
await db.clear({ gt: 1, gte: 2, lt: 3, lte: 4, foo: 5 })
})
// NOTE: adapted from encoding-down
test(`clear() does not strip nullish range options (deferred: ${deferred})`, async function (t) {
t.plan(12)
const db1 = mockLevel({
_clear: function (options, callback) {
t.is(options.gt, '\x00', 'encoded null')
t.is(options.gte, '\x00', 'encoded null')
t.is(options.lt, '\x00', 'encoded null')
t.is(options.lte, '\x00', 'encoded null')
this.nextTick(callback)
}
}, utf8Manifest, { keyEncoding: nullishEncoding, valueEncoding: nullishEncoding })
const db2 = mockLevel({
_clear: function (options, callback) {
t.is(hasOwnProperty.call(options, 'gt'), true)
t.is(hasOwnProperty.call(options, 'gte'), true)
t.is(hasOwnProperty.call(options, 'lt'), true)
t.is(hasOwnProperty.call(options, 'lte'), true)
t.is(options.gt, '\xff', 'encoded undefined')
t.is(options.gte, '\xff', 'encoded undefined')
t.is(options.lt, '\xff', 'encoded undefined')
t.is(options.lte, '\xff', 'encoded undefined')
this.nextTick(callback)
}
}, utf8Manifest, { keyEncoding: nullishEncoding, valueEncoding: nullishEncoding })
if (!deferred) {
await Promise.all([db1.open(), db2.open()])
}
const promise1 = db1.clear({
gt: null,
gte: null,
lt: null,
lte: null
})
const promise2 = db2.clear({
gt: undefined,
gte: undefined,
lt: undefined,
lte: undefined
})
await Promise.all([promise1, promise2])
})
// NOTE: adapted from encoding-down
test(`clear() does not add nullish range options (deferred: ${deferred})`, async function (t) {
t.plan(4)
const db = mockLevel({
_clear: function (options, callback) {
t.is(hasOwnProperty.call(options, 'gt'), false)
t.is(hasOwnProperty.call(options, 'gte'), false)
t.is(hasOwnProperty.call(options, 'lt'), false)
t.is(hasOwnProperty.call(options, 'lte'), false)
this.nextTick(callback)
}
})
if (!deferred) await db.open()
await db.clear({})
})
}

829
node_modules/abstract-level/test/self/iterator-test.js generated vendored Normal file
View File

@@ -0,0 +1,829 @@
'use strict'
const test = require('tape')
const { Buffer } = require('buffer')
const { AbstractLevel } = require('../..')
const { AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('../..')
const { mockLevel, mockIterator, nullishEncoding } = require('../util')
const identity = (v) => v
const utf8Manifest = { encodings: { utf8: true } }
const dualManifest = { encodings: { utf8: true, buffer: true } }
const tripleManifest = { encodings: { utf8: true, buffer: true, view: true } }
for (const deferred of [false, true]) {
// Also test default fallback implementations of keys() and values()
for (const [mode, def] of [['iterator', false], ['keys', false], ['values', false], ['keys', true], ['values', true]]) {
const Ctor = mode === 'iterator' || def ? AbstractIterator : mode === 'keys' ? AbstractKeyIterator : AbstractValueIterator
const privateMethod = def ? '_iterator' : '_' + mode
const publicMethod = mode
test(`${mode}() (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(4)
let called = false
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
t.is(this, db, 'thisArg is correct')
t.is(arguments.length, 1, 'got one argument')
const kvOptions = mode === 'iterator' || def
? { keys: mode !== 'values', values: mode !== 'keys' }
: {}
t.same(options, {
reverse: false,
limit: -1,
keyEncoding: 'utf8',
valueEncoding: 'utf8',
...kvOptions
})
called = true
return new Ctor(this, options)
}
}
const db = new MockLevel(tripleManifest)
if (!deferred) await db.open()
db[publicMethod]()
t.is(called, !deferred)
if (deferred) await db.open()
})
test(`${mode}() with custom options (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(3)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
t.is(options.foo, 123)
t.is(options.reverse, true)
t.is(options.limit, 1)
return new Ctor(this, options)
}
}
const db = new MockLevel(tripleManifest)
if (!deferred) await db.open()
db[publicMethod]({ foo: 123, reverse: true, limit: 1 })
if (deferred) await db.open()
})
for (const limit of [2, 0]) {
test(`${mode}().next() skips _next() when limit ${limit} is reached (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
let calls = 0
let yielded = 0
class MockIterator extends Ctor {
_next (callback) {
calls++
if (mode === 'iterator' || def) {
this.nextTick(callback, null, 'a', 'a')
} else {
this.nextTick(callback, null, 'a')
}
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
const it = db[publicMethod]({ limit })
for (let i = 0; i < limit + 2; i++) {
const item = await it.next()
if (item === undefined) break
yielded++
}
t.is(it.count, limit, 'final count matches limit')
t.is(calls, limit)
t.is(yielded, limit)
})
test(`${mode}().nextv() skips _nextv() when limit ${limit} is reached (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
let calls = 0
let yielded = 0
class MockIterator extends Ctor {
_nextv (size, options, callback) {
calls++
if (mode === 'iterator' || def) {
this.nextTick(callback, null, [['a', 'a']])
} else {
this.nextTick(callback, null, ['a'])
}
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
const it = db[publicMethod]({ limit })
for (let i = 0; i < limit + 2; i++) {
const items = await it.nextv(1)
yielded += items.length
if (items.length === 0) break
}
t.is(it.count, limit, 'final count matches limit')
t.is(calls, limit)
t.is(yielded, limit)
})
test(`${mode}().all() skips _all() when limit ${limit} is reached (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
let nextCount = 0
class MockIterator extends Ctor {
_next (callback) {
if (++nextCount > 10) {
throw new Error('Potential infinite loop')
} else if (mode === 'iterator' || def) {
this.nextTick(callback, null, 'a', 'a')
} else {
this.nextTick(callback, null, 'a')
}
}
_all (options, callback) {
t.fail('should not be called')
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
const it = db[publicMethod]({ limit })
// Use next() because all() auto-closes and thus can't be used twice atm
for (let i = 0; i < limit; i++) await it.next()
t.same(await it.all(), [])
})
}
test(`${mode}().nextv() reduces size for _nextv() when near limit (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
class MockIterator extends Ctor {
_nextv (size, options, callback) {
if (mode === 'iterator' || def) {
this.nextTick(callback, null, Array(size).fill(['a', 'a']))
} else {
this.nextTick(callback, null, Array(size).fill('a'))
}
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
const it = db[publicMethod]({ limit: 3 })
t.is((await it.nextv(2)).length, 2)
t.is((await it.nextv(2)).length, 1)
t.is((await it.nextv(2)).length, 0)
})
test(`${mode}().count increments by next(), nextv() and all() (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
class MockIterator extends Ctor {
_next (callback) {
if (mode === 'iterator' || def) {
this.nextTick(callback, null, 'a', 'a')
} else {
this.nextTick(callback, null, 'a')
}
}
_nextv (size, options, callback) {
if (mode === 'iterator' || def) {
this.nextTick(callback, null, [['a', 'a'], ['b', 'b']])
} else {
this.nextTick(callback, null, ['a', 'b'])
}
}
_all (options, callback) {
if (mode === 'iterator' || def) {
this.nextTick(callback, null, [['c', 'c'], ['d', 'd'], ['e', 'e']])
} else {
this.nextTick(callback, null, ['c', 'd', 'e'])
}
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
const it = db[publicMethod]()
for (let i = 0; i < 2; i++) {
t.isNot(await it.next(), undefined) // 2 * 1 = 2
t.is((await it.nextv(2)).length, 2) // 2 * 2 = 4
}
t.is(it.count, 2 + 4)
t.is((await it.all()).length, 3)
t.is(it.count, 2 + 4 + 3)
})
test(`${mode}() forwards encoding options (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(3)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
t.is(options.keyEncoding, 'utf8')
t.is(options.valueEncoding, 'buffer')
return new MockIterator(this, options)
}
}
class MockIterator extends Ctor {
_next (callback) {
if (mode === 'iterator' || def) {
this.nextTick(callback, null, '281', Buffer.from('a'))
} else if (mode === 'keys') {
this.nextTick(callback, null, '281')
} else {
this.nextTick(callback, null, Buffer.from('a'))
}
}
}
const db = new MockLevel(dualManifest)
if (!deferred) await db.open()
const item = await db[publicMethod]({ keyEncoding: 'json', valueEncoding: 'hex' }).next()
t.same(item, mode === 'iterator' ? [281, '61'] : mode === 'keys' ? 281 : '61')
})
// NOTE: adapted from encoding-down
test(`${mode}() with custom encodings that want a buffer (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(5)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
t.is(options.keyEncoding, 'buffer')
t.is(options.valueEncoding, 'buffer')
return new MockIterator(this, options)
}
}
class MockIterator extends Ctor {
_next (callback) {
if (mode === 'iterator' || def) {
this.nextTick(callback, null, Buffer.from('a'), Buffer.from('b'))
} else if (mode === 'keys') {
this.nextTick(callback, null, Buffer.from('a'))
} else {
this.nextTick(callback, null, Buffer.from('b'))
}
}
}
const db = new MockLevel(dualManifest)
const encoding = { encode: spy(identity), decode: spy(identity), format: 'buffer' }
if (!deferred) await db.open()
const it = db[publicMethod]({ keyEncoding: encoding, valueEncoding: encoding })
const item = await it.next()
t.is(encoding.encode.calls, 0, 'did not need to encode anything')
t.is(encoding.decode.calls, mode === 'iterator' ? 2 : 1)
t.same(item, mode === 'iterator' ? [Buffer.from('a'), Buffer.from('b')] : Buffer.from(mode === 'keys' ? 'a' : 'b'))
})
// NOTE: adapted from encoding-down
test(`${mode}() with custom encodings that want a string (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(5)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
t.is(options.keyEncoding, 'utf8')
t.is(options.valueEncoding, 'utf8')
return new MockIterator(this, options)
}
}
class MockIterator extends Ctor {
_next (callback) {
if (mode === 'iterator' || def) {
this.nextTick(callback, null, 'a', 'b')
} else if (mode === 'keys') {
this.nextTick(callback, null, 'a')
} else {
this.nextTick(callback, null, 'b')
}
}
}
const db = new MockLevel(dualManifest)
const encoding = { encode: spy(identity), decode: spy(identity), format: 'utf8' }
if (!deferred) await db.open()
const it = db[publicMethod]({ keyEncoding: encoding, valueEncoding: encoding })
const item = await it.next()
t.is(encoding.encode.calls, 0, 'did not need to encode anything')
t.is(encoding.decode.calls, mode === 'iterator' ? 2 : 1)
t.same(item, mode === 'iterator' ? ['a', 'b'] : mode === 'keys' ? 'a' : 'b')
})
// NOTE: adapted from encoding-down
test(`${mode}() encodes range options (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(6)
let calls = 0
const keyEncoding = {
format: 'utf8',
encode (key) {
calls++
return 'encoded_' + key
},
decode: identity
}
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
t.is(options.gt, 'encoded_3')
t.is(options.gte, 'encoded_4')
t.is(options.lt, 'encoded_5')
t.is(options.lte, 'encoded_6')
t.is(options.foo, 7)
return new Ctor(this, options)
}
}
const db = new MockLevel(utf8Manifest, { keyEncoding })
if (!deferred) await db.open()
await db[publicMethod]({ gt: 3, gte: 4, lt: 5, lte: 6, foo: 7 }).next()
t.is(calls, 4)
})
// NOTE: adapted from encoding-down
test(`${mode}() does not strip nullish range options (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(12)
const db1 = mockLevel({
[privateMethod] (options) {
t.is(options.gt, '\x00', 'encoded null')
t.is(options.gte, '\x00', 'encoded null')
t.is(options.lt, '\x00', 'encoded null')
t.is(options.lte, '\x00', 'encoded null')
return new Ctor(this, options)
}
}, utf8Manifest, { keyEncoding: nullishEncoding, valueEncoding: nullishEncoding })
const db2 = mockLevel({
[privateMethod] (options) {
t.is(hasOwnProperty.call(options, 'gt'), true)
t.is(hasOwnProperty.call(options, 'gte'), true)
t.is(hasOwnProperty.call(options, 'lt'), true)
t.is(hasOwnProperty.call(options, 'lte'), true)
t.is(options.gt, '\xff', 'encoded undefined')
t.is(options.gte, '\xff', 'encoded undefined')
t.is(options.lt, '\xff', 'encoded undefined')
t.is(options.lte, '\xff', 'encoded undefined')
return new Ctor(this, options)
}
}, utf8Manifest, { keyEncoding: nullishEncoding, valueEncoding: nullishEncoding })
if (!deferred) {
await Promise.all([db1.open(), db2.open()])
}
const promise1 = db1[publicMethod]({
gt: null,
gte: null,
lt: null,
lte: null
}).next()
const promise2 = db2[publicMethod]({
gt: undefined,
gte: undefined,
lt: undefined,
lte: undefined
}).next()
return Promise.all([promise1, promise2])
})
// NOTE: adapted from encoding-down
test(`${mode}() does not add nullish range options (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(4)
const db = mockLevel({
[privateMethod] (options) {
t.is(hasOwnProperty.call(options, 'gt'), false)
t.is(hasOwnProperty.call(options, 'gte'), false)
t.is(hasOwnProperty.call(options, 'lt'), false)
t.is(hasOwnProperty.call(options, 'lte'), false)
return new Ctor(this, options)
}
})
if (!deferred) await db.open()
await db[publicMethod]({}).next()
})
// NOTE: adapted from encoding-down
test(`${mode}() encodes seek target (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(2)
const db = mockLevel({
[privateMethod] (options) {
return new MockIterator(this, options)
}
}, utf8Manifest, { keyEncoding: 'json' })
class MockIterator extends Ctor {
_seek (target, options) {
t.is(target, '"a"', 'encoded once')
t.same(options, { keyEncoding: 'utf8' })
}
}
if (!deferred) await db.open()
const it = db[publicMethod]()
it.seek('a')
await it.next()
})
// NOTE: adapted from encoding-down
test(`${mode}() encodes seek target with custom encoding (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(1)
const targets = []
const db = mockLevel({
[privateMethod] (options) {
return new MockIterator(this, options)
}
}, utf8Manifest)
class MockIterator extends Ctor {
_seek (target) {
targets.push(target)
}
}
if (!deferred) await db.open()
db[publicMethod]().seek('a')
db[publicMethod]({ keyEncoding: 'json' }).seek('a')
db[publicMethod]().seek('b', { keyEncoding: 'json' })
await db.open()
t.same(targets, ['a', '"a"', '"b"'], 'encoded targets')
})
// NOTE: adapted from encoding-down
test(`${mode}() encodes nullish seek target (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(1)
const targets = []
const db = mockLevel({
[privateMethod] (options) {
return new MockIterator(this, options)
}
}, utf8Manifest, { keyEncoding: { encode: String, decode: identity, format: 'utf8' } })
class MockIterator extends Ctor {
_seek (target) {
targets.push(target)
}
}
if (!deferred) await db.open()
// Unlike keys, nullish targets should not be rejected;
// assume that the encoding gives these types meaning.
db[publicMethod]().seek(null)
db[publicMethod]().seek(undefined)
await db.open()
t.same(targets, ['null', 'undefined'], 'encoded')
})
test(`${mode}() has default nextv() (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
const sizes = [[1, [0]], [1, [1]], [2, [2]], [3, [3]]]
t.plan(sizes.length * 2)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
let pos = 0
class MockIterator extends Ctor {
_next (callback) {
if (mode === 'iterator' || def) {
this.nextTick(callback, null, 'k' + pos, 'v' + (pos++))
} else if (mode === 'keys') {
this.nextTick(callback, null, 'k' + (pos++))
} else {
this.nextTick(callback, null, 'v' + (pos++))
}
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
let expectedPos = 0
const it = db[publicMethod]()
for (const [size, args] of sizes) {
const actual = await it.nextv(...args)
const expected = []
for (let i = 0; i < size; i++) {
const pos = expectedPos++
if (mode === 'iterator') expected.push(['k' + pos, 'v' + pos])
else if (mode === 'keys') expected.push('k' + pos)
else expected.push('v' + pos)
}
t.is(actual.length, size)
t.same(actual, expected)
}
})
test(`${mode}() default nextv() forwards next() error (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(2)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
class MockIterator extends Ctor {
_next (callback) {
t.pass('called')
this.nextTick(callback, new Error('test'))
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
try {
await db[publicMethod]().nextv(10)
} catch (err) {
t.is(err.message, 'test')
}
})
test(`${mode}() has default all() (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(8)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
let pos = 0
class MockIterator extends Ctor {
_nextv (size, options, callback) {
t.is(size, 1000)
t.same(options, {})
if (pos === 4) {
this.nextTick(callback, null, [])
} else if (mode === 'iterator' || def) {
this.nextTick(callback, null, [[String(pos++), 'a'], [String(pos++), 'b']])
} else if (mode === 'keys') {
this.nextTick(callback, null, [String(pos++), String(pos++)])
} else {
pos += 2
this.nextTick(callback, null, ['a', 'b'])
}
}
_close (callback) {
t.pass('closed')
this.nextTick(callback)
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
t.same(await db[publicMethod]().all(), [
['0', 'a'],
['1', 'b'],
['2', 'a'],
['3', 'b']
].map(kv => mode === 'iterator' ? kv : kv[mode === 'keys' ? 0 : 1]))
})
test(`${mode}() default all() forwards nextv() error (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(2)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
class MockIterator extends Ctor {
_nextv (size, options, callback) {
t.pass('called')
this.nextTick(callback, new Error('test'))
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
try {
await db[publicMethod]().all()
} catch (err) {
t.is(err.message, 'test')
}
})
test(`${mode}() custom all() (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(3)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
class MockIterator extends Ctor {
_all (options, callback) {
t.same(options, {})
if (mode === 'iterator' || def) {
this.nextTick(callback, null, [['k0', 'v0'], ['k1', 'v1']])
} else if (mode === 'keys') {
this.nextTick(callback, null, ['k0', 'k1'])
} else {
this.nextTick(callback, null, ['v0', 'v1'])
}
}
_close (callback) {
t.pass('closed')
this.nextTick(callback)
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
t.same(await db[publicMethod]().all(), [
['k0', 'v0'],
['k1', 'v1']
].map(kv => mode === 'iterator' ? kv : kv[mode === 'keys' ? 0 : 1]))
})
test(`${mode}() custom all() forwards error and closes (deferred: ${deferred}, default implementation: ${def})`, async function (t) {
t.plan(3)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
class MockIterator extends Ctor {
_all (options, callback) {
t.pass('_all called')
this.nextTick(callback, new Error('test'))
}
_close (callback) {
t.pass('closed')
this.nextTick(callback)
}
}
const db = new MockLevel(utf8Manifest)
if (!deferred) await db.open()
try {
await db[publicMethod]().all()
} catch (err) {
t.is(err.message, 'test')
}
})
}
}
for (const deferred of [false, true]) {
// NOTE: adapted from encoding-down
test(`iterator() skips decoding keys if options.keys is false (deferred: ${deferred})`, async function (t) {
t.plan(3)
const keyEncoding = {
format: 'utf8',
decode (key) {
t.fail('should not be called')
},
encode: identity
}
const db = mockLevel({
_iterator (options) {
t.is(options.keys, false)
return mockIterator(this, options, {
_next (callback) {
this.nextTick(callback, null, '', 'value')
}
})
}
}, utf8Manifest, { keyEncoding })
if (!deferred) await db.open()
const [key, value] = await db.iterator({ keys: false }).next()
t.is(key, undefined, 'normalized key to undefined')
t.is(value, 'value', 'got value')
})
// NOTE: adapted from encoding-down
test(`iterator() skips decoding values if options.values is false (deferred: ${deferred})`, async function (t) {
t.plan(3)
const valueEncoding = {
format: 'utf8',
decode (value) {
t.fail('should not be called')
},
encode: identity
}
const db = mockLevel({
_iterator (options) {
t.is(options.values, false)
return mockIterator(this, options, {
_next (callback) {
callback(null, 'key', '')
}
})
}
}, utf8Manifest, { valueEncoding })
if (!deferred) await db.open()
const [key, value] = await db.iterator({ values: false }).next()
t.is(key, 'key', 'got key')
t.is(value, undefined, 'normalized value to undefined')
})
}
function spy (fn) {
const wrapped = function (...args) {
wrapped.calls++
return fn(...args)
}
wrapped.calls = 0
return wrapped
}

931
node_modules/abstract-level/test/self/sublevel-test.js generated vendored Normal file
View File

@@ -0,0 +1,931 @@
'use strict'
const test = require('tape')
const { Buffer } = require('buffer')
const { AbstractLevel, AbstractSublevel } = require('../..')
const { AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('../..')
const nextTick = AbstractLevel.prototype.nextTick
class NoopLevel extends AbstractLevel {
constructor (...args) {
super(
{ encodings: { utf8: true, buffer: true, view: true } },
...args
)
}
}
test('sublevel is extensible', function (t) {
t.plan(6)
class MockLevel extends AbstractLevel {
_sublevel (name, options) {
t.is(name, 'test')
t.same(options, { separator: '!', customOption: 123 })
return new MockSublevel(this, name, {
...options,
manifest: {
encodings: { ignored: true },
additionalMethods: { test: true },
events: { foo: true }
}
})
}
}
class MockSublevel extends AbstractSublevel {
test () {
this.emit('foo')
}
}
const db = new MockLevel({
encodings: { utf8: true },
additionalMethods: { ignored: true },
events: { ignored: true }
})
const sub = db.sublevel('test', { customOption: 123 })
t.is(sub.supports.encodings.ignored, undefined)
t.same(sub.supports.additionalMethods, { test: true })
t.same(sub.supports.events, {
foo: true,
// Added by AbstractLevel
opening: true,
open: true,
closing: true,
closed: true,
put: true,
del: true,
batch: true,
clear: true
})
sub.on('foo', () => t.pass('emitted'))
sub.test()
})
// NOTE: adapted from subleveldown
test('sublevel prefix and options', function (t) {
t.test('empty prefix', function (t) {
const sub = new NoopLevel().sublevel('')
t.is(sub.prefix, '!!')
t.end()
})
t.test('prefix without options', function (t) {
const sub = new NoopLevel().sublevel('prefix')
t.is(sub.prefix, '!prefix!')
t.end()
})
t.test('prefix and separator option', function (t) {
const sub = new NoopLevel().sublevel('prefix', { separator: '%' })
t.is(sub.prefix, '%prefix%')
t.end()
})
t.test('separator is trimmed from prefix', function (t) {
const sub1 = new NoopLevel().sublevel('!prefix')
t.is(sub1.prefix, '!prefix!')
const sub2 = new NoopLevel().sublevel('prefix!')
t.is(sub2.prefix, '!prefix!')
const sub3 = new NoopLevel().sublevel('!!prefix!!')
t.is(sub3.prefix, '!prefix!')
const sub4 = new NoopLevel().sublevel('@prefix@', { separator: '@' })
t.is(sub4.prefix, '@prefix@')
t.end()
})
t.test('repeated separator can not result in empty prefix', function (t) {
const sub = new NoopLevel().sublevel('!!!!')
t.is(sub.prefix, '!!')
t.end()
})
t.test('invalid sublevel prefix', function (t) {
t.throws(() => new NoopLevel().sublevel('foo\x05'), (err) => err.code === 'LEVEL_INVALID_PREFIX')
t.throws(() => new NoopLevel().sublevel('foo\xff'), (err) => err.code === 'LEVEL_INVALID_PREFIX')
t.throws(() => new NoopLevel().sublevel('foo!', { separator: '@' }), (err) => err.code === 'LEVEL_INVALID_PREFIX')
t.end()
})
t.test('legacy sublevel(down) options', function (t) {
t.throws(() => new NoopLevel().sublevel('foo', 'bar'), (err) => err.code === 'LEVEL_LEGACY')
t.throws(() => new NoopLevel().sublevel('foo', { open: () => {} }), (err) => err.code === 'LEVEL_LEGACY')
t.end()
})
// See https://github.com/Level/subleveldown/issues/78
t.test('doubly nested sublevel has correct prefix', async function (t) {
t.plan(1)
const keys = []
class MockLevel extends AbstractLevel {
_put (key, value, options, callback) {
keys.push(key)
nextTick(callback)
}
}
const db = new MockLevel({ encodings: { utf8: true } })
const sub1 = db.sublevel('1')
const sub2 = sub1.sublevel('2')
const sub3 = sub2.sublevel('3')
await sub1.put('a', 'value')
await sub2.put('b', 'value')
await sub3.put('c', 'value')
t.same(keys.sort(), [
'!1!!2!!3!c',
'!1!!2!b',
'!1!a'
])
})
t.end()
})
test('sublevel.prefixKey()', function (t) {
const db = new AbstractLevel({ encodings: { utf8: true, buffer: true, view: true } })
const sub = db.sublevel('test')
const textEncoder = new TextEncoder()
t.same(sub.prefixKey('', 'utf8'), '!test!')
t.same(sub.prefixKey('a', 'utf8'), '!test!a')
t.same(sub.prefixKey(Buffer.from(''), 'buffer'), Buffer.from('!test!'))
t.same(sub.prefixKey(Buffer.from('a'), 'buffer'), Buffer.from('!test!a'))
t.same(sub.prefixKey(textEncoder.encode(''), 'view'), textEncoder.encode('!test!'))
t.same(sub.prefixKey(textEncoder.encode('a'), 'view'), textEncoder.encode('!test!a'))
t.end()
})
// NOTE: adapted from subleveldown
test('sublevel manifest and parent db', function (t) {
t.test('sublevel inherits manifest from parent db', function (t) {
const parent = new AbstractLevel({
encodings: { utf8: true },
seek: true,
foo: true
})
const sub = parent.sublevel('')
t.is(sub.supports.foo, true, 'AbstractSublevel inherits from parent')
t.is(sub.supports.seek, true, 'AbstractSublevel inherits from parent')
t.end()
})
t.test('sublevel does not support additionalMethods', function (t) {
const parent = new AbstractLevel({
encodings: { utf8: true },
additionalMethods: { foo: true }
})
// We're expecting that AbstractSublevel removes the additionalMethod
// because it can't automatically prefix any key(-like) arguments
const sub = parent.sublevel('')
t.same(sub.supports.additionalMethods, {})
t.same(parent.supports.additionalMethods, { foo: true })
t.is(typeof sub.foo, 'undefined', 'AbstractSublevel does not expose method')
t.end()
})
t.test('sublevel.db is set to parent db', function (t) {
const db = new NoopLevel()
const sub = db.sublevel('test')
sub.once('open', function () {
t.ok(sub.db instanceof NoopLevel)
t.end()
})
})
t.end()
})
// NOTE: adapted from subleveldown
test('opening & closing sublevel', function (t) {
t.test('error from open() does not bubble up to sublevel', function (t) {
t.plan(5)
class MockLevel extends AbstractLevel {
_open (opts, cb) {
nextTick(cb, new Error('error from underlying store'))
}
}
const db = new MockLevel({ encodings: { buffer: true } })
const sub = db.sublevel('test')
db.open((err) => {
t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN')
t.is(err && err.cause && err.cause.message, 'error from underlying store')
})
sub.open((err) => {
t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN')
t.is(err && err.cause && err.cause.code, 'LEVEL_DATABASE_NOT_OPEN') // from db
t.is(err && err.cause && err.cause.cause, undefined) // but does not have underlying error
})
})
t.test('cannot create a sublevel on a closed db', function (t) {
t.plan(4)
const db = new NoopLevel()
db.once('open', function () {
db.close(function (err) {
t.error(err, 'no error')
db.sublevel('test').open(function (err) {
t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN', 'sublevel not opened')
db.open(function (err) {
t.error(err, 'no error')
db.sublevel('test').on('open', function () {
t.pass('sublevel opened')
})
})
})
})
})
})
t.test('can close db and sublevel once opened', function (t) {
t.plan(3)
const db = new NoopLevel()
db.open(function (err) {
t.ifError(err, 'no open error')
const sub = db.sublevel('test')
sub.once('open', function () {
db.close(function (err) {
t.ifError(err, 'no close error')
})
sub.close(function (err) {
t.ifError(err, 'no close error')
})
})
})
})
t.test('sublevel rejects operations if parent db is closed', function (t) {
t.plan(9)
const db = new NoopLevel()
db.open(function (err) {
t.ifError(err, 'no open error')
const sub = db.sublevel('test')
const it = sub.iterator()
sub.once('open', function () {
db.close(function (err) {
t.ifError(err, 'no close error')
sub.put('foo', 'bar', verify)
sub.get('foo', verify)
sub.del('foo', verify)
sub.clear(verify)
sub.batch([{ type: 'del', key: 'foo' }], verify)
it.next(function (err) {
t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN')
it.close(t.ifError.bind(t))
})
function verify (err) {
t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN')
}
})
})
})
})
t.test('cannot close db while sublevel is opening', function (t) {
t.plan(5)
const db = new NoopLevel()
db.open(function (err) {
t.ifError(err, 'no open error')
const sub = db.sublevel('test')
sub.open((err) => {
t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN')
})
db.close(function (err) {
t.ifError(err, 'no close error')
t.is(sub.status, 'closed')
t.is(db.status, 'closed')
})
})
})
t.test('cannot create sublevel while db is closing', function (t) {
t.plan(5)
const db = new NoopLevel()
db.open(function (err) {
t.ifError(err, 'no open error')
db.close(function (err) {
t.ifError(err, 'no close error')
t.is(db.status, 'closed')
})
const sub = db.sublevel('test')
sub.open((err) => {
t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN')
t.is(sub.status, 'closed')
})
})
})
t.test('can wrap a sublevel and reopen the wrapped sublevel', function (t) {
const db = new NoopLevel()
const sub1 = db.sublevel('test1')
const sub2 = sub1.sublevel('test2')
sub2.once('open', function () {
verify()
sub2.close(function (err) {
t.ifError(err, 'no close error')
// Prefixes should be the same after closing & reopening
// See https://github.com/Level/subleveldown/issues/78
sub2.open(function (err) {
t.ifError(err, 'no open error')
verify()
t.end()
})
})
})
function verify () {
t.is(sub1.prefix, '!test1!', 'sub1 prefix ok')
t.ok(sub1.db instanceof NoopLevel)
t.is(sub2.prefix, '!test1!!test2!', 'sub2 prefix ok')
t.ok(sub2.db instanceof NoopLevel)
}
})
// Also test default fallback implementations of keys() and values()
for (const [mode, def] of [['iterator', false], ['keys', false], ['values', false], ['keys', true], ['values', true]]) {
const Ctor = mode === 'iterator' || def ? AbstractIterator : mode === 'keys' ? AbstractKeyIterator : AbstractValueIterator
const privateMethod = def ? '_iterator' : '_' + mode
const publicMethod = mode
t.test(`error from sublevel.${mode}() bubbles up (default implementation: ${def})`, function (t) {
t.plan(2)
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
class MockIterator extends Ctor {
_next (callback) {
this.nextTick(callback, new Error('next() error from parent database'))
}
}
const db = new MockLevel({ encodings: { buffer: true } })
const sub = db.sublevel('test')
const it = sub[publicMethod]()
it.next(function (err) {
t.is(err.message, 'next() error from parent database')
it.close(function () {
t.pass('closed')
})
})
})
}
t.end()
})
test('sublevel operations are prefixed', function (t) {
t.test('sublevel.getMany() is prefixed', async function (t) {
t.plan(2)
class MockLevel extends AbstractLevel {
_getMany (keys, options, callback) {
t.same(keys, ['!test!a', '!test!b'])
t.same(options, { keyEncoding: 'utf8', valueEncoding: 'utf8' })
nextTick(callback, null, ['1', '2'])
}
}
const db = new MockLevel({ encodings: { utf8: true } })
const sub = db.sublevel('test')
await sub.open()
await sub.getMany(['a', 'b'])
})
// Also test default fallback implementations of keys() and values()
for (const [mode, def] of [['iterator', false], ['keys', false], ['values', false], ['keys', true], ['values', true]]) {
const Ctor = mode === 'iterator' || def ? AbstractIterator : mode === 'keys' ? AbstractKeyIterator : AbstractValueIterator
const privateMethod = def ? '_iterator' : '_' + mode
const publicMethod = mode
for (const deferred of [false, true]) {
t.test(`sublevel ${mode}.seek() target is prefixed (default implementation: ${def}, deferred: ${deferred})`, async function (t) {
t.plan(2)
class MockIterator extends Ctor {
_seek (target, options) {
t.is(target, '!sub!123')
t.is(options.keyEncoding, 'utf8')
}
}
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
return new MockIterator(this, options)
}
}
const db = new MockLevel({ encodings: { utf8: true } })
const sub = db.sublevel('sub', { keyEncoding: 'json' })
if (!deferred) await sub.open()
const it = sub[publicMethod]()
it.seek(123)
if (deferred) await sub.open()
})
}
}
t.test('sublevel.clear() is prefixed', async function (t) {
t.plan(4)
const calls = []
class MockLevel extends AbstractLevel {
_clear (options, callback) {
calls.push(options)
nextTick(callback)
}
}
const db = new MockLevel({ encodings: { utf8: true } })
const sub = db.sublevel('sub')
const test = async (options, expected) => {
await sub.clear(options)
t.same(calls.shift(), expected)
}
await sub.open()
await test(undefined, {
gte: '!sub!',
lte: '!sub"',
keyEncoding: 'utf8',
reverse: false,
limit: -1
})
await test({ gt: 'a' }, {
gt: '!sub!a',
lte: '!sub"',
keyEncoding: 'utf8',
reverse: false,
limit: -1
})
await test({ gte: 'a', lt: 'x' }, {
gte: '!sub!a',
lt: '!sub!x',
keyEncoding: 'utf8',
reverse: false,
limit: -1
})
await test({ lte: 'x' }, {
gte: '!sub!',
lte: '!sub!x',
keyEncoding: 'utf8',
reverse: false,
limit: -1
})
})
t.end()
})
test('sublevel encodings', function (t) {
// NOTE: adapted from subleveldown
t.test('different sublevels can have different encodings', function (t) {
t.plan(10)
const puts = []
const gets = []
class MockLevel extends AbstractLevel {
_put (key, value, { keyEncoding, valueEncoding }, callback) {
puts.push({ key, value, keyEncoding, valueEncoding })
nextTick(callback)
}
_get (key, { keyEncoding, valueEncoding }, callback) {
gets.push({ key, keyEncoding, valueEncoding })
nextTick(callback, null, puts.shift().value)
}
}
const db = new MockLevel({ encodings: { buffer: true, utf8: true } })
const sub1 = db.sublevel('test1', { valueEncoding: 'json' })
const sub2 = db.sublevel('test2', { keyEncoding: 'buffer', valueEncoding: 'buffer' })
sub1.put('foo', { some: 'json' }, function (err) {
t.error(err, 'no error')
t.same(puts, [{
key: '!test1!foo',
value: '{"some":"json"}',
keyEncoding: 'utf8',
valueEncoding: 'utf8'
}])
sub1.get('foo', function (err, value) {
t.error(err, 'no error')
t.same(value, { some: 'json' })
t.same(gets.shift(), {
key: '!test1!foo',
keyEncoding: 'utf8',
valueEncoding: 'utf8'
})
sub2.put(Buffer.from([1, 2]), Buffer.from([3]), function (err) {
t.error(err, 'no error')
t.same(puts, [{
key: Buffer.from('!test2!\x01\x02'),
value: Buffer.from([3]),
keyEncoding: 'buffer',
valueEncoding: 'buffer'
}])
sub2.get(Buffer.from([1, 2]), function (err, value) {
t.error(err, 'no error')
t.same(value, Buffer.from([3]))
t.same(gets.shift(), {
key: Buffer.from('!test2!\x01\x02'),
keyEncoding: 'buffer',
valueEncoding: 'buffer'
})
})
})
})
})
})
t.test('sublevel indirectly supports transcoded encoding', function (t) {
t.plan(5)
class MockLevel extends AbstractLevel {
_put (key, value, { keyEncoding, valueEncoding }, callback) {
t.same({ key, value, keyEncoding, valueEncoding }, {
key: Buffer.from('!test!foo'),
value: Buffer.from('{"some":"json"}'),
keyEncoding: 'buffer',
valueEncoding: 'buffer'
})
nextTick(callback)
}
_get (key, { keyEncoding, valueEncoding }, callback) {
t.same({ key, keyEncoding, valueEncoding }, {
key: Buffer.from('!test!foo'),
keyEncoding: 'buffer',
valueEncoding: 'buffer'
})
nextTick(callback, null, Buffer.from('{"some":"json"}'))
}
}
const db = new MockLevel({ encodings: { buffer: true } })
const sub = db.sublevel('test', { valueEncoding: 'json' })
sub.put('foo', { some: 'json' }, function (err) {
t.error(err, 'no error')
sub.get('foo', function (err, value) {
t.error(err, 'no error')
t.same(value, { some: 'json' })
})
})
})
t.test('concatenating sublevel Buffer keys', function (t) {
t.plan(10)
const key = Buffer.from('00ff', 'hex')
const prefixedKey = Buffer.concat([Buffer.from('!test!'), key])
class MockLevel extends AbstractLevel {
_put (key, value, options, callback) {
t.is(options.keyEncoding, 'buffer')
t.is(options.valueEncoding, 'buffer')
t.same(key, prefixedKey)
t.same(value, Buffer.from('bar'))
nextTick(callback)
}
_get (key, options, callback) {
t.is(options.keyEncoding, 'buffer')
t.is(options.valueEncoding, 'buffer')
t.same(key, prefixedKey)
nextTick(callback, null, Buffer.from('bar'))
}
}
const db = new MockLevel({ encodings: { buffer: true } })
const sub = db.sublevel('test', { keyEncoding: 'buffer' })
sub.put(key, 'bar', function (err) {
t.ifError(err)
sub.get(key, function (err, value) {
t.ifError(err)
t.is(value, 'bar')
})
})
})
t.test('concatenating sublevel Uint8Array keys', function (t) {
t.plan(10)
const key = new Uint8Array([0, 255])
const textEncoder = new TextEncoder()
const prefix = textEncoder.encode('!test!')
const prefixedKey = new Uint8Array(prefix.byteLength + key.byteLength)
prefixedKey.set(prefix, 0)
prefixedKey.set(key, prefix.byteLength)
class MockLevel extends AbstractLevel {
_put (key, value, options, callback) {
t.is(options.keyEncoding, 'view')
t.is(options.valueEncoding, 'view')
t.same(key, prefixedKey)
t.same(value, textEncoder.encode('bar'))
nextTick(callback)
}
_get (key, options, callback) {
t.is(options.keyEncoding, 'view')
t.is(options.valueEncoding, 'view')
t.same(key, prefixedKey)
nextTick(callback, null, textEncoder.encode('bar'))
}
}
const db = new MockLevel({ encodings: { view: true } })
const sub = db.sublevel('test', { keyEncoding: 'view' })
sub.put(key, 'bar', function (err) {
t.ifError(err)
sub.get(key, function (err, value) {
t.ifError(err)
t.is(value, 'bar')
})
})
})
// Also test default fallback implementations of keys() and values()
for (const [mode, def] of [['iterator', false], ['keys', false], ['values', false], ['keys', true], ['values', true]]) {
const Ctor = mode === 'iterator' || def ? AbstractIterator : mode === 'keys' ? AbstractKeyIterator : AbstractValueIterator
const privateMethod = def ? '_iterator' : '_' + mode
const publicMethod = mode
t.test(`unfixing sublevel.${mode}() Buffer keys (default implementation: ${def})`, function (t) {
t.plan(4)
const testKey = Buffer.from('00ff', 'hex')
const prefixedKey = Buffer.concat([Buffer.from('!test!'), testKey])
class MockIterator extends Ctor {
_next (callback) {
if (mode === 'iterator' || def) {
this.nextTick(callback, null, prefixedKey, 'bar')
} else if (mode === 'keys') {
this.nextTick(callback, null, prefixedKey)
} else {
this.nextTick(callback, null, 'bar')
}
}
}
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
t.is(options.keyEncoding, 'buffer')
t.is(options.valueEncoding, 'utf8')
return new MockIterator(this, options)
}
}
const db = new MockLevel({ encodings: { buffer: true, view: true, utf8: true } })
const sub = db.sublevel('test', { keyEncoding: 'buffer' })
sub[publicMethod]().next(function (err, keyOrValue) {
t.ifError(err)
t.same(keyOrValue, mode === 'values' ? 'bar' : testKey)
})
})
t.test(`unfixing sublevel.${mode}() Uint8Array keys (default implementation: ${def})`, function (t) {
t.plan(4)
const testKey = new Uint8Array([0, 255])
const textEncoder = new TextEncoder()
const prefix = textEncoder.encode('!test!')
const prefixedKey = new Uint8Array(prefix.byteLength + testKey.byteLength)
prefixedKey.set(prefix, 0)
prefixedKey.set(testKey, prefix.byteLength)
class MockIterator extends Ctor {
_next (callback) {
if (mode === 'iterator' || def) {
this.nextTick(callback, null, prefixedKey, 'bar')
} else if (mode === 'keys') {
this.nextTick(callback, null, prefixedKey)
} else {
this.nextTick(callback, null, 'bar')
}
}
}
class MockLevel extends AbstractLevel {
[privateMethod] (options) {
t.is(options.keyEncoding, 'view')
t.is(options.valueEncoding, 'utf8')
return new MockIterator(this, options)
}
}
const db = new MockLevel({ encodings: { buffer: true, view: true, utf8: true } })
const sub = db.sublevel('test', { keyEncoding: 'view' })
sub[publicMethod]().next(function (err, keyOrValue) {
t.ifError(err)
t.same(keyOrValue, mode === 'values' ? 'bar' : testKey)
})
})
}
t.end()
})
for (const chained of [false, true]) {
for (const deferred of [false, true]) {
test(`batch() with sublevel per operation (chained: ${chained}, deferred: ${deferred})`, async function (t) {
t.plan(6)
class MockLevel extends AbstractLevel {
_batch (operations, options, callback) {
t.same(operations, [
{
type: 'put',
sublevel: null,
key: '!1!a',
value: '{"foo":123}',
keyEncoding: 'utf8',
valueEncoding: 'utf8'
},
{
type: 'put',
sublevel: null,
key: '!2!a-y',
value: '[object Object]',
keyEncoding: 'utf8',
valueEncoding: 'utf8'
},
{
type: 'put',
sublevel: null,
key: '!1!b',
value: '[object Object]',
keyEncoding: 'utf8',
valueEncoding: 'utf8'
},
{
type: 'put',
sublevel: null,
key: '!2!b',
value: 'b',
keyEncoding: 'utf8',
valueEncoding: 'utf8'
},
{
type: 'del',
sublevel: null,
key: '!2!c1',
keyEncoding: 'utf8'
},
{
type: 'del',
sublevel: null,
key: '!2!c2-y',
keyEncoding: 'utf8'
},
{
type: 'del',
key: 'd-x',
keyEncoding: 'utf8'
}
])
t.same(options, {})
nextTick(callback)
}
}
const db = new MockLevel({ encodings: { utf8: true } }, {
keyEncoding: {
encode: (key) => key + '-x',
decode: (key) => key.slice(0, -2),
name: 'x',
format: 'utf8'
}
})
const sub1 = db.sublevel('1', { valueEncoding: 'json' })
const sub2 = db.sublevel('2', {
keyEncoding: {
encode: (key) => key + '-y',
decode: (key) => key.slice(0, -2),
name: 'y',
format: 'utf8'
}
})
if (!deferred) await sub1.open()
t.is(sub1.keyEncoding().name, 'utf8')
t.is(sub1.valueEncoding().name, 'json')
t.is(sub2.keyEncoding().name, 'y')
t.is(sub2.valueEncoding().name, 'utf8')
if (chained) {
await db.batch()
// keyEncoding: utf8 (sublevel), valueEncoding: json (sublevel)
.put('a', { foo: 123 }, { sublevel: sub1 })
// keyEncoding: y (sublevel), valueEncoding: utf8 (sublevel)
.put('a', { foo: 123 }, { sublevel: sub2 })
// keyEncoding: utf8 (sublevel), valueEncoding: utf8 (operation)
.put('b', { foo: 123 }, { sublevel: sub1, valueEncoding: 'utf8' })
// keyEncoding: utf8 (operation), valueEncoding: utf8 (sublevel)
.put('b', 'b', { sublevel: sub2, keyEncoding: 'utf8' })
// keyEncoding: utf8 (operation)
.del('c1', { sublevel: sub2, keyEncoding: 'utf8' })
// keyEncoding: y (sublevel)
.del('c2', { sublevel: sub2 })
// keyEncoding: x (db). Should not affect sublevels.
.del('d')
.write()
} else {
await db.batch([
{ type: 'put', sublevel: sub1, key: 'a', value: { foo: 123 } },
{ type: 'put', sublevel: sub2, key: 'a', value: { foo: 123 } },
{ type: 'put', sublevel: sub1, key: 'b', value: { foo: 123 }, valueEncoding: 'utf8' },
{ type: 'put', sublevel: sub2, key: 'b', value: 'b', keyEncoding: 'utf8' },
{ type: 'del', key: 'c1', sublevel: sub2, keyEncoding: 'utf8' },
{ type: 'del', key: 'c2', sublevel: sub2 },
{ type: 'del', key: 'd' }
])
}
})
}
}