UNPKG

node-tdd

Version:

Drop in extension for mocha to abstract commonly used test setups

123 lines (112 loc) 3.52 kB
import assert from 'assert'; import crypto from 'crypto'; import get from 'lodash.get'; import Joi from 'joi-strict'; const libs = { Math, crypto }; export default (opts) => { Joi.assert(opts, Joi.object().keys({ seed: Joi.string(), reseed: Joi.boolean() }), 'Invalid Options Provided'); let original = null; const byteToHex = []; for (let i = 0; i < 256; i += 1) { byteToHex[i] = (i + 0x100).toString(16).slice(1); } return { inject: () => { assert(original === null); original = { crypto: { randomBytes: crypto.randomBytes, randomFillSync: crypto.randomFillSync, randomUUID: crypto.randomUUID }, Math: { random: Math.random } }; const executionCounts = {}; crypto.randomBytes = (size, cb) => { // randomization is seeded "per key" const stack = new Error().stack.split('\n'); const subStack = stack.slice(stack.findIndex((e) => e.indexOf('/node_modules/') !== -1)); const stackOrigin = get(subStack, [subStack.findIndex((e) => e.indexOf('/node_modules/') === -1) - 1], ''); const originFile = get(stackOrigin.match(/^.*?\([^)]+?\/node_modules\/([^)]+):\d+:\d+\)$/), [1], ''); const key = `${originFile}@${size}`; executionCounts[key] = opts.reseed === true ? null : (executionCounts[key] || 0) + 1; let result = crypto .createHash('sha256') .update(opts.seed) .update(opts.reseed === true ? 'null' : key) .update(String(executionCounts[key])) .digest(); while (result.length < size) { result = Buffer.concat([result, crypto.createHash('sha256').update(result).digest()]); } result = result.slice(0, size); return cb ? cb(null, result) : result; }; crypto.randomFillSync = (buffer, offset, size) => { const o = offset || 0; const s = size || buffer.byteLength; crypto.randomBytes(s, (err, res) => { res.copy(buffer, o, 0, s); }); return buffer; }; crypto.randomUUID = () => { const rnds = crypto.randomBytes(16); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` // eslint-disable-next-line no-bitwise rnds[6] = (rnds[6] & 0x0f) | 0x40; // eslint-disable-next-line no-bitwise rnds[8] = (rnds[8] & 0x3f) | 0x80; return `${ byteToHex[rnds[0]] }${ byteToHex[rnds[1]] }${ byteToHex[rnds[2]] }${ byteToHex[rnds[3]] }-${ byteToHex[rnds[4]] }${ byteToHex[rnds[5]] }-${ byteToHex[rnds[6]] }${ byteToHex[rnds[7]] }-${ byteToHex[rnds[8]] }${ byteToHex[rnds[9]] }-${ byteToHex[rnds[10]] }${ byteToHex[rnds[11]] }${ byteToHex[rnds[12]] }${ byteToHex[rnds[13]] }${ byteToHex[rnds[14]] }${ byteToHex[rnds[15]] }`; }; Math.random = () => crypto.randomBytes(4).readUInt32LE() / 0xffffffff; }, release: () => { assert(original !== null); Object.entries(original).forEach(([lib, v]) => { Object.entries(v).forEach(([method, org]) => { libs[lib][method] = org; }); }); original = null; }, isInjected: () => original !== null }; };