427 lines
12 KiB
JavaScript
427 lines
12 KiB
JavaScript
'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({})
|
|
})
|
|
}
|