mirror of https://github.com/seald/nedb
parent
e3f4ee8783
commit
44ae8d7029
@ -0,0 +1 @@ |
||||
module.exports = require('@react-native-async-storage/async-storage/jest/async-storage-mock') |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@ |
||||
module.exports = {} |
@ -0,0 +1,66 @@ |
||||
/** |
||||
* Specific customUtils for the browser, where we don't have access to the Crypto and Buffer modules |
||||
*/ |
||||
|
||||
/** |
||||
* Taken from the crypto-browserify module |
||||
* https://github.com/dominictarr/crypto-browserify
|
||||
* NOTE: Math.random() does not guarantee "cryptographic quality" but we actually don't need it |
||||
*/ |
||||
const randomBytes = size => { |
||||
const bytes = new Array(size) |
||||
|
||||
for (let i = 0, r; i < size; i++) { |
||||
if ((i & 0x03) === 0) r = Math.random() * 0x100000000 |
||||
bytes[i] = r >>> ((i & 0x03) << 3) & 0xff |
||||
} |
||||
|
||||
return bytes |
||||
} |
||||
|
||||
/** |
||||
* Taken from the base64-js module |
||||
* https://github.com/beatgammit/base64-js/
|
||||
*/ |
||||
const byteArrayToBase64 = uint8 => { |
||||
const lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' |
||||
const extraBytes = uint8.length % 3 // if we have 1 byte left, pad 2 bytes
|
||||
let output = '' |
||||
let temp |
||||
|
||||
const tripletToBase64 = num => lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F] |
||||
|
||||
// go through the array every three bytes, we'll deal with trailing stuff later
|
||||
for (let i = 0, length = uint8.length - extraBytes; i < length; i += 3) { |
||||
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) |
||||
output += tripletToBase64(temp) |
||||
} |
||||
|
||||
// pad the end with zeros, but make sure to not forget the extra bytes
|
||||
if (extraBytes === 1) { |
||||
temp = uint8[uint8.length - 1] |
||||
output += lookup[temp >> 2] |
||||
output += lookup[(temp << 4) & 0x3F] |
||||
output += '==' |
||||
} else if (extraBytes === 2) { |
||||
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) |
||||
output += lookup[temp >> 10] |
||||
output += lookup[(temp >> 4) & 0x3F] |
||||
output += lookup[(temp << 2) & 0x3F] |
||||
output += '=' |
||||
} |
||||
|
||||
return output |
||||
} |
||||
|
||||
/** |
||||
* Return a random alphanumerical string of length len |
||||
* There is a very small probability (less than 1/1,000,000) for the length to be less than len |
||||
* (il the base64 conversion yields too many pluses and slashes) but |
||||
* that's not an issue here |
||||
* The probability of a collision is extremely small (need 3*10^12 documents to have one chance in a million of a collision) |
||||
* See http://en.wikipedia.org/wiki/Birthday_problem
|
||||
*/ |
||||
const uid = len => byteArrayToBase64(randomBytes(Math.ceil(Math.max(8, len * 2)))).replace(/[+/]/g, '').slice(0, len) |
||||
|
||||
module.exports.uid = uid |
@ -0,0 +1,84 @@ |
||||
/** |
||||
* Way data is stored for this database |
||||
* For a Node.js/Node Webkit database it's the file system |
||||
* For a browser-side database it's localforage, which uses the best backend available (IndexedDB then WebSQL then localStorage) |
||||
* For a react-native database, we use @react-native-async-storage/async-storage |
||||
* This version is the react-native version |
||||
*/ |
||||
const AsyncStorage = require('@react-native-async-storage/async-storage') |
||||
const exists = (filename, cback) => { |
||||
// eslint-disable-next-line node/handle-callback-err
|
||||
AsyncStorage.getItem(filename, (err, value) => { |
||||
if (value !== null) { |
||||
return cback(true) |
||||
} else { |
||||
return cback(false) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
const rename = (filename, newFilename, callback) => { |
||||
// eslint-disable-next-line node/handle-callback-err
|
||||
AsyncStorage.getItem(filename, (err, value) => { |
||||
if (value === null) { |
||||
this.storage.removeItem(newFilename, callback) |
||||
} else { |
||||
this.storage.setItem(newFilename, value, () => { |
||||
this.storage.removeItem(filename, callback) |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
const writeFile = (filename, contents, options, callback) => { |
||||
// Options do not matter in browser setup
|
||||
if (typeof options === 'function') { callback = options } |
||||
AsyncStorage.setItem(filename, contents, callback) |
||||
} |
||||
|
||||
const appendFile = (filename, toAppend, options, callback) => { |
||||
// Options do not matter in browser setup
|
||||
if (typeof options === 'function') { callback = options } |
||||
|
||||
// eslint-disable-next-line node/handle-callback-err
|
||||
AsyncStorage.getItem(filename, (err, contents) => { |
||||
contents = contents || '' |
||||
contents += toAppend |
||||
AsyncStorage.setItem(filename, contents, callback) |
||||
}) |
||||
} |
||||
|
||||
const readFile = (filename, options, callback) => { |
||||
// Options do not matter in browser setup
|
||||
if (typeof options === 'function') { callback = options } |
||||
// eslint-disable-next-line node/handle-callback-err
|
||||
AsyncStorage.getItem(filename, (err, contents) => { |
||||
return callback(null, contents || '') |
||||
}) |
||||
} |
||||
|
||||
const unlink = (filename, callback) => { |
||||
AsyncStorage.removeItem(filename, callback) |
||||
} |
||||
|
||||
// Nothing to do, no directories will be used on the browser
|
||||
const mkdir = (dir, options, callback) => callback() |
||||
|
||||
// Nothing to do, no data corruption possible in the browser
|
||||
const ensureDatafileIntegrity = (filename, callback) => callback(null) |
||||
|
||||
const crashSafeWriteFileLines = (filename, lines, callback) => { |
||||
lines.push('') // Add final new line
|
||||
writeFile(filename, lines.join('\n'), callback) |
||||
} |
||||
|
||||
// Interface
|
||||
module.exports.exists = exists |
||||
module.exports.rename = rename |
||||
module.exports.writeFile = writeFile |
||||
module.exports.crashSafeWriteFileLines = crashSafeWriteFileLines |
||||
module.exports.appendFile = appendFile |
||||
module.exports.readFile = readFile |
||||
module.exports.unlink = unlink |
||||
module.exports.mkdir = mkdir |
||||
module.exports.ensureDatafileIntegrity = ensureDatafileIntegrity |
@ -0,0 +1,108 @@ |
||||
/* eslint-env jest */ |
||||
// Forked from https://github.com/antoniopresto/react-native-local-mongodb/blob/93acbc8a9aaca86aed1d632855cd8b984501147b/test/persistence.test.js
|
||||
const { promisify } = require('util') |
||||
const AsyncStorage = require('@react-native-async-storage/async-storage') |
||||
const DataStore = require('../../') |
||||
|
||||
const getDb = async () => { |
||||
await AsyncStorage.clear() |
||||
const db = new DataStore({ filename: 'foo' }) |
||||
await promisify(db.loadDatabase.bind(db))() |
||||
return db |
||||
} |
||||
it('update', async () => { |
||||
const db = await getDb() |
||||
const items0 = await promisify(db.find.bind(db))({}) |
||||
expect(AsyncStorage.getItem).toHaveBeenCalled() |
||||
|
||||
await promisify(db.insert.bind(db))({ name: 'Maggie' }) |
||||
await promisify(db.insert.bind(db))({ name: 'Bob' }) |
||||
expect(AsyncStorage.setItem).toHaveBeenCalled() |
||||
|
||||
const items = await promisify(db.find.bind(db))({}) |
||||
|
||||
const maggie1 = await promisify(db.findOne.bind(db))({ name: 'Maggie' }) |
||||
const bob1 = await promisify(db.findOne.bind(db))({ name: 'Bob' }) |
||||
|
||||
const res = await promisify(db.update.bind(db))({ name: { $in: ['Maggie', 'Bob'] } }, { $set: { age: 1 } }, { multi: true }) |
||||
const maggie2 = await promisify(db.findOne.bind(db))({ name: 'Maggie' }) |
||||
const bob2 = await promisify(db.findOne.bind(db))({ name: 'Bob' }) |
||||
|
||||
expect(res).toEqual(2) |
||||
expect(items0).toHaveLength(0) |
||||
expect(items).toHaveLength(2) |
||||
expect(maggie1.age).toBeUndefined() |
||||
expect(bob1.age).toBeUndefined() |
||||
expect(bob2.age).toEqual(1) |
||||
expect(maggie2.age).toEqual(1) |
||||
}) |
||||
|
||||
it('remove', async () => { |
||||
const db = await getDb() |
||||
const items0 = await promisify(db.find.bind(db))({}) |
||||
expect(AsyncStorage.getItem).toHaveBeenCalled() |
||||
|
||||
await promisify(db.insert.bind(db))({ name: 'Maggie' }) |
||||
await promisify(db.insert.bind(db))({ name: 'Bob' }) |
||||
expect(AsyncStorage.setItem).toHaveBeenCalled() |
||||
|
||||
const items = await promisify(db.find.bind(db))({}) |
||||
|
||||
const res = await promisify(db.remove.bind(db))({ name: { $in: ['Bob'] } }, { multi: true }) |
||||
const bob2 = await promisify(db.findOne.bind(db))({ name: 'Bob' }) |
||||
|
||||
expect(res).toEqual(1) |
||||
expect(items0).toHaveLength(0) |
||||
expect(items).toHaveLength(2) |
||||
expect(bob2).toBeNull() |
||||
}) |
||||
|
||||
it('resolve remove nonexistent', async () => { |
||||
const db = await getDb() |
||||
const items0 = await promisify(db.find.bind(db))({}) |
||||
expect(AsyncStorage.getItem).toHaveBeenCalled() |
||||
|
||||
await promisify(db.insert.bind(db))({ name: 'Maggie' }) |
||||
await promisify(db.insert.bind(db))({ name: 'Bob' }) |
||||
expect(AsyncStorage.setItem).toHaveBeenCalled() |
||||
|
||||
const items = await promisify(db.find.bind(db))({}) |
||||
|
||||
const res = await promisify(db.remove.bind(db))({ name: 'nonexistent' }, { multi: true }) |
||||
const nonexistent = await promisify(db.findOne.bind(db))({ name: 'nonexistent' }) |
||||
|
||||
expect(res).toEqual(0) |
||||
expect(items0).toHaveLength(0) |
||||
expect(items).toHaveLength(2) |
||||
expect(nonexistent).toBeNull() |
||||
}) |
||||
|
||||
it('resolve findOne nonexistent', async () => { |
||||
const db = await getDb() |
||||
|
||||
await promisify(db.insert.bind(db))({ name: 'Maggie' }) |
||||
await promisify(db.insert.bind(db))({ name: 'Bob' }) |
||||
expect(AsyncStorage.setItem).toHaveBeenCalled() |
||||
|
||||
const items = await promisify(db.find.bind(db))({ name: 'nonexistent' }) |
||||
|
||||
const item = await promisify(db.findOne.bind(db))({ name: 'nonexistent' }) |
||||
|
||||
expect(item).toBeNull() |
||||
expect(items.length).toEqual(0) |
||||
}) |
||||
|
||||
it('should limit', async () => { |
||||
const db = await getDb() |
||||
await promisify(db.insert.bind(db))({ name: 'A' }) |
||||
await promisify(db.insert.bind(db))({ name: 'B' }) |
||||
await promisify(db.insert.bind(db))({ name: 'C' }) |
||||
await promisify(db.insert.bind(db))({ name: 'D' }) |
||||
expect(AsyncStorage.setItem).toHaveBeenCalled() |
||||
|
||||
const cursor = db.find({}).sort({ name: 1 }).skip(1).limit(2) |
||||
|
||||
const docs = await promisify(cursor.exec.bind(cursor))() |
||||
expect(docs.length).toEqual(2) |
||||
expect(docs[1].name).toEqual('C') |
||||
}) |
@ -0,0 +1,3 @@ |
||||
const browserResolve = require('browser-resolve') |
||||
|
||||
module.exports = (id, opts) => browserResolve.sync(id, { ...opts, browser: 'react-native' }) |
Loading…
Reference in new issue