From 9ca0ff48f393770f0887034dde2cda2a37a62f35 Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Sat, 11 Apr 2020 10:22:20 +0300 Subject: [PATCH] Support running test suite on a levelup db (#364) As well as skipping 'start' and 'end' tests (for multileveldown). --- README.md | 1 + test/batch-test.js | 18 ++++++++++--- test/chained-batch-test.js | 6 ++--- test/clear-test.js | 2 +- test/close-test.js | 4 +-- test/common.js | 20 +++++++++++++- test/del-test.js | 8 +++--- test/get-test.js | 54 +++++++++++++++++++++---------------- test/iterator-range-test.js | 4 +++ test/iterator-test.js | 14 +++++++--- test/manifest-test.js | 2 +- test/open-test.js | 4 +-- test/put-get-del-test.js | 39 ++++++++++++++++----------- test/put-test.js | 12 ++++----- 14 files changed, 122 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 19553276..01b328c6 100644 --- a/README.md +++ b/README.md @@ -520,6 +520,7 @@ This also serves as a signal to users of your implementation. The following opti - Reads don't operate on a [snapshot](#iterator) - Snapshots are created asynchronously - `createIfMissing` and `errorIfExists`: set to `false` if `db._open()` does not support these options. +- `legacyRange`: set to `false` if your iterator does not support the legacy `start` and `end` range options. This metadata will be moved to manifests (`db.supports`) in the future. diff --git a/test/batch-test.js b/test/batch-test.js index 4b2a4719..39068831 100644 --- a/test/batch-test.js +++ b/test/batch-test.js @@ -11,7 +11,7 @@ exports.setUp = function (test, testCommon) { } exports.args = function (test, testCommon) { - test('test callback-less, 2-arg, batch() throws', function (t) { + testCommon.promises || test('test callback-less, 2-arg, batch() throws', function (t) { t.throws( db.batch.bind(db, 'foo', {}), /Error: batch\(array\) requires a callback argument/, @@ -215,7 +215,11 @@ exports.batch = function (test, testCommon) { db.get('foo', function (err, value) { t.error(err) var result - if (isTypedArray(value)) { + + if (testCommon.encodings) { + t.is(typeof value, 'string') + result = value + } else if (isTypedArray(value)) { result = String.fromCharCode.apply(null, new Uint16Array(value)) } else { t.ok(typeof Buffer !== 'undefined' && value instanceof Buffer) @@ -244,7 +248,10 @@ exports.batch = function (test, testCommon) { db.get('foobatch1', function (err, value) { t.error(err) var result - if (isTypedArray(value)) { + if (testCommon.encodings) { + t.is(typeof value, 'string') + result = value + } else if (isTypedArray(value)) { result = String.fromCharCode.apply(null, new Uint16Array(value)) } else { t.ok(typeof Buffer !== 'undefined' && value instanceof Buffer) @@ -264,7 +271,10 @@ exports.batch = function (test, testCommon) { db.get('foobatch3', function (err, value) { t.error(err) var result - if (isTypedArray(value)) { + if (testCommon.encodings) { + t.is(typeof value, 'string') + result = value + } else if (isTypedArray(value)) { result = String.fromCharCode.apply(null, new Uint16Array(value)) } else { t.ok(typeof Buffer !== 'undefined' && value instanceof Buffer) diff --git a/test/chained-batch-test.js b/test/chained-batch-test.js index 6bb2adf1..626b0b91 100644 --- a/test/chained-batch-test.js +++ b/test/chained-batch-test.js @@ -123,7 +123,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test batch#write() with no callback', function (t) { + testCommon.promises || test('test batch#write() with no callback', function (t) { try { db.batch().write() } catch (err) { @@ -186,7 +186,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test serialize object', function (t) { + testCommon.serialize && test('test serialize object', function (t) { var batch = db.batch() var ops = collectBatchOps(batch) @@ -202,7 +202,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test custom _serialize*', function (t) { + testCommon.serialize && test('test custom _serialize*', function (t) { t.plan(4) var _db = Object.create(db) diff --git a/test/clear-test.js b/test/clear-test.js index d338cad0..e14a9799 100644 --- a/test/clear-test.js +++ b/test/clear-test.js @@ -10,7 +10,7 @@ exports.setUp = function (test, testCommon) { } exports.args = function (test, testCommon) { - test('test argument-less clear() throws', function (t) { + testCommon.promises || test('test argument-less clear() throws', function (t) { t.throws( db.clear.bind(db), /Error: clear\(\) requires a callback argument/, diff --git a/test/close-test.js b/test/close-test.js index d70a06fb..f0a72cb1 100644 --- a/test/close-test.js +++ b/test/close-test.js @@ -10,12 +10,12 @@ exports.setUp = function (test, testCommon) { exports.close = function (test, testCommon) { test('test close()', function (t) { - t.throws( + testCommon.promises || t.throws( db.close.bind(db), /Error: close\(\) requires a callback argument/, 'no-arg close() throws' ) - t.throws( + testCommon.promises || t.throws( db.close.bind(db, 'foo'), /Error: close\(\) requires a callback argument/, 'non-callback close() throws' diff --git a/test/common.js b/test/common.js index cdc216ba..de402e65 100644 --- a/test/common.js +++ b/test/common.js @@ -24,7 +24,25 @@ function testCommon (options) { errorIfExists: options.errorIfExists !== false, snapshots: options.snapshots !== false, seek: options.seek !== false, - clear: !!options.clear + clear: !!options.clear, + + // Allow skipping 'start' and 'end' tests + // TODO (next major): drop legacy range options + legacyRange: options.legacyRange !== false, + + // Support running test suite on a levelup db. All options below this line + // are undocumented and should not be used by abstract-leveldown db's (yet). + promises: !!options.promises, + status: options.status !== false, + serialize: options.serialize !== false, + + // If true, the test suite assumes a default encoding of utf8 (like levelup) + // 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 } } diff --git a/test/del-test.js b/test/del-test.js index 9dbea61a..87aedeac 100644 --- a/test/del-test.js +++ b/test/del-test.js @@ -10,7 +10,7 @@ exports.setUp = function (test, testCommon) { } exports.args = function (test, testCommon) { - test('test argument-less del() throws', function (t) { + testCommon.promises || test('test argument-less del() throws', function (t) { t.throws( db.del.bind(db), /Error: del\(\) requires a callback argument/, @@ -19,7 +19,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test callback-less, 1-arg, del() throws', function (t) { + testCommon.promises || test('test callback-less, 1-arg, del() throws', function (t) { t.throws( db.del.bind(db, 'foo'), /Error: del\(\) requires a callback argument/, @@ -28,7 +28,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test callback-less, 3-arg, del() throws', function (t) { + testCommon.promises || test('test callback-less, 3-arg, del() throws', function (t) { t.throws( db.del.bind(db, 'foo', {}), /Error: del\(\) requires a callback argument/, @@ -37,7 +37,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test custom _serialize*', function (t) { + testCommon.serialize && test('test custom _serialize*', function (t) { t.plan(3) var db = testCommon.factory() db._serializeKey = function (data) { return data } diff --git a/test/get-test.js b/test/get-test.js index 8b5190fa..4a5c8644 100644 --- a/test/get-test.js +++ b/test/get-test.js @@ -11,7 +11,7 @@ exports.setUp = function (test, testCommon) { } exports.args = function (test, testCommon) { - test('test argument-less get() throws', function (t) { + testCommon.promises || test('test argument-less get() throws', function (t) { t.throws( db.get.bind(db), /Error: get\(\) requires a callback argument/, @@ -20,7 +20,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test callback-less, 1-arg, get() throws', function (t) { + testCommon.promises || test('test callback-less, 1-arg, get() throws', function (t) { t.throws( db.get.bind(db, 'foo'), /Error: get\(\) requires a callback argument/, @@ -29,7 +29,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test callback-less, 3-arg, get() throws', function (t) { + testCommon.promises || test('test callback-less, 3-arg, get() throws', function (t) { t.throws( db.get.bind(db, 'foo', {}), /Error: get\(\) requires a callback argument/, @@ -38,7 +38,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test custom _serialize*', function (t) { + testCommon.serialize && test('test custom _serialize*', function (t) { t.plan(3) var db = testCommon.factory() db._serializeKey = function (data) { return data } @@ -61,29 +61,12 @@ exports.get = function (test, testCommon) { t.error(err) db.get('foo', function (err, value) { t.error(err) - t.ok(typeof value !== 'string', 'should not be string by default') - var result - if (isTypedArray(value)) { - result = String.fromCharCode.apply(null, new Uint16Array(value)) - } else { - t.ok(typeof Buffer !== 'undefined' && value instanceof Buffer) - try { - result = value.toString() - } catch (e) { - t.error(e, 'should not throw when converting value to a string') - } - } - - t.equal(result, 'bar') - - db.get('foo', {}, function (err, value) { // same but with {} - t.error(err) + if (!testCommon.encodings) { t.ok(typeof value !== 'string', 'should not be string by default') - var result if (isTypedArray(value)) { - result = String.fromCharCode.apply(null, new Uint16Array(value)) + var result = String.fromCharCode.apply(null, new Uint16Array(value)) } else { t.ok(typeof Buffer !== 'undefined' && value instanceof Buffer) try { @@ -92,6 +75,31 @@ exports.get = function (test, testCommon) { t.error(e, 'should not throw when converting value to a string') } } + } else { + result = value + } + + t.equal(result, 'bar') + + db.get('foo', {}, function (err, value) { // same but with {} + t.error(err) + + if (!testCommon.encodings) { + t.ok(typeof value !== 'string', 'should not be string by default') + + if (isTypedArray(value)) { + var result = String.fromCharCode.apply(null, new Uint16Array(value)) + } else { + t.ok(typeof Buffer !== 'undefined' && value instanceof Buffer) + try { + result = value.toString() + } catch (e) { + t.error(e, 'should not throw when converting value to a string') + } + } + } else { + result = value + } t.equal(result, 'bar') diff --git a/test/iterator-range-test.js b/test/iterator-range-test.js index 3d5c0909..a691503a 100644 --- a/test/iterator-range-test.js +++ b/test/iterator-range-test.js @@ -35,6 +35,10 @@ exports.setUp = function (test, testCommon) { exports.range = function (test, testCommon) { function rangeTest (name, opts, expected) { + if (!testCommon.legacyRange && ('start' in opts || 'end' in opts)) { + return + } + opts.keyAsBuffer = false opts.valueAsBuffer = false test(name, function (t) { diff --git a/test/iterator-test.js b/test/iterator-test.js index 8b82ff4f..6ce85569 100644 --- a/test/iterator-test.js +++ b/test/iterator-test.js @@ -11,7 +11,8 @@ exports.setUp = function (test, testCommon) { exports.args = function (test, testCommon) { test('test iterator has db reference', function (t) { var iterator = db.iterator() - t.ok(iterator.db === db) + // For levelup compat: may return iterator of an underlying db, that's okay. + t.ok(iterator.db === db || iterator.db) iterator.end(t.end.bind(t)) }) @@ -132,14 +133,19 @@ exports.iterator = function (test, testCommon) { var fn = function (err, key, value) { t.error(err) if (key && value) { - t.ok(Buffer.isBuffer(key), 'key argument is a Buffer') - t.ok(Buffer.isBuffer(value), 'value argument is a Buffer') + if (testCommon.encodings) { + t.is(typeof key, 'string', 'key argument is a string') + t.is(typeof value, 'string', 'value argument is a string') + } else { + t.ok(Buffer.isBuffer(key), 'key argument is a Buffer') + t.ok(Buffer.isBuffer(value), 'value argument is a Buffer') + } t.is(key.toString(), data[idx].key, 'correct key') t.is(value.toString(), data[idx].value, 'correct value') process.nextTick(next) idx++ } else { // end - t.ok(typeof err === 'undefined', 'err argument is undefined') + t.ok(err == null, 'err argument is nullish') t.ok(typeof key === 'undefined', 'key argument is undefined') t.ok(typeof value === 'undefined', 'value argument is undefined') t.is(idx, data.length, 'correct number of entries') diff --git a/test/manifest-test.js b/test/manifest-test.js index 104c8432..080c0846 100644 --- a/test/manifest-test.js +++ b/test/manifest-test.js @@ -5,7 +5,7 @@ module.exports = function (test, testCommon) { suite(test, testCommon) - test('manifest has status', function (t) { + testCommon.status && test('manifest has status', function (t) { var db = testCommon.factory() t.is(db.supports.status, true) diff --git a/test/open-test.js b/test/open-test.js index 9d7ef2bf..8c5eea1a 100644 --- a/test/open-test.js +++ b/test/open-test.js @@ -3,7 +3,7 @@ exports.setUp = function (test, testCommon) { } exports.args = function (test, testCommon) { - test('test database open no-arg throws', function (t) { + testCommon.promises || test('test database open no-arg throws', function (t) { var db = testCommon.factory() t.throws( db.open.bind(db), @@ -13,7 +13,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test callback-less, 1-arg, open() throws', function (t) { + testCommon.promises || test('test callback-less, 1-arg, open() throws', function (t) { var db = testCommon.factory() t.throws( db.open.bind(db, {}), diff --git a/test/put-get-del-test.js b/test/put-get-del-test.js index b8fc3ee7..7d5aa384 100644 --- a/test/put-get-del-test.js +++ b/test/put-get-del-test.js @@ -50,15 +50,22 @@ function makePutErrorTest (test, type, key, value, expectedError) { }) } -function makePutGetDelSuccessfulTest (test, type, key, value, expectedResult) { - var hasExpectedResult = arguments.length === 5 +function makePutGetDelSuccessfulTest (test, testCommon, type, key, value, expectedResult) { + var hasExpectedResult = arguments.length === 6 test('test put()/get()/del() with ' + type, function (t) { db.put(key, value, function (err) { t.error(err) db.get(key, function (err, _value) { t.error(err, 'no error, has key/value for `' + type + '`') - t.ok(Buffer.isBuffer(_value), 'is a Buffer') - var result = _value + + if (!testCommon.encodings) { + t.ok(Buffer.isBuffer(_value), 'is a Buffer') + var result = _value + } else { + t.is(typeof _value, 'string', 'is a string') + result = _value + } + if (hasExpectedResult) { t.equal(result.toString(), expectedResult) } else { @@ -114,50 +121,52 @@ exports.errorValues = function (test, testCommon) { exports.nonErrorKeys = function (test, testCommon) { // valid falsey keys - makePutGetDelSuccessfulTest(test, '`0` key', 0, 'foo 0') + makePutGetDelSuccessfulTest(test, testCommon, '`0` key', 0, 'foo 0') // standard String key makePutGetDelSuccessfulTest( test + , testCommon , 'long String key' , 'some long string that I\'m using as a key for this unit test, cross your fingers human, we\'re going in!' , 'foo' ) if (testCommon.bufferKeys) { - makePutGetDelSuccessfulTest(test, 'Buffer key', testBuffer, 'foo') + makePutGetDelSuccessfulTest(test, testCommon, 'Buffer key', testBuffer, 'foo') } // non-empty Array as a value - makePutGetDelSuccessfulTest(test, 'Array value', 'foo', [1, 2, 3, 4]) + makePutGetDelSuccessfulTest(test, testCommon, 'Array value', 'foo', [1, 2, 3, 4]) } exports.nonErrorValues = function (test, testCommon) { // valid falsey values - makePutGetDelSuccessfulTest(test, '`false` value', 'foo false', false) - makePutGetDelSuccessfulTest(test, '`0` value', 'foo 0', 0) - makePutGetDelSuccessfulTest(test, '`NaN` value', 'foo NaN', NaN) + makePutGetDelSuccessfulTest(test, testCommon, '`false` value', 'foo false', false) + makePutGetDelSuccessfulTest(test, testCommon, '`0` value', 'foo 0', 0) + makePutGetDelSuccessfulTest(test, testCommon, '`NaN` value', 'foo NaN', NaN) // all of the following result in an empty-string value: - makePutGetDelSuccessfulTest(test, 'empty String value', 'foo', '', '') - makePutGetDelSuccessfulTest(test, 'empty Buffer value', 'foo', Buffer.alloc(0), '') + makePutGetDelSuccessfulTest(test, testCommon, 'empty String value', 'foo', '', '') + makePutGetDelSuccessfulTest(test, testCommon, 'empty Buffer value', 'foo', Buffer.alloc(0), '') // note that an implementation may return the value as an array - makePutGetDelSuccessfulTest(test, 'empty Array value', 'foo', [], '') + makePutGetDelSuccessfulTest(test, testCommon, 'empty Array value', 'foo', [], '') // standard String value makePutGetDelSuccessfulTest( test + , testCommon , 'long String value' , 'foo' , 'some long string that I\'m using as a key for this unit test, cross your fingers human, we\'re going in!' ) // standard Buffer value - makePutGetDelSuccessfulTest(test, 'Buffer value', 'foo', testBuffer) + makePutGetDelSuccessfulTest(test, testCommon, 'Buffer value', 'foo', testBuffer) // non-empty Array as a key - makePutGetDelSuccessfulTest(test, 'Array key', [1, 2, 3, 4], 'foo') + makePutGetDelSuccessfulTest(test, testCommon, 'Array key', [1, 2, 3, 4], 'foo') } exports.tearDown = function (test, testCommon) { diff --git a/test/put-test.js b/test/put-test.js index bc77206a..8e4aad83 100644 --- a/test/put-test.js +++ b/test/put-test.js @@ -10,7 +10,7 @@ exports.setUp = function (test, testCommon) { } exports.args = function (test, testCommon) { - test('test argument-less put() throws', function (t) { + testCommon.promises || test('test argument-less put() throws', function (t) { t.throws( db.put.bind(db), /Error: put\(\) requires a callback argument/, @@ -19,7 +19,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test callback-less, 1-arg, put() throws', function (t) { + testCommon.promises || test('test callback-less, 1-arg, put() throws', function (t) { t.throws( db.put.bind(db, 'foo'), /Error: put\(\) requires a callback argument/, @@ -28,7 +28,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test callback-less, 2-arg, put() throws', function (t) { + testCommon.promises || test('test callback-less, 2-arg, put() throws', function (t) { t.throws( db.put.bind(db, 'foo', 'bar'), /Error: put\(\) requires a callback argument/, @@ -37,7 +37,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test callback-less, 3-arg, put() throws', function (t) { + testCommon.promises || test('test callback-less, 3-arg, put() throws', function (t) { t.throws( db.put.bind(db, 'foo', 'bar', {}), /Error: put\(\) requires a callback argument/, @@ -46,7 +46,7 @@ exports.args = function (test, testCommon) { t.end() }) - test('test _serialize object', function (t) { + testCommon.serialize && test('test _serialize object', function (t) { t.plan(3) var db = testCommon.factory() db._put = function (key, value, opts, callback) { @@ -59,7 +59,7 @@ exports.args = function (test, testCommon) { }) }) - test('test custom _serialize*', function (t) { + testCommon.serialize && test('test custom _serialize*', function (t) { t.plan(4) var db = testCommon.factory() db._serializeKey = db._serializeValue = function (data) { return data }