@lykmapipo/mongoose-test-helpers
Version:
mongoose test helpers
314 lines (289 loc) • 7.57 kB
JavaScript
/**
* @module mongoose-test-helpers
* @description Re-usable test helpers for mongoose
* @author lally elias <lallyelias87@mail.com>
* @since 0.1.0
* @version 0.1.0
* @license MIT
* @example
*
* import { setup, clear, drop } from '@lykmapipo/mongoose-test-helpers';
* before(done => { setup(done) });
* after(done => { clear(done) });
* after(done => { drop(done) });
*/
/* setup test environment */
/* dependencies */
import _ from 'lodash';
import {
chai,
expect,
fake,
faker,
mock,
restore,
should,
sinon,
spy,
stub,
} from '@lykmapipo/test-helpers';
import { parallel } from 'async';
import {
connect as connectDatabase,
isConnected,
isModel,
disconnect,
clear,
drop,
model as getModel,
createModel,
enableDebug,
disableDebug,
} from '@lykmapipo/mongoose-connection';
import { isInstance } from '@lykmapipo/mongoose-common';
import mongooseFaker from '@lykmapipo/mongoose-faker';
/* setup sinon mongoose */
import './sinon_mongoose';
process.env.NODE_ENV = 'test';
process.env.DEBUG = true;
process.env.MONGODB_URI = 'mongodb://127.0.0.1/test';
/**
* @function connect
* @name connect
* @description Opens the default mongoose connection
* @param {string} [url] valid mongodb conenction string. if not provided it
* will be obtained from process.env.MONGODB_URI
* @param {Function} done a callback to invoke on success or failure
* @author lally elias <lallyelias87@mail.com>
* @since 0.1.0
* @version 0.1.0
* @example
*
* connect(done);
* connect(<url>, done);
*/
export const connect = (url, done) => {
// ensure test database
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://127.0.0.1/test';
// normalize arguments
const $url = _.isFunction(url) ? MONGODB_URI : url;
const $done = _.isFunction(url) ? url : done;
// establish mongoose connection
connectDatabase($url, $done);
};
/**
* @function disconnect
* @name disconnect
* @description Close all mongoose connection
* @param {Function} done a callback to invoke on success or failure
* @author lally elias <lallyelias87@mail.com>
* @since 0.1.0
* @version 0.1.0
* @example
*
* disconnect(done);
*/
export { disconnect };
/**
* @function clear
* @name clear
* @description Clear provided collection or all if none give
* @param {string[] | string} modelNames name of models to clear
* @param {Function} done a callback to invoke on success or failure
* @author lally elias <lallyelias87@mail.com>
* @since 0.1.0
* @version 0.1.0
* @example
*
* clear('User', 'Profile', done);
* clear(done);
*/
export { clear };
/**
* @function drop
* @name drop
* @description Deletes the given database, including all collections,
* documents, and indexes
* @param {Function} done a callback to invoke on success or failure
* @author lally elias <lallyelias87@mail.com>
* @since 0.1.0
* @version 0.1.0
* @example
*
* drop(done);
*/
export { drop };
/**
* @function create
* @name create
* @param {...any} instances valid mongoose model instances and
* optional callback at end
* @description Persist given model instances
* @returns {object|object[]} created model instances
* @author lally elias <lallyelias87@mail.com>
* @since 0.1.0
* @version 0.1.0
* @example
*
* create(user, done);
* create(user, profile, done);
* create(user, profile, done);
*/
export const create = (...instances) => {
// collect provided instances
let $instances = [].concat(...instances);
// obtain callback
const $done = _.last(
_.filter([...$instances], (instance) => {
return !isInstance(instance);
})
);
// collect actual model instances
$instances = _.filter([...$instances], (instance) => {
return isInstance(instance);
});
// compact and ensure unique instances by _id
$instances = _.uniqBy(_.compact([...$instances]), '_id');
// map instances to save
// TODO for same model use insertMany
const connected = isConnected();
let saves = _.map([...$instances], (instance) => {
if (connected && instance.save) {
const save = (next) => {
const fn = instance.post || instance.save;
fn.call(instance, (error, saved) => {
next(error, saved);
});
};
return save;
}
return undefined;
});
// compact saves
saves = _.compact([...saves]);
// save
return parallel(saves, $done);
};
/**
* @function createTestModel
* @name createTestModel
* @description Create a test model for testing
* @param {object} [schema] model schema definition
* @param {...Function} [plugins] list of plugins to apply to schema
* @returns {object} valid mongoose model
* @author lally elias <lallyelias87@mail.com>
* @since 0.4.0
* @version 0.1.0
* @example
*
* const User = createTestModel();
* const User = createTestModel({ name: { type: String } }, autopopulate);
*/
export const createTestModel = (schema, ...plugins) => {
// ensure schema definition
const definition = _.merge(
{},
{
name: {
type: String,
index: true,
searchable: true,
taggable: true,
fake: (f) => f.name.findName(),
},
},
schema
);
// obtain options
const options = _.first([...plugins], _.isPlainObject);
// register dynamic model
const testModel = createModel(
definition,
{ timestamps: true, ...options },
..._.filter([...plugins, mongooseFaker], _.isFunction)
);
// return created model
return testModel;
};
/**
* @function mockModel
* @name mockModel
* @description Mock existing mongoose model
* @param {object} model valid mongoose model
* @returns {object} valid mock of provided mongoose model
* @author lally elias <lallyelias87@mail.com>
* @since 0.5.0
* @version 0.1.0
* @example
*
* const Mock = mockModel(User);
* const find = Mock.expects('find').yields(null, [{...}]);
*
* User.find((error, results) => {
* Mock.verify();
* Mock.restore();
* expect(find).to.have.been.calledOnce;
* expect(error).to.not.exist;
* expect(results).to.exist;
* expect(results).to.have.have.length(1);
* done(error, results);
* });
*/
export const mockModel = (model) => {
const mocked = isModel(model) ? sinon.mock(model) : undefined;
return mocked;
};
/**
* @function mockInstance
* @name mockInstance
* @description Mock provided model instance
* @param {object} instance valid model instance
* @returns {object} valid mock of provided model instance
* @author lally elias <lallyelias87@mail.com>
* @since 0.5.0
* @version 0.1.0
* @example
*
* const mock = mockInstance(new User());
* const save = mock.expects('save').yields(null, {...});
*
* user.save((error, results) => {
* Mock.verify();
* Mock.restore();
* expect(save).to.have.been.calledOnce;
* expect(error).to.not.exist;
* expect(result).to.exist;
* done(error, results);
* });
*/
export const mockInstance = (instance) => {
const mocked = isInstance(instance) ? sinon.mock(instance) : undefined;
return mocked;
};
/**
* @function getModel
* @name getModel
* @description Try obtain already registered
* @author lally elias <lallyelias87@mail.com>
* @since 0.1.0
* @version 0.1.0
* @example
*
* const User = getModel('User');
* const User = model('User');
*/
export const model = getModel;
export { getModel };
/* shortcuts */
export { enableDebug };
export { disableDebug };
export { chai };
export { expect };
export { fake };
export { faker };
export { mock };
export { restore };
export { should };
export { sinon };
export { spy };
export { stub };