Skip to content
This repository has been archived by the owner on Dec 1, 2024. It is now read-only.

Commit

Permalink
fixup! Add db.getMany(keys)
Browse files Browse the repository at this point in the history
  • Loading branch information
vweevers committed Sep 28, 2021
1 parent 6677fca commit 1f1ab97
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 144 deletions.
26 changes: 7 additions & 19 deletions abstract-leveldown.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,14 @@ AbstractLevelDOWN.prototype._checkValue = function (value) {
}
}

// Undocumented, only here for levelup API parity
AbstractLevelDOWN.prototype.isOpen = function () {
return this.status === 'open'
// TODO: docs and tests
AbstractLevelDOWN.prototype.isOperational = function () {
return this.status === 'open' || this._isOperational()
}

AbstractLevelDOWN.prototype.isClosed = function () {
return this.status === 'new' || this.status === 'closing' || this.status === 'closed'
// Implementation may accept operations in other states too
AbstractLevelDOWN.prototype._isOperational = function () {
return false
}

// Expose browser-compatible nextTick for dependents
Expand All @@ -381,20 +382,7 @@ AbstractLevelDOWN.prototype._nextTick = require('./next-tick')
module.exports = AbstractLevelDOWN

function maybeError (db, callback) {
if (db.status !== 'open') {
// Exemption to support deferredOpen
if (db.type === 'deferred-leveldown') {
return false
}

// TODO: deferred-leveldown and levelup are inconsistent: the former allows
// operations on any db status, the latter only if 'opening' or 'open'. We
// should define the scope of the deferredOpen feature. For now, pick the
// levelup behavior. Ref https://github.com/Level/community/issues/58
if (db.supports.deferredOpen && db.status === 'opening') {
return false
}

if (!db.isOperational()) {
db._nextTick(callback, new Error('Database is not open'))
return true
}
Expand Down
1 change: 0 additions & 1 deletion test/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ function testCommon (options) {
// and that operations return strings rather than buffers by default.
encodings: !!options.encodings,

// Not yet used, only here for symmetry with levelup's test suite.
deferredOpen: !!options.deferredOpen,
streams: !!options.streams
}
Expand Down
16 changes: 0 additions & 16 deletions test/del-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,6 @@ exports.args = function (test, testCommon) {
)
t.end()
})

testCommon.serialize && test('test custom _serialize*', function (t) {
t.plan(3)
const db = testCommon.factory()
db._serializeKey = function (data) { return data }
db._del = function (key, options, callback) {
t.deepEqual(key, { foo: 'bar' })
this._nextTick(callback)
}
db.open(function () {
db.del({ foo: 'bar' }, function (err) {
t.error(err)
db.close(t.error.bind(t))
})
})
})
}

exports.del = function (test, testCommon) {
Expand Down
165 changes: 117 additions & 48 deletions test/get-many-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,31 @@ const assertAsync = require('./util').assertAsync

let db

/**
* @param {import('tape')} test
*/
exports.setUp = function (test, testCommon) {
test('setUp db', function (t) {
db = testCommon.factory()
db.open(t.end.bind(t))
})
}

/**
* @param {import('tape')} test
*/
exports.args = function (test, testCommon) {
test('test getMany() requires an array argument (callback)', function (t) {
test('test getMany() requires an array argument (callback)', assertAsync.ctx(function (t) {
// Add 1 assertion for every assertAsync()
t.plan(4)

db.getMany('foo', assertAsync(t, function (err) {
db.getMany('foo', assertAsync(function (err) {
t.is(err && err.message, 'getMany() requires an array argument')
}))
db.getMany('foo', {}, assertAsync(t, function (err) {
db.getMany('foo', {}, assertAsync(function (err) {
t.is(err && err.message, 'getMany() requires an array argument')
}))
})
}))

test('test getMany() requires an array argument (promise)', function (t) {
t.plan(3)
Expand All @@ -38,24 +45,11 @@ exports.args = function (test, testCommon) {
t.is(err && err.message, 'getMany() requires an array argument')
})
})

testCommon.serialize && test('test custom _serialize*', function (t) {
t.plan(3)
const db = testCommon.factory()
db._serializeKey = function (data) { return data }
db._getMany = function (keys, options, callback) {
t.same(keys, [{ foo: 'bar' }])
this._nextTick(callback, null, ['foo'])
}
db.open(function () {
db.getMany([{ foo: 'bar' }], function (err) {
t.error(err)
db.close(t.error.bind(t))
})
})
})
}

/**
* @param {import('tape')} test
*/
exports.getMany = function (test, testCommon) {
test('test getMany() support is reflected in manifest', function (t) {
t.is(db.supports && db.supports.getMany, true)
Expand Down Expand Up @@ -129,27 +123,27 @@ exports.getMany = function (test, testCommon) {
})
})

test('test empty getMany()', function (t) {
test('test empty getMany()', assertAsync.ctx(function (t) {
t.plan(2 * 3)

for (const asBuffer in [true, false]) {
db.getMany([], { asBuffer }, assertAsync(t, function (err, values) {
db.getMany([], { asBuffer }, assertAsync(function (err, values) {
t.ifError(err)
t.same(values, [])
}))
}
})
}))

test('test not-found getMany()', function (t) {
test('test not-found getMany()', assertAsync.ctx(function (t) {
t.plan(2 * 3)

for (const asBuffer in [true, false]) {
db.getMany(['nope', 'another'], { asBuffer }, assertAsync(t, function (err, values) {
db.getMany(['nope', 'another'], { asBuffer }, assertAsync(function (err, values) {
t.ifError(err)
t.same(values, [undefined, undefined])
}))
}
})
}))

test('test getMany() with promise', async function (t) {
t.same(await db.getMany(['foo'], { asBuffer: false }), ['bar'])
Expand Down Expand Up @@ -188,50 +182,125 @@ exports.getMany = function (test, testCommon) {
})
})

testCommon.deferredOpen || test('test getMany() on new db', function (t) {
const db = testCommon.factory()
test('test getMany() on new db', assertAsync.ctx(function (t) {
t.plan(2 * 2 * 5)

// Also test empty array because it has a fast-path
for (const keys of [['foo'], []]) {
// Opening should make no difference, because we call it after getMany()
for (const open of [true, false]) {
const db = testCommon.factory()

if (db.type === 'deferred-leveldown') {
t.pass('exempted')
return t.end()
if (testCommon.status) {
t.is(db.status, testCommon.deferredOpen ? 'opening' : 'new')
} else {
t.pass('no status')
}

// Must be true if db supports deferredOpen
const operational = testCommon.deferredOpen || db.isOperational()

db.getMany(keys, assertAsync(function (err, values) {
if (operational) {
t.ifError(err, 'no error')
t.same(values, keys.map(_ => undefined))
} else {
t.is(err && err.message, 'Database is not open')
t.is(values, undefined)
}
}))

if (open) {
db.open(t.error.bind(t))
} else {
t.pass()
}
}
}
}))

db.getMany([], assertAsync(t, function (err) {
t.is(err && err.message, 'Database is not open')
t.end()
}))
})
test('test getMany() on opening db', assertAsync.ctx(function (t) {
t.plan(2 * 5)

test('test getMany() on closing db', function (t) {
const db = testCommon.factory()
// Also test empty array because it has a fast-path
for (const keys of [['foo'], []]) {
const db = testCommon.factory()

// Is a noop if db supports deferredOpen
db.open(assertAsync(t.error.bind(t), 'open'))

if (db.type === 'deferred-leveldown') {
t.pass('exempted')
return t.end()
// Must be true if db supports deferredOpen
const operational = testCommon.deferredOpen || db.isOperational()

db.getMany(keys, assertAsync(function (err, values) {
if (operational) {
t.ifError(err, 'no error')
t.same(values, keys.map(_ => undefined))
} else {
t.is(err && err.message, 'Database is not open')
t.is(values, undefined)
}
}))
}
}))

t.plan(4)
test('test getMany() on closed db', function (t) {
t.plan(2 * 6)

db.open(function (err) {
t.ifError(err)
// Also test empty array because it has a fast-path
for (const keys of [['foo'], []]) {
const db = testCommon.factory()

db.close(function (err) {
db.open(function (err) {
t.ifError(err)
t.is(db.isOperational(), true)

db.close(assertAsync.with(t, function (err) {
t.ifError(err)
t.is(db.isOperational(), false)

db.getMany(keys, assertAsync(function (err) {
t.is(err && err.message, 'Database is not open')
}))
}))
})
}
})

test('test getMany() on closing db', function (t) {
t.plan(2 * 4)

// Also test empty array because it has a fast-path
for (const keys of [['foo'], []]) {
const db = testCommon.factory()

db.getMany([], assertAsync(t, function (err) {
t.is(err && err.message, 'Database is not open')
db.open(assertAsync.with(t, function (err) {
t.ifError(err)

db.close(function (err) {
t.ifError(err)
})

db.getMany(keys, assertAsync(function (err) {
t.is(err && err.message, 'Database is not open')
}))
}))
})
}
})
}

/**
* @param {import('tape')} test
*/
exports.tearDown = function (test, testCommon) {
test('tearDown', function (t) {
db.close(t.end.bind(t))
})
}

/**
* @param {import('tape')} test
*/
exports.all = function (test, testCommon) {
exports.setUp(test, testCommon)
exports.args(test, testCommon)
Expand Down
16 changes: 0 additions & 16 deletions test/get-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,6 @@ exports.args = function (test, testCommon) {
)
t.end()
})

testCommon.serialize && test('test custom _serialize*', function (t) {
t.plan(3)
const db = testCommon.factory()
db._serializeKey = function (data) { return data }
db._get = function (key, options, callback) {
t.same(key, { foo: 'bar' })
this._nextTick(callback)
}
db.open(function () {
db.get({ foo: 'bar' }, function (err) {
t.error(err)
db.close(t.error.bind(t))
})
})
})
}

exports.get = function (test, testCommon) {
Expand Down
4 changes: 2 additions & 2 deletions test/iterator-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ exports.setUp = function (test, testCommon) {
exports.args = function (test, testCommon) {
test('test iterator has db reference', function (t) {
const iterator = db.iterator()
// For levelup compat: may return iterator of an underlying db, that's okay.
t.ok(iterator.db === db || iterator.db)
// For levelup & deferred-leveldown compat: may return iterator of an underlying db, that's okay.
t.ok(iterator.db === db || iterator.db === (db.db || db._db || db))
iterator.end(t.end.bind(t))
})

Expand Down
9 changes: 5 additions & 4 deletions test/put-get-del-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,22 @@ function makeGetDelErrorTests (test, testCommon, type, key, expectedError) {
async = true
})

testCommon.getMany && test('test getMany() with ' + type + ' causes error', function (t) {
testCommon.getMany && test('test getMany() with ' + type + ' causes error', assertAsync.ctx(function (t) {
// Add 1 assertion for every assertAsync()
t.plan(2 * 4)

db.getMany([key], assertAsync(t, function (err) {
db.getMany([key], assertAsync(function (err) {
t.ok(err, 'has error')
t.ok(err instanceof Error)
t.ok(err.message.match(expectedError), 'correct error message')
}))

db.getMany(['valid', key], assertAsync(t, function (err) {
db.getMany(['valid', key], assertAsync(function (err) {
t.ok(err, 'has error')
t.ok(err instanceof Error)
t.ok(err.message.match(expectedError), 'correct error message')
}))
})
}))
}

function makePutErrorTest (test, type, key, value, expectedError) {
Expand Down
Loading

0 comments on commit 1f1ab97

Please sign in to comment.