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')
})
}
}