427 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			427 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict'
 | |
| 
 | |
| // TODO: move to per-method test files
 | |
| 
 | |
| const test = require('tape')
 | |
| const { Buffer } = require('buffer')
 | |
| const { mockLevel, mockChainedBatch, nullishEncoding } = require('../util')
 | |
| const identity = (v) => v
 | |
| 
 | |
| const utf8Manifest = { encodings: { utf8: true } }
 | |
| const dualManifest = { encodings: { utf8: true, buffer: true } }
 | |
| const hasOwnProperty = Object.prototype.hasOwnProperty
 | |
| 
 | |
| for (const deferred of [false, true]) {
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`get() encodes utf8 key (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(4)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _get (key, options, callback) {
 | |
|         t.is(key, '8')
 | |
|         t.is(options.keyEncoding, 'utf8')
 | |
|         t.is(options.valueEncoding, 'utf8')
 | |
|         this.nextTick(callback, null, 'foo')
 | |
|       }
 | |
|     }, utf8Manifest)
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     t.same(await db.get(8), 'foo')
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`get() takes encoding options (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(4)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _get (key, options, callback) {
 | |
|         t.is(key, '[1,"2"]')
 | |
|         t.is(options.keyEncoding, 'utf8')
 | |
|         t.is(options.valueEncoding, 'utf8')
 | |
|         this.nextTick(callback, null, '123')
 | |
|       }
 | |
|     }, utf8Manifest)
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     t.same(await db.get([1, '2'], { keyEncoding: 'json', valueEncoding: 'json' }), 123)
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`get() with custom value encoding that wants a buffer (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(3)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _get (key, options, callback) {
 | |
|         t.same(key, 'key')
 | |
|         t.same(options, { keyEncoding: 'utf8', valueEncoding: 'buffer' })
 | |
|         this.nextTick(callback, null, Buffer.alloc(1))
 | |
|       }
 | |
|     }, dualManifest, {
 | |
|       keyEncoding: 'utf8',
 | |
|       valueEncoding: { encode: identity, decode: identity, format: 'buffer' }
 | |
|     })
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     t.same(await db.get('key'), Buffer.alloc(1))
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`get() with custom value encoding that wants a string (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(3)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _get (key, options, callback) {
 | |
|         t.same(key, Buffer.from('key'))
 | |
|         t.same(options, { keyEncoding: 'buffer', valueEncoding: 'utf8' })
 | |
|         this.nextTick(callback, null, 'x')
 | |
|       }
 | |
|     }, dualManifest, {
 | |
|       keyEncoding: 'buffer',
 | |
|       valueEncoding: { encode: identity, decode: identity, format: 'utf8' }
 | |
|     })
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     t.same(await db.get(Buffer.from('key')), 'x')
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`put() encodes utf8 key and value (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(4)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _put (key, value, options, callback) {
 | |
|         t.is(key, '8')
 | |
|         t.is(value, '4')
 | |
|         t.is(options.keyEncoding, 'utf8')
 | |
|         t.is(options.valueEncoding, 'utf8')
 | |
|         this.nextTick(callback)
 | |
|       }
 | |
|     }, utf8Manifest)
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     await db.put(8, 4)
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`put() takes encoding options (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(4)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _put (key, value, options, callback) {
 | |
|         t.is(key, '[1,"2"]')
 | |
|         t.is(value, '{"x":3}')
 | |
|         t.is(options.keyEncoding, 'utf8')
 | |
|         t.is(options.valueEncoding, 'utf8')
 | |
|         this.nextTick(callback)
 | |
|       }
 | |
|     }, utf8Manifest)
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     await db.put([1, '2'], { x: 3 }, { keyEncoding: 'json', valueEncoding: 'json' })
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`del() encodes utf8 key (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(2)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _del (key, options, callback) {
 | |
|         t.is(key, '2')
 | |
|         t.is(options.keyEncoding, 'utf8')
 | |
|         this.nextTick(callback)
 | |
|       }
 | |
|     }, utf8Manifest)
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     await db.del(2)
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`del() takes keyEncoding option (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(2)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _del (key, options, callback) {
 | |
|         t.is(key, '[1,"2"]')
 | |
|         t.is(options.keyEncoding, 'utf8')
 | |
|         this.nextTick(callback)
 | |
|       }
 | |
|     }, utf8Manifest)
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     await db.del([1, '2'], { keyEncoding: 'json' })
 | |
|   })
 | |
| 
 | |
|   test(`getMany() encodes utf8 key (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(4)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _getMany (keys, options, callback) {
 | |
|         t.same(keys, ['8', '29'])
 | |
|         t.is(options.keyEncoding, 'utf8')
 | |
|         t.is(options.valueEncoding, 'utf8')
 | |
|         this.nextTick(callback, null, ['foo', 'bar'])
 | |
|       }
 | |
|     }, utf8Manifest)
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     t.same(await db.getMany([8, 29]), ['foo', 'bar'])
 | |
|   })
 | |
| 
 | |
|   test(`getMany() takes encoding options (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(4)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _getMany (keys, options, callback) {
 | |
|         t.same(keys, ['[1,"2"]', '"x"'])
 | |
|         t.is(options.keyEncoding, 'utf8')
 | |
|         t.is(options.valueEncoding, 'utf8')
 | |
|         this.nextTick(callback, null, ['123', '"hi"'])
 | |
|       }
 | |
|     }, utf8Manifest)
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     t.same(await db.getMany([[1, '2'], 'x'], { keyEncoding: 'json', valueEncoding: 'json' }), [123, 'hi'])
 | |
|   })
 | |
| 
 | |
|   test(`getMany() with custom value encoding that wants a buffer (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(3)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _getMany (keys, options, callback) {
 | |
|         t.same(keys, ['key'])
 | |
|         t.same(options, { keyEncoding: 'utf8', valueEncoding: 'buffer' })
 | |
|         this.nextTick(callback, null, [Buffer.alloc(1)])
 | |
|       }
 | |
|     }, dualManifest, {
 | |
|       keyEncoding: 'utf8',
 | |
|       valueEncoding: { encode: identity, decode: identity, format: 'buffer' }
 | |
|     })
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     t.same(await db.getMany(['key']), [Buffer.alloc(1)])
 | |
|   })
 | |
| 
 | |
|   test(`getMany() with custom value encoding that wants a string (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(3)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _getMany (keys, options, callback) {
 | |
|         t.same(keys, [Buffer.from('key')])
 | |
|         t.same(options, { keyEncoding: 'buffer', valueEncoding: 'utf8' })
 | |
|         this.nextTick(callback, null, ['x'])
 | |
|       }
 | |
|     }, dualManifest, {
 | |
|       keyEncoding: 'buffer',
 | |
|       valueEncoding: { encode: identity, decode: identity, format: 'utf8' }
 | |
|     })
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     t.same(await db.getMany([Buffer.from('key')]), ['x'])
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`chainedBatch.put() and del() encode utf8 key and value (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(deferred ? 2 : 4)
 | |
| 
 | |
|     let db
 | |
| 
 | |
|     if (deferred) {
 | |
|       db = mockLevel({
 | |
|         _batch (array, options, callback) {
 | |
|           t.same(array, [
 | |
|             { type: 'put', key: '1', value: '2', keyEncoding: 'utf8', valueEncoding: 'utf8' },
 | |
|             { type: 'del', key: '3', keyEncoding: 'utf8' }
 | |
|           ])
 | |
|           t.same(options, {})
 | |
|           this.nextTick(callback)
 | |
|         }
 | |
|       }, utf8Manifest)
 | |
|     } else {
 | |
|       db = mockLevel({
 | |
|         _chainedBatch () {
 | |
|           return mockChainedBatch(this, {
 | |
|             _put: function (key, value, options) {
 | |
|               t.same({ key, value }, { key: '1', value: '2' })
 | |
|               t.same(options, { keyEncoding: 'utf8', valueEncoding: 'utf8' })
 | |
|             },
 | |
|             _del: function (key, options) {
 | |
|               t.is(key, '3')
 | |
|               t.same(options, { keyEncoding: 'utf8' })
 | |
|             }
 | |
|           })
 | |
|         }
 | |
|       }, utf8Manifest)
 | |
|     }
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     await db.batch().put(1, 2).del(3).write()
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`chainedBatch.put() and del() take encoding options (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(deferred ? 2 : 4)
 | |
| 
 | |
|     let db
 | |
| 
 | |
|     const putOptions = { keyEncoding: 'json', valueEncoding: 'json' }
 | |
|     const delOptions = { keyEncoding: 'json' }
 | |
| 
 | |
|     if (deferred) {
 | |
|       db = mockLevel({
 | |
|         _batch (array, options, callback) {
 | |
|           t.same(array, [
 | |
|             { type: 'put', key: '"1"', value: '{"x":[2]}', keyEncoding: 'utf8', valueEncoding: 'utf8' },
 | |
|             { type: 'del', key: '"3"', keyEncoding: 'utf8' }
 | |
|           ])
 | |
|           t.same(options, {})
 | |
|           this.nextTick(callback)
 | |
|         }
 | |
|       }, utf8Manifest)
 | |
|     } else {
 | |
|       db = mockLevel({
 | |
|         _chainedBatch () {
 | |
|           return mockChainedBatch(this, {
 | |
|             _put: function (key, value, options) {
 | |
|               t.same({ key, value }, { key: '"1"', value: '{"x":[2]}' })
 | |
|               t.same(options, { keyEncoding: 'utf8', valueEncoding: 'utf8' })
 | |
|             },
 | |
|             _del: function (key, options) {
 | |
|               t.is(key, '"3"')
 | |
|               t.same(options, { keyEncoding: 'utf8' })
 | |
|             }
 | |
|           })
 | |
|         }
 | |
|       }, utf8Manifest)
 | |
|     }
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     await db.batch().put('1', { x: [2] }, putOptions).del('3', delOptions).write()
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`clear() receives keyEncoding option (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(1)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _clear: function (options, callback) {
 | |
|         t.same(options, { keyEncoding: 'utf8', reverse: false, limit: -1 })
 | |
|         this.nextTick(callback)
 | |
|       }
 | |
|     }, utf8Manifest)
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     await db.clear()
 | |
|   })
 | |
| 
 | |
|   test(`clear() takes keyEncoding option (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(1)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _clear: function (options, callback) {
 | |
|         t.same(options, { keyEncoding: 'utf8', gt: '"a"', reverse: false, limit: -1 })
 | |
|         this.nextTick(callback)
 | |
|       }
 | |
|     }, utf8Manifest)
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     await db.clear({ keyEncoding: 'json', gt: 'a' })
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`clear() encodes range options (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(5)
 | |
| 
 | |
|     const keyEncoding = {
 | |
|       format: 'utf8',
 | |
|       encode: function (key) {
 | |
|         return 'encoded_' + key
 | |
|       },
 | |
|       decode: identity
 | |
|     }
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _clear: function (options, callback) {
 | |
|         t.is(options.gt, 'encoded_1')
 | |
|         t.is(options.gte, 'encoded_2')
 | |
|         t.is(options.lt, 'encoded_3')
 | |
|         t.is(options.lte, 'encoded_4')
 | |
|         t.is(options.foo, 5)
 | |
|         this.nextTick(callback)
 | |
|       }
 | |
|     }, utf8Manifest, { keyEncoding })
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     await db.clear({ gt: 1, gte: 2, lt: 3, lte: 4, foo: 5 })
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`clear() does not strip nullish range options (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(12)
 | |
| 
 | |
|     const db1 = mockLevel({
 | |
|       _clear: function (options, callback) {
 | |
|         t.is(options.gt, '\x00', 'encoded null')
 | |
|         t.is(options.gte, '\x00', 'encoded null')
 | |
|         t.is(options.lt, '\x00', 'encoded null')
 | |
|         t.is(options.lte, '\x00', 'encoded null')
 | |
|         this.nextTick(callback)
 | |
|       }
 | |
|     }, utf8Manifest, { keyEncoding: nullishEncoding, valueEncoding: nullishEncoding })
 | |
| 
 | |
|     const db2 = mockLevel({
 | |
|       _clear: function (options, callback) {
 | |
|         t.is(hasOwnProperty.call(options, 'gt'), true)
 | |
|         t.is(hasOwnProperty.call(options, 'gte'), true)
 | |
|         t.is(hasOwnProperty.call(options, 'lt'), true)
 | |
|         t.is(hasOwnProperty.call(options, 'lte'), true)
 | |
| 
 | |
|         t.is(options.gt, '\xff', 'encoded undefined')
 | |
|         t.is(options.gte, '\xff', 'encoded undefined')
 | |
|         t.is(options.lt, '\xff', 'encoded undefined')
 | |
|         t.is(options.lte, '\xff', 'encoded undefined')
 | |
| 
 | |
|         this.nextTick(callback)
 | |
|       }
 | |
|     }, utf8Manifest, { keyEncoding: nullishEncoding, valueEncoding: nullishEncoding })
 | |
| 
 | |
|     if (!deferred) {
 | |
|       await Promise.all([db1.open(), db2.open()])
 | |
|     }
 | |
| 
 | |
|     const promise1 = db1.clear({
 | |
|       gt: null,
 | |
|       gte: null,
 | |
|       lt: null,
 | |
|       lte: null
 | |
|     })
 | |
| 
 | |
|     const promise2 = db2.clear({
 | |
|       gt: undefined,
 | |
|       gte: undefined,
 | |
|       lt: undefined,
 | |
|       lte: undefined
 | |
|     })
 | |
| 
 | |
|     await Promise.all([promise1, promise2])
 | |
|   })
 | |
| 
 | |
|   // NOTE: adapted from encoding-down
 | |
|   test(`clear() does not add nullish range options (deferred: ${deferred})`, async function (t) {
 | |
|     t.plan(4)
 | |
| 
 | |
|     const db = mockLevel({
 | |
|       _clear: function (options, callback) {
 | |
|         t.is(hasOwnProperty.call(options, 'gt'), false)
 | |
|         t.is(hasOwnProperty.call(options, 'gte'), false)
 | |
|         t.is(hasOwnProperty.call(options, 'lt'), false)
 | |
|         t.is(hasOwnProperty.call(options, 'lte'), false)
 | |
|         this.nextTick(callback)
 | |
|       }
 | |
|     })
 | |
| 
 | |
|     if (!deferred) await db.open()
 | |
|     await db.clear({})
 | |
|   })
 | |
| }
 |