253 lines
8.0 KiB
JavaScript

'use strict'
const { Buffer } = require('buffer')
const identity = (v) => v
exports.all = function (test, testCommon) {
exports.sequence(test, testCommon)
exports.seek(test, testCommon)
}
exports.sequence = function (test, testCommon) {
for (const deferred of [false, true]) {
for (const mode of ['iterator', 'keys', 'values']) {
test(`${mode}().seek() throws if next() has not completed (deferred: ${deferred})`, async function (t) {
const db = testCommon.factory()
if (!deferred) await db.open()
const it = db[mode]()
const promise = it.next()
t.throws(() => it.seek('two'), (err) => err.code === 'LEVEL_ITERATOR_BUSY')
await promise
await db.close()
})
test(`${mode}().seek() does not throw after close() (deferred: ${deferred})`, async function (t) {
const db = testCommon.factory()
if (!deferred) await db.open()
const it = db[mode]()
await it.close()
t.doesNotThrow(() => it.seek('two'))
await db.close()
})
}
}
}
exports.seek = function (test, testCommon) {
const testData = () => [
{ type: 'put', key: 'one', value: '1' },
{ type: 'put', key: 'two', value: '2' },
{ type: 'put', key: 'three', value: '3' }
]
for (const mode of ['iterator', 'keys', 'values']) {
const mapEntry = mode === 'iterator' ? e => e : mode === 'keys' ? e => e[0] : e => e[1]
test(`${mode}().seek() to string target`, async function (t) {
const db = testCommon.factory()
await db.batch(testData())
const it = db[mode]()
it.seek('two')
t.same(await it.next(), mapEntry(['two', '2']), 'match')
t.same(await it.next(), undefined, 'end of iterator')
return db.close()
})
if (testCommon.supports.encodings.buffer) {
// TODO: make this test meaningful, with bytes outside the utf8 range
test(`${mode}().seek() to buffer target`, async function (t) {
const db = testCommon.factory()
await db.batch(testData())
const it = db[mode]({ keyEncoding: 'buffer' })
it.seek(Buffer.from('two'))
t.same(await it.next(), mapEntry([Buffer.from('two'), '2']), 'match')
t.same(await it.next(), undefined, 'end of iterator')
return db.close()
})
}
test(`${mode}().seek() to target with custom encoding`, async function (t) {
const db = testCommon.factory()
await db.batch(testData())
const it = db[mode]()
const keyEncoding = { encode: () => 'two', decode: identity, format: 'utf8' }
it.seek('xyz', { keyEncoding })
t.same(await it.next(), mapEntry(['two', '2']), 'match')
t.same(await it.next(), undefined, 'end of iterator')
return db.close()
})
test(`${mode}().seek() on reverse iterator`, async function (t) {
const db = testCommon.factory()
await db.batch(testData())
const it = db[mode]({ reverse: true, limit: 1 })
// Should land on key equal to or smaller than 'three!' which is 'three'
it.seek('three!')
t.same(await it.next(), mapEntry(['three', '3']), 'match')
t.same(await it.next(), undefined, 'end of iterator')
return db.close()
})
test(`${mode}().seek() to out of range target`, async function (t) {
const db = testCommon.factory()
await db.batch(testData())
const it = db[mode]()
it.seek('zzz')
t.same(await it.next(), undefined, 'end of iterator')
return db.close()
})
test(`${mode}().seek() on reverse iterator to out of range target`, async function (t) {
const db = testCommon.factory()
await db.batch(testData())
const it = db[mode]({ reverse: true })
it.seek('zzz')
t.same(await it.next(), mapEntry(['two', '2']), 'match')
t.same(await it.next(), mapEntry(['three', '3']), 'match')
t.same(await it.next(), mapEntry(['one', '1']), 'match')
t.same(await it.next(), undefined, 'end of iterator')
return db.close()
})
if (testCommon.supports.snapshots) {
for (const reverse of [false, true]) {
for (const deferred of [false, true]) {
test(`${mode}().seek() respects snapshot (reverse: ${reverse}, deferred: ${deferred})`, async function (t) {
const db = testCommon.factory()
if (!deferred) await db.open()
const it = db[mode]({ reverse })
// Add entry after having created the iterator (and its snapshot)
await db.put('a', 'a')
// Seeking should not create a new snapshot, which'd include the new entry
it.seek('a')
t.same(await it.next(), undefined)
return db.close()
})
}
}
}
test(`${mode}().seek() respects range`, function (t) {
const db = testCommon.factory()
db.open(function (err) {
t.error(err, 'no error from open()')
// Can't use Array.fill() because IE
const ops = []
for (let i = 0; i < 10; i++) {
ops.push({ type: 'put', key: String(i), value: String(i) })
}
db.batch(ops, function (err) {
t.error(err, 'no error from batch()')
let pending = 0
expect({ gt: '5' }, '4', undefined)
expect({ gt: '5' }, '5', undefined)
expect({ gt: '5' }, '6', '6')
expect({ gte: '5' }, '4', undefined)
expect({ gte: '5' }, '5', '5')
expect({ gte: '5' }, '6', '6')
// The gte option should take precedence over gt.
expect({ gte: '5', gt: '7' }, '4', undefined)
expect({ gte: '5', gt: '7' }, '5', '5')
expect({ gte: '5', gt: '7' }, '6', '6')
expect({ gte: '5', gt: '3' }, '4', undefined)
expect({ gte: '5', gt: '3' }, '5', '5')
expect({ gte: '5', gt: '3' }, '6', '6')
expect({ lt: '5' }, '4', '4')
expect({ lt: '5' }, '5', undefined)
expect({ lt: '5' }, '6', undefined)
expect({ lte: '5' }, '4', '4')
expect({ lte: '5' }, '5', '5')
expect({ lte: '5' }, '6', undefined)
// The lte option should take precedence over lt.
expect({ lte: '5', lt: '3' }, '4', '4')
expect({ lte: '5', lt: '3' }, '5', '5')
expect({ lte: '5', lt: '3' }, '6', undefined)
expect({ lte: '5', lt: '7' }, '4', '4')
expect({ lte: '5', lt: '7' }, '5', '5')
expect({ lte: '5', lt: '7' }, '6', undefined)
expect({ lt: '5', reverse: true }, '4', '4')
expect({ lt: '5', reverse: true }, '5', undefined)
expect({ lt: '5', reverse: true }, '6', undefined)
expect({ lte: '5', reverse: true }, '4', '4')
expect({ lte: '5', reverse: true }, '5', '5')
expect({ lte: '5', reverse: true }, '6', undefined)
expect({ gt: '5', reverse: true }, '4', undefined)
expect({ gt: '5', reverse: true }, '5', undefined)
expect({ gt: '5', reverse: true }, '6', '6')
expect({ gte: '5', reverse: true }, '4', undefined)
expect({ gte: '5', reverse: true }, '5', '5')
expect({ gte: '5', reverse: true }, '6', '6')
expect({ gt: '7', lt: '8' }, '7', undefined)
expect({ gte: '7', lt: '8' }, '7', '7')
expect({ gte: '7', lt: '8' }, '8', undefined)
expect({ gt: '7', lte: '8' }, '8', '8')
function expect (range, target, expected) {
pending++
const ite = db[mode](range)
ite.seek(target)
ite.next(function (err, item) {
t.error(err, 'no error from next()')
const json = JSON.stringify(range)
const msg = 'seek(' + target + ') on ' + json + ' yields ' + expected
// Either a key or value depending on mode
t.is(item, expected, msg)
ite.close(function (err) {
t.error(err, 'no error from close()')
if (!--pending) db.close(t.end.bind(t))
})
})
}
})
})
})
}
}