forked from public/fvtt-cthulhu-eternal
Initial import with skill sheet working
This commit is contained in:
263
node_modules/abstract-level/test/self/abstract-iterator-test.js
generated
vendored
Normal file
263
node_modules/abstract-level/test/self/abstract-iterator-test.js
generated
vendored
Normal 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')
|
||||
}
|
||||
}
|
||||
})
|
||||
213
node_modules/abstract-level/test/self/async-iterator-test.js
generated
vendored
Normal file
213
node_modules/abstract-level/test/self/async-iterator-test.js
generated
vendored
Normal 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')
|
||||
})
|
||||
}
|
||||
}
|
||||
75
node_modules/abstract-level/test/self/attach-resource-test.js
generated
vendored
Normal file
75
node_modules/abstract-level/test/self/attach-resource-test.js
generated
vendored
Normal 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
90
node_modules/abstract-level/test/self/defer-test.js
generated
vendored
Normal 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)
|
||||
})
|
||||
98
node_modules/abstract-level/test/self/deferred-chained-batch-test.js
generated
vendored
Normal file
98
node_modules/abstract-level/test/self/deferred-chained-batch-test.js
generated
vendored
Normal 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')
|
||||
})
|
||||
})
|
||||
})
|
||||
298
node_modules/abstract-level/test/self/deferred-iterator-test.js
generated
vendored
Normal file
298
node_modules/abstract-level/test/self/deferred-iterator-test.js
generated
vendored
Normal 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()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
93
node_modules/abstract-level/test/self/deferred-operations-test.js
generated
vendored
Normal file
93
node_modules/abstract-level/test/self/deferred-operations-test.js
generated
vendored
Normal 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
426
node_modules/abstract-level/test/self/encoding-test.js
generated
vendored
Normal 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
829
node_modules/abstract-level/test/self/iterator-test.js
generated
vendored
Normal 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
931
node_modules/abstract-level/test/self/sublevel-test.js
generated
vendored
Normal 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' }
|
||||
])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user