UNPKG

fxmgr

Version:

fixture-setup helper, designed mainly for bdd test-runners, (but not exclusively)

138 lines (132 loc) 4.81 kB
module.exports = ({ util = require('util'), assert = require('assert'), Promise = global.Promise, redis = require('async-redis').createClient, color = v => util.inspect(v, { colors: process.stdout.isTTY }), blue = v => process.stdout.isTTY ? `\u001b[36m${v}\u001b[39m` : v, config: redisOptions = {}, client: c, postTestCleanup: cleanup = true, defaultDataType = 'strings', mgr, } = {}) => mgr = { connect: () => (c ? Promise.resolve(c) : new Promise( (accept, reject) => mgr.client = c = redis(redisOptions) .on('error', reject) .once('connect', () => c.removeListener('error', reject) && accept(c)), ) ) .then( ({ server_info: { redis_version: version, role }, address, selected_db: db = 0 }) => Object.assign(c, { [util.inspect.custom]: () => `${blue('[RedisClient]')} { @ ${color(address)}, role: ${color(role)}, dbVer: ${color(version)}, #db: ${color(db)} }`, //eslint-disable-line max-len }), ), disconnect: () => c ? new Promise((acc, rej) => c.quit(err => err ? rej(err) : acc())) : Promise.resolve(), client: c, batch: ({ step, client = c, multi = client.multi(), mset = arr => arr && arr.length && multi.mset(arr.reduce((args, en) => (args.push(en.key, en.value), args), [])), del = arr => arr && arr.length && multi.del(arr.map(en => en.key)), mget = (arr, cb) => arr && arr.length ? multi.mget(arr.map(en => en.key), cb) : cb(null, []), reject = [], ensure = mustExist => mget( mustExist, (err, vals) => err ? reject.push(err.stack) : vals.forEach((v, ix) => null === v && reject.push(`missing mustExist key: ${mustExist[ix].key}`)), ), equal = eql => mget( eql, (err, vals) => err ? reject.push(err.stack) : vals.forEach((v, ix) => v ? eql[ix].value === v || reject.push(`wrong value in key: ${eql[ix].key}, expected: ${eql[ix].value}, found: ${v}`) : reject.push(`missing mustEql key: ${eql[ix].key}`)), ), batch, dataTypeNotImplemented = dataType => ({ seed: () => Promise.reject(new Error(`Not Implemented ${dataType}.seed`)), setup: () => Promise.reject(new Error(`Not Implemented ${dataType}.setup`)), teardown: () => Promise.reject(new Error(`Not Implemented ${dataType}.teardown`)), }), } = {}) => batch = { strings: { seed: ({ insert }) => (mset(insert), batch), setup: ({ insert, clear, mustEql: eq, mustExist: ex }) => (del(clear), mset(insert), ensure(ex), equal(eq), batch), teardown: ({ insert, clear, mustEql: eq, mustExist: ex }) => (del(clear), del(insert), ensure(ex), equal(eq), batch), }, lists: dataTypeNotImplemented('lists'), sets: dataTypeNotImplemented('sets'), hashes: dataTypeNotImplemented('hashes'), sortedSets: dataTypeNotImplemented('storedSets'), exec: () => new Promise((acc, rej) => multi.exec((err, res) => err ? rej(err) : acc(res))) .then(() => assert.deepEqual(reject, [], 'missing or invalid string keys') || { [step]: 'ok' }), }, useData: (entities, { mgr: { connect, disconnect, client, batch, } = mgr, supportSteps = ['seed', 'setup', 'teardown'], stepIndex = () => ({ strings: [], hashes: [], sets: [], sortedSets: [] }), ixByStageAndType = supportSteps.reduce( (ix, step) => Object.values(entities).reduce( (ix, { dataType = defaultDataType, [step]: data, }) => data && ix[step][dataType].push(data) && ix, ix, ), supportSteps.reduce((ix, step) => Object.assign(ix, { [step]: stepIndex() }), {}), ), runStep = step => Object.entries(ixByStageAndType[step]).reduce( (batch, [dataType, arrFx]) => (arrFx.forEach(fx => batch[dataType][step](fx)), batch), batch({ step, client }), ) .exec(), } = {}) => Object.defineProperty({ ixByStageAndType, seed: keepAlive => Promise.resolve() .then(connect) .then(() => runStep('seed')) .then(keepAlive ? Promise.resolve : disconnect), beforeAll: () => Promise.resolve() .then(connect) .then(() => runStep('setup')), afterAll: () => Promise.resolve() .then(connect) .then(() => cleanup ? true || runStep('teardown') : Promise.resolve()) .finally(disconnect), }, 'client', { get: () => client || c, enumerable: true, }), }