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({})
})
}