forked from public/fvtt-cthulhu-eternal
253 lines
8.0 KiB
JavaScript
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))
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
}
|