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