├── .babelrc ├── .editorconfig ├── .eslintrc.json ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docker-compose.yml ├── docs ├── FAQ.md └── README.md ├── package.json ├── scripts └── mocha-bootload.js ├── src ├── assets │ ├── migrations │ │ ├── create-table.ts │ │ └── skeleton.ts │ ├── models │ │ ├── index.js │ │ └── model.ts │ └── seeders │ │ └── skeleton.js ├── commands │ ├── database.js │ ├── init.js │ ├── migrate.js │ ├── migrate_undo.js │ ├── migrate_undo_all.js │ ├── migration_generate.js │ ├── model_generate.js │ ├── seed.js │ ├── seed_generate.js │ └── seed_one.js ├── core │ ├── migrator.js │ └── yargs.js ├── helpers │ ├── asset-helper.js │ ├── config-helper.js │ ├── generic-helper.js │ ├── index.js │ ├── init-helper.js │ ├── migration-helper.js │ ├── model-helper.js │ ├── path-helper.js │ ├── template-helper.js │ ├── umzug-helper.js │ ├── version-helper.js │ └── view-helper.js └── sequelize.js └── test ├── db ├── db-create.test.js ├── db-drop.test.js ├── migrate-json.test.js ├── migrate.test.js ├── migrate │ ├── schema │ │ └── add_timestamps.test.js │ ├── status.test.js │ ├── undo.test.js │ └── undo │ │ ├── all.test.js │ │ └── all_to.test.js ├── seed-json.test.js ├── seed.test.js └── seed │ ├── all.test.js │ ├── undo.test.js │ └── undo │ └── all.test.js ├── environment-variable.test.js ├── help.test.js ├── init.test.js ├── migration └── create.test.js ├── model └── create.test.js ├── options.test.js ├── seed └── create.test.js ├── support ├── assets │ ├── migrations │ │ ├── 20111117063700-createPerson.js │ │ ├── 20111130161100-emptyMigration.js │ │ ├── 20111205064000-renamePersonToUser.js │ │ ├── 20111205162700-addSignatureColumnToUser.js │ │ ├── 20111205167000-addUniqueNameColumnToUser.js │ │ ├── 20111206061400-removeShopIdColumnFromUser.js │ │ ├── 20111206063000-changeSignatureColumnOfUserToMendatory.js │ │ ├── 20111206163300-renameSignatureColumnOfUserToSig.js │ │ ├── 20130909174103-createFunctionGetAnAnswer.js │ │ ├── 20130909174253-renameFunctionGetAnAnswerGetTheAnswer.js │ │ ├── 20130909175000-deleteFunctionGetTheAnswer.js │ │ ├── 20130909175939-createTestTableForTrigger.js │ │ ├── 20130909180846-createTriggerOnTriggerTestTable.js │ │ ├── 20130909181148-renameTriggerUpdatedAtToUpdateUpdatedAt.js │ │ ├── 20130909185621-deleteTriggerUpdateUpdatedAt.js │ │ ├── 20170526153000-createPost.js │ │ ├── invalid │ │ │ └── 20141208213500-createPerson.js │ │ └── new │ │ │ └── 20141208213500-createPerson.js │ ├── project.js │ └── seeders │ │ ├── 20111117063700-seedPerson.js │ │ ├── 20111117063900-seedPerson2.js │ │ └── new │ │ └── 20141208213500-seedPerson.js ├── config │ ├── config.js │ └── options.js ├── helpers.js ├── index.js └── tmp │ └── .gitkeep ├── url.test.js └── version.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { 5 | "node": "4.0" 6 | }, 7 | "exclude": [ 8 | "transform-async-to-generator", 9 | "transform-regenerator" 10 | ], 11 | "useBuiltIns": true 12 | }] 13 | ], 14 | "plugins": [ 15 | "transform-object-rest-spread", 16 | ["transform-async-to-module-method", { 17 | "module": "bluebird", 18 | "method": "coroutine" 19 | }] 20 | ], 21 | "ignore": [ 22 | "src/assets" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | indent_size = 4 22 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "no-extra-parens": "warn", 5 | "valid-jsdoc": "off", 6 | "new-cap": ["warn", {"properties": false}], 7 | "comma-spacing": ["error", {"before": false, "after": true}], 8 | "no-extra-boolean-cast": "warn", 9 | "strict": ["warn", "global"], 10 | "no-var": "error", 11 | "prefer-const": "error", 12 | "semi": ["error", "always"], 13 | "space-before-function-paren": ["warn", "always"], 14 | "keyword-spacing": ["warn"], 15 | "prefer-arrow-callback": "error", 16 | "arrow-parens": ["error", "as-needed"], 17 | "comma-style": ["warn", "last"], 18 | "no-bitwise": "off", 19 | "no-cond-assign": ["error", "except-parens"], 20 | "curly": "off", 21 | "eqeqeq": "error", 22 | "no-extend-native": "error", 23 | "wrap-iife": ["error", "any"], 24 | "indent": ["error", 2, {"SwitchCase": 1}], 25 | "no-use-before-define": "off", 26 | "no-caller": "error", 27 | "no-undef": "error", 28 | "no-unused-vars": "error", 29 | "no-irregular-whitespace": "error", 30 | "max-depth": ["error", 8], 31 | "quotes": ["error", "single", {"avoidEscape": true}], 32 | "linebreak-style": "error", 33 | "no-loop-func": "warn", 34 | "object-shorthand": "error", 35 | "one-var-declaration-per-line": "warn", 36 | "comma-dangle": "warn", 37 | "no-shadow": "warn", 38 | "camelcase": "warn" 39 | }, 40 | "parserOptions": { 41 | "ecmaVersion": 2017, 42 | "sourceType": "module" 43 | }, 44 | "env": { 45 | "node": true, 46 | "mocha": true, 47 | "es6": true 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | ## What you are doing? 11 | 12 | 13 | ```js 14 | // code here 15 | ``` 16 | 17 | ## What do you expect to happen? 18 | _I wanted Foo!_ 19 | 20 | ## What is actually happening? 21 | _But the output was bar!_ 22 | 23 | _Output, either JSON or SQL_ 24 | 25 | 26 | __Dialect:__ mysql / postgres / sqlite / mssql / any 27 | __Database version:__ XXX 28 | __Sequelize CLI version:__ XXX 29 | __Sequelize version:__ XXX 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Editor files 11 | .vscode 12 | .idea 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # Compiled binary addons (http://nodejs.org/api/addons.html) 21 | build/Release 22 | 23 | # Extra folders 24 | node_modules 25 | lib 26 | test/support/tmp/* 27 | !test/support/tmp/.gitkeep 28 | 29 | # Extra files 30 | package-lock.json 31 | npm-debug.log 32 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | node_modules 17 | 18 | package-lock.json 19 | .vscode 20 | npm-debug.log 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | dist: trusty 3 | 4 | language: node_js 5 | 6 | cache: 7 | directories: 8 | - node_modules 9 | 10 | jobs: 11 | include: 12 | - stage: tests 13 | node_js: 4 14 | env: DIALECT=mysql SEQUELIZE=3.30.4 15 | - stage: tests 16 | node_js: 4 17 | env: DIALECT=postgres SEQUELIZE=3.30.4 18 | - stage: tests 19 | node_js: 4 20 | env: DIALECT=sqlite SEQUELIZE=3.30.4 21 | - stage: tests 22 | node_js: 6 23 | env: DIALECT=postgres SEQUELIZE=latest 24 | - stage: tests 25 | node_js: 6 26 | env: DIALECT=sqlite SEQUELIZE=latest 27 | 28 | before_script: 29 | - "mysql -e 'create database sequelize_test;'" 30 | - "psql -c 'create database sequelize_test;' -U postgres" 31 | - npm install sequelize@$(echo $SEQUELIZE) 32 | - npm run build 33 | 34 | script: 35 | - npm run test 36 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## Future 5 | ## v3.2.0 - 3rd, Dec 2017 6 | 7 | ### Fixed 8 | - Better messages when files/folders already exists [#569](https://github.com/sequelize/cli/issues/569) 9 | - Specify ARRAY type with `model:create` [#155](https://github.com/sequelize/cli/issues/155) 10 | 11 | ### Changed 12 | - Revert: safer configuration using environment variables for production [#594](https://github.com/sequelize/cli/pull/594) 13 | 14 | 15 | ## v3.1.0 - 18th, Nov 2017 16 | 17 | ### Fixed 18 | - Pass full config to sequelize constructor [#584](https://github.com/sequelize/cli/issues/584) 19 | 20 | ### Added 21 | - `db:migrate` support for from / to arguments [#581](https://github.com/sequelize/cli/pull/581) 22 | 23 | ### Changed 24 | - Safer configuration using environment variables for production [#579](https://github.com/sequelize/cli/pull/579) 25 | 26 | - Updated dependencies 27 | 28 | ## v3.0.0 - 23rd, Sep 2017 29 | 30 | ### Fixed 31 | - `db:create/drop` should properly quote database name [#545](https://github.com/sequelize/cli/issues/545) 32 | 33 | ### Added 34 | - `--debug` support, print full stack for errors, when available [#552](https://github.com/sequelize/cli/pull/552) 35 | 36 | ### Changed 37 | - All errors are now properly formatted and outputted to `console.error` [#552](https://github.com/sequelize/cli/pull/552) 38 | 39 | ### Internal 40 | - Refactor to use centralized `log/error/warn` methods [#552](https://github.com/sequelize/cli/pull/552) 41 | 42 | ## v3.0.0-3 - 16th, Sep 2017 43 | 44 | ### Fixed 45 | - Error when creating nested config path [#534](https://github.com/sequelize/cli/issues/534) 46 | 47 | ### Added 48 | - `db:create` and `db:drop` for MySQL, Postgres and MSSQL [#70](https://github.com/sequelize/cli/issues/70) 49 | 50 | ## v3.0.0-2 - 10th, Sep 2017 51 | 52 | ### Fixed 53 | - `.sequelizerc` is not properly read [#536](https://github.com/sequelize/cli/issues/536) 54 | 55 | ## v3.0.0-1 - 9th, Sep 2017 56 | 57 | ### Added 58 | - Warning with Sequelize v4 59 | 60 | ### Changed 61 | - Skeletons are now ES6 based [#482](https://github.com/sequelize/cli/issues/482) 62 | 63 | ## v3.0.0-0 - 8th, Sep 2017 64 | 65 | ### Removed 66 | - `db:migrate:old_schema` 67 | - `--coffee` support 68 | - `help:*` commands 69 | 70 | ### Fixed 71 | - Drop Gulp [#344](https://github.com/sequelize/cli/issues/344) 72 | - NSP Vulnerability [#466](https://github.com/sequelize/cli/issues/466) 73 | - Non functional with Node 7 [#383](https://github.com/sequelize/cli/issues/383) 74 | - Unable to find Gulp [#138](https://github.com/sequelize/cli/issues/138) 75 | - Reintegrate Gulp [#5](https://github.com/sequelize/cli/issues/5) 76 | - Unable to seed multiple files [#523](https://github.com/sequelize/cli/issues/523) 77 | - No Gulpfile found [#480](https://github.com/sequelize/cli/issues/480) 78 | - Exit Code(1) when migration undo fail [#394](https://github.com/sequelize/cli/issues/394) 79 | - `--coffee` flag dont work with `.rc` file [#244](https://github.com/sequelize/cli/issues/244) 80 | - CLI dont halt on error [#106](https://github.com/sequelize/cli/issues/106) 81 | 82 | ### Improvements 83 | - No Gulp, better startup time 84 | - Proper exit codes 85 | - Remove dependency on `moment` , `findup-sync`, `gulp` and `gulp-help` 86 | 87 | ## v2.8.0 - 2017-08-04 88 | ### Added 89 | - Timestamps db:migrate:schema:timestamps:add [#460](https://github.com/sequelize/cli/pull/460) 90 | - Support for db:migrate:undo:all with `--to`, which allow reverting to a specific version [#459](https://github.com/sequelize/cli/pull/459) 91 | 92 | ### Changed 93 | - Updated dependencies 94 | 95 | ### Docs 96 | - FAQ section [#486](https://github.com/sequelize/cli/pull/486) 97 | 98 | ## v2.7.0 - 2017-03-25 99 | ### Added 100 | - Named db:migrate:undo [#387](https://github.com/sequelize/cli/pull/387) 101 | 102 | ### Changed 103 | - Fixed promise warning in gulp-helper [#439](https://github.com/sequelize/cli/issues/439) 104 | 105 | ## v2.6.0 - 2017-03-17 106 | ### Added 107 | - Async configuration support [#359](https://github.com/sequelize/cli/pull/359) 108 | 109 | ### Changed 110 | - Print error stack when reporting seeder errors [#411](https://github.com/sequelize/cli/pull/411) 111 | - Fixed seed:undo:all with `none` storage [#433](https://github.com/sequelize/cli/pull/433) 112 | - Updated dependencies 113 | 114 | ## v2.5.0 - 2016-12-07 115 | ### Added 116 | - Add underscored option to generated model when created with --underscored [#380](https://github.com/sequelize/cli/issues/380) 117 | - Add db:migrate:status [#385](https://github.com/sequelize/cli/issues/385) 118 | 119 | ### Changed 120 | - Make it possible to use ":" in passwords in a DSN [#334](https://github.com/sequelize/cli/issues/334) 121 | - Catch error for db:migrate:undo[:all] [#353](https://github.com/sequelize/cli/issues/353) 122 | - Example usage of raw sql in migration [#363](https://github.com/sequelize/cli/issues/363) 123 | - Fixed typo: containts -> contains [#381](https://github.com/sequelize/cli/issues/381) 124 | - Updated dependencies 125 | 126 | ## v2.4.0 - 2016-04-25 127 | ### Added 128 | - Log filtering helper 129 | 130 | ### Changed 131 | - Fixed some promises that were not being returned 132 | - Updated dependencies 133 | - Migration and seed filenames now use UTC date instead of local date 134 | - Output filtering 135 | 136 | ## v2.3.1 - 2016-01-14 137 | ### Changed 138 | - Update lodash to 4.0.0 139 | 140 | ## v2.3.0 - 2016-01-11 141 | ### Changed 142 | - Default use of `seeder` memorizes nothing 143 | - Update umzug to 1.8.0 144 | 145 | ## v2.2.1 - 2015-11-24 146 | ### Fixed 147 | - Filtering of js files in models/index.js 148 | 149 | ## v2.2.0 - 2015-11-23 150 | ### Changed 151 | - Respect `url` property in configuration 152 | 153 | ## v2.1.1 - 2015-11-19 154 | ### Changed 155 | - Respect `dialectOptions` property when instantiating sequelize 156 | 157 | ## v2.1.0 - 2015-11-01 158 | ### Added 159 | - Option for usage of snake case timestamp columns in migrations (e.g. `sequelize model:create --underscored`) 160 | 161 | ## v2.0.0 - 2015-10-14 162 | ### Added 163 | - Read configuration from config.js if available 164 | 165 | ### Changed 166 | - Replace deprecated fs.existsSync method 167 | 168 | ## v1.9.2 - 2015-10-14 169 | ### Changed 170 | - Make options path process relative 171 | 172 | ## v1.9.1 - 2015-09-08 173 | ### Changed 174 | - Move gulp path resolution to function 175 | - Fix handling of duplicate CLI options 176 | - Add options object to seed template 177 | 178 | ## v1.9.0 - 2015-09-01 179 | ### Added 180 | - Seeding mechanism 181 | 182 | ## v1.8.3 - 2015-08-28 183 | ### Changed 184 | - Cascade package resolution 185 | 186 | ### Removed 187 | - Duplicate reference to gulp 188 | 189 | ## v1.8.2 - 2015-08-27 190 | ### Changed 191 | - Use node's native module resolution mechanism 192 | 193 | ## v1.8.1 - 2015-08-27 194 | ### Changed 195 | - Filter password from parsed URL 196 | 197 | ## v1.8.0 - 2015-08-27 198 | ### Added 199 | - Support for env variable usage in model/index.js 200 | 201 | ## v1.7.4 - 2015-07-28 202 | ### Changed 203 | - Return exit code 1 when migrations failed 204 | 205 | ## v1.7.3 - 2015-07-26 206 | ### Changed 207 | - Fix check of associate method 208 | 209 | ## v1.7.2 - 2015-07-16 210 | ### Added 211 | - Sequelize v3 to build matrix 212 | 213 | ### Changed 214 | - Compatibility with Sequelize v3 215 | 216 | ## v1.7.1 - 2015-07-09 217 | ### Added 218 | - Documentation about env variable 219 | - Test for use*env*variable 220 | 221 | ### Changed 222 | - Fix env variable use for sqlite 223 | - Update available tasks 224 | 225 | ## v1.7.0 - 2015-04-19 226 | ### Added 227 | - More gulp tasks 228 | 229 | ### Changed 230 | - Generate new migration style 231 | - Adopt new JSHint/JSCS rules 232 | - Update JSHint and JSCS rules 233 | 234 | ## v1.6.0 - 2015-04-02 235 | ### Added 236 | - Possibility to use a different table name for the meta data 237 | 238 | ## v1.5.0 - 2015-03-24 239 | ### Added 240 | - `sequelize db:migrate:undo:all` that rolls back all migrations. 241 | 242 | ## v1.4.0 - 2015-03-13 243 | ### Added 244 | - Possibility to log executed migrations to a JSON file 245 | 246 | ## v1.3.2 - 2015-03-13 247 | ### Changed 248 | - Fix gulp resolution for Windows 249 | 250 | ## 1.3.1 251 | ### Changed 252 | - Update umzug to 1.6.0 253 | 254 | ## 1.3.0 255 | ### Changed 256 | - Add builds for different sequelize versions 257 | - Fix raw queries in sequelize 2.0 258 | 259 | ## 1.2.0 260 | ### Changed 261 | - Add logging of statements and improve messages 262 | 263 | ## 1.1.0 264 | ### Changed 265 | - Update dependencies 266 | 267 | ## 1.0.8 268 | ### Changed 269 | - Fix usage of js2coffee 2.0 270 | 271 | ## 1.0.7 272 | ### Changed 273 | - Fix regexp application for non migration files. 274 | 275 | ## 1.0.6 276 | ### Changed 277 | - Add support for more fine-granular harmony flags 278 | 279 | ## 1.0.5 280 | ### Changed 281 | - Added support for auto-migrate from old schema [#82](https://github.com/sequelize/cli/issues/82) 282 | 283 | ## 1.0.4 284 | ### Changed 285 | - Fix output filtering 286 | - Fix EventEmitter deprecation warnings. 287 | 288 | ## 1.0.3 289 | ### Changed 290 | - Use the url module to parse database URLs 291 | 292 | ## 1.0.1 293 | ### Changed 294 | - Fix global installation of the CLI 295 | 296 | ## 1.0.0 297 | ### Changed 298 | - Big migration refactoring which uses a new schema table and the umzug lib. 299 | 300 | ## 0.3.3 301 | ### Changed 302 | - Fix for default attributes in generated migrations. 303 | - Auto-generate coffee files with js2coffee. 304 | 305 | ## 0.3.2 306 | ### Changed 307 | - Add default attributes to generated migrations. 308 | 309 | ## 0.3.1 310 | ### Changed 311 | - Fix alignment of comment in generated model file 312 | - Fix global installation 313 | 314 | ## 0.3.0 315 | ### Changed 316 | - Add Node.JS version to the teaser 317 | - Add dialect and the respective version to the teaser 318 | 319 | ## 0.2.6 320 | ### Changed 321 | - Do not load lodash in `models/index.js`. 322 | 323 | ## 0.2.5 324 | ### Changed 325 | - Prefer `--env` over the environment variable `NODE_ENV`. 326 | - Search by default for a file called `.sequelizerc` and treat it as `--options-path`. 327 | 328 | ## 0.2.4 329 | ### Changed 330 | - Fix unqualified sequelize instantiation which enforced the mysql module. 331 | 332 | ## 0.2.3 333 | ### Changed 334 | - Fix `--migrations-path` for relative paths. 335 | 336 | ## 0.2.2 337 | ### Changed 338 | - Fix for MS Windows. 339 | 340 | ## 0.2.1 341 | ### Changed 342 | - Fix `_.includes`. 343 | 344 | ## 0.2.0 345 | ### Changed 346 | - `sequelize model:create` creates a model and its respective migration file. 347 | 348 | ## 0.1.1 349 | ### Changed 350 | - Fix illegal character. 351 | 352 | ## 0.1.0 353 | ### Changed 354 | - `sequelize init` now creates a `models` folder and a `models/index.js` file. [#11](https://github.com/sequelize/cli/pull/11) 355 | 356 | ## 0.0.4 357 | ### Changed 358 | - Fix --config flag. [#4](https://github.com/sequelize/cli/pull/4) 359 | - Fix custom logging option. [#3](https://github.com/sequelize/cli/pull/3) 360 | 361 | ## 0.0.3 362 | ### Changed 363 | - Fix conflict within projects that are already shipping gulp. [#2](https://github.com/sequelize/cli/pull/2) 364 | - Add harmony support. [#6](https://github.com/sequelize/cli/pull/6) 365 | 366 | ## 0.0.2 367 | ### Changed 368 | - Added the binary to the package.json 369 | 370 | ## 0.0.1 371 | ### Changed 372 | - First working version 373 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Sequelize CLI is entirely driven by community contributions. Any little contribution with code or documentation work helps us keeping this project running. 4 | 5 | You can help us in various ways 6 | 7 | ## Reporting Bugs 8 | 9 | ### Security Issues 10 | 11 | Although CLI is never supposed to be web facing, we still want to fix any security issues. Please contact project maintainers **privately**. You can find more information [here](https://github.com/sequelize/sequelize/blob/master/CONTACT.md). 12 | 13 | ### General Issues or Feature Requests 14 | 15 | Github issues should follow specified template. When you start creating a new issue, an empty template will be already filled. 16 | 17 | Please make sure issue you are reporting is strictly related to Sequelize CLI. 18 | 19 | If you want to propose new features to Sequelize CLI, you may ignore issue template. You still need to clearly state new feature. Feature request should give various examples, API suggestions and references to support idea behind it. 20 | 21 | ## Fixing Bugs or Implementing Features 22 | 23 | ### Preparing your environment 24 | 25 | Start with cloning Sequelize CLI repo 26 | 27 | ```bash 28 | $ git clone git@github.com:sequelize/cli.git 29 | 30 | $ git clone https://github.com/sequelize/cli.git # Using HTTPS 31 | ``` 32 | 33 | Make sure you have all required dependencies, you will need 34 | 35 | - Node v4 or above 36 | - NPM v3 or above 37 | 38 | Now go to cloned repository folder 39 | 40 | ```bash 41 | $ cd /path/to/cloned/repository 42 | ``` 43 | 44 | Install required modules 45 | 46 | ```bash 47 | $ npm install 48 | ``` 49 | 50 | ### Running tests 51 | 52 | By default CLI use SQLite, which requires no database configuration. We use Babel to build CLI. Running default test command will automatically build it for you. 53 | 54 | ```bash 55 | $ npm test 56 | ``` 57 | 58 | Test can take about 7 to 10 minutes to finish, subjected to hardware configuration. 59 | 60 | ## Improving Documentation 61 | 62 | If you want to improve or expand our documentation you can start with documentation in `/docs` folder of this repository. 63 | 64 | You can also help by improving [Migration section](http://docs.sequelizejs.com/manual/tutorial/migrations.html). Please read more about contributing to Sequelize Docs [here](https://github.com/sequelize/sequelize/blob/master/CONTRIBUTING.DOCS.md) 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 sequelize 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sequelize/cli [![npm version](https://badge.fury.io/js/sequelize-cli.svg)](https://badge.fury.io/js/sequelize-cli) [![Build Status](https://travis-ci.org/sequelize/cli.svg?branch=master)](https://travis-ci.org/sequelize/cli) [![Greenkeeper badge](https://badges.greenkeeper.io/sequelize/cli.svg)](https://greenkeeper.io/) 2 | 3 | The Sequelize Command Line Interface (CLI) _for TypeScript_ 4 | 5 | ## Table of Contents 6 | - [Installation](#installation) 7 | - [Contributing](#contributing) 8 | - [Sequelize Support](#sequelize-support) 9 | - [Documentation](#documentation) 10 | 11 | ## Installation 12 | 13 | ### Globally 14 | Install CLI globally with 15 | 16 | ```bash 17 | $ npm install -g sequelize-cli-typescript 18 | ``` 19 | 20 | Now you can run CLI using following command anywhere 21 | 22 | ```bash 23 | $ sequelize 24 | ``` 25 | 26 | ### Locally 27 | Install CLI locally to your `node_modules` folder with 28 | 29 | ```bash 30 | $ npm install --save sequelize-cli-typescript 31 | ``` 32 | 33 | You should be able to run CLI with 34 | 35 | ```bash 36 | $ node_modules/.bin/sequelize 37 | ``` 38 | 39 | ### Differences from Sequelize-Cli _(non-TypeScript)_ 40 | 41 | With sequelize-cli, the ```model:generate``` command would produce _JavaScript_ files in two folders: 42 | /models and /migrations, or other folders as specified in your .sequelizerc file. The ```db:migrate``` 43 | command would then exe ute these _JavaScript_ files to update your database. 44 | 45 | With sequelize-cli-typescript, ```model:generate``` produces _TypeScript_ files in the same two folders 46 | (or again, as specified in your .sequelizerc file). But before you can run ```db:migrate``` you must 47 | compile your migrations. (The step of compiling your migrations is left to you.) 48 | 49 | You could compile your migrations along with your other code, or as part of a separate script. After you have compiled 50 | your migrations, then you can run ```db:migrate```. 51 | 52 | It's usually the case that the compiled _JavaScript_ code will be put in a different directory than 53 | the source _TypeScript_ code, so whereas sequelize-cli had one ```migrations-path``` setting, 54 | sequelize-cli-typescript has two: ```migrations-source-path``` and ```migrations-compiled-path```, which 55 | default to /migrations and /migrations/compiled respectively. 56 | 57 | 58 | ### Usage 59 | ``` 60 | Sequelize CLI [Node: 6.11.2, CLI: 3.0.0, ORM: 4.8.0] 61 | 62 | Commands: 63 | db:migrate Run pending migrations 64 | db:migrate:schema:timestamps:add Update migration table to have timestamps 65 | db:migrate:status List the status of all migrations 66 | db:migrate:undo Reverts a migration 67 | db:migrate:undo:all Revert all migrations ran 68 | db:seed Run specified seeder 69 | db:seed:undo Deletes data from the database 70 | db:seed:all Run every seeder 71 | db:seed:undo:all Deletes data from the database 72 | db:create Create database specified by configuration 73 | db:drop Drop database specified by configuration 74 | init Initializes project 75 | init:config Initializes configuration 76 | init:migrations Initializes migrations 77 | init:models Initializes models 78 | init:seeders Initializes seeders 79 | migration:generate Generates a new migration file [aliases: migration:create] 80 | model:generate Generates a model and its migration [aliases: model:create] 81 | seed:generate Generates a new seed file [aliases: seed:create] 82 | 83 | Options: 84 | --version Show version number [boolean] 85 | --help Show help [boolean] 86 | ``` 87 | 88 | ## Contributing 89 | 90 | Sequelize CLI is always looking for contributions. You can help us with fixing bugs, reporting bugs or improving documentation. 91 | 92 | Please read [contributing documentation](CONTRIBUTING.md) 93 | 94 | ## Sequelize Support 95 | 96 | CLI v3 fully supports Sequelize v3. Support for Sequelize v4 is still experimental. 97 | 98 | Full support for Sequelize v4 is coming soon with [Sequelize CLI v4](https://github.com/sequelize/cli/issues/441) 99 | 100 | ## Documentation 101 | 102 | - [Migrations Documentation](http://docs.sequelizejs.com/manual/tutorial/migrations.html) 103 | - [CLI Options](docs/README.md) 104 | - [Frequently Asked Questions](docs/FAQ.md) 105 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | # PostgreSQL 5 | postgres-95: 6 | image: camptocamp/postgis:9.5 7 | environment: 8 | POSTGRES_USER: postgres 9 | POSTGRES_PASSWORD: postgres 10 | POSTGRES_DB: sequelize_test 11 | ports: 12 | - "5432:5432" 13 | 14 | # MySQL 15 | mysql-57: 16 | image: mysql:5.7 17 | environment: 18 | MYSQL_ALLOW_EMPTY_PASSWORD: "yes" 19 | MYSQL_DATABASE: sequelize_test 20 | MYSQL_USER: root 21 | ports: 22 | - "3306:3306" 23 | -------------------------------------------------------------------------------- /docs/FAQ.md: -------------------------------------------------------------------------------- 1 | The Sequelize Command Line Interface (CLI) Frequently Asked Question 2 | 3 | ## Initialize sequelize to create necessary files in the project 4 | ``` 5 | $ sequelize init 6 | ``` 7 | 8 | ## How can I generate a model? 9 | Specify model name with `--name` argument. List of table fields can be passed with `--attributes` option 10 | ``` 11 | $ sequelize model:create --name User --attributes name:string,state:boolean,birth:date,card:integer 12 | ``` 13 | 14 | ## How can I create a migration? 15 | Specify migration name with `--name` argument 16 | ``` 17 | $ sequelize migration:create --name 18 | ``` 19 | 20 | ## What is the command to execute all migrations? 21 | ``` 22 | $ sequelize db:migrate 23 | ``` 24 | ## How can I make a migrations rollback? 25 | ``` 26 | $ sequelize db:migrate:undo:all 27 | ``` 28 | 29 | ## How can I create a seeder? 30 | Specify seeder name with `--name` argument 31 | ``` 32 | $ sequelize seed:create --name 33 | ``` 34 | 35 | ## How can I run the seeders? 36 | ``` 37 | $ sequelize db:seed:all 38 | ``` 39 | 40 | ## How can I make the seeders rollback? 41 | ``` 42 | $ sequelize db:seed:undo:all 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Options 2 | 3 | The manuals will show all the flags and options which are available for the respective tasks. 4 | If you find yourself in a situation where you always define certain flags in order to 5 | make the CLI compliant to your project, you can move those definitions also into a file called 6 | `.sequelizerc`. The file will get `require`d if available and can therefore be either a JSON file 7 | or a Node.JS script that exports a hash. 8 | 9 | ### Example for a Node.JS script 10 | 11 | ```js 12 | var path = require('path') 13 | 14 | module.exports = { 15 | 'config': path.resolve('config', 'database.json'), 16 | 'migrations-path': path.resolve('db', 'migrate') 17 | } 18 | ``` 19 | 20 | This will configure the CLI to always treat `config/database.json` as config file and 21 | `db/migrate` as the directory for migrations. 22 | 23 | ### Configuration file 24 | 25 | By default the CLI will try to use the file `config/config.js` and `config/config.json`. You can modify that path either via the `--config` flag or via the option mentioned earlier. Here is how a configuration file might look like (this is the one that `sequelize init` generates): 26 | 27 | ```json 28 | { 29 | "development": { 30 | "username": "root", 31 | "password": null, 32 | "database": "database_development", 33 | "host": "127.0.0.1", 34 | "dialect": "mysql" 35 | }, 36 | "test": { 37 | "username": "root", 38 | "password": null, 39 | "database": "database_test", 40 | "host": "127.0.0.1", 41 | "dialect": "mysql" 42 | }, 43 | "production": { 44 | "username": "root", 45 | "password": null, 46 | "database": "database_production", 47 | "host": "127.0.0.1", 48 | "dialect": "mysql" 49 | } 50 | } 51 | ``` 52 | 53 | The properties can also be combined to a `url`: 54 | 55 | ```json 56 | { 57 | "development": { 58 | "url": "mysql://root:password@mysql_host.com/database_name", 59 | "dialect": "mysql" 60 | } 61 | } 62 | ``` 63 | 64 | In case of a JS file it obviously needs to `module.exports` the object. 65 | Optionally, it's possible to put all the configuration to the `url` option. The format is explained in the section below. 66 | 67 | ### Configuration Connection String 68 | 69 | As an alternative to the `--config` option with configuration files defining your database, you can 70 | use the `--url` option to pass in a connection string. For example: 71 | 72 | `sequelize db:migrate --url 'mysql://root:password@mysql_host.com/database_name'` 73 | 74 | ### Configuration Connection Environment Variable 75 | 76 | Another possibility is to store the URL in an environment variable and to tell 77 | the CLI to lookup a certain variable during runtime. Let's assume you have an 78 | environment variable called `DB_CONNECTION_STRING` which stores the value 79 | `mysql://root:password@mysql_host.com/database_name`. In order to make the CLI 80 | use it, you have to use declare it in your config file: 81 | 82 | ```json 83 | { 84 | "production": { 85 | "use_env_variable": "DB_CONNECTION_STRING" 86 | } 87 | } 88 | ``` 89 | 90 | With v2.0.0 of the CLI you can also just directly access the environment variables inside the `config/config.js`: 91 | 92 | ```js 93 | module.exports = { 94 | "production": { 95 | "hostname": process.env.DB_HOSTNAME 96 | } 97 | } 98 | ``` 99 | 100 | ### Configuration for connecting over SSL 101 | 102 | Ensure ssl is specified in both `dialectOptions` and in the base config. 103 | 104 | ``` 105 | { 106 | "production": { 107 | "use_env_variable":"DB_CONNECTION_STRING", 108 | "dialect":"postgres", 109 | "ssl": true, 110 | "dialectOptions": { 111 | "ssl": true 112 | } 113 | } 114 | } 115 | ``` 116 | 117 | ### Storage 118 | 119 | There are three types of storage that you can use: `sequelize`, `json`, and `none`. 120 | 121 | - `sequelize` : stores migrations and seeds in a table on the sequelize database 122 | - `json` : stores migrations and seeds on a json file 123 | - `none` : does not store any migration/seed 124 | 125 | 126 | #### Migration 127 | 128 | By default the CLI will create a table in your database called `SequelizeMeta` containing an entry 129 | for each executed migration. To change this behavior, there are three options you can add to the 130 | configuration file. Using `migrationStorage`, you can choose the type of storage to be used for 131 | migrations. If you choose `json`, you can specify the path of the file using `migrationStoragePath` 132 | or the CLI will write to the file `sequelize-meta.json`. If you want to keep the information in the 133 | database, using `sequelize`, but want to use a different table, you can change the table name using 134 | `migrationStorageTableName`. 135 | 136 | ```json 137 | { 138 | "development": { 139 | "username": "root", 140 | "password": null, 141 | "database": "database_development", 142 | "host": "127.0.0.1", 143 | "dialect": "mysql", 144 | 145 | // Use a different storage type. Default: sequelize 146 | "migrationStorage": "json", 147 | 148 | // Use a different file name. Default: sequelize-meta.json 149 | "migrationStoragePath": "sequelizeMeta.json", 150 | 151 | // Use a different table name. Default: SequelizeMeta 152 | "migrationStorageTableName": "sequelize_meta" 153 | } 154 | } 155 | ``` 156 | 157 | NOTE: The `none` storage is not recommended as a migration storage. If you decide to use it, be 158 | aware of the implications of having no record of what migrations did or didn't run. 159 | 160 | 161 | #### Seed 162 | 163 | By default the CLI will not save any seed that is executed. If you choose to change this behavior (!), 164 | you can use `seederStorage` in the configuration file to change the storage type. If you choose `json`, 165 | you can specify the path of the file using `seederStoragePath` or the CLI will write to the file 166 | `sequelize-data.json`. If you want to keep the information in the database, using `sequelize`, you can 167 | specify the table name using `seederStorageTableName`, or it will default to `SequelizeData`. 168 | 169 | ```json 170 | { 171 | "development": { 172 | "username": "root", 173 | "password": null, 174 | "database": "database_development", 175 | "host": "127.0.0.1", 176 | "dialect": "mysql", 177 | // Use a different storage. Default: none 178 | "seederStorage": "json", 179 | // Use a different file name. Default: sequelize-data.json 180 | "seederStoragePath": "sequelizeData.json", 181 | // Use a different table name. Default: SequelizeData 182 | "seederStorageTableName": "sequelize_data" 183 | } 184 | } 185 | ``` 186 | 187 | 188 | ### Dialect specific options 189 | 190 | In order to pass options to the underlying database connectors, you can add the property `dialectOptions` 191 | to your configuration like this: 192 | 193 | ```js 194 | var fs = require('fs'); 195 | 196 | module.exports = { 197 | development: { 198 | dialect: 'mysql', 199 | dialectOptions: { 200 | ssl: { 201 | ca: fs.readFileSync(__dirname + '/mysql-ca.crt') 202 | } 203 | } 204 | } 205 | }; 206 | ``` 207 | 208 | ### Schema migration 209 | 210 | Sequelize CLI continue to use schema from `v2` and fully compatible with `v2`. If you are still using old schema from pre `v2`, use `v2` to upgrade to current schema with `db:migrate:old_schema` 211 | 212 | #### Timestamps 213 | 214 | Since v2.8.0 the CLI supports a adding timestamps to the schema for saving the executed migrations. You can opt-in for timestamps by running the following command: 215 | 216 | ```bash 217 | $ sequelize db:migrate:schema:timestamps:add 218 | ``` 219 | 220 | ### The migration schema 221 | 222 | The CLI uses [umzug](https://github.com/sequelize/umzug) and its migration schema. This means a migration has to look like this: 223 | 224 | ```js 225 | "use strict"; 226 | 227 | module.exports = { 228 | up: function(queryInterface, Sequelize, done) { 229 | done(); 230 | }, 231 | 232 | down: function(queryInterface) { 233 | return new Promise(function (resolve, reject) { 234 | resolve(); 235 | }); 236 | } 237 | }; 238 | ``` 239 | 240 | Please note that you can either return a Promise or call the third argument of the function once your asynchronous logic was executed. If you pass something to the callback function (the `done` function) it will be treated as erroneous execution. 241 | 242 | Additional note: If you need to access the sequelize instance, you can easily do that via `queryInterface.sequelize`. For example `queryInterface.sequelize.query('CREATE TABLE mytable();')`. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sequelize-cli-typescript", 3 | "version": "3.2.0c", 4 | "description": "The Sequelize CLI (TypeScript)", 5 | "bin": { 6 | "sequelize": "./lib/sequelize" 7 | }, 8 | "dependencies": { 9 | "bluebird": "^3.5.1", 10 | "cli-color": "^1.2.0", 11 | "fs-extra": "^4.0.2", 12 | "js-beautify": "^1.7.4", 13 | "lodash": "^4.17.4", 14 | "resolve": "^1.5.0", 15 | "umzug": "^2.1.0", 16 | "yargs": "^8.0.2" 17 | }, 18 | "devDependencies": { 19 | "babel-cli": "^6.24.1", 20 | "babel-plugin-transform-async-to-module-method": "^6.24.1", 21 | "babel-plugin-transform-object-rest-spread": "^6.23.0", 22 | "babel-preset-env": "^1.6.1", 23 | "babel-register": "^6.24.1", 24 | "eslint": "^4.11.0", 25 | "expect.js": "^0.3.1", 26 | "gulp": "^3.9.1", 27 | "mocha": "^3.5.0", 28 | "mysql": "^2.15.0", 29 | "nodeify": "^1.0.1", 30 | "pg": "^6.*", 31 | "pg-hstore": "^2.3.2", 32 | "sequelize": "^4.*", 33 | "sqlite3": "^3.1.8", 34 | "through2": "^2.0.3" 35 | }, 36 | "options": { 37 | "mocha": "--require scripts/mocha-bootload.js --check-leaks --timeout 30000 --colors --reporter spec" 38 | }, 39 | "eslintIgnore": [ 40 | "test/support", 41 | "src/assets" 42 | ], 43 | "scripts": { 44 | "build": "npm run build-clean && babel src -d lib && npm run build-bin && npm run build-assets", 45 | "build-bin": "mv ./lib/sequelize.js ./lib/sequelize && chmod +x ./lib/sequelize", 46 | "build-assets": "cp -R ./src/assets ./lib/", 47 | "build-clean": "rm -rf ./lib/", 48 | "lint": "eslint test src", 49 | "test-raw": "mocha $npm_package_options_mocha 'test/**/*.test.js'", 50 | "test": "npm run lint && npm run build && npm run test-raw" 51 | }, 52 | "repository": { 53 | "type": "git", 54 | "url": "git://github.com/douglas-treadwell/sequelize-cli-typescript.git" 55 | }, 56 | "keywords": [ 57 | "sequelize", 58 | "cli", 59 | "typescript" 60 | ], 61 | "author": "Sascha Depold ", 62 | "contributors": [ 63 | { 64 | "name": "Sushant Dhiman", 65 | "email": "sushantdhiman@outlook.com" 66 | }, 67 | { 68 | "name": "Paulo R Lopes", 69 | "email": "prplopes@gmail.com" 70 | }, 71 | { 72 | "name": "Douglas Treadwell", 73 | "email": "douglas.treadwell@gmail.com" 74 | } 75 | ], 76 | "license": "MIT", 77 | "bugs": { 78 | "url": "https://github.com/douglas-treadwell/sequelize-cli-typescript/issues" 79 | }, 80 | "homepage": "https://github.com/douglas-treadwell/sequelize-cli-typescript", 81 | "engines": { 82 | "node": ">=4.0.0" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /scripts/mocha-bootload.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); -------------------------------------------------------------------------------- /src/assets/migrations/create-table.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, SequelizeStatic } from 'sequelize'; 2 | 3 | export = { 4 | up: (queryInterface: QueryInterface, Sequelize: SequelizeStatic) => { 5 | return queryInterface.createTable('<%= tableName %>', { 6 | id: { 7 | allowNull: false, 8 | autoIncrement: true, 9 | primaryKey: true, 10 | type: Sequelize.INTEGER 11 | }, 12 | 13 | <% attributes.forEach(function (attribute) { %> 14 | <%= attribute.fieldName %>: { 15 | type: Sequelize.<%= attribute.dataFunction ? `${attribute.dataFunction.toUpperCase()}(Sequelize.${attribute.dataType.toUpperCase()})` : attribute.dataType.toUpperCase() %> 16 | }, 17 | <% }) %> 18 | 19 | <%= createdAt %>: { 20 | allowNull: false, 21 | type: Sequelize.DATE 22 | }, 23 | 24 | <%= updatedAt %>: { 25 | allowNull: false, 26 | type: Sequelize.DATE 27 | } 28 | }); 29 | }, 30 | 31 | down: (queryInterface: QueryInterface, Sequelize: SequelizeStatic) => { 32 | return queryInterface.dropTable('<%= tableName %>'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /src/assets/migrations/skeleton.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, SequelizeStatic } from 'sequelize'; 2 | 3 | export = { 4 | up: (queryInterface: QueryInterface, Sequelize: SequelizeStatic) => { 5 | /* 6 | Add altering commands here. 7 | Return a promise to correctly handle asynchronicity. 8 | 9 | Example: 10 | return queryInterface.createTable('users', { id: Sequelize.INTEGER }); 11 | */ 12 | }, 13 | 14 | down: (queryInterface: QueryInterface, Sequelize: SequelizeStatic) => { 15 | /* 16 | Add reverting commands here. 17 | Return a promise to correctly handle asynchronicity. 18 | 19 | Example: 20 | return queryInterface.dropTable('users'); 21 | */ 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/assets/models/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var Sequelize = require('sequelize'); 6 | var basename = path.basename(__filename); 7 | var env = process.env.NODE_ENV || 'development'; 8 | var config = require(<%= configFile %>)[env]; 9 | var db = {}; 10 | 11 | if (config.use_env_variable) { 12 | var sequelize = new Sequelize(process.env[config.use_env_variable], config); 13 | } else { 14 | var sequelize = new Sequelize(config.database, config.username, config.password, config); 15 | } 16 | 17 | fs 18 | .readdirSync(__dirname) 19 | .filter(file => { 20 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); 21 | }) 22 | .forEach(file => { 23 | var model = sequelize['import'](path.join(__dirname, file)); 24 | db[model.name] = model; 25 | }); 26 | 27 | Object.keys(db).forEach(modelName => { 28 | if (db[modelName].associate) { 29 | db[modelName].associate(db); 30 | } 31 | }); 32 | 33 | db.sequelize = sequelize; 34 | db.Sequelize = Sequelize; 35 | 36 | module.exports = db; 37 | -------------------------------------------------------------------------------- /src/assets/models/model.ts: -------------------------------------------------------------------------------- 1 | import { Sequelize, DataTypes } from 'sequelize'; 2 | 3 | <% 4 | function getType(dataType) { 5 | dataType = dataType.toLowerCase(); 6 | 7 | if ( ['tinyint', 'smallint', 'mediumint', 'integer', 'bigint', 'float', 'double', 'decimal', 'real'].indexOf(dataType) !== -1 ) { 8 | return 'number;'; 9 | } 10 | 11 | if ( ['char', 'string', 'text', 'blob'].indexOf(dataType) !== -1 ) { 12 | return 'string;'; 13 | } 14 | 15 | if ( ['date'].indexOf(dataType) !== -1 ) { 16 | return 'Date;'; 17 | } 18 | 19 | if ( ['dateonly', 'time', 'now', 'json', 'jsonb'].indexOf(dataType) !== -1 ) { 20 | return 'string; // actually a ' + dataType + ' column'; 21 | } 22 | 23 | if ( ['enum'].indexOf(dataType) !== -1 ) { 24 | return "string; // replace with 'validValue1' | 'validValue2', ..."; 25 | } 26 | 27 | if ( ['boolean'].indexOf(dataType) !== -1 ) { 28 | return 'boolean;'; 29 | } 30 | 31 | if ( ['uuid', 'uuidv1', 'uuidv4'].indexOf(dataType) !== -1 ) { 32 | return 'string;'; 33 | } 34 | 35 | return dataType; 36 | } 37 | %> 38 | 39 | export interface <%= name[0].toUpperCase() + name.substr(1) %>Attributes { 40 | <% attributes.forEach(function(attribute) { 41 | %><%= attribute.fieldName %>?: <%= getType(attribute.dataType) %> 42 | <% 43 | }) %> 44 | } 45 | 46 | export interface <%= name[0].toUpperCase() + name.substr(1) %>Instance { 47 | id: number; 48 | createdAt: Date; 49 | updatedAt: Date; 50 | 51 | <% attributes.forEach(function(attribute) { 52 | %><%= attribute.fieldName %>: <%= getType(attribute.dataType) %> 53 | <% 54 | }) %> 55 | } 56 | 57 | export = (sequelize: Sequelize, DataTypes: DataTypes) => { 58 | var <%= name %> = sequelize.define('<%= name %>', { 59 | <% attributes.forEach(function(attribute, index) { %><%= attribute.fieldName %>: DataTypes.<%= attribute.dataFunction ? `${attribute.dataFunction.toUpperCase()}(DataTypes.${attribute.dataType.toUpperCase()})` : attribute.dataType.toUpperCase() %><%= (Object.keys(attributes).length - 1) > index ? ',' : '' %><% }) %> 60 | }<%= underscored ? ', { underscored: true }' : '' %>); 61 | 62 | <%= name %>.associate = function(models) { 63 | // associations can be defined here 64 | }; 65 | 66 | return <%= name %>; 67 | }; 68 | -------------------------------------------------------------------------------- /src/assets/seeders/skeleton.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | /* 6 | Add altering commands here. 7 | Return a promise to correctly handle asynchronicity. 8 | 9 | Example: 10 | return queryInterface.bulkInsert('Person', [{ 11 | name: 'John Doe', 12 | isBetaMember: false 13 | }], {}); 14 | */ 15 | }, 16 | 17 | down: (queryInterface, Sequelize) => { 18 | /* 19 | Add reverting commands here. 20 | Return a promise to correctly handle asynchronicity. 21 | 22 | Example: 23 | return queryInterface.bulkDelete('Person', null, {}); 24 | */ 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/commands/database.js: -------------------------------------------------------------------------------- 1 | import { _baseOptions } from '../core/yargs'; 2 | import { logMigrator } from '../core/migrator'; 3 | 4 | import helpers from '../helpers'; 5 | import { cloneDeep, defaults } from 'lodash'; 6 | import clc from 'cli-color'; 7 | 8 | const Sequelize = helpers.generic.getSequelize(); 9 | 10 | exports.builder = yargs => _baseOptions(yargs).help().argv; 11 | exports.handler = async function (args) { 12 | const command = args._[0]; 13 | 14 | // legacy, gulp used to do this 15 | await helpers.config.init(); 16 | 17 | const sequelize = getDatabaseLessSequelize(); 18 | const config = helpers.config.readConfig(); 19 | 20 | switch (command) { 21 | case 'db:create': 22 | await sequelize.query(`CREATE DATABASE ${sequelize.getQueryInterface().quoteIdentifier(config.database)}`, { 23 | type: sequelize.QueryTypes.RAW 24 | }).catch(e => helpers.view.error(e)); 25 | 26 | helpers.view.log( 27 | 'Database', 28 | clc.blueBright(config.database), 29 | 'created.' 30 | ); 31 | 32 | break; 33 | case 'db:drop': 34 | await sequelize.query(`DROP DATABASE ${sequelize.getQueryInterface().quoteIdentifier(config.database)}`, { 35 | type: sequelize.QueryTypes.RAW 36 | }).catch(e => helpers.view.error(e)); 37 | 38 | helpers.view.log( 39 | 'Database', 40 | clc.blueBright(config.database), 41 | 'dropped.' 42 | ); 43 | 44 | break; 45 | } 46 | 47 | process.exit(0); 48 | }; 49 | 50 | function getDatabaseLessSequelize () { 51 | let config = null; 52 | 53 | try { 54 | config = helpers.config.readConfig(); 55 | } catch (e) { 56 | helpers.view.error(e); 57 | } 58 | 59 | config = cloneDeep(config); 60 | config = defaults(config, { logging: logMigrator }); 61 | 62 | switch (config.dialect) { 63 | case 'postgres': 64 | case 'postgres-native': 65 | config.database = 'postgres'; 66 | break; 67 | 68 | case 'mysql': 69 | delete config.database; 70 | break; 71 | 72 | case 'mssql': 73 | config.database = 'master'; 74 | break; 75 | 76 | default: 77 | helpers.view.error(`Dialect ${config.dialect} does not support db:create / db:drop commands`); 78 | } 79 | 80 | try { 81 | return new Sequelize(config); 82 | } catch (e) { 83 | helpers.view.error(e); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/commands/init.js: -------------------------------------------------------------------------------- 1 | import { _baseOptions } from '../core/yargs'; 2 | import helpers from '../helpers'; 3 | 4 | exports.builder = yargs => _baseOptions(yargs) 5 | .option('force', { 6 | describe: 'Will drop the existing config folder and re-create it', 7 | type: 'boolean', 8 | default: false 9 | }) 10 | .help() 11 | .argv; 12 | 13 | exports.handler = async function (argv) { 14 | const command = argv._[0]; 15 | 16 | switch (command) { 17 | case 'init': 18 | await initConfig(argv); 19 | await initModels(argv); 20 | await initMigrations(argv); 21 | await initSeeders(argv); 22 | break; 23 | 24 | case 'init:config': 25 | await initConfig(argv); 26 | break; 27 | 28 | case 'init:models': 29 | await initModels(argv); 30 | break; 31 | 32 | case 'init:migrations': 33 | await initMigrations(argv); 34 | break; 35 | 36 | case 'init:seeders': 37 | await initSeeders(argv); 38 | break; 39 | } 40 | 41 | process.exit(0); 42 | }; 43 | 44 | function initConfig (args) { 45 | if (!helpers.config.configFileExists() || !!args.force) { 46 | helpers.config.writeDefaultConfig(); 47 | helpers.view.log('Created "' + helpers.config.relativeConfigFile() + '"'); 48 | } else { 49 | helpers.view.notifyAboutExistingFile(helpers.config.relativeConfigFile()); 50 | process.exit(1); 51 | } 52 | } 53 | 54 | function initModels (args) { 55 | helpers.init.createModelsFolder(!!args.force); 56 | helpers.init.createModelsIndexFile(!!args.force); 57 | } 58 | 59 | function initMigrations (args) { 60 | helpers.init.createMigrationsFolder(!!args.force); 61 | } 62 | 63 | function initSeeders (args) { 64 | helpers.init.createSeedersFolder(!!args.force); 65 | } -------------------------------------------------------------------------------- /src/commands/migrate.js: -------------------------------------------------------------------------------- 1 | import { _baseOptions } from '../core/yargs'; 2 | import { getMigrator, ensureCurrentMetaSchema, addTimestampsToSchema } from '../core/migrator'; 3 | 4 | import helpers from '../helpers'; 5 | import _ from 'lodash'; 6 | 7 | exports.builder = yargs => _baseOptions(yargs) 8 | .option('to', { 9 | describe: 'Migration name to run migrations until', 10 | type: 'string' 11 | }) 12 | .option('from', { 13 | describe: 'Migration name to start migrations from (excluding)', 14 | type: 'string' 15 | }) 16 | .help() 17 | .argv; 18 | exports.handler = async function (args) { 19 | const command = args._[0]; 20 | 21 | // legacy, gulp used to do this 22 | await helpers.config.init(); 23 | 24 | switch (command) { 25 | case 'db:migrate': 26 | await migrate(args); 27 | break; 28 | case 'db:migrate:schema:timestamps:add': 29 | await migrateSchemaTimestampAdd(args); 30 | break; 31 | case 'db:migrate:status': 32 | await migrationStatus(args); 33 | break; 34 | } 35 | 36 | process.exit(0); 37 | }; 38 | 39 | function migrate(args) { 40 | return getMigrator('migration', args).then(migrator => { 41 | return ensureCurrentMetaSchema(migrator) 42 | .then(() => migrator.pending()) 43 | .then(migrations => { 44 | const options = {}; 45 | if (migrations.length === 0) { 46 | helpers.view.log('No migrations were executed, database schema was already up to date.'); 47 | process.exit(0); 48 | } 49 | if (args.to) { 50 | if (migrations.filter(migration => migration.file === args.to).length === 0) { 51 | helpers.view.log('No migrations were executed, database schema was already up to date.'); 52 | process.exit(0); 53 | } 54 | options.to = args.to; 55 | } 56 | if (args.from) { 57 | if (migrations.map(migration => migration.file).lastIndexOf(args.from) === -1) { 58 | helpers.view.log('No migrations were executed, database schema was already up to date.'); 59 | process.exit(0); 60 | } 61 | options.from = args.from; 62 | } 63 | return options; 64 | }) 65 | .then(options => migrator.up(options)); 66 | }).catch(e => helpers.view.error(e)); 67 | } 68 | 69 | function migrationStatus(args) { 70 | return getMigrator('migration', args).then(migrator => { 71 | return ensureCurrentMetaSchema(migrator) 72 | .then(() => migrator.executed()) 73 | .then(migrations => { 74 | _.forEach(migrations, migration => { 75 | helpers.view.log('up', migration.file); 76 | }); 77 | }).then(() => migrator.pending()) 78 | .then(migrations => { 79 | _.forEach(migrations, migration => { 80 | helpers.view.log('down', migration.file); 81 | }); 82 | }); 83 | }).catch(e => helpers.view.error(e)); 84 | } 85 | 86 | function migrateSchemaTimestampAdd(args) { 87 | return getMigrator('migration', args).then(migrator => { 88 | return addTimestampsToSchema(migrator) 89 | .then(items => { 90 | if (items) { 91 | helpers.view.log('Successfully added timestamps to MetaTable.'); 92 | } else { 93 | helpers.view.log('MetaTable already has timestamps.'); 94 | } 95 | }); 96 | }).catch(e => helpers.view.error(e)); 97 | } 98 | -------------------------------------------------------------------------------- /src/commands/migrate_undo.js: -------------------------------------------------------------------------------- 1 | import { _baseOptions } from '../core/yargs'; 2 | import { getMigrator, ensureCurrentMetaSchema } from '../core/migrator'; 3 | 4 | import helpers from '../helpers'; 5 | 6 | exports.builder = 7 | yargs => 8 | _baseOptions(yargs) 9 | .option('name', { 10 | describe: 'Name of the migration to undo', 11 | type: 'string' 12 | }) 13 | .help() 14 | .argv; 15 | 16 | exports.handler = async function (args) { 17 | // legacy, gulp used to do this 18 | await helpers.config.init(); 19 | 20 | await migrateUndo(args); 21 | 22 | process.exit(0); 23 | }; 24 | 25 | function migrateUndo (args) { 26 | return getMigrator('migration', args).then(migrator => { 27 | return ensureCurrentMetaSchema(migrator).then(() => migrator.executed()) 28 | .then(migrations => { 29 | if (migrations.length === 0) { 30 | helpers.view.log('No executed migrations found.'); 31 | process.exit(0); 32 | } 33 | }) 34 | .then(() => { 35 | if (args.name) { 36 | return migrator.down(args.name); 37 | } else { 38 | return migrator.down(); 39 | } 40 | }); 41 | }).catch(e => helpers.view.error(e)); 42 | } -------------------------------------------------------------------------------- /src/commands/migrate_undo_all.js: -------------------------------------------------------------------------------- 1 | import { _baseOptions } from '../core/yargs'; 2 | import { getMigrator, ensureCurrentMetaSchema } from '../core/migrator'; 3 | 4 | import helpers from '../helpers'; 5 | 6 | exports.builder = 7 | yargs => 8 | _baseOptions(yargs) 9 | .option('to', { 10 | describe: 'Revert to the provided migration', 11 | default: 0, 12 | type: 'string' 13 | }) 14 | .help() 15 | .argv; 16 | 17 | exports.handler = async function (args) { 18 | // legacy, gulp used to do this 19 | await helpers.config.init(); 20 | 21 | await migrationUndoAll(args); 22 | 23 | process.exit(0); 24 | }; 25 | 26 | function migrationUndoAll (args) { 27 | return getMigrator('migration', args).then(migrator => { 28 | return ensureCurrentMetaSchema(migrator).then(() => migrator.executed()) 29 | .then(migrations => { 30 | if (migrations.length === 0) { 31 | helpers.view.log('No executed migrations found.'); 32 | process.exit(0); 33 | } 34 | }) 35 | .then(() => migrator.down({ to: args.to || 0 })); 36 | }).catch(e => helpers.view.error(e)); 37 | } -------------------------------------------------------------------------------- /src/commands/migration_generate.js: -------------------------------------------------------------------------------- 1 | import { _baseOptions, _underscoreOption } from '../core/yargs'; 2 | 3 | import helpers from '../helpers'; 4 | import fs from 'fs'; 5 | import clc from 'cli-color'; 6 | 7 | exports.builder = 8 | yargs => 9 | _underscoreOption( 10 | _baseOptions(yargs) 11 | .option('name', { 12 | describe: 'Defines the name of the migration', 13 | type: 'string', 14 | demandOption: true 15 | }) 16 | ) 17 | .help() 18 | .argv; 19 | 20 | exports.handler = function (args) { 21 | helpers.init.createMigrationsFolder(); 22 | 23 | fs.writeFileSync( 24 | helpers.path.getMigrationSourcePath(args.name), 25 | helpers.template.render('migrations/skeleton.ts', {}, { 26 | beautify: false 27 | }) 28 | ); 29 | 30 | helpers.view.log( 31 | 'New migration was created at', 32 | clc.blueBright(helpers.path.getMigrationSourcePath(args.name)), 33 | '.' 34 | ); 35 | 36 | process.exit(0); 37 | }; 38 | -------------------------------------------------------------------------------- /src/commands/model_generate.js: -------------------------------------------------------------------------------- 1 | import { _baseOptions, _underscoreOption } from '../core/yargs'; 2 | 3 | import helpers from '../helpers'; 4 | import clc from 'cli-color'; 5 | 6 | exports.builder = 7 | yargs => 8 | _underscoreOption( 9 | _baseOptions(yargs) 10 | .option('name', { 11 | describe: 'Defines the name of the new model', 12 | type: 'string', 13 | demandOption: true 14 | }) 15 | .option('attributes', { 16 | describe: 'A list of attributes', 17 | type: 'string', 18 | demandOption: true 19 | }) 20 | ) 21 | .help() 22 | .argv; 23 | 24 | exports.handler = function (args) { 25 | ensureModelsFolder(); 26 | ensureMigrationsFolder(); 27 | checkModelFileExistence(args); 28 | 29 | helpers.model.generateFile(args); 30 | helpers.migration.generateTableCreationFile(args); 31 | helpers.view.log( 32 | 'New model was created at', 33 | clc.blueBright(helpers.path.getModelPath(args.name)), 34 | '.' 35 | ); 36 | helpers.view.log( 37 | 'New migration was created at', 38 | clc.blueBright(helpers.path.getMigrationSourcePath(args.name)), 39 | '.' 40 | ); 41 | 42 | process.exit(0); 43 | }; 44 | 45 | function ensureModelsFolder () { 46 | if (!helpers.path.existsSync(helpers.path.getModelsPath())) { 47 | helpers.view.error( 48 | 'Unable to find models path (' + 49 | helpers.path.getModelsPath() + 50 | '). Did you run ' + clc.blueBright('sequelize init') + '?' 51 | ); 52 | } 53 | } 54 | 55 | function ensureMigrationsFolder () { 56 | if (!helpers.path.existsSync(helpers.path.getMigrationsSourcePath())) { 57 | helpers.view.error( 58 | 'Unable to find migrations (source) path (' + 59 | helpers.path.getMigrationsSourcePath() + 60 | '). Did you run ' + clc.blueBright('sequelize init') + '?' 61 | ); 62 | } 63 | 64 | if (!helpers.path.existsSync(helpers.path.getMigrationsCompiledPath())) { 65 | helpers.view.error( 66 | 'Unable to find migrations (compiled) path (' + 67 | helpers.path.getMigrationsCompiledPath() + 68 | '). Did you compile your migrations?' 69 | ); 70 | } 71 | } 72 | 73 | function checkModelFileExistence (args) { 74 | const modelPath = helpers.path.getModelPath(args.name); 75 | 76 | if (!args.force && helpers.model.modelFileExists(modelPath)) { 77 | helpers.view.notifyAboutExistingFile(modelPath); 78 | process.exit(1); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/commands/seed.js: -------------------------------------------------------------------------------- 1 | import { _baseOptions } from '../core/yargs'; 2 | import { getMigrator } from '../core/migrator'; 3 | 4 | import helpers from '../helpers'; 5 | import _ from 'lodash'; 6 | 7 | exports.builder = yargs => _baseOptions(yargs).help().argv; 8 | exports.handler = async function (args) { 9 | const command = args._[0]; 10 | 11 | // legacy, gulp used to do this 12 | await helpers.config.init(); 13 | 14 | switch (command) { 15 | case 'db:seed:all': 16 | await seedAll(args); 17 | break; 18 | 19 | case 'db:seed:undo:all': 20 | await seedUndoAll(args); 21 | break; 22 | } 23 | 24 | process.exit(0); 25 | }; 26 | 27 | function seedAll (args) { 28 | return getMigrator('seeder', args).then(migrator => { 29 | return migrator.pending() 30 | .then(seeders => { 31 | if (seeders.length === 0) { 32 | helpers.view.log('No seeders found.'); 33 | return; 34 | } 35 | 36 | return migrator.up({ migrations: _.chain(seeders).map('file').value() }); 37 | }); 38 | }).catch(e => helpers.view.error(e)); 39 | } 40 | 41 | function seedUndoAll (args) { 42 | return getMigrator('seeder', args).then(migrator => { 43 | return ( 44 | helpers.umzug.getStorage('seeder') === 'none' ? migrator.pending() : migrator.executed() 45 | ) 46 | .then(seeders => { 47 | if (seeders.length === 0) { 48 | helpers.view.log('No seeders found.'); 49 | return; 50 | } 51 | 52 | return migrator.down({ migrations: _.chain(seeders).map('file').reverse().value() }); 53 | }); 54 | }).catch(e => helpers.view.error(e)); 55 | } 56 | -------------------------------------------------------------------------------- /src/commands/seed_generate.js: -------------------------------------------------------------------------------- 1 | import { _baseOptions } from '../core/yargs'; 2 | 3 | import helpers from '../helpers'; 4 | import fs from 'fs'; 5 | import clc from 'cli-color'; 6 | 7 | exports.builder = 8 | yargs => 9 | _baseOptions(yargs) 10 | .option('name', { 11 | describe: 'Defines the name of the seed', 12 | type: 'string', 13 | demandOption: true 14 | }) 15 | .help() 16 | .argv; 17 | 18 | exports.handler = function (args) { 19 | helpers.init.createSeedersFolder(); 20 | 21 | fs.writeFileSync( 22 | helpers.path.getSeederPath(args.name), 23 | helpers.template.render('seeders/skeleton.js', {}, { 24 | beautify: false 25 | }) 26 | ); 27 | 28 | helpers.view.log( 29 | 'New seed was created at', 30 | clc.blueBright(helpers.path.getSeederPath(args.name)), 31 | '.' 32 | ); 33 | 34 | process.exit(0); 35 | }; 36 | -------------------------------------------------------------------------------- /src/commands/seed_one.js: -------------------------------------------------------------------------------- 1 | import { _baseOptions } from '../core/yargs'; 2 | import { getMigrator } from '../core/migrator'; 3 | 4 | import helpers from '../helpers'; 5 | import path from 'path'; 6 | 7 | exports.builder = 8 | yargs => 9 | _baseOptions(yargs) 10 | .option('seed', { 11 | describe: 'List of seed files', 12 | type: 'array' 13 | }) 14 | .help() 15 | .argv; 16 | 17 | exports.handler = async function (args) { 18 | const command = args._[0]; 19 | 20 | // legacy, gulp used to do this 21 | await helpers.config.init(); 22 | 23 | // filter out cmd names 24 | // for case like --seeders-path seeders --seed seedPerson.js db:seed 25 | const seeds= (args.seed || []) 26 | .filter(name => name !== 'db:seed' && name !== 'db:seed:undo') 27 | .map(file => path.basename(file)); 28 | 29 | 30 | switch (command) { 31 | case 'db:seed': 32 | await getMigrator('seeder', args).then(migrator => { 33 | return migrator.up(seeds); 34 | }).catch(e => helpers.view.error(e)); 35 | break; 36 | 37 | case 'db:seed:undo': 38 | await getMigrator('seeder', args).then(migrator => { 39 | return migrator.down({ migrations: seeds }); 40 | }).catch(e => helpers.view.error(e)); 41 | break; 42 | } 43 | 44 | process.exit(0); 45 | }; 46 | 47 | -------------------------------------------------------------------------------- /src/core/migrator.js: -------------------------------------------------------------------------------- 1 | import Umzug from 'umzug'; 2 | import Bluebird from 'bluebird'; 3 | import _ from 'lodash'; 4 | 5 | import helpers from '../helpers/index'; 6 | 7 | const Sequelize = helpers.generic.getSequelize(); 8 | 9 | export function logMigrator (s) { 10 | if (s.indexOf('Executing') !== 0) { 11 | helpers.view.log(s); 12 | } 13 | } 14 | 15 | function getSequelizeInstance () { 16 | let config = null; 17 | 18 | try { 19 | config = helpers.config.readConfig(); 20 | } catch (e) { 21 | helpers.view.error(e); 22 | } 23 | 24 | config = _.defaults(config, { logging: logMigrator }); 25 | 26 | try { 27 | return new Sequelize(config); 28 | } catch (e) { 29 | helpers.view.error(e); 30 | } 31 | } 32 | 33 | export function getMigrator (type, args) { 34 | return Bluebird.try(() => { 35 | if (!(helpers.config.configFileExists() || args.url)) { 36 | helpers.view.error( 37 | 'Cannot find "' + helpers.config.getConfigFile() + 38 | '". Have you run "sequelize init"?' 39 | ); 40 | process.exit(1); 41 | } 42 | 43 | let migratorPath = helpers.path.getPath(type); 44 | 45 | if ( type === 'migration' ) { 46 | migratorPath = helpers.path.getMigrationsCompiledPath(); 47 | } 48 | 49 | const sequelize = getSequelizeInstance(); 50 | const migrator = new Umzug({ 51 | storage: helpers.umzug.getStorage(type), 52 | storageOptions: helpers.umzug.getStorageOptions(type, { sequelize }), 53 | logging: helpers.view.log, 54 | migrations: { 55 | params: [sequelize.getQueryInterface(), Sequelize], 56 | path: migratorPath, 57 | pattern: /\.js$/, 58 | wrap: fun => { 59 | if (fun.length === 3) { 60 | return Bluebird.promisify(fun); 61 | } else { 62 | return fun; 63 | } 64 | } 65 | } 66 | }); 67 | 68 | return sequelize 69 | .authenticate() 70 | .then(() => migrator) 71 | .catch(e => helpers.view.error(e)); 72 | }); 73 | } 74 | 75 | export function ensureCurrentMetaSchema (migrator) { 76 | const queryInterface = migrator.options.storageOptions.sequelize.getQueryInterface(); 77 | const tableName = migrator.options.storageOptions.tableName; 78 | const columnName = migrator.options.storageOptions.columnName; 79 | 80 | return ensureMetaTable(queryInterface, tableName) 81 | .then(table => { 82 | const columns = Object.keys(table); 83 | 84 | if (columns.length === 1 && columns[0] === columnName) { 85 | return; 86 | } else if (columns.length === 3 && columns.indexOf('createdAt') >= 0) { 87 | return; 88 | } 89 | }) 90 | .catch(() => {}); 91 | } 92 | 93 | function ensureMetaTable (queryInterface, tableName) { 94 | return queryInterface.showAllTables() 95 | .then(tableNames => { 96 | if (tableNames.indexOf(tableName) === -1) { 97 | throw new Error('No MetaTable table found.'); 98 | } 99 | return queryInterface.describeTable(tableName); 100 | }); 101 | } 102 | 103 | /** 104 | * Add timestamps 105 | * 106 | * @return {Promise} 107 | */ 108 | export function addTimestampsToSchema (migrator) { 109 | const sequelize = migrator.options.storageOptions.sequelize; 110 | const queryInterface = sequelize.getQueryInterface(); 111 | const tableName = migrator.options.storageOptions.tableName; 112 | 113 | return ensureMetaTable(queryInterface, tableName) 114 | .then(table => { 115 | if (table.createdAt) { 116 | return; 117 | } 118 | 119 | return ensureCurrentMetaSchema(migrator) 120 | .then(() => queryInterface.renameTable(tableName, tableName + 'Backup')) 121 | .then(() => { 122 | const sql = queryInterface.QueryGenerator.selectQuery(tableName + 'Backup'); 123 | return helpers.generic.execQuery(sequelize, sql, { type: 'SELECT', raw: true }); 124 | }) 125 | .then(result => { 126 | const SequelizeMeta = sequelize.define(tableName, { 127 | name: { 128 | type: Sequelize.STRING, 129 | allowNull: false, 130 | unique: true, 131 | primaryKey: true, 132 | autoIncrement: false 133 | } 134 | }, { 135 | tableName, 136 | timestamps: true 137 | }); 138 | 139 | return SequelizeMeta.sync() 140 | .then(() => { 141 | return SequelizeMeta.bulkCreate(result); 142 | }); 143 | }); 144 | }); 145 | } 146 | -------------------------------------------------------------------------------- /src/core/yargs.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import yargs from 'yargs'; 3 | import path from 'path'; 4 | 5 | function loadRCFile(optionsPath) { 6 | const rcFile = optionsPath || path.resolve(process.cwd(), '.sequelizerc'); 7 | const rcFileResolved = path.resolve(rcFile); 8 | return fs.existsSync(rcFileResolved) 9 | ? JSON.parse(JSON.stringify(require(rcFileResolved))) 10 | : {}; 11 | } 12 | 13 | const args = yargs 14 | .config(loadRCFile(yargs.argv.optionsPath)); 15 | 16 | export default function getYArgs () { 17 | return args; 18 | } 19 | 20 | export function _baseOptions (yargs) { 21 | return yargs 22 | .option('env', { 23 | describe: 'The environment to run the command in', 24 | default: 'development', 25 | type: 'string' 26 | }) 27 | .option('config', { 28 | describe: 'The path to the config file', 29 | type: 'string' 30 | }) 31 | .option('options-path', { 32 | describe: 'The path to a JSON file with additional options', 33 | type: 'string' 34 | }) 35 | .option('migrations-source-path', { 36 | describe: 'The path to the migrations (source) folder', 37 | default: 'migrations', 38 | type: 'string' 39 | }) 40 | .option('migrations-compiled-path', { 41 | describe: 'The path to the migrations (compiled) folder', 42 | default: 'migrations/compiled', 43 | type: 'string' 44 | }) 45 | .option('seeders-path', { 46 | describe: 'The path to the seeders folder', 47 | default: 'seeders', 48 | type: 'string' 49 | }) 50 | .option('models-path', { 51 | describe: 'The path to the models folder', 52 | default: 'models', 53 | type: 'string' 54 | }) 55 | .option('url', { 56 | describe: 'The database connection string to use. Alternative to using --config files', 57 | type: 'string' 58 | }) 59 | .option('debug', { 60 | describe: 'When available show various debug information', 61 | default: false, 62 | type: 'boolean' 63 | }); 64 | } 65 | 66 | export function _underscoreOption (yargs) { 67 | return yargs 68 | .option('underscored', { 69 | describe: "Use snake case for the timestamp's attribute names", 70 | default: false, 71 | type: 'boolean' 72 | }); 73 | } 74 | -------------------------------------------------------------------------------- /src/helpers/asset-helper.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | import path from 'path'; 3 | 4 | const assets = { 5 | copy: (from, to) => { 6 | fs.copySync(path.resolve(__dirname, '..', 'assets', from), to); 7 | }, 8 | 9 | read: assetPath => { 10 | return fs.readFileSync(path.resolve(__dirname, '..', 'assets', assetPath)).toString(); 11 | }, 12 | 13 | write: (targetPath, content) => { 14 | fs.writeFileSync(targetPath, content); 15 | }, 16 | 17 | inject: (filePath, token, content) => { 18 | const fileContent = fs.readFileSync(filePath).toString(); 19 | fs.writeFileSync(filePath, fileContent.replace(token, content)); 20 | }, 21 | 22 | injectConfigFilePath: (filePath, configPath) => { 23 | this.inject(filePath, '__CONFIG_FILE__', configPath); 24 | }, 25 | 26 | mkdirp: pathToCreate => { 27 | fs.mkdirpSync(pathToCreate); 28 | } 29 | }; 30 | 31 | module.exports = assets; 32 | module.exports.default = assets; -------------------------------------------------------------------------------- /src/helpers/config-helper.js: -------------------------------------------------------------------------------- 1 | import Bluebird from 'bluebird'; 2 | import path from 'path'; 3 | import fs from 'fs'; 4 | import url from 'url'; 5 | import _ from 'lodash'; 6 | import helpers from './index'; 7 | import getYArgs from '../core/yargs'; 8 | 9 | const args = getYArgs().argv; 10 | 11 | const api = { 12 | config: undefined, 13 | rawConfig: undefined, 14 | error: undefined, 15 | init () { 16 | return Bluebird.resolve() 17 | .then(() => { 18 | let config; 19 | 20 | if (args.url) { 21 | config = api.parseDbUrl(args.url); 22 | } else { 23 | try { 24 | config = require(api.getConfigFile()); 25 | } catch (e) { 26 | api.error = e; 27 | } 28 | } 29 | return config; 30 | }) 31 | .then(config => { 32 | if (typeof config === 'object' || config === undefined) { 33 | return config; 34 | } else if (config.length === 1) { 35 | return Bluebird.promisify(config)(); 36 | } else { 37 | return config(); 38 | } 39 | }) 40 | .then(config => { 41 | api.rawConfig = config; 42 | }) 43 | .then(() => { 44 | // Always return the full config api 45 | return api; 46 | }); 47 | }, 48 | getConfigFile () { 49 | if (args.config) { 50 | return path.resolve(process.cwd(), args.config); 51 | } 52 | 53 | const defaultPath = path.resolve(process.cwd(), 'config', 'config.json'); 54 | const alternativePath = defaultPath.replace('.json', '.js'); 55 | 56 | return helpers.path.existsSync(alternativePath) ? alternativePath : defaultPath; 57 | }, 58 | 59 | relativeConfigFile () { 60 | return path.relative(process.cwd(), api.getConfigFile()); 61 | }, 62 | 63 | configFileExists () { 64 | return helpers.path.existsSync(api.getConfigFile()); 65 | }, 66 | 67 | getDefaultConfig () { 68 | return JSON.stringify({ 69 | development: { 70 | username: 'root', 71 | password: null, 72 | database: 'database_development', 73 | host: '127.0.0.1', 74 | dialect: 'mysql' 75 | }, 76 | test: { 77 | username: 'root', 78 | password: null, 79 | database: 'database_test', 80 | host: '127.0.0.1', 81 | dialect: 'mysql' 82 | }, 83 | production: { 84 | username: 'root', 85 | password: null, 86 | database: 'database_production', 87 | host: '127.0.0.1', 88 | dialect: 'mysql' 89 | } 90 | }, undefined, 2) + '\n'; 91 | }, 92 | 93 | writeDefaultConfig () { 94 | const configPath = path.dirname(api.getConfigFile()); 95 | 96 | if (!helpers.path.existsSync(configPath)) { 97 | helpers.asset.mkdirp(configPath); 98 | } 99 | 100 | fs.writeFileSync(api.getConfigFile(), api.getDefaultConfig()); 101 | }, 102 | 103 | readConfig () { 104 | if (!api.config) { 105 | const env = helpers.generic.getEnvironment(); 106 | 107 | if (api.rawConfig === undefined) { 108 | throw new Error( 109 | 'Error reading "' + 110 | api.relativeConfigFile() + 111 | '". Error: ' + api.error 112 | ); 113 | } 114 | 115 | if (typeof api.rawConfig !== 'object') { 116 | throw new Error( 117 | 'Config must be an object or a promise for an object: ' + 118 | api.relativeConfigFile() 119 | ); 120 | } 121 | 122 | if (args.url) { 123 | helpers.view.log('Parsed url ' + api.filteredUrl(args.url, api.rawConfig)); 124 | } else { 125 | helpers.view.log('Loaded configuration file "' + api.relativeConfigFile() + '".'); 126 | } 127 | 128 | if (api.rawConfig[env]) { 129 | helpers.view.log('Using environment "' + env + '".'); 130 | 131 | api.rawConfig = api.rawConfig[env]; 132 | } 133 | 134 | // The Sequelize library needs a function passed in to its logging option 135 | if (api.rawConfig.logging && !_.isFunction(api.rawConfig.logging)) { 136 | api.rawConfig.logging = console.log; 137 | } 138 | 139 | // in case url is present - we overwrite the configuration 140 | if (api.rawConfig.url) { 141 | api.rawConfig = _.merge(api.rawConfig, api.parseDbUrl(api.rawConfig.url)); 142 | } else if (api.rawConfig.use_env_variable) { 143 | api.rawConfig = _.merge( 144 | api.rawConfig, 145 | api.parseDbUrl(process.env[api.rawConfig.use_env_variable]) 146 | ); 147 | } 148 | 149 | api.config = api.rawConfig; 150 | } 151 | return api.config; 152 | }, 153 | 154 | filteredUrl (uri, config) { 155 | const regExp = new RegExp(':?' + (config.password || '') + '@'); 156 | return uri.replace(regExp, ':*****@'); 157 | }, 158 | 159 | urlStringToConfigHash (urlString) { 160 | try { 161 | const urlParts = url.parse(urlString); 162 | let result = { 163 | database: urlParts.pathname.replace(/^\//, ''), 164 | host: urlParts.hostname, 165 | port: urlParts.port, 166 | protocol: urlParts.protocol.replace(/:$/, ''), 167 | ssl: urlParts.query ? urlParts.query.indexOf('ssl=true') >= 0 : false 168 | }; 169 | 170 | if (urlParts.auth) { 171 | result = _.assign(result, { 172 | username: urlParts.auth.split(':')[0], 173 | password: urlParts.auth.split(':')[1] 174 | }); 175 | } 176 | 177 | return result; 178 | } catch (e) { 179 | throw new Error('Error parsing url: ' + urlString); 180 | } 181 | }, 182 | 183 | parseDbUrl (urlString) { 184 | let config = api.urlStringToConfigHash(urlString); 185 | 186 | config = _.assign(config, { 187 | dialect: config.protocol 188 | }); 189 | 190 | if (config.dialect === 'sqlite' && config.database.indexOf(':memory') !== 0) { 191 | config = _.assign(config, { 192 | storage: '/' + config.database 193 | }); 194 | } 195 | 196 | return config; 197 | } 198 | }; 199 | 200 | module.exports = api; 201 | -------------------------------------------------------------------------------- /src/helpers/generic-helper.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | const resolve = require('resolve').sync; 4 | import getYArgs from '../core/yargs'; 5 | 6 | const args = getYArgs().argv; 7 | 8 | const generic = { 9 | getEnvironment: () => { 10 | return args.env || process.env.NODE_ENV || 'development'; 11 | }, 12 | 13 | getSequelize: file => { 14 | const resolvePath = file ? path.join('sequelize', file) : 'sequelize'; 15 | const resolveOptions = { basedir: process.cwd() }; 16 | 17 | let sequelizePath; 18 | 19 | try { 20 | sequelizePath = require.resolve(resolvePath, resolveOptions); 21 | } catch (e) { 22 | } 23 | 24 | try { 25 | sequelizePath = sequelizePath || resolve(resolvePath, resolveOptions); 26 | } catch (e) { 27 | console.error('Unable to resolve sequelize package in ' + process.cwd()); 28 | process.exit(1); 29 | } 30 | 31 | return require(sequelizePath); 32 | }, 33 | 34 | execQuery: (sequelize, sql, options) => { 35 | if (sequelize.query.length === 2) { 36 | return sequelize.query(sql, options); 37 | } else { 38 | return sequelize.query(sql, null, options); 39 | } 40 | } 41 | }; 42 | 43 | module.exports = generic; 44 | module.exports.default = generic; 45 | -------------------------------------------------------------------------------- /src/helpers/index.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs'; 3 | 4 | module.exports = {}; 5 | 6 | fs 7 | .readdirSync(__dirname) 8 | .filter(file => file.indexOf('.') !== 0 && file.indexOf('index.js') === -1) 9 | .forEach(file => { 10 | module.exports[file.replace('-helper.js', '')] = require(path.resolve(__dirname, file)); 11 | }); 12 | 13 | module.exports.default = module.exports; 14 | -------------------------------------------------------------------------------- /src/helpers/init-helper.js: -------------------------------------------------------------------------------- 1 | import helpers from './index'; 2 | import path from 'path'; 3 | import fs from 'fs'; 4 | 5 | function createFolder (folderName, folder, force) { 6 | if (force && fs.existsSync(folder) === true) { 7 | helpers.view.log('Deleting the ' + folderName + ' folder. (--force)'); 8 | 9 | try { 10 | fs.readdirSync(folder).forEach(filename => { 11 | fs.unlinkSync(path.resolve(folder, filename)); 12 | }); 13 | } catch (e) { 14 | helpers.view.error(e); 15 | } 16 | 17 | try { 18 | fs.rmdirSync(folder); 19 | helpers.view.log('Successfully deleted the ' + folderName + ' folder.'); 20 | } catch (e) { 21 | helpers.view.error(e); 22 | } 23 | } 24 | 25 | try { 26 | if (fs.existsSync(folder) === false) { 27 | helpers.asset.mkdirp(folder); 28 | helpers.view.log('Successfully created ' + folderName + ' folder at "' + folder + '".'); 29 | } else { 30 | helpers.view.log(folderName + ' folder at "' + folder + '" already exists.'); 31 | } 32 | } catch (e) { 33 | helpers.view.error(e); 34 | } 35 | }; 36 | 37 | const init = { 38 | createMigrationsFolder: force => { 39 | createFolder('migrations', helpers.path.getMigrationsSourcePath(), force); 40 | }, 41 | 42 | createSeedersFolder: force => { 43 | createFolder('seeders', helpers.path.getPath('seeder'), force); 44 | }, 45 | 46 | createModelsFolder: force => { 47 | createFolder('models', helpers.path.getModelsPath(), force); 48 | }, 49 | 50 | createModelsIndexFile: force => { 51 | const modelsPath = helpers.path.getModelsPath(); 52 | const indexPath = path.resolve( 53 | modelsPath, 54 | helpers.path.addFileExtension('index') 55 | ); 56 | 57 | if (!helpers.path.existsSync(modelsPath)) { 58 | helpers.view.log('Models folder not available.'); 59 | } else if (helpers.path.existsSync(indexPath) && !force) { 60 | helpers.view.notifyAboutExistingFile(indexPath); 61 | } else { 62 | const relativeConfigPath = path.relative( 63 | helpers.path.getModelsPath(), 64 | helpers.config.getConfigFile() 65 | ); 66 | 67 | helpers.asset.write( 68 | indexPath, 69 | helpers.template.render('models/index.js', { 70 | configFile: '__dirname + \'/' + relativeConfigPath + '\'' 71 | }, { 72 | beautify: false 73 | }) 74 | ); 75 | } 76 | } 77 | }; 78 | 79 | module.exports = init; 80 | module.exports.default = init; 81 | -------------------------------------------------------------------------------- /src/helpers/migration-helper.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import helpers from './index'; 3 | 4 | const Sequelize = helpers.generic.getSequelize(); 5 | 6 | module.exports = { 7 | getTableName (modelName) { 8 | return Sequelize.Utils.pluralize(modelName); 9 | }, 10 | 11 | generateTableCreationFileContent (args) { 12 | return helpers.template.render('migrations/create-table.ts', { 13 | tableName: this.getTableName(args.name), 14 | attributes: helpers.model.transformAttributes(args.attributes), 15 | createdAt: args.underscored ? 'created_at' : 'createdAt', 16 | updatedAt: args.underscored ? 'updated_at' : 'updatedAt' 17 | }); 18 | }, 19 | 20 | generateMigrationName (args) { 21 | return _.trimStart(_.kebabCase('create-' + args.name), '-'); 22 | }, 23 | 24 | generateTableCreationFile (args) { 25 | const migrationName = this.generateMigrationName(args); 26 | const migrationPath = helpers.path.getMigrationSourcePath(migrationName); 27 | 28 | helpers.asset.write(migrationPath, this.generateTableCreationFileContent(args)); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/helpers/model-helper.js: -------------------------------------------------------------------------------- 1 | import helpers from './index'; 2 | 3 | const validAttributeFunctionType = 'array'; 4 | 5 | function formatAttributes (attribute) { 6 | let result; 7 | const split = attribute.split(':'); 8 | 9 | if (split.length === 2) { 10 | result = { fieldName: split[0], dataType: split[1], dataFunction: null }; 11 | } else if (split.length === 3) { 12 | const isValidFunction = validAttributeFunctionType === split[1].toLowerCase(); 13 | const isValidValue = validAttributeFunctionType !== split[2].toLowerCase(); 14 | 15 | if (isValidFunction && isValidValue) { 16 | result = { fieldName: split[0], dataType: split[2], dataFunction: split[1] }; 17 | } 18 | } 19 | return result; 20 | } 21 | 22 | module.exports = { 23 | transformAttributes (flag) { 24 | /* 25 | possible flag formats: 26 | - first_name:string,last_name:string,bio:text,reviews:array:string 27 | - 'first_name:string last_name:string bio:text reviews:array:string' 28 | - 'first_name:string, last_name:string, bio:text, reviews:array:string' 29 | */ 30 | 31 | const set = flag.replace(/,/g, ' ').split(/\s+/); 32 | const result = []; 33 | 34 | set.forEach(attribute => { 35 | const formattedAttribute = formatAttributes(attribute); 36 | 37 | if (formattedAttribute) { 38 | result.push(formattedAttribute); 39 | } 40 | }); 41 | 42 | return result; 43 | }, 44 | 45 | generateFileContent (args) { 46 | return helpers.template.render('models/model.ts', { 47 | name: args.name, 48 | attributes: this.transformAttributes(args.attributes), 49 | underscored: args.underscored 50 | }); 51 | }, 52 | 53 | generateFile (args) { 54 | const modelPath = helpers.path.getModelPath(args.name); 55 | 56 | helpers.asset.write(modelPath, this.generateFileContent(args)); 57 | }, 58 | 59 | modelFileExists (filePath) { 60 | return helpers.path.existsSync(filePath); 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /src/helpers/path-helper.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs'; 3 | 4 | const resolve = require('resolve').sync; 5 | import getYArgs from '../core/yargs'; 6 | 7 | const args = getYArgs().argv; 8 | 9 | function format (i) { 10 | return parseInt(i, 10) < 10 ? '0' + i : i; 11 | }; 12 | 13 | function getCurrentYYYYMMDDHHmms () { 14 | const date = new Date(); 15 | return [ 16 | date.getUTCFullYear(), 17 | format(date.getUTCMonth() + 1), 18 | format(date.getUTCDate()), 19 | format(date.getUTCHours()), 20 | format(date.getUTCMinutes()), 21 | format(date.getUTCSeconds()) 22 | ].join(''); 23 | } 24 | 25 | module.exports = { 26 | getPath (type) { 27 | type = type + 's'; 28 | 29 | let result = args[type + 'Path'] || path.resolve(process.cwd(), type); 30 | 31 | if (path.normalize(result) !== path.resolve(result)) { 32 | // the path is relative 33 | result = path.resolve(process.cwd(), result); 34 | } 35 | 36 | return result; 37 | }, 38 | 39 | getFileName (type, name, options) { 40 | return this.addFileExtension( 41 | [ 42 | getCurrentYYYYMMDDHHmms(), 43 | name ? name : 'unnamed-' + type 44 | ].join('-'), 45 | options 46 | ); 47 | }, 48 | 49 | getFileExtension () { 50 | return 'ts'; 51 | }, 52 | 53 | addFileExtension (basename, options) { 54 | return [basename, this.getFileExtension(options)].join('.'); 55 | }, 56 | 57 | getMigrationSourcePath (migrationName) { 58 | return path.resolve(this.getMigrationsSourcePath(), this.getFileName('migration', migrationName)); 59 | }, 60 | 61 | getMigrationCompiledPath (migrationName) { 62 | return path.resolve(this.getMigrationsCompiledPath(), this.getFileName('migration', migrationName)); 63 | }, 64 | 65 | getMigrationsSourcePath() { 66 | return args.migrationsSourcePath || path.resolve(process.cwd(), 'migrations'); 67 | }, 68 | 69 | getMigrationsCompiledPath() { 70 | return args.migrationsCompiledPath || path.resolve(process.cwd(), 'migrations/compiled'); 71 | }, 72 | 73 | getSeederPath (seederName) { 74 | return path.resolve(this.getPath('seeder'), this.getFileName('seeder', seederName)); 75 | }, 76 | 77 | getModelsPath () { 78 | return args.modelsPath || path.resolve(process.cwd(), 'models'); 79 | }, 80 | 81 | getModelPath (modelName) { 82 | return path.resolve( 83 | this.getModelsPath(), 84 | this.addFileExtension(modelName.toLowerCase()) 85 | ); 86 | }, 87 | 88 | resolve (packageName) { 89 | let result; 90 | 91 | try { 92 | result = resolve(packageName, { basedir: process.cwd() }); 93 | result = require(result); 94 | } catch (e) { 95 | try { 96 | result = require(packageName); 97 | } catch (err) {} 98 | } 99 | 100 | return result; 101 | }, 102 | 103 | existsSync (pathToCheck) { 104 | if (fs.accessSync) { 105 | try { 106 | fs.accessSync(pathToCheck, fs.R_OK); 107 | return true; 108 | } catch (e) { 109 | return false; 110 | } 111 | } else { 112 | return fs.existsSync(pathToCheck); 113 | } 114 | } 115 | }; 116 | -------------------------------------------------------------------------------- /src/helpers/template-helper.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import beautify from 'js-beautify'; 3 | import helpers from './index'; 4 | 5 | module.exports = { 6 | render (path, locals, options) { 7 | options = _.assign({ 8 | beautify: true, 9 | indent_size: 4, 10 | preserve_newlines: true, 11 | max_preserve_newlines: 2, 12 | end_with_newline: true 13 | }, options || {}); 14 | 15 | const template = helpers.asset.read(path); 16 | let content = _.template(template)(locals || {}); 17 | 18 | if (options.beautify) { 19 | content = beautify(content, options); 20 | } 21 | 22 | return content; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/helpers/umzug-helper.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import _ from 'lodash'; 3 | import helpers from './index'; 4 | 5 | const storage = { 6 | migration: 'sequelize', 7 | seeder: 'none' 8 | }; 9 | const storageTableName = { 10 | migration: 'SequelizeMeta', 11 | seeder: 'SequelizeData' 12 | }; 13 | const storageJsonName = { 14 | migration: 'sequelize-meta.json', 15 | seeder: 'sequelize-data.json' 16 | }; 17 | 18 | module.exports = { 19 | getStorageOption (property, fallback) { 20 | return helpers.config.readConfig()[property] || fallback; 21 | }, 22 | 23 | getStorage (type) { 24 | return this.getStorageOption(type + 'Storage', storage[type]); 25 | }, 26 | 27 | getStoragePath (type) { 28 | const fallbackPath = path.join(process.cwd(), storageJsonName[type]); 29 | 30 | return this.getStorageOption(type + 'StoragePath', fallbackPath); 31 | }, 32 | 33 | getTableName (type) { 34 | return this.getStorageOption(type + 'StorageTableName', storageTableName[type]); 35 | }, 36 | 37 | getStorageOptions (type, extraOptions) { 38 | const options = {}; 39 | 40 | if (this.getStorage(type) === 'json') { 41 | options.path = this.getStoragePath(type); 42 | } else if (this.getStorage(type) === 'sequelize') { 43 | options.tableName = this.getTableName(type); 44 | } 45 | 46 | _.assign(options, extraOptions); 47 | 48 | return options; 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /src/helpers/version-helper.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import helpers from './index'; 3 | 4 | const packageJson = require(path.resolve(__dirname, '..', '..', 'package.json')); 5 | 6 | module.exports = { 7 | getCliVersion () { 8 | return packageJson.version; 9 | }, 10 | 11 | getOrmVersion () { 12 | return helpers.generic.getSequelize('package.json').version; 13 | }, 14 | 15 | getDialect () { 16 | try { 17 | return helpers.config.readConfig(); 18 | } catch (e) { 19 | return null; 20 | } 21 | }, 22 | 23 | getDialectName () { 24 | const config = this.getDialect(); 25 | 26 | if (config) { 27 | return { 28 | 'sqlite': 'sqlite3', 29 | 'postgres': 'pg', 30 | 'mariadb': 'mariasql', 31 | 'mysql': 'mysql' 32 | }[config.dialect]; 33 | } else { 34 | return null; 35 | } 36 | }, 37 | 38 | getNodeVersion () { 39 | return process.version.replace('v', ''); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /src/helpers/view-helper.js: -------------------------------------------------------------------------------- 1 | import clc from 'cli-color'; 2 | import _ from 'lodash'; 3 | import helpers from './index'; 4 | import getYArgs from '../core/yargs'; 5 | 6 | const args = getYArgs().argv; 7 | 8 | module.exports = { 9 | teaser () { 10 | const versions = [ 11 | 'Node: ' + helpers.version.getNodeVersion(), 12 | 'CLI: ' + helpers.version.getCliVersion(), 13 | 'ORM: ' + helpers.version.getOrmVersion() 14 | ]; 15 | 16 | this.log(); 17 | this.log(clc.underline('Sequelize CLI [' + versions.join(', ') + ']')); 18 | this.log(); 19 | 20 | // Remove in v4 21 | if (helpers.version.getOrmVersion().match(/^4./)) { 22 | this.warn( 23 | 'This version of Sequelize CLI is not ' + 24 | 'fully compatible with Sequelize v4. ' + 25 | 'https://github.com/sequelize/cli#sequelize-support' 26 | ); 27 | this.log(); 28 | } 29 | }, 30 | 31 | log () { 32 | console.log.apply(this, arguments); 33 | }, 34 | 35 | error (error) { 36 | let message = error; 37 | 38 | if (error instanceof Error) { 39 | message = !args.debug 40 | ? error.message 41 | : error.stack; 42 | } 43 | 44 | this.log(); 45 | console.error(`${clc.red('ERROR:')} ${message}`); 46 | this.log(); 47 | 48 | process.exit(1); 49 | }, 50 | 51 | warn (message) { 52 | this.log(`${clc.yellow('WARNING:')} ${message}`); 53 | }, 54 | 55 | notifyAboutExistingFile (file) { 56 | this.error( 57 | 'The file ' + clc.blueBright(file) + ' already exists. ' + 58 | 'Run command with --force to overwrite it.' 59 | ); 60 | }, 61 | 62 | pad (s, smth) { 63 | let margin = smth; 64 | 65 | if (_.isObject(margin)) { 66 | margin = Object.keys(margin); 67 | } 68 | 69 | if (Array.isArray(margin)) { 70 | margin = Math.max.apply(null, margin.map(o => { 71 | return o.length; 72 | })); 73 | } 74 | 75 | return s + new Array(margin - s.length + 1).join(' '); 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /src/sequelize.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import getYArgs from './core/yargs'; 4 | import cliPackage from '../package'; 5 | import Promise from 'bluebird'; 6 | import { isEmpty } from 'lodash'; 7 | 8 | const yargs = getYArgs(); 9 | 10 | Promise.coroutine.addYieldHandler(yieldedValue => { 11 | if (Array.isArray(yieldedValue)) { 12 | return Promise.all(yieldedValue); 13 | } 14 | }); 15 | 16 | Promise.coroutine.addYieldHandler(yieldedValue => { 17 | if (isEmpty(yieldedValue)) { 18 | return Promise.resolve(yieldedValue); 19 | } 20 | }); 21 | 22 | import init from './commands/init'; 23 | import migrate from './commands/migrate'; 24 | import migrateUndo from './commands/migrate_undo'; 25 | import migrateUndoAll from './commands/migrate_undo_all'; 26 | import seed from './commands/seed'; 27 | import seedOne from './commands/seed_one'; 28 | import migrationGenerate from './commands/migration_generate'; 29 | import modelGenerate from './commands/model_generate'; 30 | import seedGenerate from './commands/seed_generate'; 31 | import database from './commands/database'; 32 | 33 | import helpers from './helpers/index'; 34 | 35 | helpers.view.teaser(); 36 | 37 | const cli = yargs 38 | .command('db:migrate', 'Run pending migrations', migrate) 39 | .command('db:migrate:schema:timestamps:add', 'Update migration table to have timestamps', migrate) 40 | .command('db:migrate:status', 'List the status of all migrations', migrate) 41 | .command('db:migrate:undo', 'Reverts a migration', migrateUndo) 42 | .command('db:migrate:undo:all', 'Revert all migrations ran', migrateUndoAll) 43 | .command('db:seed', 'Run specified seeder', seedOne) 44 | .command('db:seed:undo', 'Deletes data from the database', seedOne) 45 | .command('db:seed:all', 'Run every seeder', seed) 46 | .command('db:seed:undo:all', 'Deletes data from the database', seed) 47 | .command('db:create', 'Create database specified by configuration', database) 48 | .command('db:drop', 'Drop database specified by configuration', database) 49 | .command('init', 'Initializes project', init) 50 | .command('init:config', 'Initializes configuration', init) 51 | .command('init:migrations', 'Initializes migrations', init) 52 | .command('init:models', 'Initializes models', init) 53 | .command('init:seeders', 'Initializes seeders', init) 54 | .command(['migration:generate', 'migration:create'], 'Generates a new migration file', migrationGenerate) 55 | .command(['model:generate', 'model:create'], 'Generates a model and its migration', modelGenerate) 56 | .command(['seed:generate', 'seed:create'], 'Generates a new seed file', seedGenerate) 57 | .version(() => cliPackage.version) 58 | .wrap(yargs.terminalWidth()) 59 | .strict() 60 | .help(); 61 | 62 | const args = cli.argv; 63 | 64 | // if no command then show help 65 | if (!args._[0]) { 66 | cli.showHelp(); 67 | } 68 | -------------------------------------------------------------------------------- /test/db/db-create.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/../support'); 3 | const helpers = require(__dirname + '/../support/helpers'); 4 | const gulp = require('gulp'); 5 | const _ = require('lodash'); 6 | 7 | const prepare = function (flag, callback, options) { 8 | options = _.assign({ config: {} }, options || {}); 9 | 10 | const configPath = 'config/config.json'; 11 | const config = _.assign({}, helpers.getTestConfig(), options.config); 12 | const configContent = JSON.stringify(config); 13 | 14 | gulp 15 | .src(Support.resolveSupportPath('tmp')) 16 | .pipe(helpers.clearDirectory()) 17 | .pipe(helpers.runCli('init')) 18 | .pipe(helpers.removeFile(configPath)) 19 | .pipe(helpers.overwriteFile(configContent, configPath)) 20 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 21 | .pipe(helpers.teardown(callback)); 22 | }; 23 | 24 | describe(Support.getTestDialectTeaser('db:create'), () => { 25 | if (Support.dialectIsPostgres()) { 26 | it('correctly creates database', function (done) { 27 | const databaseName = `my_test_db_${_.random(10000, 99999)}`; 28 | prepare( 29 | 'db:create', 30 | () => { 31 | this.sequelize.query(`SELECT 1 as exists FROM pg_database WHERE datname = '${databaseName}';`, { 32 | type: this.sequelize.QueryTypes.SELECT 33 | }).then(result => { 34 | expect(result[0].exists).to.eql(1); 35 | done(); 36 | }); 37 | }, { 38 | config: { 39 | database: databaseName 40 | } 41 | }); 42 | }); 43 | 44 | it('correctly creates database with hyphen #545', function (done) { 45 | const databaseName = `my_test-db_${_.random(10000, 99999)}`; 46 | prepare( 47 | 'db:create', 48 | () => { 49 | this.sequelize.query(`SELECT 1 as exists FROM pg_database WHERE datname = '${databaseName}';`, { 50 | type: this.sequelize.QueryTypes.SELECT 51 | }).then(result => { 52 | expect(result[0].exists).to.eql(1); 53 | done(); 54 | }); 55 | }, { 56 | config: { 57 | database: databaseName 58 | } 59 | }); 60 | }); 61 | } 62 | 63 | if (Support.dialectIsMySQL()) { 64 | it('correctly creates database', function (done) { 65 | const databaseName = `my_test_db_${_.random(10000, 99999)}`; 66 | prepare( 67 | 'db:create', 68 | () => { 69 | this.sequelize.query(`SELECT IF('${databaseName}' IN(SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA), 1, 0) AS found;`, { 70 | type: this.sequelize.QueryTypes.SELECT 71 | }).then(result => { 72 | expect(result[0].found).to.eql(1); 73 | done(); 74 | }); 75 | }, { 76 | config: { 77 | database: databaseName 78 | } 79 | }); 80 | }); 81 | 82 | it('correctly creates database with hyphen #545', function (done) { 83 | const databaseName = `my_test-db_${_.random(10000, 99999)}`; 84 | prepare( 85 | 'db:create', 86 | () => { 87 | this.sequelize.query(`SELECT IF('${databaseName}' IN(SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA), 1, 0) AS found;`, { 88 | type: this.sequelize.QueryTypes.SELECT 89 | }).then(result => { 90 | expect(result[0].found).to.eql(1); 91 | done(); 92 | }); 93 | }, { 94 | config: { 95 | database: databaseName 96 | } 97 | }); 98 | }); 99 | } 100 | }); 101 | -------------------------------------------------------------------------------- /test/db/db-drop.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/../support'); 3 | const helpers = require(__dirname + '/../support/helpers'); 4 | const gulp = require('gulp'); 5 | const _ = require('lodash'); 6 | 7 | const prepare = function (flag, callback, options) { 8 | options = _.assign({ config: {} }, options || {}); 9 | 10 | const configPath = 'config/config.json'; 11 | const config = _.assign({}, helpers.getTestConfig(), options.config); 12 | const configContent = JSON.stringify(config); 13 | 14 | gulp 15 | .src(Support.resolveSupportPath('tmp')) 16 | .pipe(helpers.clearDirectory()) 17 | .pipe(helpers.runCli('init')) 18 | .pipe(helpers.removeFile(configPath)) 19 | .pipe(helpers.overwriteFile(configContent, configPath)) 20 | .pipe(helpers.runCli('db:create')) 21 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 22 | .pipe(helpers.teardown(callback)); 23 | }; 24 | 25 | describe(Support.getTestDialectTeaser('db:drop'), () => { 26 | if (Support.dialectIsPostgres()) { 27 | it('correctly drops database', function (done) { 28 | const databaseName = `my_test_db_${_.random(10000, 99999)}`; 29 | prepare( 30 | 'db:drop', 31 | () => { 32 | this.sequelize.query(`SELECT 1 as exists FROM pg_database WHERE datname = '${databaseName}';`, { 33 | type: this.sequelize.QueryTypes.SELECT 34 | }).then(result => { 35 | expect(result).to.be.empty; 36 | done(); 37 | }); 38 | }, { 39 | config: { 40 | database: databaseName 41 | } 42 | }); 43 | }); 44 | } 45 | 46 | if (Support.dialectIsMySQL()) { 47 | it('correctly drops database', function (done) { 48 | const databaseName = `my_test_db_${_.random(10000, 99999)}`; 49 | prepare( 50 | 'db:drop', 51 | () => { 52 | this.sequelize.query(`SELECT IF('${databaseName}' IN(SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA), 1, 0) AS found;`, { 53 | type: this.sequelize.QueryTypes.SELECT 54 | }).then(result => { 55 | expect(result[0].found).to.eql(0); 56 | done(); 57 | }); 58 | }, { 59 | config: { 60 | database: databaseName 61 | } 62 | }); 63 | }); 64 | } 65 | }); 66 | -------------------------------------------------------------------------------- /test/db/migrate-json.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/../support'); 3 | const helpers = require(__dirname + '/../support/helpers'); 4 | const gulp = require('gulp'); 5 | const fs = require('fs'); 6 | const _ = require('lodash'); 7 | 8 | [ 9 | 'db:migrate', 10 | 'db:migrate --migrations-path migrations', 11 | '--migrations-path migrations db:migrate', 12 | 'db:migrate --migrations-path ./migrations', 13 | 'db:migrate --migrations-path ./migrations/', 14 | 'db:migrate --config ../../support/tmp/config/config.json', 15 | 'db:migrate --config ' + Support.resolveSupportPath('tmp', 'config', 'config.json'), 16 | 'db:migrate --config ../../support/tmp/config/config.js' 17 | ].forEach(flag => { 18 | const prepare = function (callback, options) { 19 | options = _.assign({ config: {} }, options || {}); 20 | 21 | let configPath = 'config/'; 22 | let migrationFile = options.migrationFile || 'createPerson'; 23 | const config = _.assign({ 24 | migrationStorage: 'json' 25 | }, helpers.getTestConfig(), options.config); 26 | let configContent = JSON.stringify(config); 27 | 28 | migrationFile = migrationFile + '.js'; 29 | 30 | if (flag.match(/config\.js$/)) { 31 | configPath = configPath + 'config.js'; 32 | configContent = 'module.exports = ' + configContent; 33 | } else { 34 | configPath = configPath + 'config.json'; 35 | } 36 | 37 | gulp 38 | .src(Support.resolveSupportPath('tmp')) 39 | .pipe(helpers.clearDirectory()) 40 | .pipe(helpers.runCli('init')) 41 | .pipe(helpers.removeFile('config/config.json')) 42 | .pipe(helpers.copyMigration(migrationFile)) 43 | .pipe(helpers.overwriteFile(configContent, configPath)) 44 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 45 | .pipe(helpers.teardown(callback)); 46 | }; 47 | 48 | describe(Support.getTestDialectTeaser(flag) + ' (JSON)', () => { 49 | describe('the migration storage file', () => { 50 | it('should be written to the default location', done => { 51 | const storageFile = Support.resolveSupportPath('tmp', 'sequelize-meta.json'); 52 | 53 | prepare(() => { 54 | expect(fs.statSync(storageFile).isFile()).to.be(true); 55 | expect(fs.readFileSync(storageFile).toString()) 56 | .to.match(/^\[\n "\d{14}-createPerson\.(js)"\n\]$/); 57 | done(); 58 | }); 59 | }); 60 | 61 | it('should be written to the specified location', done => { 62 | const storageFile = Support.resolveSupportPath('tmp', 'custom-meta.json'); 63 | 64 | prepare(() => { 65 | expect(fs.statSync(storageFile).isFile()).to.be(true); 66 | expect(fs.readFileSync(storageFile).toString()) 67 | .to.match(/^\[\n "\d{14}-createPerson\.(js)"\n\]$/); 68 | done(); 69 | }, { config: { migrationStoragePath: storageFile } }); 70 | }); 71 | }); 72 | 73 | it('creates the respective table', function (done) { 74 | const self = this; 75 | 76 | prepare(() => { 77 | helpers.readTables(self.sequelize, tables => { 78 | expect(tables).to.have.length(1); 79 | expect(tables).to.contain('Person'); 80 | done(); 81 | }); 82 | }); 83 | }); 84 | 85 | describe('the logging option', () => { 86 | it('does not print sql queries by default', done => { 87 | prepare((__, stdout) => { 88 | expect(stdout).to.not.contain('Executing'); 89 | done(); 90 | }); 91 | }); 92 | 93 | it('interprets a custom option', done => { 94 | prepare((__, stdout) => { 95 | expect(stdout).to.contain('Executing'); 96 | done(); 97 | }, { config: { logging: true } }); 98 | }); 99 | }); 100 | 101 | describe('promise based migrations', () => { 102 | it('correctly creates two tables', function (done) { 103 | const self = this; 104 | 105 | prepare(() => { 106 | helpers.readTables(self.sequelize, tables => { 107 | expect(tables.sort()).to.eql([ 108 | 'Person', 109 | 'Task' 110 | ]); 111 | done(); 112 | }); 113 | }, { 114 | migrationFile: 'new/*createPerson', 115 | config: { promisifyMigrations: false } 116 | }); 117 | }); 118 | }); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /test/db/migrate.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const expect = require('expect.js'); 3 | const Support = require(__dirname + '/../support'); 4 | const helpers = require(__dirname + '/../support/helpers'); 5 | const gulp = require('gulp'); 6 | const _ = require('lodash'); 7 | 8 | [ 9 | 'db:migrate', 10 | 'db:migrate --migrations-path migrations', 11 | '--migrations-path migrations db:migrate', 12 | 'db:migrate --migrations-path ./migrations', 13 | 'db:migrate --migrations-path ./migrations/', 14 | 'db:migrate --config ../../support/tmp/config/config.json', 15 | 'db:migrate --config ' + Support.resolveSupportPath('tmp', 'config', 'config.json'), 16 | 'db:migrate --config ../../support/tmp/config/config.js', 17 | 'db:migrate --config ../../support/tmp/config/config-promise.js' 18 | ].forEach(flag => { 19 | const prepare = function (callback, options) { 20 | options = _.assign({ config: {} }, options || {}); 21 | options.cli = options.cli || {}; 22 | _.defaults(options.cli, { pipeStdout: true }); 23 | 24 | let configPath = 'config/'; 25 | let migrationFile = options.migrationFile || 'createPerson'; 26 | const config = _.assign({}, helpers.getTestConfig(), options.config); 27 | let configContent = JSON.stringify(config); 28 | 29 | migrationFile = migrationFile + '.js'; 30 | if (flag.match(/config\.js$/)) { 31 | configPath = configPath + 'config.js'; 32 | configContent = 'module.exports = ' + configContent; 33 | } else if (flag.match(/config-promise\.js/)) { 34 | configPath = configPath + 'config-promise.js'; 35 | configContent = '' + 36 | 'var b = require("bluebird");' + 37 | 'module.exports = b.resolve(' + configContent + ');'; 38 | } else { 39 | configPath = configPath + 'config.json'; 40 | } 41 | 42 | let result = ''; 43 | 44 | return gulp 45 | .src(Support.resolveSupportPath('tmp')) 46 | .pipe(helpers.clearDirectory()) 47 | .pipe(helpers.runCli('init')) 48 | .pipe(helpers.removeFile('config/config.json')) 49 | .pipe(helpers.copyMigration(migrationFile)) 50 | .pipe(helpers.overwriteFile(configContent, configPath)) 51 | .pipe(helpers.runCli(flag, options.cli)) 52 | .on('error', e => { 53 | callback(e); 54 | }) 55 | .on('data', data => { 56 | result += data.toString(); 57 | }) 58 | .on('end', () => { 59 | callback(null, result); 60 | }); 61 | }; 62 | 63 | describe(Support.getTestDialectTeaser(flag), () => { 64 | it('creates a SequelizeMeta table', function (done) { 65 | const self = this; 66 | 67 | prepare(() => { 68 | helpers.readTables(self.sequelize, tables => { 69 | expect(tables).to.have.length(2); 70 | expect(tables).to.contain('SequelizeMeta'); 71 | done(); 72 | }); 73 | }); 74 | }); 75 | 76 | it('creates the respective table', function (done) { 77 | const self = this; 78 | 79 | prepare(() => { 80 | helpers.readTables(self.sequelize, tables => { 81 | expect(tables).to.have.length(2); 82 | expect(tables).to.contain('Person'); 83 | done(); 84 | }); 85 | }); 86 | }); 87 | 88 | it('fails with a not 0 exit code', done => { 89 | prepare(done, { 90 | migrationFile: 'invalid/*createPerson', 91 | cli: { exitCode: 1 } 92 | }); 93 | }); 94 | 95 | describe('the logging option', () => { 96 | it('does not print sql queries by default', done => { 97 | prepare((__, stdout) => { 98 | expect(stdout).to.not.contain('Executing'); 99 | done(); 100 | }); 101 | }); 102 | 103 | it('interpretes a custom option', done => { 104 | prepare((__, stdout) => { 105 | expect(stdout).to.contain('Executing'); 106 | done(); 107 | }, { config: { logging: true } }); 108 | }); 109 | }); 110 | 111 | describe('promise based migrations', () => { 112 | it('correctly creates two tables', function (done) { 113 | const self = this; 114 | 115 | prepare(() => { 116 | helpers.readTables(self.sequelize, tables => { 117 | expect(tables.sort()).to.eql([ 118 | 'Person', 119 | 'SequelizeMeta', 120 | 'Task' 121 | ]); 122 | done(); 123 | }); 124 | }, { 125 | migrationFile: 'new/*createPerson', 126 | config: { promisifyMigrations: false } 127 | }); 128 | }); 129 | }); 130 | 131 | describe('custom meta table name', () => { 132 | it('correctly uses the defined table name', function (done) { 133 | const self = this; 134 | 135 | prepare(() => { 136 | helpers.readTables(self.sequelize, tables => { 137 | expect(tables.sort()).to.eql(['Person', 'Task', 'sequelize_meta']); 138 | done(); 139 | }); 140 | }, { 141 | migrationFile: 'new/*createPerson', 142 | config: { migrationStorageTableName: 'sequelize_meta' } 143 | }); 144 | }); 145 | }); 146 | }); 147 | }); 148 | 149 | describe(Support.getTestDialectTeaser('db:migrate'), () => { 150 | describe('with config.js', () => { 151 | const prepare = function (callback) { 152 | const config = helpers.getTestConfig(); 153 | const configContent = 'module.exports = ' + JSON.stringify(config); 154 | let result = ''; 155 | 156 | return gulp 157 | .src(Support.resolveSupportPath('tmp')) 158 | .pipe(helpers.clearDirectory()) 159 | .pipe(helpers.runCli('init')) 160 | .pipe(helpers.removeFile('config/config.json')) 161 | .pipe(helpers.copyMigration('createPerson.js')) 162 | .pipe(helpers.overwriteFile(configContent, 'config/config.js')) 163 | .pipe(helpers.runCli('db:migrate')) 164 | .on('error', e => { 165 | callback(e); 166 | }) 167 | .on('data', data => { 168 | result += data.toString(); 169 | }) 170 | .on('end', () => { 171 | callback(null, result); 172 | }); 173 | }; 174 | 175 | it('creates a SequelizeMeta table', function (done) { 176 | prepare(() => { 177 | helpers.readTables(this.sequelize, tables => { 178 | expect(tables).to.have.length(2); 179 | expect(tables).to.contain('SequelizeMeta'); 180 | done(); 181 | }); 182 | }); 183 | }); 184 | 185 | it('creates the respective table', function (done) { 186 | prepare(() => { 187 | helpers.readTables(this.sequelize, tables => { 188 | expect(tables).to.have.length(2); 189 | expect(tables).to.contain('Person'); 190 | done(); 191 | }); 192 | }); 193 | }); 194 | }); 195 | }); 196 | 197 | describe(Support.getTestDialectTeaser('db:migrate'), () => { 198 | describe('with config.json and url option', () => { 199 | const prepare = function (callback) { 200 | const config = { url: helpers.getTestUrl() }; 201 | const configContent = 'module.exports = ' + JSON.stringify(config); 202 | let result = ''; 203 | 204 | return gulp 205 | .src(Support.resolveSupportPath('tmp')) 206 | .pipe(helpers.clearDirectory()) 207 | .pipe(helpers.runCli('init')) 208 | .pipe(helpers.removeFile('config/config.json')) 209 | .pipe(helpers.copyMigration('createPerson.js')) 210 | .pipe(helpers.overwriteFile(configContent, 'config/config.js')) 211 | .pipe(helpers.runCli('db:migrate')) 212 | .on('error', e => { 213 | callback(e); 214 | }) 215 | .on('data', data => { 216 | result += data.toString(); 217 | }) 218 | .on('end', () => { 219 | callback(null, result); 220 | }); 221 | }; 222 | 223 | it('creates a SequelizeMeta table', function (done) { 224 | const self = this; 225 | 226 | prepare(() => { 227 | helpers.readTables(self.sequelize, tables => { 228 | expect(tables).to.have.length(2); 229 | expect(tables).to.contain('SequelizeMeta'); 230 | done(); 231 | }); 232 | }); 233 | }); 234 | 235 | it('creates the respective table', function (done) { 236 | const self = this; 237 | 238 | prepare(() => { 239 | helpers.readTables(self.sequelize, tables => { 240 | expect(tables).to.have.length(2); 241 | expect(tables).to.contain('Person'); 242 | done(); 243 | }); 244 | }); 245 | }); 246 | }); 247 | }); 248 | 249 | describe(Support.getTestDialectTeaser('db:migrate'), () => { 250 | describe('optional migration parameters', () => { 251 | const prepare = function (runArgs = '', callback) { 252 | const config = { url: helpers.getTestUrl() }; 253 | const configContent = 'module.exports = ' + JSON.stringify(config); 254 | let result = ''; 255 | 256 | return gulp 257 | .src(Support.resolveSupportPath('tmp')) 258 | .pipe(helpers.clearDirectory()) 259 | .pipe(helpers.runCli('init')) 260 | .pipe(helpers.removeFile('config/config.json')) 261 | .pipe(helpers.copyMigration('createPerson.js')) 262 | .pipe(helpers.copyMigration('renamePersonToUser.js')) 263 | .pipe(helpers.copyMigration('createTestTableForTrigger.js')) 264 | .pipe(helpers.copyMigration('createPost.js')) 265 | .pipe(helpers.overwriteFile(configContent, 'config/config.js')) 266 | .pipe(helpers.runCli('db:migrate ' + runArgs)) 267 | .on('error', e => { 268 | callback(e); 269 | }) 270 | .on('data', data => { 271 | result += data.toString(); 272 | }) 273 | .on('end', () => { 274 | callback(null, result); 275 | }); 276 | }; 277 | 278 | const runCli = function (cliArgs, callback) { 279 | let result = ''; 280 | // avoid double callbacks 281 | let done = callback; 282 | return gulp 283 | .src(Support.resolveSupportPath('tmp')) 284 | .pipe(helpers.runCli(cliArgs, { pipeStdout: true, exitCode: 0 })) 285 | .on('error', e => { 286 | done(e); 287 | done = () => {}; 288 | }) 289 | .on('data', data => { 290 | result += data.toString(); 291 | }) 292 | .on('end', () => { 293 | done(null, result); 294 | }); 295 | }; 296 | 297 | it('--to', function (done) { 298 | const self = this; 299 | const migrationsPath = Support.resolveSupportPath('assets', 'migrations'); 300 | const migrations = fs.readdirSync(migrationsPath); 301 | const createTriggers = migrations.filter(migration => migration.indexOf('createTestTableForTrigger') > -1); 302 | 303 | prepare('--to ' + createTriggers, () => { 304 | helpers.readTables(self.sequelize, tables => { 305 | expect(tables).to.have.length(3); 306 | expect(tables).to.contain('User'); 307 | expect(tables).to.contain('trigger_test'); 308 | done(); 309 | }); 310 | }); 311 | }); 312 | 313 | it('--to full migration in two parts', function (done) { 314 | const self = this; 315 | const migrationsPath = Support.resolveSupportPath('assets', 'migrations'); 316 | const migrations = fs.readdirSync(migrationsPath); 317 | const createTriggers = migrations.filter(migration => migration.indexOf('createTestTableForTrigger') > -1); 318 | const createPost = migrations.filter(migration => migration.indexOf('createPost') > -1); 319 | 320 | prepare('--to ' + createTriggers, () => { 321 | helpers.readTables(self.sequelize, tables => { 322 | expect(tables).to.have.length(3); 323 | expect(tables).to.contain('User'); 324 | expect(tables).to.contain('trigger_test'); 325 | runCli('db:migrate --to ' + createPost, () => { 326 | helpers.readTables(self.sequelize, tables => { 327 | expect(tables).to.have.length(4); 328 | expect(tables).to.contain('Post'); 329 | done(); 330 | }); 331 | }); 332 | }); 333 | }); 334 | }); 335 | 336 | it('--to should exit with 0 when there are no migrations', function (done) { 337 | const self = this; 338 | const migrationsPath = Support.resolveSupportPath('assets', 'migrations'); 339 | const migrations = fs.readdirSync(migrationsPath); 340 | const createTriggers = migrations.filter(migration => migration.indexOf('createTestTableForTrigger') > -1); 341 | 342 | prepare('--to ' + createTriggers, () => { 343 | helpers.readTables(self.sequelize, tables => { 344 | expect(tables).to.have.length(3); 345 | expect(tables).to.contain('User'); 346 | runCli('db:migrate --to ' + createTriggers, (err, result) => { 347 | expect(result).to.contain('No migrations were executed, database schema was already up to date.'); 348 | done(err); 349 | }); 350 | }); 351 | }); 352 | }); 353 | 354 | it('--from', function (done) { 355 | const self = this; 356 | const migrationsPath = Support.resolveSupportPath('assets', 'migrations'); 357 | const migrations = fs.readdirSync(migrationsPath); 358 | const createPersonMigration = migrations.filter(migration => migration.indexOf('renamePersonToUser') > -1); 359 | 360 | prepare('--from ' + createPersonMigration, () => { 361 | helpers.readTables(self.sequelize, tables => { 362 | expect(tables).to.have.length(3); 363 | expect(tables).to.contain('Post'); 364 | expect(tables).to.contain('trigger_test'); 365 | done(); 366 | }); 367 | }); 368 | }); 369 | 370 | it('--from should exit with 0 when there are no migrations', function (done) { 371 | const self = this; 372 | const migrationsPath = Support.resolveSupportPath('assets', 'migrations'); 373 | const migrations = fs.readdirSync(migrationsPath); 374 | const createPersonMigration = migrations.filter(migration => migration.indexOf('renamePersonToUser') > -1); 375 | const createPost = migrations.filter(migration => migration.indexOf('createPost') > -1); 376 | 377 | prepare('--from ' + createPersonMigration, () => { 378 | helpers.readTables(self.sequelize, tables => { 379 | expect(tables).to.have.length(3); 380 | expect(tables).to.contain('Post'); 381 | expect(tables).to.contain('trigger_test'); 382 | runCli('db:migrate --from ' + createPost, (err, result) => { 383 | expect(result).to.contain('No migrations were executed, database schema was already up to date.'); 384 | done(err); 385 | }); 386 | }); 387 | }); 388 | }); 389 | 390 | 391 | it('--to and --from together', function (done) { 392 | const self = this; 393 | const migrationsPath = Support.resolveSupportPath('assets', 'migrations'); 394 | const migrations = fs.readdirSync(migrationsPath); 395 | const createPersonMigration = migrations.filter(migration => migration.indexOf('renamePersonToUser') > -1); 396 | const createPost = migrations.filter(migration => migration.indexOf('createTestTableForTrigger') > -1); 397 | 398 | prepare('--from ' + createPersonMigration + ' --to ' + createPost, () => { 399 | helpers.readTables(self.sequelize, tables => { 400 | expect(tables).to.have.length(2); 401 | expect(tables).to.contain('trigger_test'); 402 | done(); 403 | }); 404 | }); 405 | }); 406 | }); 407 | }); 408 | -------------------------------------------------------------------------------- /test/db/migrate/schema/add_timestamps.test.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const expect = require('expect.js'); 4 | const Support = require(__dirname + '/../../../support'); 5 | const helpers = require(__dirname + '/../../../support/helpers'); 6 | const gulp = require('gulp'); 7 | 8 | [ 9 | 'db:migrate:schema:timestamps:add' 10 | ].forEach(flag => { 11 | const prepare = function (config, callback) { 12 | config = helpers.getTestConfig(config); 13 | 14 | gulp 15 | .src(Support.resolveSupportPath('tmp')) 16 | .pipe(helpers.clearDirectory()) 17 | .pipe(helpers.runCli('init')) 18 | .pipe(helpers.copyMigration('createPerson.js')) 19 | .pipe(helpers.copyMigration('renamePersonToUser.js')) 20 | .pipe(helpers.overwriteFile(JSON.stringify(config), 'config/config.json')) 21 | .pipe(helpers.runCli('db:migrate')) 22 | .pipe(helpers.teardown(callback)); 23 | }; 24 | 25 | describe(Support.getTestDialectTeaser(flag), () => { 26 | beforeEach(function (done) { 27 | prepare.call(this, null, () => { 28 | return gulp 29 | .src(Support.resolveSupportPath('tmp')) 30 | .pipe(helpers.runCli(flag)) 31 | .pipe(helpers.teardown(done)); 32 | }); 33 | }); 34 | 35 | it('renames the original table', function (done) { 36 | const self = this; 37 | 38 | helpers.readTables(self.sequelize, tables => { 39 | expect(tables).to.have.length(3); 40 | expect(tables.indexOf('SequelizeMeta')).to.be.above(-1); 41 | expect(tables.indexOf('SequelizeMetaBackup')).to.be.above(-1); 42 | done(); 43 | }); 44 | }); 45 | 46 | it('keeps the data in the original table', function (done) { 47 | helpers.execQuery( 48 | this.sequelize, 49 | this.sequelize.getQueryInterface().QueryGenerator.selectQuery('SequelizeMetaBackup'), 50 | { raw: true } 51 | ).then(items => { 52 | expect(items.length).to.equal(2); 53 | done(); 54 | }); 55 | }); 56 | 57 | it('keeps the structure of the original table', function (done) { 58 | const self = this; 59 | 60 | helpers.readTables(self.sequelize, () => { 61 | return self 62 | .sequelize 63 | .getQueryInterface() 64 | .describeTable('SequelizeMetaBackup') 65 | .then(fields => { 66 | expect(Object.keys(fields).sort()).to.eql(['name']); 67 | done(); 68 | return null; 69 | }); 70 | }); 71 | }); 72 | 73 | it('creates a new SequelizeMeta table with the new structure', function (done) { 74 | this.sequelize.getQueryInterface().describeTable('SequelizeMeta').then(fields => { 75 | expect(Object.keys(fields).sort()).to.eql(['createdAt', 'name', 'updatedAt']); 76 | done(); 77 | }); 78 | }); 79 | 80 | it('copies the entries into the new table', function (done) { 81 | helpers.execQuery( 82 | this.sequelize, 83 | this.sequelize.getQueryInterface().QueryGenerator.selectQuery('SequelizeMeta'), 84 | { raw: true, type: 'SELECT' } 85 | ).then(items => { 86 | expect(items[0].name).to.eql('20111117063700-createPerson.js'); 87 | expect(items[1].name).to.eql('20111205064000-renamePersonToUser.js'); 88 | done(); 89 | }); 90 | }); 91 | 92 | it('is possible to undo one of the already executed migrations', function (done) { 93 | const self = this; 94 | 95 | gulp 96 | .src(Support.resolveSupportPath('tmp')) 97 | .pipe(helpers.runCli('db:migrate:undo')) 98 | .pipe(helpers.teardown(() => { 99 | helpers.execQuery( 100 | self.sequelize, 101 | self.sequelize.getQueryInterface().QueryGenerator.selectQuery('SequelizeMeta'), 102 | { raw: true, type: 'SELECT' } 103 | ).then(items => { 104 | expect(items[0].name).to.eql('20111117063700-createPerson.js'); 105 | done(); 106 | }); 107 | })); 108 | }); 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /test/db/migrate/status.test.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const Support = require(__dirname + '/../../support'); 4 | const helpers = require(__dirname + '/../../support/helpers'); 5 | const gulp = require('gulp'); 6 | 7 | [ 8 | 'db:migrate:status' 9 | ].forEach(flag => { 10 | describe(Support.getTestDialectTeaser(flag), () => { 11 | it('is correctly reports a down and an up migration', done => { 12 | gulp 13 | .src(Support.resolveSupportPath('tmp')) 14 | .pipe(helpers.clearDirectory()) 15 | .pipe(helpers.runCli('init')) 16 | .pipe(helpers.copyMigration('createPerson.js')) 17 | .pipe(helpers.overwriteFile(JSON.stringify(helpers.getTestConfig()), 'config/config.json')) 18 | .pipe(helpers.runCli('db:migrate', { pipeStdout: false })) 19 | .pipe(helpers.copyMigration('renamePersonToUser.js')) 20 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 21 | .pipe(helpers.ensureContent('up 20111117063700-createPerson.js')) 22 | .pipe(helpers.ensureContent('down 20111205064000-renamePersonToUser.js')) 23 | .pipe(helpers.teardown(done)); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/db/migrate/undo.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/../../support'); 3 | const helpers = require(__dirname + '/../../support/helpers'); 4 | const gulp = require('gulp'); 5 | const fs = require('fs'); 6 | 7 | [ 8 | 'db:migrate:undo' 9 | ].forEach(flag => { 10 | const prepare = function (callback, _flag) { 11 | _flag = _flag || flag; 12 | 13 | gulp 14 | .src(Support.resolveSupportPath('tmp')) 15 | .pipe(helpers.clearDirectory()) 16 | .pipe(helpers.runCli('init')) 17 | .pipe(helpers.copyMigration('createPerson.js')) 18 | .pipe(helpers.overwriteFile(JSON.stringify(helpers.getTestConfig()), 'config/config.json')) 19 | .pipe(helpers.runCli(_flag, { pipeStdout: true })) 20 | .pipe(helpers.teardown(callback)); 21 | }; 22 | 23 | describe(Support.getTestDialectTeaser(flag), () => { 24 | it('creates a SequelizeMeta table', function (done) { 25 | const self = this; 26 | 27 | prepare(() => { 28 | helpers.readTables(self.sequelize, tables => { 29 | expect(tables).to.have.length(1); 30 | expect(tables[0]).to.equal('SequelizeMeta'); 31 | done(); 32 | }); 33 | }); 34 | }); 35 | 36 | it('stops execution if no migrations have been done yet', done => { 37 | prepare((err, output) => { 38 | expect(err).to.equal(null); 39 | expect(output).to.contain('No executed migrations found.'); 40 | done(); 41 | }); 42 | }); 43 | 44 | it('is correctly undoing a migration if they have been done already', function (done) { 45 | const self = this; 46 | 47 | prepare(() => { 48 | helpers.readTables(self.sequelize, tables => { 49 | expect(tables).to.have.length(2); 50 | expect(tables[0]).to.equal('Person'); 51 | 52 | gulp 53 | .src(Support.resolveSupportPath('tmp')) 54 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 55 | .pipe(helpers.teardown(() => { 56 | helpers.readTables(self.sequelize, tables => { 57 | expect(tables).to.have.length(1); 58 | expect(tables[0]).to.equal('SequelizeMeta'); 59 | done(); 60 | }); 61 | })); 62 | }); 63 | }, 'db:migrate'); 64 | }); 65 | 66 | it('correctly undoes a named migration', function (done) { 67 | const self = this; 68 | 69 | prepare(() => { 70 | const migrationsPath = Support.resolveSupportPath('tmp', 'migrations'); 71 | const migrations = fs.readdirSync(migrationsPath); 72 | const createPersonMigration = migrations[0]; 73 | 74 | helpers.readTables(self.sequelize, tables => { 75 | expect(tables).to.have.length(2); 76 | expect(tables[0]).to.equal('Person'); 77 | 78 | gulp 79 | .src(Support.resolveSupportPath('tmp')) 80 | .pipe(helpers.copyMigration('emptyMigration.js')) 81 | .pipe(helpers.runCli('db:migrate')) 82 | .pipe(helpers.runCli(flag + ' --name ' + createPersonMigration, { pipeStdout: true })) 83 | .pipe(helpers.teardown(() => { 84 | helpers.readTables(self.sequelize, tables => { 85 | expect(tables).to.have.length(1); 86 | expect(tables[0]).to.equal('SequelizeMeta'); 87 | helpers.countTable(self.sequelize, 'SequelizeMeta', count => { 88 | expect(count).to.eql([{ count: 1 }]); 89 | done(); 90 | }); 91 | }); 92 | })); 93 | }); 94 | }, 'db:migrate'); 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /test/db/migrate/undo/all.test.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const expect = require('expect.js'); 4 | const Support = require(__dirname + '/../../../support'); 5 | const helpers = require(__dirname + '/../../../support/helpers'); 6 | const gulp = require('gulp'); 7 | 8 | [ 9 | 'db:migrate:undo:all' 10 | ].forEach(flag => { 11 | const prepare = function (callback, _flag) { 12 | _flag = _flag || flag; 13 | 14 | gulp 15 | .src(Support.resolveSupportPath('tmp')) 16 | .pipe(helpers.clearDirectory()) 17 | .pipe(helpers.runCli('init')) 18 | .pipe(helpers.copyMigration('createPerson.js')) 19 | .pipe(helpers.copyMigration('renamePersonToUser.js')) 20 | .pipe(helpers.overwriteFile(JSON.stringify(helpers.getTestConfig()), 'config/config.json')) 21 | .pipe(helpers.runCli(_flag, { pipeStdout: true })) 22 | .pipe(helpers.teardown(callback)); 23 | }; 24 | 25 | describe(Support.getTestDialectTeaser(flag), () => { 26 | it('creates a SequelizeMeta table', function (done) { 27 | const self = this; 28 | 29 | prepare(() => { 30 | helpers.readTables(self.sequelize, tables => { 31 | expect(tables).to.have.length(1); 32 | expect(tables[0]).to.equal('SequelizeMeta'); 33 | done(); 34 | }); 35 | }); 36 | }); 37 | 38 | it('stops execution if no migrations have been done yet', done => { 39 | prepare((err, output) => { 40 | expect(err).to.equal(null); 41 | expect(output).to.contain('No executed migrations found.'); 42 | done(); 43 | }); 44 | }); 45 | 46 | it('is correctly undoing all migrations if they have been done already', function (done) { 47 | const self = this; 48 | 49 | prepare(() => { 50 | helpers.readTables(self.sequelize, tables => { 51 | expect(tables).to.have.length(2); 52 | expect(tables).to.contain('User'); 53 | expect(tables).to.contain('SequelizeMeta'); 54 | 55 | gulp 56 | .src(Support.resolveSupportPath('tmp')) 57 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 58 | .pipe(helpers.teardown(() => { 59 | helpers.readTables(self.sequelize, tables => { 60 | expect(tables).to.have.length(1); 61 | expect(tables[0]).to.equal('SequelizeMeta'); 62 | done(); 63 | }); 64 | })); 65 | }); 66 | }, 'db:migrate'); 67 | }); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/db/migrate/undo/all_to.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/../../../support'); 3 | const helpers = require(__dirname + '/../../../support/helpers'); 4 | const gulp = require('gulp'); 5 | 6 | [ 7 | 'db:migrate:undo:all --to 20130909175939-createTestTableForTrigger.js' 8 | ].forEach(flag => { 9 | const prepare = function (callback, _flag) { 10 | _flag = _flag || flag; 11 | 12 | gulp 13 | .src(Support.resolveSupportPath('tmp')) 14 | .pipe(helpers.clearDirectory()) 15 | .pipe(helpers.runCli('init')) 16 | .pipe(helpers.copyMigration('createPerson.js')) 17 | .pipe(helpers.copyMigration('renamePersonToUser.js')) 18 | .pipe(helpers.copyMigration('createTestTableForTrigger.js')) 19 | .pipe(helpers.copyMigration('createPost.js')) 20 | .pipe(helpers.overwriteFile(JSON.stringify(helpers.getTestConfig()), 'config/config.json')) 21 | .pipe(helpers.runCli(_flag, { pipeStdout: true })) 22 | .pipe(helpers.teardown(callback)); 23 | }; 24 | 25 | describe(Support.getTestDialectTeaser(flag), () => { 26 | it('creates a SequelizeMeta table', function (done) { 27 | const self = this; 28 | 29 | prepare(() => { 30 | helpers.readTables(self.sequelize, tables => { 31 | expect(tables).to.have.length(1); 32 | expect(tables[0]).to.equal('SequelizeMeta'); 33 | done(); 34 | }); 35 | }); 36 | }); 37 | 38 | it('stops execution if no migrations have been done yet', done => { 39 | prepare((err, output) => { 40 | expect(err).to.equal(null); 41 | expect(output).to.contain('No executed migrations found.'); 42 | done(); 43 | }); 44 | }); 45 | 46 | it('is properly undoing migration with --to option and all migrations after', function (done) { 47 | const self = this; 48 | 49 | prepare(() => { 50 | helpers.readTables(self.sequelize, tables => { 51 | expect(tables).to.have.length(4); 52 | expect(tables).to.contain('User'); 53 | expect(tables).to.contain('SequelizeMeta'); 54 | expect(tables).to.contain('Post'); 55 | expect(tables).to.contain('trigger_test'); 56 | 57 | helpers.countTable(self.sequelize, 'SequelizeMeta', result => { 58 | expect(result[0].count).to.eql(4); 59 | 60 | gulp 61 | .src(Support.resolveSupportPath('tmp')) 62 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 63 | .pipe(helpers.teardown(() => { 64 | helpers.readTables(self.sequelize, tables => { 65 | expect(tables).to.have.length(2); 66 | expect(tables).to.contain('SequelizeMeta'); 67 | expect(tables).to.contain('User'); 68 | 69 | helpers.countTable(self.sequelize, 'SequelizeMeta', result => { 70 | expect(result[0].count).to.eql(2); 71 | done(); 72 | }); 73 | }); 74 | })); 75 | }); 76 | 77 | }); 78 | }, 'db:migrate'); 79 | }); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /test/db/seed-json.test.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const expect = require('expect.js'); 4 | const Support = require(__dirname + '/../support'); 5 | const helpers = require(__dirname + '/../support/helpers'); 6 | const gulp = require('gulp'); 7 | const fs = require('fs'); 8 | const _ = require('lodash'); 9 | 10 | [ 11 | 'db:seed --seed seedPerson.js', 12 | 'db:seed --seed seedPerson.js --seeders-path seeders', 13 | '--seeders-path seeders --seed seedPerson.js db:seed', 14 | 'db:seed --seed seedPerson.js --seeders-path ./seeders', 15 | 'db:seed --seed seedPerson.js --seeders-path ./seeders/', 16 | 'db:seed --seed seedPerson.js --config ../../support/tmp/config/config.json', 17 | 'db:seed --seed seedPerson.js --config ' + 18 | Support.resolveSupportPath('tmp', 'config', 'config.json'), 19 | 'db:seed --seed seedPerson.js --config ../../support/tmp/config/config.js' 20 | ].forEach(flag => { 21 | const prepare = function (callback, options) { 22 | options = _.assign({ config: {} }, options || {}); 23 | 24 | let configPath = 'config/'; 25 | let seederFile = options.seederFile || 'seedPerson'; 26 | const config = _.assign({}, helpers.getTestConfig(), options.config); 27 | let configContent = JSON.stringify(config); 28 | const migrationFile = 'createPerson.js'; 29 | 30 | seederFile = seederFile + '.js'; 31 | 32 | if (flag.match(/config\.js$/)) { 33 | configPath = configPath + 'config.js'; 34 | configContent = 'module.exports = ' + configContent; 35 | } else { 36 | configPath = configPath + 'config.json'; 37 | } 38 | 39 | gulp 40 | .src(Support.resolveSupportPath('tmp')) 41 | .pipe(helpers.clearDirectory()) 42 | .pipe(helpers.runCli('init')) 43 | .pipe(helpers.removeFile('config/config.json')) 44 | .pipe(helpers.copyMigration(migrationFile)) 45 | .pipe(helpers.copySeeder(seederFile)) 46 | .pipe(helpers.overwriteFile(configContent, configPath)) 47 | .pipe(helpers.runCli('db:migrate' + 48 | (flag.indexOf('config') === -1 ? '' : flag.replace('db:seed --seed seedPerson.js', '')) 49 | )) 50 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 51 | .pipe(helpers.teardown(callback)); 52 | }; 53 | 54 | describe(Support.getTestDialectTeaser(flag) + ' (JSON)', () => { 55 | it('populates the respective table', function (done) { 56 | const self = this; 57 | 58 | prepare(() => { 59 | helpers.countTable(self.sequelize, 'Person', result => { 60 | expect(result).to.have.length(1); 61 | expect(result[0].count).to.eql(1); 62 | done(); 63 | }); 64 | }); 65 | }); 66 | 67 | describe('the seeder storage file', () => { 68 | it('should be written to the specified location', done => { 69 | const storageFile = Support.resolveSupportPath('tmp', 'custom-data.json'); 70 | 71 | prepare(() => { 72 | expect(fs.statSync(storageFile).isFile()).to.be(true); 73 | expect(fs.readFileSync(storageFile).toString()) 74 | .to.match(/^\[\n "seedPerson\.(js)"\n\]$/); 75 | done(); 76 | }, { config: { seederStoragePath: storageFile, seederStorage: 'json' } }); 77 | }); 78 | }); 79 | 80 | describe('the logging option', () => { 81 | it('does not print sql queries by default', done => { 82 | prepare((__, stdout) => { 83 | expect(stdout).to.not.contain('Executing'); 84 | done(); 85 | }); 86 | }); 87 | 88 | it('interprets a custom option', done => { 89 | prepare((__, stdout) => { 90 | expect(stdout).to.contain('Executing'); 91 | done(); 92 | }, { config: { logging: true } }); 93 | }); 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /test/db/seed.test.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const expect = require('expect.js'); 4 | const Support = require(__dirname + '/../support'); 5 | const helpers = require(__dirname + '/../support/helpers'); 6 | const gulp = require('gulp'); 7 | const _ = require('lodash'); 8 | 9 | [ 10 | 'db:seed --seed seedPerson.js', 11 | 'db:seed --seeders-path seeders --seed seedPerson.js', 12 | '--seeders-path seeders db:seed --seed seedPerson.js', 13 | 'db:seed --seeders-path ./seeders --seed seedPerson.js', 14 | 'db:seed --seeders-path ./seeders/ --seed seedPerson.js', 15 | 'db:seed --seed seedPerson.js --config ../../support/tmp/config/config.json', 16 | 'db:seed --seed seedPerson.js --config ' + 17 | Support.resolveSupportPath('tmp', 'config', 'config.json'), 18 | 'db:seed --seed seedPerson.js --config ../../support/tmp/config/config.js', 19 | 'db:seed --seed seedPerson.js --config ../../support/tmp/config/config-promise.js' 20 | ].forEach(flag => { 21 | const prepare = function (callback, options) { 22 | options = _.assign({ config: {} }, options || {}); 23 | 24 | let configPath = 'config/'; 25 | let seederFile = 'seedPerson'; 26 | const config = _.assign({}, helpers.getTestConfig(), options.config); 27 | let configContent = JSON.stringify(config); 28 | const migrationFile = 'createPerson.js'; 29 | 30 | seederFile = seederFile + '.js'; 31 | 32 | if (flag.match(/config\.js$/)) { 33 | configPath = configPath + 'config.js'; 34 | configContent = 'module.exports = ' + configContent; 35 | } else if (flag.match(/config-promise\.js/)) { 36 | configPath = configPath + 'config-promise.js'; 37 | configContent = '' + 38 | 'var b = require("bluebird");' + 39 | 'module.exports = b.resolve(' + configContent + ');'; 40 | } else { 41 | configPath = configPath + 'config.json'; 42 | } 43 | 44 | gulp 45 | .src(Support.resolveSupportPath('tmp')) 46 | .pipe(helpers.clearDirectory()) 47 | .pipe(helpers.runCli('init')) 48 | .pipe(helpers.removeFile('config/config.json')) 49 | .pipe(helpers.copyMigration(migrationFile)) 50 | .pipe(helpers.copySeeder(seederFile)) 51 | .pipe(helpers.overwriteFile(configContent, configPath)) 52 | .pipe(helpers.runCli('db:migrate' + 53 | (flag.indexOf('config') === -1 ? '' : flag.replace('db:seed --seed seedPerson.js', '')) 54 | )) 55 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 56 | .pipe(helpers.teardown(callback)); 57 | }; 58 | 59 | describe(Support.getTestDialectTeaser(flag), () => { 60 | it('creates a SequelizeData table', function (done) { 61 | const self = this; 62 | 63 | prepare(() => { 64 | helpers.readTables(self.sequelize, tables => { 65 | expect(tables).to.have.length(3); 66 | expect(tables).to.contain('SequelizeData'); 67 | done(); 68 | }); 69 | }, { config: { seederStorage: 'sequelize' } }); 70 | }); 71 | 72 | it('populates the respective table', function (done) { 73 | const self = this; 74 | 75 | prepare(() => { 76 | helpers.countTable(self.sequelize, 'Person', result => { 77 | expect(result).to.have.length(1); 78 | expect(result[0].count).to.eql(1); 79 | done(); 80 | }); 81 | }); 82 | }); 83 | 84 | describe('the logging option', () => { 85 | it('does not print sql queries by default', done => { 86 | prepare((__, stdout) => { 87 | expect(stdout).to.not.contain('Executing'); 88 | done(); 89 | }); 90 | }); 91 | 92 | it('interpretes a custom option', done => { 93 | prepare((__, stdout) => { 94 | expect(stdout).to.contain('Executing'); 95 | done(); 96 | }, { config: { logging: true } }); 97 | }); 98 | }); 99 | 100 | describe('custom meta table name', () => { 101 | it('correctly uses the defined table name', function (done) { 102 | const self = this; 103 | 104 | prepare(() => { 105 | helpers.readTables(self.sequelize, tables => { 106 | expect(tables.sort()).to.eql(['Person', 'SequelizeMeta', 'sequelize_data']); 107 | done(); 108 | }); 109 | }, { 110 | config: { seederStorage: 'sequelize', seederStorageTableName: 'sequelize_data' } 111 | }); 112 | }); 113 | }); 114 | }); 115 | }); 116 | -------------------------------------------------------------------------------- /test/db/seed/all.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/../../support'); 3 | const helpers = require(__dirname + '/../../support/helpers'); 4 | const gulp = require('gulp'); 5 | const _ = require('lodash'); 6 | 7 | [ 8 | 'db:seed:all', 9 | 'db:seed:all --seeders-path seeders', 10 | '--seeders-path seeders db:seed:all', 11 | 'db:seed:all --seeders-path ./seeders', 12 | 'db:seed:all --seeders-path ./seeders/', 13 | 'db:seed:all --config ../../support/tmp/config/config.json', 14 | 'db:seed:all --config ' + Support.resolveSupportPath('tmp', 'config', 'config.json'), 15 | 'db:seed:all --config ../../support/tmp/config/config.js' 16 | ].forEach(flag => { 17 | const prepare = function (callback, options) { 18 | options = _.assign({ config: {} }, options || {}); 19 | 20 | let configPath = 'config/'; 21 | let seederFile = options.seederFile || 'seedPerson'; 22 | const config = _.assign({}, helpers.getTestConfig(), options.config); 23 | let configContent = JSON.stringify(config); 24 | const migrationFile = 'createPerson.js'; 25 | 26 | seederFile = seederFile + '.js'; 27 | 28 | if (flag.match(/config\.js$/)) { 29 | configPath = configPath + 'config.js'; 30 | configContent = 'module.exports = ' + configContent; 31 | } else { 32 | configPath = configPath + 'config.json'; 33 | } 34 | 35 | gulp 36 | .src(Support.resolveSupportPath('tmp')) 37 | .pipe(helpers.clearDirectory()) 38 | .pipe(helpers.runCli('init')) 39 | .pipe(helpers.removeFile('config/config.json')) 40 | .pipe(helpers.copyMigration(migrationFile)) 41 | .pipe(helpers.copySeeder(seederFile)) 42 | .pipe(helpers.overwriteFile(configContent, configPath)) 43 | .pipe(helpers.runCli('db:migrate' + 44 | (flag.indexOf('config') === -1 ? '' : flag.replace('db:seed:all', '')) 45 | )) 46 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 47 | .pipe(helpers.teardown(callback)); 48 | }; 49 | 50 | describe(Support.getTestDialectTeaser(flag), () => { 51 | it('creates a SequelizeData table', function (done) { 52 | const self = this; 53 | 54 | prepare(() => { 55 | helpers.readTables(self.sequelize, tables => { 56 | expect(tables).to.have.length(3); 57 | expect(tables).to.contain('SequelizeData'); 58 | done(); 59 | }); 60 | }, { config: { seederStorage: 'sequelize' } }); 61 | }); 62 | 63 | it('populates the respective table', function (done) { 64 | const self = this; 65 | 66 | prepare(() => { 67 | helpers.countTable(self.sequelize, 'Person', result => { 68 | expect(result).to.have.length(1); 69 | expect(result[0].count).to.eql(1); 70 | done(); 71 | }); 72 | }); 73 | }); 74 | 75 | describe('the logging option', () => { 76 | it('does not print sql queries by default', done => { 77 | prepare((__, stdout) => { 78 | expect(stdout).to.not.contain('Executing'); 79 | done(); 80 | }); 81 | }); 82 | 83 | it('interpretes a custom option', done => { 84 | prepare((__, stdout) => { 85 | expect(stdout).to.contain('Executing'); 86 | done(); 87 | }, { config: { logging: true } }); 88 | }); 89 | }); 90 | 91 | describe('custom meta table name', () => { 92 | it('correctly uses the defined table name', function (done) { 93 | const self = this; 94 | 95 | prepare(() => { 96 | helpers.readTables(self.sequelize, tables => { 97 | expect(tables.sort()).to.eql(['Person', 'SequelizeMeta', 'sequelize_data']); 98 | done(); 99 | }); 100 | }, { 101 | config: { seederStorage: 'sequelize', seederStorageTableName: 'sequelize_data' } 102 | }); 103 | }); 104 | }); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /test/db/seed/undo.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/../../support'); 3 | const helpers = require(__dirname + '/../../support/helpers'); 4 | const gulp = require('gulp'); 5 | 6 | [ 7 | 'db:seed:undo --seed seedPerson.js' 8 | ].forEach(flag => { 9 | const prepare = function (callback, options) { 10 | const _flag = options.flag || flag; 11 | 12 | const pipeline = gulp 13 | .src(Support.resolveSupportPath('tmp')) 14 | .pipe(helpers.clearDirectory()) 15 | .pipe(helpers.runCli('init')) 16 | .pipe(helpers.copyMigration('createPerson.js')); 17 | 18 | if ( options.copySeeds ) { 19 | pipeline.pipe(helpers.copySeeder('seedPerson.js')); 20 | } 21 | 22 | pipeline.pipe(helpers.overwriteFile(JSON.stringify(helpers.getTestConfig()), 23 | 'config/config.json')) 24 | .pipe(helpers.runCli('db:migrate')) 25 | .pipe(helpers.runCli(_flag, { pipeStderr: true })) 26 | .pipe(helpers.teardown(callback)); 27 | }; 28 | 29 | describe(Support.getTestDialectTeaser(flag), () => { 30 | it('stops execution if no seeder file is found', done => { 31 | prepare((err, output) => { 32 | expect(output).to.contain('Unable to find migration'); 33 | done(); 34 | }, {copySeeds: false}); 35 | }); 36 | 37 | it('is correctly undoing a seeder if they have been done already', function (done) { 38 | const self = this; 39 | 40 | prepare(() => { 41 | helpers.readTables(self.sequelize, tables => { 42 | expect(tables).to.have.length(2); 43 | expect(tables[0]).to.equal('Person'); 44 | 45 | gulp 46 | .src(Support.resolveSupportPath('tmp')) 47 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 48 | .pipe(helpers.teardown(() => { 49 | helpers.countTable(self.sequelize, 'Person', res => { 50 | expect(res).to.have.length(1); 51 | expect(res[0].count).to.eql(0); 52 | done(); 53 | }); 54 | })); 55 | }); 56 | }, {flag: 'db:seed:all', copySeeds: true}); 57 | }); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/db/seed/undo/all.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/../../../support'); 3 | const helpers = require(__dirname + '/../../../support/helpers'); 4 | const gulp = require('gulp'); 5 | const _ = require('lodash'); 6 | 7 | [ 8 | 'db:seed:undo:all' 9 | ].forEach(flag => { 10 | const prepare = function (callback, options) { 11 | const _flag = options.flag || flag; 12 | const config = _.assign({}, helpers.getTestConfig(), options.config || {}); 13 | 14 | const pipeline = gulp 15 | .src(Support.resolveSupportPath('tmp')) 16 | .pipe(helpers.clearDirectory()) 17 | .pipe(helpers.runCli('init')) 18 | .pipe(helpers.copyMigration('createPerson.js')); 19 | 20 | if ( options.copySeeds ) { 21 | pipeline.pipe(helpers.copySeeder('seedPerson.js')) 22 | .pipe(helpers.copySeeder('seedPerson2.js')); 23 | } 24 | 25 | pipeline.pipe(helpers.overwriteFile(JSON.stringify(config), 26 | 'config/config.json')) 27 | .pipe(helpers.runCli('db:migrate')) 28 | .pipe(helpers.runCli(_flag, { pipeStdout: true })) 29 | .pipe(helpers.teardown(callback)); 30 | }; 31 | 32 | describe(Support.getTestDialectTeaser(flag), () => { 33 | it('stops execution if no seeders have been found', done => { 34 | prepare((err, output) => { 35 | expect(err).to.equal(null); 36 | expect(output).to.contain('No seeders found.'); 37 | done(); 38 | }, {copySeeds: false}); 39 | }); 40 | 41 | it('is correctly undoing all seeders if they have been done already', function (done) { 42 | const self = this; 43 | 44 | prepare(() => { 45 | helpers.countTable(self.sequelize, 'Person', res => { 46 | expect(res).to.have.length(1); 47 | expect(res[0].count).to.eql(2); 48 | 49 | gulp 50 | .src(Support.resolveSupportPath('tmp')) 51 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 52 | .pipe(helpers.teardown(() => { 53 | helpers.countTable(self.sequelize, 'Person', res => { 54 | expect(res).to.have.length(1); 55 | expect(res[0].count).to.eql(0); 56 | done(); 57 | }); 58 | })); 59 | }); 60 | }, {flag: 'db:seed:all', copySeeds: true}); 61 | }); 62 | 63 | it('is correctly undoing all seeders when storage is none', function (done) { 64 | const self = this; 65 | 66 | prepare(() => { 67 | helpers.countTable(self.sequelize, 'Person', res => { 68 | expect(res).to.have.length(1); 69 | expect(res[0].count).to.eql(2); 70 | 71 | gulp 72 | .src(Support.resolveSupportPath('tmp')) 73 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 74 | .pipe(helpers.teardown(() => { 75 | helpers.countTable(self.sequelize, 'Person', res => { 76 | expect(res).to.have.length(1); 77 | expect(res[0].count).to.eql(0); 78 | done(); 79 | }); 80 | })); 81 | }); 82 | }, { 83 | flag: 'db:seed:all', 84 | copySeeds: true, 85 | config: { seederStorage: 'none' } 86 | }); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /test/environment-variable.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/support'); 3 | const helpers = require(__dirname + '/support/helpers'); 4 | const gulp = require('gulp'); 5 | 6 | const prepare = function (callback) { 7 | gulp 8 | .src(Support.resolveSupportPath('tmp')) 9 | .pipe(helpers.clearDirectory()) 10 | .pipe(helpers.runCli('init')) 11 | .pipe(helpers.copyMigration('createPerson.js')) 12 | .pipe( 13 | helpers.overwriteFile( 14 | JSON.stringify({ use_env_variable: 'SEQUELIZE_DB_URL' }), 15 | 'config/config.json' 16 | ) 17 | ) 18 | .pipe(helpers.runCli('db:migrate', { env: { SEQUELIZE_DB_URL: helpers.getTestUrl() } })) 19 | .pipe(helpers.teardown(callback)); 20 | }; 21 | 22 | describe(Support.getTestDialectTeaser('use_env_variable'), () => { 23 | beforeEach(prepare); 24 | 25 | it('correctly runs the migration', function (done) { 26 | helpers.readTables(this.sequelize, tables => { 27 | expect(tables).to.have.length(2); 28 | expect(tables).to.contain('SequelizeMeta'); 29 | done(); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/help.test.js: -------------------------------------------------------------------------------- 1 | const Support = require(__dirname + '/support'); 2 | const helpers = require(__dirname + '/support/helpers'); 3 | const gulp = require('gulp'); 4 | 5 | [ 6 | '--help' 7 | ].forEach(flag => { 8 | describe(Support.getTestDialectTeaser(flag), () => { 9 | it('prints the help', done => { 10 | gulp 11 | .src(process.cwd()) 12 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 13 | .pipe(helpers.ensureContent('Commands:\n')) 14 | .pipe(helpers.teardown(done)); 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/init.test.js: -------------------------------------------------------------------------------- 1 | const Support = require(__dirname + '/support'); 2 | const helpers = require(__dirname + '/support/helpers'); 3 | const gulp = require('gulp'); 4 | 5 | [ 6 | 'init' 7 | ].forEach(flag => { 8 | describe(Support.getTestDialectTeaser(flag), () => { 9 | (function (folders) { 10 | folders.forEach(folder => { 11 | it('creates "' + folder + '"', done => { 12 | let sourcePath = Support.resolveSupportPath('tmp'); 13 | let file = folder; 14 | 15 | if (folder.indexOf('/') > -1) { 16 | const split = folder.split('/'); 17 | 18 | file = split.pop(); 19 | sourcePath = Support.resolveSupportPath('tmp', split.join('/')); 20 | } 21 | 22 | gulp 23 | .src(Support.resolveSupportPath('tmp')) 24 | .pipe(helpers.clearDirectory()) 25 | .pipe(helpers.runCli(flag)) 26 | .pipe(helpers.teardown(() => { 27 | gulp 28 | .src(sourcePath) 29 | .pipe(helpers.listFiles()) 30 | .pipe(helpers.ensureContent(file)) 31 | .pipe(helpers.teardown(done)); 32 | })); 33 | }); 34 | }); 35 | })([ 36 | 'config', 37 | 'config/config.json', 38 | 'migrations', 39 | 'models', 40 | 'models/index.js' 41 | ]); 42 | 43 | it('creates a custom config folder', done => { 44 | gulp 45 | .src(Support.resolveSupportPath('tmp')) 46 | .pipe(helpers.clearDirectory()) 47 | .pipe(helpers.runCli(flag + ' --config my-config/config/config.json')) 48 | .pipe(helpers.listFiles()) 49 | .pipe(helpers.ensureContent('my-config')) 50 | .pipe(helpers.teardown(done)); 51 | }); 52 | 53 | it('creates a custom migrations folder', done => { 54 | gulp 55 | .src(Support.resolveSupportPath('tmp')) 56 | .pipe(helpers.clearDirectory()) 57 | .pipe(helpers.runCli(flag + ' --migrations-path ./db/migrate')) 58 | .pipe(helpers.listFiles()) 59 | .pipe(helpers.ensureContent('db')) 60 | .pipe(helpers.teardown(done)); 61 | }); 62 | 63 | it('creates a custom config file', done => { 64 | gulp 65 | .src(Support.resolveSupportPath('tmp')) 66 | .pipe(helpers.clearDirectory()) 67 | .pipe(helpers.runCli(flag + ' --config config/database.json')) 68 | .pipe(helpers.teardown(() => { 69 | gulp 70 | .src(Support.resolveSupportPath('tmp', 'config')) 71 | .pipe(helpers.listFiles()) 72 | .pipe(helpers.ensureContent('database.json')) 73 | .pipe(helpers.teardown(done)); 74 | })); 75 | }); 76 | 77 | it('creates a custom models folder', done => { 78 | gulp 79 | .src(Support.resolveSupportPath('tmp')) 80 | .pipe(helpers.clearDirectory()) 81 | .pipe(helpers.runCli(flag + ' --models-path daos')) 82 | .pipe(helpers.listFiles()) 83 | .pipe(helpers.ensureContent('daos')) 84 | .pipe(helpers.teardown(done)); 85 | }); 86 | 87 | describe('models/index.js', () => { 88 | it('correctly injects the reference to the default config file', done => { 89 | gulp 90 | .src(Support.resolveSupportPath('tmp')) 91 | .pipe(helpers.clearDirectory()) 92 | .pipe(helpers.runCli(flag)) 93 | .pipe(helpers.teardown(() => { 94 | gulp 95 | .src(Support.resolveSupportPath('tmp', 'models')) 96 | .pipe(helpers.readFile('index.js')) 97 | .pipe(helpers.ensureContent('__dirname + \'/../config/config.json\'')) 98 | .pipe(helpers.teardown(done)); 99 | })); 100 | }); 101 | 102 | it('correctly injects the reference to the custom config file', done => { 103 | gulp 104 | .src(Support.resolveSupportPath('tmp')) 105 | .pipe(helpers.clearDirectory()) 106 | .pipe(helpers.runCli(flag + ' --config my/configuration-file.json')) 107 | .pipe(helpers.teardown(() => { 108 | gulp 109 | .src(Support.resolveSupportPath('tmp', 'models')) 110 | .pipe(helpers.readFile('index.js')) 111 | .pipe(helpers.ensureContent('__dirname + \'/../my/configuration-file.json\'')) 112 | .pipe(helpers.teardown(done)); 113 | })); 114 | }); 115 | }); 116 | 117 | it('does not overwrite an existing config.json file', done => { 118 | gulp 119 | .src(Support.resolveSupportPath('tmp')) 120 | .pipe(helpers.clearDirectory()) 121 | .pipe(helpers.runCli(flag)) 122 | .pipe(helpers.overwriteFile('foo', 'config/config.json')) 123 | .pipe(helpers.runCli(flag, { exitCode: 1 })) 124 | .pipe(helpers.readFile('config/config.json')) 125 | .pipe(helpers.ensureContent('foo')) 126 | .pipe(helpers.teardown(done)); 127 | }); 128 | }); 129 | }); 130 | -------------------------------------------------------------------------------- /test/migration/create.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/../support'); 3 | const helpers = require(__dirname + '/../support/helpers'); 4 | const gulp = require('gulp'); 5 | 6 | [ 7 | 'migration:create' 8 | ].forEach(flag => { 9 | describe(Support.getTestDialectTeaser(flag), () => { 10 | const migrationFile = 'foo.js'; 11 | const prepare = function (callback) { 12 | gulp 13 | .src(Support.resolveSupportPath('tmp')) 14 | .pipe(helpers.clearDirectory()) 15 | .pipe(helpers.runCli('init')) 16 | .pipe(helpers.runCli(flag + ' --name=foo')) 17 | .pipe(helpers.teardown(callback)); 18 | }; 19 | 20 | it('creates a new file with the current timestamp', done => { 21 | prepare(() => { 22 | const date = new Date(); 23 | const format = function (i) { 24 | return parseInt(i, 10) < 10 ? '0' + i : i; 25 | }; 26 | const sDate = [ 27 | date.getUTCFullYear(), 28 | format(date.getUTCMonth() + 1), 29 | format(date.getUTCDate()), 30 | format(date.getUTCHours()), 31 | format(date.getUTCMinutes()) 32 | ].join(''); 33 | const expectation = new RegExp(sDate + '..-' + migrationFile); 34 | 35 | gulp 36 | .src(Support.resolveSupportPath('tmp', 'migrations')) 37 | .pipe(helpers.listFiles()) 38 | .pipe(helpers.ensureContent(expectation)) 39 | .pipe(helpers.teardown(done)); 40 | }); 41 | }); 42 | 43 | it('adds a skeleton with an up and a down method', done => { 44 | prepare(() => { 45 | gulp 46 | .src(Support.resolveSupportPath('tmp', 'migrations')) 47 | .pipe(helpers.readFile('*-' + migrationFile)) 48 | .pipe(helpers.expect(stdout => { 49 | expect(stdout).to.contain('up: (queryInterface, Sequelize) => {'); 50 | expect(stdout).to.contain('down: (queryInterface, Sequelize) => {'); 51 | })) 52 | .pipe(helpers.teardown(done)); 53 | }); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/model/create.test.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const expect = require('expect.js'); 4 | const Support = require(__dirname + '/../support'); 5 | const helpers = require(__dirname + '/../support/helpers'); 6 | const gulp = require('gulp'); 7 | const _ = require('lodash'); 8 | 9 | [ 10 | 'model:create' 11 | ].forEach(flag => { 12 | describe(Support.getTestDialectTeaser(flag), () => { 13 | const combineFlags = function (flags) { 14 | let result = flag; 15 | 16 | _.forEach(flags || {}, (value, key) => { 17 | result = result + ' --' + key + ' ' + value; 18 | }); 19 | 20 | return result; 21 | }; 22 | 23 | const prepare = function (options, callback) { 24 | options = _.assign({ 25 | flags: {}, 26 | cli: { pipeStdout: true } 27 | }, options || {}); 28 | 29 | gulp 30 | .src(Support.resolveSupportPath('tmp')) 31 | .pipe(helpers.clearDirectory()) 32 | .pipe(helpers.runCli('init')) 33 | .pipe(helpers.runCli(combineFlags(options.flags), options.cli)) 34 | .pipe(helpers.teardown(callback)); 35 | }; 36 | 37 | describe('name', () => { 38 | describe('when missing', () => { 39 | it('exits with an error code', done => { 40 | prepare({ 41 | flags: { attributes: 'first_name:string' }, 42 | cli: { exitCode: 1 } 43 | }, done); 44 | }); 45 | 46 | it('notifies the user about a missing name flag', done => { 47 | prepare({ 48 | flags: { attributes: 'first_name:string' }, 49 | cli: { pipeStderr: true } 50 | }, (err, stdout) => { 51 | expect(stdout).to.match(/Missing required argument: name/); 52 | done(); 53 | }); 54 | }); 55 | }); 56 | }); 57 | 58 | describe('attributes', () => { 59 | describe('when missing', () => { 60 | it('exits with an error code', done => { 61 | prepare({ 62 | flags: { name: 'User' }, 63 | cli: { exitCode: 1 } 64 | }, done); 65 | }); 66 | 67 | it('notifies the user about a missing attributes flag', done => { 68 | prepare({ 69 | flags: { name: 'User' }, 70 | cli: { pipeStderr: true } 71 | }, (err, stdout) => { 72 | expect(stdout).to.match(/Missing required argument: attributes/); 73 | done(); 74 | }); 75 | }); 76 | }); 77 | 78 | ;[ 79 | 'first_name:string,last_name:string,bio:text,reviews:array:text', 80 | '\'first_name:string last_name:string bio:text reviews:array:text\'', 81 | '\'first_name:string, last_name:string, bio:text, reviews:array:text\'' 82 | ].forEach(attributes => { 83 | describe('--attributes ' + attributes, () => { 84 | it('exits with exit code 0', done => { 85 | prepare({ 86 | flags: { name: 'User', attributes }, 87 | cli: { exitCode: 0 } 88 | }, done); 89 | }); 90 | 91 | it('creates the model file', done => { 92 | prepare({ 93 | flags: { name: 'User', attributes } 94 | }, () => { 95 | gulp 96 | .src(Support.resolveSupportPath('tmp', 'models')) 97 | .pipe(helpers.listFiles()) 98 | .pipe(helpers.ensureContent('user.js')) 99 | .pipe(helpers.teardown(done)); 100 | }); 101 | }); 102 | 103 | it('generates the model attributes correctly', done => { 104 | prepare({ 105 | flags: { name: 'User', attributes } 106 | }, () => { 107 | gulp 108 | .src(Support.resolveSupportPath('tmp', 'models')) 109 | .pipe(helpers.readFile('user.js')) 110 | .pipe(helpers.ensureContent('sequelize.define(\'User\'')) 111 | .pipe(helpers.ensureContent('first_name: DataTypes.STRING')) 112 | .pipe(helpers.ensureContent('last_name: DataTypes.STRING')) 113 | .pipe(helpers.ensureContent('bio: DataTypes.TEXT')) 114 | .pipe(helpers.ensureContent('reviews: DataTypes.ARRAY(DataTypes.TEXT)')) 115 | .pipe(helpers.teardown(done)); 116 | }); 117 | }); 118 | 119 | it('creates the migration file', done => { 120 | prepare({ 121 | flags: { name: 'User', attributes } 122 | }, () => { 123 | gulp 124 | .src(Support.resolveSupportPath('tmp', 'migrations')) 125 | .pipe(helpers.listFiles()) 126 | .pipe(helpers.ensureContent(/\d+-create-user.js/)) 127 | .pipe(helpers.teardown(done)); 128 | }); 129 | }); 130 | 131 | [ 132 | { underscored: true, createdAt: 'created_at', updatedAt: 'updated_at'}, 133 | { underscored: false, createdAt: 'createdAt', updatedAt: 'updatedAt'} 134 | ].forEach(attrUnd => { 135 | describe((attrUnd.underscored ? '' : 'without ') + '--underscored', () => { 136 | it('generates the migration content correctly', done => { 137 | const flags = { 138 | name: 'User', 139 | attributes 140 | }; 141 | 142 | if ( attrUnd.underscored ) { 143 | flags.underscored = attrUnd.underscored; 144 | } 145 | 146 | prepare({ 147 | flags 148 | }, () => { 149 | gulp 150 | .src(Support.resolveSupportPath('tmp', 'migrations')) 151 | .pipe(helpers.readFile('*-create-user.js')) 152 | .pipe(helpers.ensureContent('return queryInterface')) 153 | .pipe(helpers.ensureContent('.createTable(\'Users\', {')) 154 | .pipe(helpers.ensureContent( 155 | 'first_name: {\n type: Sequelize.STRING\n },' 156 | )) 157 | .pipe(helpers.ensureContent( 158 | 'last_name: {\n type: Sequelize.STRING\n },' 159 | )) 160 | .pipe(helpers.ensureContent( 161 | 'bio: {\n type: Sequelize.TEXT\n },' 162 | )) 163 | .pipe(helpers.ensureContent( 164 | 'reviews: {\n type: Sequelize.ARRAY(Sequelize.TEXT)\n },' 165 | )) 166 | .pipe(helpers.ensureContent([ 167 | ' id: {', 168 | ' allowNull: false,', 169 | ' autoIncrement: true,', 170 | ' primaryKey: true,', 171 | ' type: Sequelize.INTEGER', 172 | ' },' 173 | ].join('\n'))) 174 | .pipe(helpers.ensureContent([ 175 | ' ' + attrUnd.createdAt + ': {', 176 | ' allowNull: false,', 177 | ' type: Sequelize.DATE', 178 | ' },' 179 | ].join('\n'))) 180 | .pipe(helpers.ensureContent([ 181 | ' ' + attrUnd.updatedAt + ': {', 182 | ' allowNull: false,', 183 | ' type: Sequelize.DATE', 184 | ' }' 185 | ].join('\n'))) 186 | .pipe(helpers.ensureContent('});')) 187 | .pipe(helpers.ensureContent('.dropTable(\'Users\')')) 188 | .pipe(helpers.teardown(done)); 189 | }); 190 | }); 191 | 192 | it('generates the model content correctly', done => { 193 | const flags = { 194 | name: 'User', 195 | attributes 196 | }; 197 | 198 | const targetContent = attrUnd.underscored ? 199 | 'underscored: true' 200 | : '{\n classMethods'; 201 | 202 | if ( attrUnd.underscored ) { 203 | flags.underscored = attrUnd.underscored; 204 | } 205 | 206 | prepare({ 207 | flags 208 | }, () => { 209 | gulp 210 | .src(Support.resolveSupportPath('tmp', 'models')) 211 | .pipe(helpers.readFile('user.js')) 212 | .pipe(helpers.ensureContent(targetContent)) 213 | .pipe(helpers.teardown(done)); 214 | }); 215 | }); 216 | }); 217 | }); 218 | 219 | describe('when called twice', () => { 220 | beforeEach(function (done) { 221 | this.flags = { name: 'User', attributes }; 222 | prepare({ flags: this.flags }, done); 223 | }); 224 | 225 | it('exits with an error code', function (done) { 226 | gulp 227 | .src(Support.resolveSupportPath('tmp')) 228 | .pipe(helpers.runCli(combineFlags(this.flags), { exitCode: 1 })) 229 | .pipe(helpers.teardown(done)); 230 | }); 231 | 232 | it('notifies the user about the possibility of --flags', function (done) { 233 | gulp 234 | .src(Support.resolveSupportPath('tmp')) 235 | .pipe(helpers.runCli(combineFlags(this.flags), { pipeStderr: true })) 236 | .pipe(helpers.teardown((err, stderr) => { 237 | expect(stderr).to.contain('already exists'); 238 | done(); 239 | })); 240 | }); 241 | }); 242 | }); 243 | }); 244 | }); 245 | }); 246 | }); 247 | -------------------------------------------------------------------------------- /test/options.test.js: -------------------------------------------------------------------------------- 1 | const Support = require(__dirname + '/support'); 2 | const helpers = require(__dirname + '/support/helpers'); 3 | const gulp = require('gulp'); 4 | const optionsPath = Support.resolveSupportPath('config', 'options.js'); 5 | 6 | describe(Support.getTestDialectTeaser('options'), () => { 7 | describe('--options-path', () => { 8 | [ 9 | optionsPath, 10 | require('path').relative(Support.resolveSupportPath('tmp'), optionsPath) 11 | ].forEach(path => { 12 | it('using options file instead of cli switches (' + path + ')', done => { 13 | gulp 14 | .src(Support.resolveSupportPath('tmp')) 15 | .pipe(helpers.clearDirectory()) 16 | .pipe(helpers.runCli('init --options-path ' + path)) 17 | .pipe(helpers.listFiles()) 18 | .pipe(helpers.ensureContent('models')) 19 | .pipe(helpers.teardown(done)); 20 | }); 21 | }); 22 | }); 23 | 24 | describe('.sequelizerc', () => { 25 | it('uses .sequelizerc file', done => { 26 | const configContent = ` 27 | var path = require('path'); 28 | 29 | module.exports = { 30 | 'config': path.resolve('config-new', 'database.json'), 31 | 'migrations-path': path.resolve('migrations-new') 32 | }; 33 | `; 34 | 35 | gulp 36 | .src(Support.resolveSupportPath('tmp')) 37 | .pipe(helpers.clearDirectory()) 38 | .pipe(helpers.copyFile(optionsPath, '.sequelizerc')) 39 | .pipe(helpers.overwriteFile(configContent, '.sequelizerc')) 40 | .pipe(helpers.runCli('init')) 41 | .pipe(helpers.listFiles()) 42 | .pipe(helpers.ensureContent('migrations-new')) 43 | .pipe(helpers.ensureContent('config-new')) 44 | .pipe(helpers.teardown(done)); 45 | }); 46 | 47 | it('prefers CLI arguments over .sequelizerc file', done => { 48 | const configPath = Support.resolveSupportPath('tmp', 'config', 'config.js'); 49 | 50 | gulp 51 | .src(Support.resolveSupportPath('tmp')) 52 | .pipe(helpers.clearDirectory()) 53 | .pipe(helpers.copyFile(optionsPath, '.sequelizerc')) 54 | .pipe(helpers.runCli('init --config=' + configPath)) 55 | .pipe(helpers.listFiles()) 56 | .pipe(helpers.ensureContent('models')) 57 | .pipe(helpers.teardown(done)); 58 | }); 59 | }); 60 | }); -------------------------------------------------------------------------------- /test/seed/create.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/../support'); 3 | const helpers = require(__dirname + '/../support/helpers'); 4 | const gulp = require('gulp'); 5 | 6 | [ 7 | 'seed:create' 8 | ].forEach(flag => { 9 | describe(Support.getTestDialectTeaser(flag), () => { 10 | const seedFile = 'foo.js'; 11 | 12 | const prepare = function (callback) { 13 | gulp 14 | .src(Support.resolveSupportPath('tmp')) 15 | .pipe(helpers.clearDirectory()) 16 | .pipe(helpers.runCli('init')) 17 | .pipe(helpers.runCli(flag + ' --name=foo')) 18 | .pipe(helpers.teardown(callback)); 19 | }; 20 | 21 | it('creates a new file with the current timestamp', done => { 22 | prepare(() => { 23 | const date = new Date(); 24 | const format = function (i) { 25 | return parseInt(i, 10) < 10 ? '0' + i : i; 26 | }; 27 | const sDate = [ 28 | date.getUTCFullYear(), 29 | format(date.getUTCMonth() + 1), 30 | format(date.getUTCDate()), 31 | format(date.getUTCHours()), 32 | format(date.getUTCMinutes()) 33 | ].join(''); 34 | 35 | const expectation = new RegExp(sDate + '..-' + seedFile); 36 | 37 | gulp 38 | .src(Support.resolveSupportPath('tmp', 'seeders')) 39 | .pipe(helpers.listFiles()) 40 | .pipe(helpers.ensureContent(expectation)) 41 | .pipe(helpers.teardown(done)); 42 | }); 43 | }); 44 | 45 | it('adds a skeleton with an up and a down method', done => { 46 | prepare(() => { 47 | gulp 48 | .src(Support.resolveSupportPath('tmp', 'seeders')) 49 | .pipe(helpers.readFile('*-' + seedFile)) 50 | .pipe(helpers.expect(stdout => { 51 | expect(stdout).to.contain('up: (queryInterface, Sequelize) => {'); 52 | expect(stdout).to.contain('down: (queryInterface, Sequelize) => {'); 53 | })) 54 | .pipe(helpers.teardown(done)); 55 | }); 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20111117063700-createPerson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration 8 | .createTable('Person', { 9 | name: DataTypes.STRING, 10 | isBetaMember: { 11 | type: DataTypes.BOOLEAN, 12 | defaultValue: false, 13 | allowNull: false 14 | } 15 | }), done); 16 | }, 17 | 18 | down: function (migration, DataTypes, done) { 19 | nodeify(migration.dropTable('Person'), done); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20111130161100-emptyMigration.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: function (migration, DataTypes, done) { 5 | done(); 6 | }, 7 | down: function (migration, DataTypes, done) { 8 | done(); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20111205064000-renamePersonToUser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration.renameTable('Person', 'User'), done); 8 | }, 9 | 10 | down: function (migration, DataTypes, done) { 11 | nodeify(migration.renameTable('User', 'Person'), done); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20111205162700-addSignatureColumnToUser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: function (migration, DataTypes, done) { 5 | migration 6 | .addColumn('User', 'isAdmin', { 7 | type: DataTypes.BOOLEAN, 8 | defaultValue: false, 9 | allowNull: false 10 | }) 11 | .done(function (err) { 12 | if (err) { 13 | done(err); 14 | } else { 15 | migration 16 | .addColumn('User', 'signature', DataTypes.TEXT) 17 | .done(function (err) { 18 | if (err) { 19 | done(err); 20 | } else { 21 | migration.addColumn('User', 'shopId', { 22 | type: DataTypes.INTEGER, 23 | allowNull: true 24 | }).done(function () { 25 | done(); 26 | }); 27 | } 28 | }); 29 | } 30 | }); 31 | }, 32 | 33 | down: function (migration, DataTypes, done) { 34 | migration.removeColumn('User', 'signature').done(function (err) { 35 | if (err) { 36 | done(err); 37 | } else { 38 | migration.removeColumn('User', 'shopId').done(function (err) { 39 | if (err) { 40 | done(err); 41 | } else { 42 | migration.removeColumn('User', 'isAdmin').done(function () { 43 | done(); 44 | }); 45 | } 46 | }); 47 | } 48 | }); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20111205167000-addUniqueNameColumnToUser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify( 8 | migration.addColumn('User', 'uniqueName', { type: DataTypes.STRING }), 9 | function () { 10 | nodeify( 11 | migration.changeColumn('User', 'uniqueName', { 12 | type: DataTypes.STRING, 13 | allowNull: false, 14 | unique: true 15 | }), 16 | done 17 | ); 18 | } 19 | ); 20 | }, 21 | 22 | down: function (migration, DataTypes, done) { 23 | nodeify(migration.removeColumn('User', 'uniqueName'), done); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20111206061400-removeShopIdColumnFromUser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration.removeColumn('User', 'shopId'), done); 8 | }, 9 | 10 | down: function (migration, DataTypes, done) { 11 | nodeify(migration.addColumn('User', 'shopId', { 12 | type: DataTypes.INTEGER, 13 | allowNull: true 14 | }), done); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20111206063000-changeSignatureColumnOfUserToMendatory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration.changeColumn('User', 'signature', { 8 | type: DataTypes.STRING, 9 | allowNull: false, 10 | defaultValue: 'Signature' 11 | }), done); 12 | }, 13 | 14 | down: function (migration, DataTypes, done) { 15 | done(); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20111206163300-renameSignatureColumnOfUserToSig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration.renameColumn('User', 'signature', 'sig'), done); 8 | }, 9 | 10 | down: function (migration, DataTypes, done) { 11 | nodeify(migration.renameColumn('User', 'sig', 'signature'), done); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20130909174103-createFunctionGetAnAnswer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration.createFunction('get_an_answer', [], 'int', 'plpgsql', 'RETURN 42;'), done); 8 | }, 9 | down: function (migration, DataTypes, done) { 10 | nodeify(migration.dropFunction('get_an_answer', []), done); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20130909174253-renameFunctionGetAnAnswerGetTheAnswer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration.renameFunction('get_an_answer', [], 'get_the_answer'), done); 8 | }, 9 | down: function (migration, DataTypes, done) { 10 | nodeify(migration.renameFunction('get_the_answer', [], 'get_an_answer'), done); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20130909175000-deleteFunctionGetTheAnswer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration.dropFunction('get_the_answer', []), done); 8 | }, 9 | down: function (migration, DataTypes, done) { 10 | nodeify(migration.createFunction('get_the_answer', 'int', 'plpgsql', 'RETURN 42;'), done); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20130909175939-createTestTableForTrigger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify( 8 | migration.createTable('trigger_test', { 9 | name: DataTypes.STRING, 10 | updated_at: { 11 | type: DataTypes.DATE, 12 | defaultValue: DataTypes.NOW, 13 | allowNull: false 14 | } 15 | }), 16 | done 17 | ); 18 | }, 19 | 20 | down: function (migration, DataTypes, done) { 21 | nodeify(migration.dropTable('trigger_test'), done); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20130909180846-createTriggerOnTriggerTestTable.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify( 8 | migration.createTrigger( 9 | 'trigger_test', 10 | 'updated_at', 11 | 'before', 12 | { update: true }, 13 | 'bump_updated_at', 14 | [] 15 | ), 16 | done 17 | ); 18 | }, 19 | down: function (migration, DataTypes, done) { 20 | nodeify(migration.dropTrigger('trigger_test', 'updated_at'), done); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20130909181148-renameTriggerUpdatedAtToUpdateUpdatedAt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration.renameTrigger('trigger_test', 'updated_at', 'update_updated_at'), done); 8 | }, 9 | down: function (migration, DataTypes, done) { 10 | nodeify(migration.renameTrigger('trigger_test', 'update_updated_at', 'updated_at'), done); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20130909185621-deleteTriggerUpdateUpdatedAt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration.dropTrigger('trigger_test', 'update_updated_at'), done); 8 | }, 9 | 10 | down: function (migration, DataTypes, done) { 11 | nodeify( 12 | migration.createTrigger( 13 | 'trigger_test', 14 | 'update_updated_at', 15 | 'before', 16 | { update: true }, 17 | 'bump_updated_at', 18 | [] 19 | ), 20 | done 21 | ); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /test/support/assets/migrations/20170526153000-createPost.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration 8 | .createTable('Post', { 9 | title: DataTypes.STRING, 10 | body: DataTypes.TEXT 11 | }), done); 12 | }, 13 | 14 | down: function (migration, DataTypes, done) { 15 | nodeify(migration.dropTable('Post'), done); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /test/support/assets/migrations/invalid/20141208213500-createPerson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Bluebird = require('bluebird'); 4 | 5 | module.exports = { 6 | up: function (db) { 7 | return Bluebird 8 | .delay(1000) 9 | .then(function () { 10 | return db.sequelize.query('INVALID QUERY'); 11 | }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /test/support/assets/migrations/new/20141208213500-createPerson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Bluebird = require('bluebird'); 4 | var Sequelize = require('sequelize'); 5 | 6 | module.exports = { 7 | up: function (db) { 8 | return Bluebird 9 | .delay(1000) 10 | .then(function () { 11 | return db.createTable('Person', { name: Sequelize.STRING }); 12 | }) 13 | .then(function () { 14 | return db.createTable('Task', { title: Sequelize.STRING }); 15 | }); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /test/support/assets/project.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (sequelize, DataTypes) { 4 | return sequelize.define('Project' + parseInt(Math.random() * 9999999999999999, 10), { 5 | name: DataTypes.STRING 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /test/support/assets/seeders/20111117063700-seedPerson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration.bulkInsert('Person', [{ 8 | name: 'John Doe', 9 | isBetaMember: false 10 | }], { 11 | name: {}, 12 | isBetaMember: {} 13 | }), done); 14 | }, 15 | down: function (migration, DataTypes, done) { 16 | nodeify(migration.bulkDelete('Person', null, {}), done); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /test/support/assets/seeders/20111117063900-seedPerson2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeify = require('nodeify'); 4 | 5 | module.exports = { 6 | up: function (migration, DataTypes, done) { 7 | nodeify(migration 8 | .bulkInsert('Person', [{ 9 | name: 'Jane Doe', 10 | isBetaMember: false 11 | }], { 12 | name: {}, 13 | isBetaMember: {} 14 | }), done); 15 | }, 16 | down: function (migration, DataTypes, done) { 17 | nodeify(migration.bulkDelete('Person', null, {}), done); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /test/support/assets/seeders/new/20141208213500-seedPerson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Bluebird = require('bluebird'); 4 | 5 | module.exports = { 6 | up: function (db) { 7 | return Bluebird 8 | .delay(1000) 9 | .then(function () { 10 | return db.bulkInsert('Person', [{ name: 'John Doe' }], { name: {} }); 11 | }) 12 | .then(function () { 13 | return db.insert('Task', [{ title: 'Find own identity' }], { name: {} }); 14 | }); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /test/support/config/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | username: process.env.SEQ_USER || 'root', 5 | password: process.env.SEQ_PW || null, 6 | database: process.env.SEQ_DB || 'sequelize_test', 7 | host: process.env.SEQ_HOST || '127.0.0.1', 8 | pool: { 9 | maxConnections: process.env.SEQ_POOL_MAX || 5, 10 | maxIdleTime: process.env.SEQ_POOL_IDLE || 30000 11 | }, 12 | 13 | rand: function () { 14 | return parseInt(Math.random() * 999, 10); 15 | }, 16 | 17 | // Make maxIdleTime small so that tests exit promptly 18 | mysql: { 19 | database: process.env.SEQ_MYSQL_DB || process.env.SEQ_DB || 'sequelize_test', 20 | username: process.env.SEQ_MYSQL_USER || process.env.SEQ_USER || 'root', 21 | password: process.env.SEQ_MYSQL_PW || process.env.SEQ_PW || null, 22 | host: process.env.SEQ_MYSQL_HOST || process.env.SEQ_HOST || '127.0.0.1', 23 | port: process.env.SEQ_MYSQL_PORT || process.env.SEQ_PORT || 3306, 24 | pool: { 25 | maxConnections: process.env.SEQ_MYSQL_POOL_MAX || process.env.SEQ_POOL_MAX || 5, 26 | maxIdleTime: process.env.SEQ_MYSQL_POOL_IDLE || process.env.SEQ_POOL_IDLE || 3000 27 | } 28 | }, 29 | 30 | sqlite: { 31 | }, 32 | 33 | postgres: { 34 | database: process.env.SEQ_PG_DB || process.env.SEQ_DB || 'sequelize_test', 35 | username: process.env.SEQ_PG_USER || process.env.SEQ_USER || 'postgres', 36 | password: process.env.SEQ_PG_PW || process.env.SEQ_PW || 'postgres', 37 | host: process.env.SEQ_PG_HOST || process.env.SEQ_HOST || '127.0.0.1', 38 | port: process.env.SEQ_PG_PORT || process.env.SEQ_PORT || 5432, 39 | pool: { 40 | maxConnections: process.env.SEQ_PG_POOL_MAX || process.env.SEQ_POOL_MAX || 5, 41 | maxIdleTime: process.env.SEQ_PG_POOL_IDLE || process.env.SEQ_POOL_IDLE || 3000 42 | } 43 | }, 44 | 45 | mariadb: { 46 | database: process.env.SEQ_MYSQL_DB || process.env.SEQ_DB || 'sequelize_test', 47 | username: process.env.SEQ_MYSQL_USER || process.env.SEQ_USER || 'root', 48 | password: process.env.SEQ_MYSQL_PW || process.env.SEQ_PW || null, 49 | host: process.env.SEQ_MYSQL_HOST || process.env.SEQ_HOST || '127.0.0.1', 50 | port: process.env.SEQ_MYSQL_PORT || process.env.SEQ_PORT || 3306, 51 | pool: { 52 | maxConnections: process.env.SEQ_MYSQL_POOL_MAX || process.env.SEQ_POOL_MAX || 5, 53 | maxIdleTime: process.env.SEQ_MYSQL_POOL_IDLE || process.env.SEQ_POOL_IDLE || 3000 54 | } 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /test/support/config/options.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | 5 | module.exports = { 6 | 'config': path.resolve('config', 'database.json'), 7 | 'migrations-path': path.resolve('db', 'migrate') 8 | }; 9 | -------------------------------------------------------------------------------- /test/support/helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var exec = require('child_process').exec; 5 | var support = require('./index'); 6 | var through = require('through2'); 7 | var expect = require('expect.js'); 8 | var path = require('path'); 9 | var fs = require('fs-extra'); 10 | 11 | module.exports = { 12 | getTestConfig: function (mixin) { 13 | var dialect = support.getTestDialect(); 14 | var config = require(support.resolveSupportPath('config', 'config.js')); 15 | 16 | config.sqlite.storage = support.resolveSupportPath('tmp', 'test.sqlite'); 17 | config = _.extend( 18 | config, 19 | config[dialect], 20 | mixin || {}, 21 | { dialect: dialect } 22 | ); 23 | 24 | return config; 25 | }, 26 | 27 | getTestUrl: function () { 28 | return support.getTestUrl(this.getTestConfig()); 29 | }, 30 | 31 | clearDirectory: function () { 32 | return through.obj(function (file, encoding, callback) { 33 | exec('rm -rf ./* && rm -f ./.sequelizerc', { cwd: file.path }, function (err) { 34 | callback(err, file); 35 | }); 36 | }); 37 | }, 38 | 39 | removeFile: function (filePath) { 40 | return through.obj(function (file, encoding, callback) { 41 | exec('rm ' + filePath, { cwd: file.path }, function (err) { 42 | callback(err, file); 43 | }); 44 | }); 45 | }, 46 | 47 | runCli: function (args, options) { 48 | options = options || {}; 49 | 50 | return through.obj(function (file, encoding, callback) { 51 | var command = support.getCliCommand(file.path, args); 52 | var env = _.extend({}, process.env, options.env); 53 | 54 | logToFile(command); 55 | 56 | exec(command, { cwd: file.path, env: env }, function (err, stdout, stderr) { 57 | var result = file; 58 | 59 | logToFile({err: err, stdout: stdout, stderr: stderr}); 60 | 61 | if (stdout) { 62 | expect(stdout).to.not.contain('EventEmitter'); 63 | } 64 | 65 | if (options.pipeStdout) { 66 | result = stdout; 67 | } else if (options.pipeStderr) { 68 | result = stderr; 69 | } 70 | 71 | if (options.exitCode) { 72 | try { 73 | expect(err).to.be.ok(); 74 | expect(err.code).to.equal(1); 75 | callback(null, result); 76 | } catch (e) { 77 | callback(e, result); 78 | } 79 | } else { 80 | err = options.pipeStderr ? null : err; 81 | callback(err, result); 82 | } 83 | }); 84 | }); 85 | }, 86 | 87 | copyFile: function (from, to) { 88 | return through.obj(function (file, encoding, callback) { 89 | fs.copy(from, path.resolve(file.path, to), function (err) { 90 | callback(err, file); 91 | }); 92 | }); 93 | }, 94 | 95 | listFiles: function (subPath) { 96 | return through.obj(function (file, encoding, callback) { 97 | var cwd = path.resolve(file.path, subPath || ''); 98 | 99 | exec('ls -ila', { cwd: cwd }, callback); 100 | }); 101 | }, 102 | 103 | expect: function (fun) { 104 | return through.obj(function (stdout, encoding, callback) { 105 | try { 106 | fun(stdout); 107 | callback(null, stdout); 108 | } catch (e) { 109 | console.log(e); 110 | callback(e, null); 111 | } 112 | }); 113 | }, 114 | 115 | ensureContent: function (needle) { 116 | return this.expect(function (stdout) { 117 | if (needle instanceof RegExp) { 118 | expect(stdout).to.match(needle); 119 | } else { 120 | expect(stdout).to.contain(needle); 121 | } 122 | }); 123 | }, 124 | 125 | overwriteFile: function (content, pathToFile) { 126 | return through.obj(function (file, encoding, callback) { 127 | var filePath = path.join(file.path, pathToFile); 128 | 129 | fs.writeFile(filePath, content, function (err) { 130 | callback(err, file); 131 | }); 132 | }); 133 | }, 134 | 135 | readFile: function (pathToFile) { 136 | return through.obj(function (file, encoding, callback) { 137 | exec('cat ' + pathToFile, { cwd: file.path }, callback); 138 | }); 139 | }, 140 | 141 | copyMigration: function (fileName, migrationsFolder) { 142 | migrationsFolder = migrationsFolder || 'migrations'; 143 | 144 | return through.obj(function (file, encoding, callback) { 145 | var migrationSource = support.resolveSupportPath('assets', 'migrations'); 146 | var migrationTarget = path.resolve(file.path, migrationsFolder); 147 | 148 | exec('cp ' + migrationSource + '/*' + fileName + ' ' + migrationTarget + '/', function (err) { 149 | callback(err, file); 150 | }); 151 | }); 152 | }, 153 | 154 | copySeeder: function (fileName, seedersFolder) { 155 | seedersFolder = seedersFolder || 'seeders'; 156 | 157 | return through.obj(function (file, encoding, callback) { 158 | var seederSource = support.resolveSupportPath('assets', 'seeders'); 159 | var seederTarget = path.resolve(file.path, seedersFolder); 160 | 161 | exec('cp ' + seederSource + '/*' + fileName + ' ' + seederTarget + '/' + fileName, 162 | function (err) { 163 | callback(err, file); 164 | } 165 | ); 166 | }); 167 | }, 168 | 169 | teardown: function (done) { 170 | return through.obj(function (smth, encoding, callback) { 171 | callback(); 172 | done(null, smth); 173 | }); 174 | }, 175 | 176 | readTables: function (sequelize, callback) { 177 | return sequelize 178 | .getQueryInterface() 179 | .showAllTables() 180 | .then(function (tables) { 181 | return callback(tables.sort()); 182 | }); 183 | }, 184 | 185 | countTable: function (sequelize, table, callback) { 186 | var QueryGenerator = sequelize.getQueryInterface().QueryGenerator; 187 | 188 | return sequelize 189 | .query('SELECT count(*) as count FROM ' + QueryGenerator.quoteTable(table)) 190 | .then(function (result) { 191 | return callback((result.length === 2) ? result[0] : result ); 192 | }); 193 | }, 194 | execQuery: function(sequelize, sql, options) { 195 | if (sequelize.query.length === 2) { 196 | return sequelize.query(sql, options); 197 | } else { 198 | return sequelize.query(sql, null, options); 199 | } 200 | } 201 | }; 202 | 203 | function logToFile (thing) { 204 | var text = (typeof thing === 'string') ? thing : JSON.stringify(thing); 205 | var logPath = __dirname + '/../../logs'; 206 | var logFile = logPath + '/test.log'; 207 | 208 | fs.mkdirpSync(logPath); 209 | fs.appendFileSync(logFile, '[' + new Date() + '] ' + text + '\n'); 210 | } 211 | -------------------------------------------------------------------------------- /test/support/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var Sequelize = require('sequelize'); 6 | var _ = require('lodash'); 7 | var DataTypes = Sequelize; 8 | var Config = require(__dirname + '/config/config'); 9 | var expect = require('expect.js'); 10 | var execQuery = function (sequelize, sql, options) { 11 | if (sequelize.query.length === 2) { 12 | return sequelize.query(sql, options); 13 | } else { 14 | return sequelize.query(sql, null, options); 15 | } 16 | } 17 | 18 | var Support = { 19 | Sequelize: Sequelize, 20 | 21 | initTests: function (options) { 22 | var sequelize = this.createSequelizeInstance(options); 23 | 24 | this.clearDatabase(sequelize, function () { 25 | if (options.context) { 26 | options.context.sequelize = sequelize; 27 | } 28 | 29 | if (options.beforeComplete) { 30 | options.beforeComplete(sequelize, DataTypes); 31 | } 32 | 33 | if (options.onComplete) { 34 | options.onComplete(sequelize, DataTypes); 35 | } 36 | }); 37 | }, 38 | 39 | prepareTransactionTest: function (sequelize, callback) { 40 | var dialect = Support.getTestDialect(); 41 | 42 | if (dialect === 'sqlite') { 43 | var options = _.extend({}, sequelize.options, { 44 | storage: path.join(__dirname, 'tmp', 'db.sqlite') 45 | }); 46 | var _sequelize = new Sequelize(sequelize.config.datase, null, null, options); 47 | 48 | _sequelize.sync({ force: true }).then(function () { 49 | callback(_sequelize); 50 | }); 51 | } else { 52 | callback(sequelize); 53 | } 54 | }, 55 | 56 | createSequelizeInstance: function (options) { 57 | options = options || {}; 58 | options.dialect = options.dialect || 'sqlite'; 59 | 60 | var config = Config[options.dialect]; 61 | var sequelizeOptions = _.defaults(options, { 62 | host: options.host || config.host, 63 | logging: false, 64 | dialect: options.dialect, 65 | port: options.port || process.env.SEQ_PORT || config.port, 66 | pool: config.pool, 67 | dialectOptions: options.dialectOptions || {} 68 | }); 69 | 70 | if (process.env.DIALECT === 'postgres-native') { 71 | sequelizeOptions.native = true; 72 | } 73 | 74 | if (!!config.storage) { 75 | sequelizeOptions.storage = config.storage; 76 | } 77 | 78 | return this.getSequelizeInstance( 79 | config.database, 80 | config.username, 81 | config.password, 82 | sequelizeOptions 83 | ); 84 | }, 85 | 86 | getSequelizeInstance: function (db, user, pass, options) { 87 | options = options || {}; 88 | options.dialect = options.dialect || this.getTestDialect(); 89 | return new Sequelize(db, user, pass, options); 90 | }, 91 | 92 | clearDatabase: function (sequelize, callback) { 93 | sequelize 94 | .getQueryInterface() 95 | .dropAllTables() 96 | .then(function () { 97 | if (sequelize.daoFactoryManager) { 98 | sequelize.daoFactoryManager.daos = []; 99 | } else { 100 | sequelize.modelManager.models = []; 101 | } 102 | 103 | return sequelize 104 | .getQueryInterface() 105 | .dropAllEnums() 106 | .then(callback) 107 | .catch(function (err) { 108 | console.log('Error in support.clearDatabase() dropAllEnums() :: ', err); 109 | }); 110 | }) 111 | .catch(function (err) { 112 | console.log('Error in support.clearDatabase() dropAllTables() :: ', err); 113 | }); 114 | }, 115 | 116 | getSupportedDialects: function () { 117 | var dir = __dirname + '/../../node_modules/sequelize/lib/dialects'; 118 | 119 | return fs.readdirSync(dir).filter(function (file) { 120 | return ((file.indexOf('.js') === -1) && (file.indexOf('abstract') === -1)); 121 | }); 122 | }, 123 | 124 | checkMatchForDialects: function (dialect, value, expectations) { 125 | if (!!expectations[dialect]) { 126 | expect(value).to.match(expectations[dialect]); 127 | } else { 128 | throw new Error('Undefined expectation for \'' + dialect + '\'!'); 129 | } 130 | }, 131 | 132 | getTestDialect: function () { 133 | var envDialect = process.env.DIALECT || 'sqlite'; 134 | 135 | if (envDialect === 'postgres-native') { 136 | envDialect = 'postgres'; 137 | } 138 | 139 | if (this.getSupportedDialects().indexOf(envDialect) === -1) { 140 | throw new Error('The dialect you have passed is unknown. Did you really mean: ' + envDialect); 141 | } 142 | 143 | return envDialect; 144 | }, 145 | 146 | dialectIsMySQL: function (strict) { 147 | var envDialect = this.getTestDialect(); 148 | 149 | if (strict === undefined) { 150 | strict = false; 151 | } 152 | 153 | if (strict) { 154 | return envDialect === 'mysql'; 155 | } else { 156 | return ['mysql', 'mariadb'].indexOf(envDialect) !== -1; 157 | } 158 | }, 159 | 160 | dialectIsPostgres: function (strict) { 161 | var envDialect = this.getTestDialect(); 162 | return ['postgres', 'postgres-native'].indexOf(envDialect) !== -1; 163 | }, 164 | 165 | getTestDialectTeaser: function (moduleName) { 166 | var dialect = this.getTestDialect(); 167 | 168 | if (process.env.DIALECT === 'postgres-native') { 169 | dialect = 'postgres-native'; 170 | } 171 | 172 | return '[' + dialect.toUpperCase() + '] lib/sequelize ' + moduleName; 173 | }, 174 | 175 | getTestUrl: function (config) { 176 | var url = null; 177 | var dbConfig = config[config.dialect]; 178 | 179 | if (config.dialect === 'sqlite') { 180 | url = 'sqlite://' + dbConfig.storage; 181 | } else { 182 | var credentials = dbConfig.username; 183 | 184 | if (dbConfig.password) { 185 | credentials += ':' + dbConfig.password; 186 | } 187 | 188 | url = config.dialect + '://' + credentials + '@' + dbConfig.host + 189 | ':' + dbConfig.port + '/' + dbConfig.database; 190 | } 191 | 192 | return url; 193 | }, 194 | 195 | getCliPath: function (cwd) { 196 | return path.resolve(cwd, path.resolve(process.cwd(), 'lib', 'sequelize')); 197 | }, 198 | 199 | getCliCommand: function (cwd, flags) { 200 | return this.getCliPath(cwd) + ' ' + flags; 201 | }, 202 | 203 | getSupportDirectoryPath: function () { 204 | return path.resolve(__dirname); 205 | }, 206 | 207 | resolveSupportPath: function () { 208 | var args = [].slice.apply(arguments); 209 | 210 | args = [this.getSupportDirectoryPath()].concat(args); 211 | return path.resolve.apply(path, args); 212 | } 213 | }; 214 | 215 | var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() }); 216 | 217 | // For Postgres' HSTORE functionality and to properly execute it's commands we'll need this... 218 | before(function (done) { 219 | var dialect = Support.getTestDialect(); 220 | 221 | if (dialect !== 'postgres' && dialect !== 'postgres-native') { 222 | return done(); 223 | } 224 | 225 | execQuery(sequelize, 'CREATE EXTENSION IF NOT EXISTS hstore', {raw: true}).then(function () { 226 | done(); 227 | }); 228 | }); 229 | 230 | beforeEach(function (done) { 231 | Support.clearDatabase(sequelize, function () { 232 | if (sequelize.options.dialect === 'sqlite') { 233 | var options = sequelize.options; 234 | 235 | options.storage = Support.resolveSupportPath('tmp', 'test.sqlite'); 236 | sequelize = new Support.Sequelize('', '', '', options); 237 | } 238 | 239 | this.sequelize = sequelize; 240 | 241 | done(); 242 | }.bind(this)); 243 | }); 244 | 245 | module.exports = Support; 246 | -------------------------------------------------------------------------------- /test/support/tmp/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglas-treadwell/sequelize-cli-typescript/d185545c73471d6ce04d2df4baae65dee0f3f584/test/support/tmp/.gitkeep -------------------------------------------------------------------------------- /test/url.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/support'); 3 | const helpers = require(__dirname + '/support/helpers'); 4 | const gulp = require('gulp'); 5 | 6 | [ 7 | '--url' 8 | ].forEach(flag => { 9 | const prepare = function (callback) { 10 | gulp 11 | .src(Support.resolveSupportPath('tmp')) 12 | .pipe(helpers.clearDirectory()) 13 | .pipe(helpers.runCli('init')) 14 | .pipe(helpers.copyMigration('createPerson.js')) 15 | .pipe(helpers.overwriteFile(JSON.stringify(helpers.getTestConfig()), 'config/config.json')) 16 | .pipe(helpers.runCli('db:migrate ' + flag + '=' + helpers.getTestUrl(), { pipeStdout: true })) 17 | .pipe(helpers.teardown(callback)); 18 | }; 19 | 20 | describe(Support.getTestDialectTeaser(flag), () => { 21 | beforeEach(function (done) { 22 | prepare((err, stdout) => { 23 | this.stdout = stdout; 24 | done(); 25 | }); 26 | }); 27 | 28 | it('creates a SequelizeMeta table', function (done) { 29 | helpers.readTables(this.sequelize, tables => { 30 | expect(tables).to.have.length(2); 31 | expect(tables).to.contain('SequelizeMeta'); 32 | done(); 33 | }); 34 | }); 35 | 36 | it('creates the respective table via url', function (done) { 37 | helpers.readTables(this.sequelize, tables => { 38 | expect(tables).to.have.length(2); 39 | expect(tables).to.contain('Person'); 40 | done(); 41 | }); 42 | }); 43 | 44 | it('prints the parsed URL', function () { 45 | expect(this.stdout).to.contain('Parsed url'); 46 | }); 47 | 48 | it('filters the password', function () { 49 | const config = helpers.getTestConfig(); 50 | 51 | if (Support.getTestDialect() === 'sqlite') { 52 | return; 53 | } 54 | 55 | expect(this.stdout).to.contain( 56 | config.dialect + '://' + config.username + ':*****@' + config.host + 57 | ':' + config.port + '/' + config.database 58 | ); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/version.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Support = require(__dirname + '/support'); 3 | const version = (require(__dirname + '/../package.json')).version; 4 | const helpers = require(__dirname + '/support/helpers'); 5 | const gulp = require('gulp'); 6 | 7 | [ 8 | '--version' 9 | ].forEach(flag => { 10 | describe(Support.getTestDialectTeaser(flag), () => { 11 | it('prints the version', done => { 12 | expect(version).to.not.be.empty; 13 | 14 | gulp 15 | .src(process.cwd()) 16 | .pipe(helpers.runCli(flag, { pipeStdout: true })) 17 | .pipe(helpers.ensureContent(version)) 18 | .pipe(helpers.teardown(done)); 19 | }); 20 | }); 21 | }); 22 | --------------------------------------------------------------------------------