├── .gitignore ├── .jsbeautifyrc ├── .jshintrc ├── .npmignore ├── .travis.yml ├── Gruntfile.js ├── README.md ├── index.js ├── lib ├── .gitkeep ├── schema_graph.js └── seed.js ├── models └── User.js ├── package.json ├── seeds ├── development │ ├── UserSeed.js │ └── user_seed.js ├── production │ └── UserSeed.js └── test │ └── UserSeed.js └── test ├── bootstrap.spec.js ├── graph.spec.js ├── index.spec.js └── seed.spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | #Temporary data 6 | .tmp 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # Deployed apps should consider commenting this line out: 27 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 28 | node_modules 29 | doc 30 | startup.sh -------------------------------------------------------------------------------- /.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "js": { 3 | "jslint_happy": true, 4 | "indent_size": 2, 5 | "wrap_line_length": 80 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "browser": true, 4 | "camelcase": true, 5 | "curly": true, 6 | "eqeqeq": true, 7 | "esnext": true, 8 | "immed": true, 9 | "latedef": true, 10 | "newcap": true, 11 | "noarg": true, 12 | "node": true, 13 | "mocha": true, 14 | "quotmark": "single", 15 | "strict": true, 16 | "undef": true, 17 | "unused": true, 18 | "expr": true, 19 | "ignore": true, 20 | "globals": { 21 | "User": true, 22 | "sails": true, 23 | "_": true, 24 | "async": true 25 | } 26 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | api 2 | config 3 | node_modules 4 | ssl 5 | .DS_STORE 6 | *~ 7 | .idea 8 | nbproject 9 | test 10 | .git 11 | .gitignore 12 | .tmp 13 | *.swo 14 | *.swp 15 | *.swn 16 | *.swm 17 | .jshintrc 18 | .editorconfig 19 | doc.html 20 | seeds 21 | fixtures 22 | .travis.yml 23 | Grintfile -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | services: mongodb 3 | node_js: 4 | - "6.9.2" 5 | before_script: 6 | - npm install -g grunt-cli -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (grunt) { 4 | 5 | // Add the grunt-mocha-test and jshint tasks. 6 | grunt.loadNpmTasks('grunt-mocha-test'); 7 | grunt.loadNpmTasks('grunt-contrib-jshint'); 8 | 9 | grunt.initConfig({ 10 | // Configure a mochaTest task 11 | mochaTest: { 12 | test: { 13 | options: { 14 | reporter: 'spec', 15 | timeout: 20000 16 | }, 17 | src: ['test/**/*.js'] 18 | } 19 | }, 20 | jshint: { 21 | options: { 22 | reporter: require('jshint-stylish'), 23 | jshintrc: '.jshintrc' 24 | }, 25 | all: [ 26 | 'Gruntfile.js', 27 | 'index.js', 28 | 'lib/**/*.js', 29 | 'test/**/*.js' 30 | ] 31 | } 32 | }); 33 | 34 | //custom tasks 35 | grunt.registerTask('default', ['jshint', 'mochaTest']); 36 | grunt.registerTask('test', ['jshint', 'mochaTest']); 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | seed-mongoose 2 | ==================== 3 | 4 | [![Build Status](https://travis-ci.org/lykmapipo/seed-mongoose.svg?branch=master)](https://travis-ci.org/lykmapipo/seed-mongoose) 5 | 6 | DRY data seeding for [mongoose](https://github.com/Automattic/mongoose). 7 | 8 | Simplify `mongoose` data seeding based on the current running environment of your application. You may use `seed-mongoose` during `test`, `development` and even seed your application with default data during deployment in `production` environment. 9 | 10 | ## Requirements 11 | - [NodeJS v6.9.2+](https://nodejs.org) 12 | 13 | ## Installation 14 | ```js 15 | $ npm install --save seed-mongoose 16 | ``` 17 | 18 | *You may opt to install [Faker](https://github.com/marak/Faker.js/) as your test and development seed generator* 19 | ```js 20 | $ npm install --save-dev faker 21 | ``` 22 | 23 | ## Usage 24 | ```js 25 | require('seed-mongoose')(); 26 | 27 | ... 28 | 29 | mongoose.connect(uristring, mongoOptions, function () { 30 | require('seed-mongoose')({ 31 | suffix: '_seed', 32 | logger: winston, 33 | mongoose: mongoose //This is required 34 | }, function (error, results) { 35 | ... 36 | }); 37 | }); 38 | 39 | ``` 40 | 41 | 42 | ## How it works 43 | By default `seed-mongoose` look for environment specific seeds in the `seeds` directory inside `process.cwd()` of your application. Example, if you need to seed your application during `test` you will have to create `seeds/test` and add `model seed files` inside it. 44 | 45 | `seed-mongoose` will load any file suffix-ed with `Seed` as a seed unless custom `suffix` provided in [configurations](#configuration). Example, if you want to seed your `User` model during `test` your need to write your seed as folow: 46 | 47 | ```js 48 | //in seeds/test/UserSeed.js 49 | var faker = require('faker'); 50 | 51 | //array of plain object 52 | //to seed in User model 53 | module.exports = [{ 54 | username: faker.internet.userName(), 55 | email: faker.internet.email() 56 | }]; 57 | ``` 58 | When `connecting event` fired by mongoose connection, `seed-mongoose` will then apply all data seed available for the current application environment 59 | . 60 | 61 | ## Seed Types 62 | `seed-mongoose` accept `array type`, `plain object` and `functional` type seeds. 63 | 64 | #### Object Seed Type 65 | ```js 66 | //in seeds/test/UserSeed.js 67 | var faker = require('faker'); 68 | 69 | //object to seed 70 | //in User model 71 | module.exports = { 72 | username: faker.internet.userName(), 73 | email: faker.internet.email() 74 | }; 75 | ``` 76 | 77 | #### Array Seed Type 78 | ```js 79 | //in seeds/test/UserSeed.js 80 | var faker = require('faker'); 81 | 82 | //array of data to seed 83 | module.exports = [{ 84 | username: faker.internet.userName(), 85 | email: faker.internet.email() 86 | }]; 87 | ``` 88 | 89 | #### Functional Seed Type 90 | ```js 91 | //in seeds/test/UserSeed.js 92 | var faker = require('faker'); 93 | 94 | //function to be evaluated to obtain data 95 | module.exports = function(done) { 96 | 97 | var data = [{ 98 | username: faker.internet.userName(), 99 | email: faker.internet.email() 100 | }, { 101 | username: faker.internet.userName(), 102 | email: faker.internet.email() 103 | }]; 104 | 105 | //remember to tell when your are done 106 | done(null, data); 107 | }; 108 | ``` 109 | 110 | 111 | The same convection must be followed for `development` and `production` environment. 112 | 113 | *Note: Environment specific folder are named after their environment name, e.g if environment is `test`, then to make sure your test seeds are loaded they must be placed under `seeds/test` folder for `seed-mongoose` to pick and apply your seeds. Your may look this repo `seeds folder` to see example* 114 | 115 | ## Configuration 116 | `seed-mongoose` accept application defined configurations. 117 | 118 | Simply, pass the config object into it as below: 119 | ```js 120 | var mongoose = require('mongoose'); 121 | 122 | ... 123 | 124 | var seed = require('seed-mongoose')({ 125 | cwd: 'data', 126 | path: 'fixtures', 127 | logger:console, 128 | environment: 'development', 129 | mongoose: mongoose 130 | }); 131 | 132 | ... 133 | 134 | ``` 135 | 136 | - `cwd` current project working directory. Default to `process.cwd()` 137 | - `path` seed path relative to `cwd`. Default to `seeds` 138 | - `suffix` suffix to use match seeds when loading seeds from a seed directory. Default to `Seed` 139 | - `logger` logger to be used to log progress. Default to `console` 140 | - `environment` seeding environment. Default to `process.env.NODE_ENV` 141 | 142 | 143 | ## Testing 144 | 145 | * Clone this repository 146 | 147 | * Install `grunt-cli` global 148 | 149 | ```sh 150 | $ npm install -g grunt-cli 151 | ``` 152 | 153 | * Install all development dependencies 154 | 155 | ```sh 156 | $ npm install 157 | ``` 158 | 159 | * Then run test 160 | 161 | ```sh 162 | $ npm test 163 | ``` 164 | 165 | ## Contribute 166 | 167 | Fork this repo and push in your ideas. Do not forget to add a bit of test(s) of what value you adding. 168 | 169 | ## Licence 170 | 171 | Copyright (c) 2015 lykmapipo & Contributors 172 | 173 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 174 | 175 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 176 | 177 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 178 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | //dependencies 5 | const path = require('path'); 6 | const _ = require('lodash'); 7 | const async = require('async'); 8 | const inflection = require('inflection'); 9 | const seed = require(path.join(__dirname, 'lib', 'seed')); 10 | const schemaGraph = require(path.join(__dirname, 'lib', 'schema_graph')); 11 | let mongoose; 12 | 13 | //TODO handle deep nested relations(follow relation) 14 | //TODO 1 level deep only 15 | 16 | 17 | /** 18 | * @name loadSeeds 19 | * @description loading seed's data into configured model persistent storage 20 | * @private 21 | * @type {Function} 22 | */ 23 | function loadSeeds(options) { 24 | //obtain logger 25 | const logger = options.logger; 26 | 27 | //deduce seeds path to use 28 | //based on current environment 29 | const seedsPath = 30 | path.join(options.cwd, options.path, options.environment); 31 | 32 | //log seed environment 33 | logger && 34 | (logger.debug || logger.log)('start seeding %s data', options.environment); 35 | 36 | //log seed location 37 | logger && (logger.debug || logger.log)('seeding from %s', seedsPath); 38 | 39 | //load all seeds available 40 | //in `seedsPath` 41 | const seeds = require('require-all')({ 42 | dirname: seedsPath, 43 | filter: new RegExp('(.+' + options.suffix + ')\.js$'), 44 | excludeDirs: /^\.(git|svn)$/ 45 | }); 46 | 47 | //map with model name as a key 48 | //and seed as a value 49 | //to help in ordering seeding behavior 50 | let modelSeedMap = {}; 51 | 52 | //create model name - seed map 53 | _.keys(seeds) 54 | .forEach(function (seed) { 55 | // deduce model name 56 | let modelName = 57 | seed.replace(new RegExp(options.suffix + '$'), ''); 58 | 59 | //pluralize model global id if enable 60 | modelName = inflection.classify(modelName); 61 | 62 | //grab data to load 63 | //from the seed data attribute 64 | modelSeedMap[modelName] = seeds[seed]; 65 | 66 | }); 67 | 68 | return modelSeedMap; 69 | 70 | } 71 | 72 | 73 | function load(options, done) { 74 | 75 | //obtain logger 76 | const logger = options.logger; 77 | 78 | //load seeds 79 | const seeds = loadSeeds(options); 80 | 81 | //obtain model graph 82 | const graph = schemaGraph(mongoose); 83 | 84 | //obtain models 85 | const modelNames = _.map(graph, 'modelName'); 86 | 87 | //prepare works 88 | let works = []; 89 | _.forEach(modelNames, function (modelName) { 90 | //get seed data 91 | let data = seeds[modelName]; 92 | 93 | if (data) { 94 | 95 | works.push(function (next) { 96 | async.waterfall([ 97 | function fetchSeedData(then) { 98 | if (_.isFunction(data)) { 99 | data(then); 100 | } else { 101 | then(null, data); 102 | } 103 | }, 104 | function normalizeSeedData(data, then) { 105 | data = [].concat(data); 106 | _.compact(data); 107 | then(null, data); 108 | }, 109 | function seedData(data, then) { 110 | seed.many({ 111 | modelName: modelName, 112 | data: data 113 | }, then); 114 | } 115 | ], next); 116 | 117 | }); 118 | 119 | } 120 | 121 | }); 122 | 123 | _.compact(works); 124 | async.series(works, function (error, seeds) { 125 | // clear seeded cache 126 | seed.seeded = {}; 127 | 128 | //clear mongoose 129 | seed.mongoose = undefined; 130 | 131 | //log seed environment 132 | logger && 133 | (logger.debug || logger.log)('finish seeding %s data', options.environment); 134 | 135 | done(error, seeds); 136 | 137 | }); 138 | 139 | } 140 | 141 | 142 | /** 143 | * @function 144 | * @description DRY data seeding for mongoose. 145 | * 146 | * @param {Object} options seed configurations 147 | */ 148 | exports = module.exports = function (options, done) { 149 | 150 | //normalize options 151 | if (_.isFunction(options)) { 152 | done = options; 153 | options = {}; 154 | } 155 | 156 | //obtain provided mongoose 157 | mongoose = options.mongoose || require('mongoose'); 158 | seed.mongoose = mongoose; 159 | delete options.mongoose; 160 | 161 | //defaults configurations 162 | options = _.merge({}, { 163 | //set seeding to be active by default 164 | active: true, 165 | 166 | //current working directory 167 | cwd: process.cwd(), 168 | 169 | //directory where seeds resides 170 | //relative to `process.cwd()` 171 | path: 'seeds', 172 | 173 | //suffix to use match seeds when loading 174 | //seeds from a seed directory 175 | suffix: 'Seed', 176 | 177 | //logger to log seeding progress 178 | logger: null, 179 | 180 | //detect seeding environment 181 | //default to development environment 182 | environment: _.get(process.env, 'NODE_ENV', 'development') 183 | 184 | }, options); 185 | 186 | load(options, done); 187 | 188 | }; 189 | -------------------------------------------------------------------------------- /lib/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lykmapipo/seed-mongoose/e9938c74c352d088b8b75c2c37c5dbca9bd85b28/lib/.gitkeep -------------------------------------------------------------------------------- /lib/schema_graph.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @module graph 5 | * @description generate schema graph based on their dependencies from 6 | * registered mongoose models 7 | * @since 0.4.3 8 | * @version 0.5.0 9 | * @public 10 | */ 11 | 12 | 13 | //dependencies 14 | const _ = require('lodash'); 15 | 16 | 17 | /** 18 | * @name parentRefs 19 | * @type {Function} 20 | * @description iterate through schema definition to obtain refs pointing to 21 | * other schema type but act as parent(belongs to) 22 | * @param {Schema} schema valid mongoose schema 23 | * @param {[String]} modelNames collection of registered mongoose model names 24 | * @return {[String]} 25 | */ 26 | function parentRefs(modelName, schema, modelNames) { 27 | let refs = []; 28 | // 29 | //iterate over schema path to obtain refs 30 | schema.eachPath(function (path, schemaType) { 31 | // 32 | //ensure schema instance is object 33 | const isObjectId = _.get(schemaType, 'instance') === 'ObjectID'; 34 | // 35 | //check if it has a reference 36 | const ref = _.get(schemaType, 'options.ref'); 37 | // 38 | //ensure ref exists as a model 39 | const isRefAModel = _.indexOf(modelNames, ref) > -1; 40 | // 41 | //collect refs 42 | if (isObjectId && isRefAModel && ref !== modelName) { 43 | refs.push({ path: path, modelName: ref }); 44 | } 45 | }); 46 | 47 | return _.compact(refs); 48 | } 49 | 50 | 51 | /** 52 | * @name childRefs 53 | * @type {Function} 54 | * @description iterate through schema definition to obtain refs pointing to 55 | * other schema type but ach as children(s) 56 | * @param {Schema} schema valid mongoose schema 57 | * @param {[String]} modelNames collection of registered mongoose model names 58 | * @return {[String]} 59 | */ 60 | function childRefs(modelName, schema, modelNames) { 61 | let refs = []; 62 | // 63 | //iterate over schema path to obtain refs 64 | schema.eachPath(function (path, schemaType) { 65 | // 66 | //ensure schema instance is object 67 | const isObjectId = 68 | _.get(schemaType, 'caster.instance') === 'ObjectID'; 69 | // 70 | //check if it has a reference 71 | const ref = _.get(schemaType, 'options.ref') || 72 | _.get(schemaType, 'caster.options.ref'); 73 | // 74 | //ensure ref exists as a model 75 | const isRefAModel = _.indexOf(modelNames, ref) > -1; 76 | // 77 | //collect refs 78 | if (isObjectId && isRefAModel && ref !== modelName) { 79 | refs.push({ path: path, modelName: ref }); 80 | } 81 | }); 82 | 83 | return refs; 84 | } 85 | 86 | 87 | /** 88 | * @name selfParentRefs 89 | * @type {Function} 90 | * @description iterate through schema definition to obtain refs pointing to 91 | * self schema type but act as children(s) 92 | * @param {Schema} schema valid mongoose schema 93 | * @param {[String]} modelNames collection of registered mongoose model names 94 | * @return {[String]} 95 | */ 96 | function selfParentRefs(modelName, schema, modelNames) { 97 | let refs = []; 98 | // 99 | //iterate over schema path to obtain direct self refs 100 | schema.eachPath(function (path, schemaType) { 101 | // 102 | //ensure schema instance is object 103 | const isObjectId = _.get(schemaType, 'instance') === 'ObjectID'; 104 | // 105 | //check if it has a reference 106 | const ref = _.get(schemaType, 'options.ref'); 107 | // 108 | //ensure ref exists as a model 109 | const isRefAModel = _.indexOf(modelNames, ref) > -1; 110 | // 111 | //collect self refs 112 | if (isObjectId && isRefAModel && ref === modelName) { 113 | refs.push(path); 114 | } 115 | }); 116 | 117 | return _.compact(refs); 118 | } 119 | 120 | 121 | /** 122 | * @name selfChildRefs 123 | * @type {Function} 124 | * @description iterate through schema definition to obtain refs pointing to 125 | * self schema type but act as parent(belongs to) 126 | * @param {Schema} schema valid mongoose schema 127 | * @param {[String]} modelNames collection of registered mongoose model names 128 | * @return {[String]} 129 | */ 130 | function selfChildRefs(modelName, schema, modelNames) { 131 | let refs = []; 132 | // 133 | //iterate over schema path to obtain direct self refs 134 | schema.eachPath(function (path, schemaType) { 135 | // 136 | //ensure schema instance is object 137 | const isObjectId = 138 | _.get(schemaType, 'caster.instance') === 'ObjectID'; 139 | // 140 | //check if it has a reference 141 | const ref = _.get(schemaType, 'options.ref') || 142 | _.get(schemaType, 'caster.options.ref'); 143 | // 144 | //ensure ref exists as a model 145 | const isRefAModel = _.indexOf(modelNames, ref) > -1; 146 | // 147 | //collect self refs 148 | if (isObjectId && isRefAModel && ref === modelName) { 149 | refs.push(path); 150 | } 151 | }); 152 | 153 | return _.compact(refs); 154 | } 155 | 156 | 157 | exports = module.exports = function (mongoose) { 158 | 159 | //collect model graph 160 | let graph = []; 161 | 162 | //start graphing at the top of all models 163 | // 164 | //ensure unique modelNames 165 | const modelNames = _.uniq(mongoose.modelNames()); 166 | // 167 | // iterate over model names to build graph 168 | modelNames.forEach(function (modelName) { 169 | //obtain model and schema 170 | const Model = mongoose.model(modelName); 171 | const schema = Model.schema; 172 | if (Model && schema) { 173 | graph.push({ 174 | modelName: modelName, 175 | selfParentRefs: selfParentRefs(modelName, schema, 176 | modelNames), 177 | selfChildRefs: selfChildRefs(modelName, schema, 178 | modelNames), 179 | parentRefs: parentRefs(modelName, schema, 180 | modelNames), 181 | childRefs: childRefs(modelName, schema, 182 | modelNames) 183 | }); 184 | } 185 | }); 186 | 187 | //update graph score 188 | graph = _.map(graph, function (graphed) { 189 | //initialize non dependencies score 190 | graphed.score = 1000; 191 | 192 | //reduce by other parent refs 193 | if (_.size(graphed.parentRefs) > 0) { 194 | graphed.score = graphed.score - _.size(graphed.parentRefs); 195 | } 196 | 197 | //reduce by self parent refs 198 | if (_.size(graphed.selfParentRefs) > 0) { 199 | graphed.score = graphed.score - _.size(graphed.selfParentRefs); 200 | } 201 | 202 | //reduce by other child refs 203 | if (_.size(graphed.childRefs) > 0) { 204 | graphed.score = graphed.score - _.size(graphed.childRefs); 205 | } 206 | 207 | //reduce by self child refs 208 | if (_.size(graphed.selfChildRefs) > 0) { 209 | graphed.score = graphed.score - _.size(graphed.selfChildRefs); 210 | } 211 | 212 | return graphed; 213 | 214 | }); 215 | 216 | return _.reverse(_.sortBy(graph, 'score')); 217 | 218 | }; 219 | -------------------------------------------------------------------------------- /lib/seed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | //dependencies 5 | const path = require('path'); 6 | const _ = require('lodash'); 7 | const async = require('async'); 8 | const hashObject = require('object-hash'); 9 | const schemaGraph = require(path.join(__dirname, '..', 'lib', 'schema_graph')); 10 | 11 | 12 | /** 13 | * @name seeded 14 | * @description hash containing already seeded data 15 | * 16 | * It contain object hash as a key and object id as 17 | * a value. 18 | * 19 | * @type {Object} 20 | * @private 21 | */ 22 | exports.seeded = {}; 23 | 24 | 25 | /** 26 | * @name single 27 | * @description seed single model data based on it graph 28 | * @param {Object} options modelName, data and model graph to be 29 | * used in seeding 30 | * @param {String} options.modelName name of mongoose model to seed 31 | * data into 32 | * @param {Object} options.data data to seed into the model 33 | * @param {Object} options.graph model relation graph 34 | * @param {Function} done a callback to invoke on seeding 35 | * @return {Object} seeded data 36 | * @type {Function} 37 | * @public 38 | */ 39 | exports.single = function (options, done) { 40 | 41 | //require mongoose 42 | const mongoose = exports.mongoose || require('mongoose'); 43 | 44 | //load schema graph 45 | let graph = _.merge({}, options.graph, 46 | _.find(schemaGraph(mongoose), { modelName: options.modelName })); 47 | 48 | //obtain model 49 | const Model = mongoose.model(options.modelName); 50 | 51 | //create data hash 52 | let data = options.data; 53 | const hash = hashObject(data); 54 | //ignore seeding if it has an object id 55 | if (data._id) { 56 | exports.seeded[hash] = data._id; 57 | } 58 | 59 | //ensure model not seeded yet 60 | if (!exports.seeded[hash]) { 61 | let series = {}; 62 | 63 | //seed other ref(other model) parent 64 | if (!_.isEmpty(graph.parentRefs)) { 65 | _.forEach(graph.parentRefs, function (parentRef) { 66 | if (data[parentRef.path]) { 67 | series[parentRef.path] = function (next) { 68 | exports.single({ 69 | modelName: parentRef.modelName, 70 | data: data[parentRef.path] 71 | }, function (error, seeded) { 72 | //update ref 73 | if (!error) { 74 | data[parentRef.path] = seeded._id; 75 | } 76 | next(error, seeded); 77 | }); 78 | }; 79 | } 80 | }); 81 | } 82 | 83 | //seed self(same model) ref parent 84 | if (!_.isEmpty(graph.selfParentRefs)) { 85 | _.forEach(graph.selfParentRefs, function (selfParentRef) { 86 | if (data[selfParentRef]) { 87 | series[selfParentRef] = function (next) { 88 | exports.single({ 89 | modelName: options.modelName, 90 | data: data[selfParentRef] 91 | }, function (error, seeded) { 92 | //update ref 93 | if (!error) { 94 | data[selfParentRef] = seeded._id; 95 | } 96 | next(error, seeded); 97 | }); 98 | }; 99 | } 100 | }); 101 | } 102 | 103 | //seed ref child 104 | if (!_.isEmpty(graph.childRefs)) { 105 | _.forEach(graph.childRefs, function (childRef) { 106 | if (data[childRef.path]) { 107 | //seed kids in series 108 | series[childRef.path] = function (next) { 109 | exports.many({ 110 | modelName: childRef.modelName, 111 | data: data[childRef.path] 112 | }, function (error, seeded) { 113 | if (!error) { 114 | data[childRef.path] = _.uniq(_.map(seeded, '_id')); 115 | } 116 | next(error, seeded); 117 | }); 118 | }; 119 | } 120 | }); 121 | } 122 | 123 | //seed self ref child 124 | if (!_.isEmpty(graph.selfChildRefs)) { 125 | _.forEach(graph.selfChildRefs, function (selfChildRef) { 126 | if (data[selfChildRef]) { 127 | //seed kids in series 128 | series[selfChildRef] = function (next) { 129 | exports.many({ 130 | modelName: options.modelName, 131 | data: data[selfChildRef] 132 | }, function (error, seeded) { 133 | if (!error) { 134 | data[selfChildRef] = _.uniq(_.map(seeded, '_id')); 135 | } 136 | next(error, seeded); 137 | }); 138 | }; 139 | } 140 | }); 141 | } 142 | 143 | //seed data(self) 144 | series.self = function (next) { 145 | async.waterfall([ 146 | function find(then) { 147 | //prepare find existing criteria 148 | let criteria = {}; 149 | _.forEach(_.keys(data), function (field) { 150 | if (!_.isArray(data[field])) { 151 | criteria[field] = data[field]; 152 | } 153 | }); 154 | Model.findOne(criteria, then); 155 | }, 156 | function create(found, then) { 157 | if (found) { 158 | then(null, found); 159 | } else { 160 | Model.create(data, then); 161 | } 162 | } 163 | ], next); 164 | }; 165 | 166 | async.series(series, function (error, seeded) { 167 | //update hash 168 | if (!error) { 169 | exports.seeded[hash] = seeded.self._id; 170 | } 171 | //update self parent refs 172 | done(error, seeded.self); 173 | }); 174 | 175 | } 176 | 177 | //ignore and continue 178 | else { 179 | data = _.merge({}, data, { _id: exports.seeded[hash] }); 180 | done(null, data); 181 | } 182 | 183 | }; 184 | 185 | 186 | /** 187 | * @name many 188 | * @description seed many model data based on it graph 189 | * @param {Object} options modelName, data and model graph to be 190 | * used in seeding 191 | * @param {String} options.modelName name of mongoose model to seed 192 | * data into 193 | * @param {[Object]} options.data collection of data to seed into 194 | * the model 195 | * @param {Object} options.graph model relation graph 196 | * @param {Function} done a callback to invoke on seeding 197 | * @return {Object} seeded data 198 | * @type {Function} 199 | * @public 200 | */ 201 | exports.many = function (options, done) { 202 | //normalize seeded data 203 | options = _.merge({}, options); 204 | options.data = _.compact([].concat(options.data)); 205 | 206 | //prepare series seed data 207 | const seedSeries = _.map(options.data, function (data) { 208 | return function (next) { 209 | exports.single({ 210 | modelName: options.modelName, 211 | data: data, 212 | graph: {} 213 | }, next); 214 | }; 215 | }); 216 | 217 | async.series(seedSeries, done); 218 | 219 | }; 220 | -------------------------------------------------------------------------------- /models/User.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //dependencies 4 | const mongoose = require('mongoose'); 5 | const Schema = mongoose.Schema; 6 | const ObjectId = Schema.Types.ObjectId; 7 | 8 | const UserSchema = new Schema({ 9 | parent: { 10 | type: ObjectId, 11 | ref: 'User', 12 | index: true 13 | }, 14 | 15 | guardian: { 16 | type: ObjectId, 17 | ref: 'User' 18 | }, 19 | 20 | username: { 21 | type: String 22 | }, 23 | 24 | email: { 25 | type: String 26 | }, 27 | 28 | children: { 29 | type: [ObjectId], 30 | ref: 'User' 31 | }, 32 | 33 | kids: [{ 34 | type: ObjectId, 35 | ref: 'User' 36 | }] 37 | 38 | }); 39 | 40 | //export model 41 | module.exports = mongoose.model('User', UserSchema); 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seed-mongoose", 3 | "version": "0.5.0", 4 | "description": "DRY data seeding for mongoose", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "grunt test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/lykmapipo/seed-mongoose.git" 12 | }, 13 | "author": "Lally Elias", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/lykmapipo/seed-mongoose/issues" 17 | }, 18 | "homepage": "https://github.com/lykmapipo/seed-mongoose", 19 | "contributors": [{ 20 | "name": "lykmapipo", 21 | "github": "https://github.com/lykmapipo" 22 | }], 23 | "keywords": [ 24 | "mongoose", 25 | "mongoose-plugin", 26 | "seed", 27 | "fixtures", 28 | "factory", 29 | "database", 30 | "load", 31 | "populate", 32 | "initialize", 33 | "test", 34 | "development", 35 | "production" 36 | ], 37 | "dependencies": { 38 | "async": "^2.1.5", 39 | "inflection": "^1.12.0", 40 | "lodash": "^4.17.4", 41 | "object-hash": "^1.1.5", 42 | "require-all": "^2.2.0" 43 | }, 44 | "devDependencies": { 45 | "chai": "^3.5.0", 46 | "faker": "^3.1.0", 47 | "grunt": "^1.0.1", 48 | "grunt-contrib-jshint": "^1.1.0", 49 | "grunt-mocha-test": "^0.13.2", 50 | "jshint-stylish": "^2.2.1", 51 | "mocha": "^3.2.0", 52 | "mongoose": "^4.8.4" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /seeds/development/UserSeed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const faker = require('faker'); 4 | 5 | //array of data to seed 6 | //it may also be an object 7 | module.exports = [{ 8 | username: faker.internet.userName(), 9 | email: faker.internet.email() 10 | }]; 11 | -------------------------------------------------------------------------------- /seeds/development/user_seed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const faker = require('faker'); 4 | 5 | //array of data to seed 6 | //it may also be an object 7 | module.exports = [{ 8 | username: faker.internet.userName(), 9 | email: faker.internet.email() 10 | }]; 11 | -------------------------------------------------------------------------------- /seeds/production/UserSeed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const faker = require('faker'); 4 | 5 | //array of data to seed 6 | //it may also be an object 7 | module.exports = [{ 8 | username: faker.internet.userName(), 9 | email: faker.internet.email() 10 | }]; 11 | -------------------------------------------------------------------------------- /seeds/test/UserSeed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const faker = require('faker'); 4 | 5 | //function to be evaluated to obtain data 6 | //it may also be an object or array 7 | module.exports = function (done) { 8 | 9 | const data = [{ 10 | parent: { 11 | username: 'Good Joe', 12 | email: 'goodjoe@seedmongoose.dt' 13 | }, 14 | 15 | guardian: { //to test parent & gurdian only seed once 16 | username: 'Good Joe', 17 | email: 'goodjoe@seedmongoose.dt' 18 | }, 19 | 20 | username: faker.internet.userName(), 21 | email: faker.internet.email(), 22 | 23 | children: [{ 24 | username: faker.internet.userName(), 25 | email: faker.internet.email() 26 | }, { 27 | username: faker.internet.userName(), 28 | email: faker.internet.email() 29 | }], 30 | 31 | kids: [{ 32 | username: faker.internet.userName(), 33 | email: faker.internet.email() 34 | }, { 35 | username: faker.internet.userName(), 36 | email: faker.internet.email() 37 | }] 38 | 39 | }, { //test prevent seed object of same hash multiple times 40 | username: 'gi chan', 41 | email: 'gichan@seedmongoose.dt', 42 | }, { 43 | username: 'gi chan', 44 | email: 'gichan@seedmongoose.dt', 45 | }]; 46 | 47 | done(null, data); 48 | }; 49 | -------------------------------------------------------------------------------- /test/bootstrap.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //set environment to test 4 | process.env.NODE_ENV = 'test'; 5 | 6 | //dependencies 7 | const mongoose = require('mongoose'); 8 | mongoose.Promise = global.Promise; 9 | 10 | //allow mongoose query debuging 11 | // mongoose.set('debug', true); 12 | 13 | 14 | before(function (done) { 15 | mongoose.connect('mongodb://localhost/seed-mongoose', done); 16 | }); 17 | 18 | 19 | after(function (done) { 20 | mongoose.connection.dropDatabase(done); 21 | }); 22 | -------------------------------------------------------------------------------- /test/graph.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //dependencies 4 | const path = require('path'); 5 | const _ = require('lodash'); 6 | const mongoose = require('mongoose'); 7 | const Schema = mongoose.Schema; 8 | const ObjectId = Schema.Types.ObjectId; 9 | const expect = require('chai').expect; 10 | const schemaGraph = require(path.join(__dirname, '..', 'lib', 'schema_graph')); 11 | 12 | describe('schema graph', function () { 13 | 14 | it('should be a function', function () { 15 | expect(schemaGraph).to.exist; 16 | expect(schemaGraph).to.be.a('function'); 17 | }); 18 | 19 | it('should be able to build graph from flat schema', function () { 20 | 21 | const FlatSchema = new Schema({ 22 | name: { 23 | type: String 24 | } 25 | }); 26 | mongoose.model('Flat', FlatSchema); 27 | const graph = schemaGraph(mongoose); 28 | 29 | expect(graph).to.be.an('array'); 30 | 31 | const graphed = _.find(graph, { modelName: 'Flat' }); 32 | expect(graphed.modelName).to.include('Flat'); 33 | expect(graphed.score).to.equal(1000); 34 | 35 | }); 36 | 37 | describe('self refs', function () { 38 | 39 | before(function () { 40 | const SelfFlatSchema = new Schema({ 41 | name: { 42 | type: String 43 | }, 44 | parent: { 45 | type: ObjectId, 46 | ref: 'SelfFlat' 47 | }, 48 | wrong: { 49 | type: ObjectId, 50 | ref: 'WrongFlat' 51 | }, 52 | childrens: [{ 53 | type: ObjectId, 54 | ref: 'SelfFlat' 55 | }], 56 | kids: { 57 | type: [ObjectId], 58 | ref: 'SelfFlat' 59 | } 60 | }); 61 | mongoose.model('SelfFlat', SelfFlatSchema); 62 | }); 63 | 64 | it('should be able to build graph from parent self ref', 65 | function () { 66 | 67 | const graph = schemaGraph(mongoose); 68 | 69 | expect(graph).to.be.an('array'); 70 | expect(_.map(graph, 'modelName')).to.include('SelfFlat'); 71 | 72 | const selfFlat = _.find(graph, { modelName: 'SelfFlat' }); 73 | expect(selfFlat.selfParentRefs).to.include('parent'); 74 | expect(selfFlat.selfParentRefs).to.not.include('wrong'); 75 | 76 | }); 77 | 78 | it('should be able to build graph from array child self ref', 79 | function () { 80 | 81 | const graph = schemaGraph(mongoose); 82 | 83 | expect(graph).to.be.an('array'); 84 | expect(_.map(graph, 'modelName')).to.include('SelfFlat'); 85 | 86 | const selfFlat = _.find(graph, { modelName: 'SelfFlat' }); 87 | expect(selfFlat.selfChildRefs) 88 | .to.include.members(['kids', 'childrens']); 89 | 90 | }); 91 | 92 | }); 93 | 94 | describe('schema refs', function () { 95 | 96 | before(function () { 97 | const RefSchema = new Schema({ 98 | parent: { 99 | type: ObjectId, 100 | ref: 'Flat' 101 | }, 102 | kids: { 103 | type: [ObjectId], 104 | ref: 'SelfFlat' 105 | } 106 | }); 107 | mongoose.model('Ref', RefSchema); 108 | }); 109 | 110 | it('should be able to build graph from direct ref', function () { 111 | const graph = schemaGraph(mongoose); 112 | 113 | expect(graph).to.be.an('array'); 114 | expect(_.map(graph, 'modelName')).to.include('Ref'); 115 | 116 | const ref = _.find(graph, { modelName: 'Ref' }); 117 | expect(ref.parentRefs).to.have.length(1); 118 | expect(ref.parentRefs[0].path).be.equal('parent'); 119 | expect(ref.parentRefs[0].modelName).be.equal('Flat'); 120 | 121 | expect(ref.childRefs).to.have.length(1); 122 | expect(ref.childRefs[0].path).be.equal('kids'); 123 | expect(ref.childRefs[0].modelName).be.equal('SelfFlat'); 124 | 125 | }); 126 | 127 | }); 128 | 129 | }); 130 | -------------------------------------------------------------------------------- /test/index.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //dependencies 4 | const path = require('path'); 5 | const expect = require('chai').expect; 6 | const seed = require(path.join(__dirname, '..')); 7 | require(path.join(__dirname, '..', 'models', 'User')); 8 | 9 | describe('seed mongoose', function () { 10 | 11 | it('should be a functional module', function () { 12 | expect(seed).to.exist; 13 | expect(seed).to.be.a('function'); 14 | }); 15 | 16 | it('should be able to seed test data', function (done) { 17 | seed({ environment: 'test' }, function (error, seeds) { 18 | expect(error).to.not.exist; 19 | expect(seeds).to.exist; 20 | expect(seeds).to.have.length.above(0); 21 | done(error, seeds); 22 | }); 23 | }); 24 | 25 | it('should be able to seed development data', function (done) { 26 | seed({ environment: 'development' }, function (error, seeds) { 27 | expect(error).to.not.exist; 28 | expect(seeds).to.exist; 29 | expect(seeds).to.have.length.above(0); 30 | done(error, seeds); 31 | }); 32 | }); 33 | 34 | it('should be able to seed producton data', function (done) { 35 | seed({ environment: 'production' }, function (error, seeds) { 36 | expect(error).to.not.exist; 37 | expect(seeds).to.exist; 38 | expect(seeds).to.have.length.above(0); 39 | done(error, seeds); 40 | }); 41 | }); 42 | 43 | }); 44 | -------------------------------------------------------------------------------- /test/seed.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //dependencies 4 | const path = require('path'); 5 | const _ = require('lodash'); 6 | const async = require('async'); 7 | const hashObject = require('object-hash'); 8 | const expect = require('chai').expect; 9 | const faker = require('faker'); 10 | const mongoose = require('mongoose'); 11 | const Schema = mongoose.Schema; 12 | const ObjectId = Schema.ObjectId; 13 | const seed = require(path.join(__dirname, '..', 'lib', 'seed')); 14 | let Simple; 15 | let SimpleRef; 16 | let OtherRef; 17 | 18 | describe('seed', function () { 19 | 20 | before(function () { 21 | const SimpleSchema = new Schema({ 22 | first: { type: String, unique: true } 23 | }); 24 | Simple = mongoose.model('Simple', SimpleSchema); 25 | }); 26 | 27 | before(function () { 28 | const SimpleRefSchema = new Schema({ 29 | start: { type: ObjectId, ref: 'SimpleRef' }, 30 | last: { type: ObjectId, ref: 'SimpleRef' }, 31 | first: { type: String }, 32 | kids: [{ type: ObjectId, ref: 'SimpleRef' }] 33 | }); 34 | SimpleRef = mongoose.model('SimpleRef', SimpleRefSchema); 35 | }); 36 | 37 | before(function () { 38 | const OtherRefSchema = new Schema({ 39 | start: { type: ObjectId, ref: 'Simple' }, 40 | last: { type: ObjectId, ref: 'Simple' }, 41 | first: { type: String }, 42 | kids: [{ type: ObjectId, ref: 'Simple' }] 43 | }); 44 | OtherRef = mongoose.model('OtherRef', OtherRefSchema); 45 | }); 46 | 47 | it('should be an object', function () { 48 | expect(seed).to.be.an('object'); 49 | expect(seed).to.include.keys('seeded', 'single'); 50 | expect(seed.seeded).to.be.a('object'); 51 | expect(seed.single).to.be.a('function'); 52 | }); 53 | 54 | 55 | it('should be able to seed single simple model', function (done) { 56 | const data = { first: faker.name.findName() }; 57 | const hash = hashObject(data); 58 | seed.single({ 59 | modelName: 'Simple', 60 | data: data, 61 | graph: {} 62 | }, function (error, seeded) { 63 | 64 | expect(error).to.not.exist; 65 | expect(seeded).to.exist; 66 | 67 | expect(seed.seeded[hash]).to.exist; 68 | 69 | done(error, seeded); 70 | 71 | }); 72 | 73 | }); 74 | 75 | it('should not be able to seed single simple model twice', function (done) { 76 | const data = { first: faker.name.findName() }; 77 | const hash = hashObject(data); 78 | 79 | async.series({ 80 | 81 | first: function (next) { 82 | seed.single({ 83 | modelName: 'Simple', 84 | data: data, 85 | graph: {} 86 | }, next); 87 | }, 88 | 89 | repeat: function (next) { 90 | seed.single({ 91 | modelName: 'Simple', 92 | data: data, 93 | graph: {} 94 | }, next); 95 | } 96 | 97 | }, function (error, seeded) { 98 | 99 | expect(error).to.not.exist; 100 | expect(seeded).to.exist; 101 | 102 | expect(seed.seeded[hash]).to.exist; 103 | 104 | expect(seeded.first._id) 105 | .to.eql(seeded.repeat._id); 106 | 107 | done(error, seeded); 108 | 109 | }); 110 | 111 | }); 112 | 113 | it('should be able to seed multiple simple model', function (done) { 114 | const data = { first: faker.name.findName() }; 115 | const hash = hashObject(data); 116 | seed.many({ 117 | modelName: 'Simple', 118 | data: [data], 119 | graph: {} 120 | }, function (error, seeded) { 121 | 122 | expect(error).to.not.exist; 123 | expect(seeded).to.exist; 124 | 125 | expect(seed.seeded[hash]).to.exist; 126 | 127 | done(error, seeded); 128 | 129 | }); 130 | 131 | }); 132 | 133 | it('should not be able to seed multiple same simple model twice', 134 | function (done) { 135 | const data = { first: faker.name.findName() }; 136 | const hash = hashObject(data); 137 | seed.many({ 138 | modelName: 'Simple', 139 | data: [data, data], 140 | graph: {} 141 | }, function (error, seeded) { 142 | 143 | expect(error).to.not.exist; 144 | expect(seeded).to.exist; 145 | 146 | expect(seed.seeded[hash]).to.exist; 147 | 148 | expect(_.first(seeded)._id) 149 | .to.eql(_.last(seeded)._id); 150 | 151 | done(error, seeded); 152 | 153 | }); 154 | 155 | }); 156 | 157 | it('should be able to seed self ref model', function (done) { 158 | const start = { first: faker.name.findName() }; 159 | const data = { start: start, first: faker.name.findName() }; 160 | const startHash = hashObject(start); 161 | 162 | seed.single({ 163 | modelName: 'SimpleRef', 164 | data: data, 165 | graph: {} 166 | }, function (error, seeded) { 167 | 168 | expect(error).to.not.exist; 169 | expect(seeded).to.exist; 170 | 171 | expect(seed.seeded[startHash]).to.exist; 172 | 173 | expect(seeded.start).to.exist; 174 | 175 | done(error, seeded); 176 | 177 | }); 178 | 179 | }); 180 | 181 | it('should not be able to seed self ref model twice', function (done) { 182 | const start = { first: faker.name.findName() }; 183 | const last = start; 184 | const data = { start: start, last: last, first: faker.name.findName() }; 185 | const startHash = hashObject(start); 186 | const lastHash = hashObject(last); 187 | 188 | seed.single({ 189 | modelName: 'SimpleRef', 190 | data: data, 191 | graph: {} 192 | }, function (error, seeded) { 193 | 194 | expect(error).to.not.exist; 195 | expect(seeded).to.exist; 196 | 197 | expect(seed.seeded[startHash]).to.exist; 198 | expect(seed.seeded[lastHash]).to.exist; 199 | 200 | expect(seeded.start).to.be.eql(seeded.last); 201 | 202 | done(error, seeded); 203 | 204 | }); 205 | 206 | }); 207 | 208 | it('should be able to seed collection of self child ref model', 209 | function (done) { 210 | const kids = [ 211 | { first: faker.name.findName() }, 212 | { first: faker.name.findName() } 213 | ]; 214 | const data = { first: faker.name.findName(), kids: kids }; 215 | 216 | seed.single({ 217 | modelName: 'SimpleRef', 218 | data: data, 219 | graph: {} 220 | }, function (error, seeded) { 221 | 222 | expect(error).to.not.exist; 223 | expect(seeded).to.exist; 224 | 225 | expect(seeded.kids).to.exist; 226 | expect(seeded.kids).to.have.length(2); 227 | 228 | done(error, seeded); 229 | 230 | }); 231 | 232 | }); 233 | 234 | it('should not be able to seed collection of self child ref model twice', 235 | function (done) { 236 | const first = { first: faker.name.findName() }; 237 | const last = first; 238 | const kids = [ 239 | first, 240 | last 241 | ]; 242 | const data = { first: faker.name.findName(), kids: kids }; 243 | 244 | seed.single({ 245 | modelName: 'SimpleRef', 246 | data: data, 247 | graph: {} 248 | }, function (error, seeded) { 249 | 250 | expect(error).to.not.exist; 251 | expect(seeded).to.exist; 252 | 253 | expect(seeded.kids).to.exist; 254 | expect(seeded.kids).to.have.length(1); 255 | 256 | done(error, seeded); 257 | 258 | }); 259 | 260 | }); 261 | 262 | it('should be able to seed other ref model', function (done) { 263 | const start = { first: faker.name.findName() }; 264 | const data = { start: start, first: faker.name.findName() }; 265 | const startHash = hashObject(start); 266 | 267 | seed.single({ 268 | modelName: 'OtherRef', 269 | data: data, 270 | graph: {} 271 | }, function (error, seeded) { 272 | 273 | expect(error).to.not.exist; 274 | expect(seeded).to.exist; 275 | 276 | expect(seed.seeded[startHash]).to.exist; 277 | 278 | expect(seeded.start).to.exist; 279 | 280 | done(error, seeded); 281 | 282 | }); 283 | 284 | }); 285 | 286 | it('should not be able to seed other ref model twice', function (done) { 287 | const start = { first: faker.name.findName() }; 288 | const last = start; 289 | const data = { start: start, last: last, first: faker.name.findName() }; 290 | const startHash = hashObject(start); 291 | 292 | seed.single({ 293 | modelName: 'OtherRef', 294 | data: data, 295 | graph: {} 296 | }, function (error, seeded) { 297 | 298 | expect(error).to.not.exist; 299 | expect(seeded).to.exist; 300 | 301 | expect(seed.seeded[startHash]).to.exist; 302 | 303 | expect(seeded.start).to.exist; 304 | expect(seeded.last).to.exist; 305 | expect(seeded.start).to.be.eql(seeded.last); 306 | 307 | done(error, seeded); 308 | 309 | }); 310 | 311 | }); 312 | 313 | it('should be able to seed collection of other ref model', 314 | function (done) { 315 | const kids = [ 316 | { first: faker.name.findName() }, 317 | { first: faker.name.findName() } 318 | ]; 319 | const data = { first: faker.name.findName(), kids: kids }; 320 | 321 | seed.single({ 322 | modelName: 'OtherRef', 323 | data: data, 324 | graph: {} 325 | }, function (error, seeded) { 326 | 327 | expect(error).to.not.exist; 328 | expect(seeded).to.exist; 329 | 330 | expect(seeded.kids).to.exist; 331 | expect(seeded.kids).to.have.length(2); 332 | 333 | done(error, seeded); 334 | 335 | }); 336 | 337 | }); 338 | 339 | it('should not be able to seed collection of other ref model twice', 340 | function (done) { 341 | const first = { first: faker.name.findName() }; 342 | const kids = [ 343 | first, 344 | first 345 | ]; 346 | const data = { first: faker.name.findName(), kids: kids }; 347 | 348 | seed.single({ 349 | modelName: 'OtherRef', 350 | data: data, 351 | graph: {} 352 | }, function (error, seeded) { 353 | 354 | expect(error).to.not.exist; 355 | expect(seeded).to.exist; 356 | 357 | expect(seeded.kids).to.exist; 358 | expect(seeded.kids).to.have.length(1); 359 | 360 | done(error, seeded); 361 | 362 | }); 363 | 364 | }); 365 | 366 | afterEach(function (done) { 367 | Simple.remove(done); 368 | }); 369 | 370 | afterEach(function (done) { 371 | SimpleRef.remove(done); 372 | }); 373 | 374 | afterEach(function (done) { 375 | OtherRef.remove(done); 376 | }); 377 | 378 | }); 379 | --------------------------------------------------------------------------------