├── .editorconfig
├── .esdoc.json
├── .eslintrc.json
├── .github
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── stale.yml
├── .gitignore
├── .npmignore
├── .npmrc
├── .travis.yml
├── CONTACT.md
├── CONTRIBUTING.DOCS.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── appveyor-setup.ps1
├── appveyor.yml
├── codecov.yml
├── docker-compose.yml
├── docs
├── ROUTER
├── associations.md
├── css
│ ├── style.css
│ └── theme.css
├── favicon.ico
├── getting-started.md
├── hooks.md
├── images
│ ├── bitovi-logo.png
│ ├── clevertech.png
│ ├── connected-cars.png
│ ├── filsh.png
│ ├── logo-small.png
│ ├── logo-snaplytics-green.png
│ ├── logo.png
│ ├── metamarkets.png
│ ├── shutterstock.png
│ ├── slack.svg
│ └── walmart-labs-logo.png
├── imprint.md
├── index.md
├── instances.md
├── legacy.md
├── migrations.md
├── models-definition.md
├── models-usage.md
├── plugins
│ └── esdoc-sequelize.js
├── querying.md
├── raw-queries.md
├── scopes.md
├── scripts
│ └── script.js
├── transactions.md
├── upgrade-to-v4.md
├── usage.md
└── whos-using.md
├── index.js
├── lib
├── associations
│ ├── base.js
│ ├── belongs-to-many.js
│ ├── belongs-to.js
│ ├── has-many.js
│ ├── has-one.js
│ ├── helpers.js
│ ├── index.js
│ └── mixin.js
├── data-types.js
├── deferrable.js
├── dialects
│ ├── abstract
│ │ ├── connection-manager.js
│ │ ├── index.js
│ │ ├── query-generator.js
│ │ └── query.js
│ ├── mssql
│ │ ├── connection-manager.js
│ │ ├── data-types.js
│ │ ├── index.js
│ │ ├── query-generator.js
│ │ ├── query-interface.js
│ │ ├── query.js
│ │ └── resource-lock.js
│ ├── mysql
│ │ ├── connection-manager.js
│ │ ├── data-types.js
│ │ ├── index.js
│ │ ├── query-generator.js
│ │ ├── query-interface.js
│ │ └── query.js
│ ├── parserStore.js
│ ├── postgres
│ │ ├── connection-manager.js
│ │ ├── data-types.js
│ │ ├── hstore.js
│ │ ├── index.js
│ │ ├── query-generator.js
│ │ ├── query.js
│ │ └── range.js
│ └── sqlite
│ │ ├── connection-manager.js
│ │ ├── data-types.js
│ │ ├── index.js
│ │ ├── query-generator.js
│ │ ├── query-interface.js
│ │ └── query.js
├── errors.js
├── errors
│ └── index.js
├── hooks.js
├── instance-validator.js
├── model-manager.js
├── model.js
├── operators.js
├── promise.js
├── query-interface.js
├── query-types.js
├── sequelize.js
├── sql-string.js
├── transaction.js
├── utils.js
└── utils
│ ├── inherits.js
│ ├── logger.js
│ ├── parameter-validator.js
│ └── validator-extras.js
├── package.json
├── sscce_template.js
└── test
├── config
├── .docker.env
├── config.js
└── options.js
├── integration
├── assets
│ ├── es6project.js
│ └── project.js
├── associations
│ ├── alias.test.js
│ ├── belongs-to-many.test.js
│ ├── belongs-to.test.js
│ ├── has-many.test.js
│ ├── has-one.test.js
│ ├── multiple-level-filters.test.js
│ ├── scope.test.js
│ └── self.test.js
├── cls.test.js
├── configuration.test.js
├── data-types.test.js
├── dialects
│ ├── abstract
│ │ └── connection-manager.test.js
│ ├── mssql
│ │ ├── connection-manager.test.js
│ │ └── query-queue.test.js
│ ├── mysql
│ │ ├── associations.test.js
│ │ ├── connector-manager.test.js
│ │ ├── dao-factory.test.js
│ │ └── errors.test.js
│ ├── postgres
│ │ ├── associations.test.js
│ │ ├── connection-manager.test.js
│ │ ├── dao.test.js
│ │ ├── data-types.test.js
│ │ ├── error.test.js
│ │ ├── hstore.test.js
│ │ ├── query-interface.test.js
│ │ └── range.test.js
│ └── sqlite
│ │ ├── dao-factory.test.js
│ │ ├── dao.test.js
│ │ └── test.sqlite
├── error.test.js
├── hooks
│ ├── associations.test.js
│ ├── bulkOperation.test.js
│ ├── count.test.js
│ ├── create.test.js
│ ├── destroy.test.js
│ ├── find.test.js
│ ├── hooks.test.js
│ ├── restore.test.js
│ ├── updateAttributes.test.js
│ ├── upsert.test.js
│ └── validate.test.js
├── include.test.js
├── include
│ ├── find.test.js
│ ├── findAll.test.js
│ ├── findAndCountAll.test.js
│ ├── paranoid.test.js
│ ├── schema.test.js
│ └── seperate.test.js
├── instance.test.js
├── instance.validations.test.js
├── instance
│ ├── update.test.js
│ └── values.test.js
├── json.test.js
├── model.test.js
├── model
│ ├── attributes.test.js
│ ├── attributes
│ │ ├── field.test.js
│ │ ├── operators.test.js
│ │ └── types.test.js
│ ├── count.test.js
│ ├── create.test.js
│ ├── create
│ │ └── include.test.js
│ ├── find.test.js
│ ├── findAll.test.js
│ ├── findAll
│ │ ├── group.test.js
│ │ ├── groupedLimit.test.js
│ │ └── order.test.js
│ ├── geography.test.js
│ ├── geometry.test.js
│ ├── json.test.js
│ ├── optimistic_locking.test.js
│ ├── paranoid.test.js
│ ├── schema.test.js
│ ├── scope.test.js
│ ├── scope
│ │ ├── aggregate.test.js
│ │ ├── associations.test.js
│ │ ├── count.test.js
│ │ ├── destroy.test.js
│ │ ├── find.test.js
│ │ ├── findAndCount.test.js
│ │ └── update.test.js
│ ├── searchPath.test.js
│ ├── sync.test.js
│ ├── update.test.js
│ └── upsert.test.js
├── pool.test.js
├── query-interface.test.js
├── replication.test.js
├── schema.test.js
├── sequelize.test.js
├── sequelize.transaction.test.js
├── sequelize
│ ├── deferrable.test.js
│ └── log.test.js
├── support.js
├── timezone.test.js
├── tmp
│ └── .gitkeep
├── transaction.test.js
├── trigger.test.js
├── utils.test.js
└── vectors.test.js
├── support.js
├── supportShim.js
├── tmp
└── .gitkeep
└── unit
├── associations
├── association.test.js
├── belongs-to-many.test.js
├── belongs-to.test.js
├── dont-modify-options.test.js
├── has-many.test.js
└── has-one.test.js
├── configuration.test.js
├── connection-manager.test.js
├── dialects
├── abstract
│ ├── query-generator.test.js
│ └── query.test.js
├── mssql
│ ├── connection-manager.js
│ ├── query-generator.test.js
│ ├── query.js
│ └── resource-lock.test.js
├── mysql
│ ├── errors.test.js
│ └── query-generator.test.js
├── postgres
│ └── query-generator.test.js
└── sqlite
│ └── query-generator.test.js
├── errors.test.js
├── hooks.test.js
├── increment.test.js
├── instance-validator.test.js
├── instance
├── build.test.js
├── changed.test.js
├── decrement.test.js
├── destroy.test.js
├── get.test.js
├── increment.test.js
├── is-soft-deleted.test.js
├── previous.test.js
├── reload.test.js
├── restore.test.js
├── save.test.js
├── set.test.js
└── to-json.test.js
├── model
├── bulkcreate.test.js
├── count.test.js
├── define.test.js
├── destroy.test.js
├── find-create-find.test.js
├── find-or-create.test.js
├── findall.test.js
├── findone.test.js
├── helpers.test.js
├── include.test.js
├── indexes.test.js
├── overwriting-builtins.test.js
├── removeAttribute.test.js
├── schema.test.js
├── scope.test.js
├── update.test.js
├── upsert.test.js
└── validation.test.js
├── promise.test.js
├── query.test.js
├── sql
├── add-column.test.js
├── add-constraint.test.js
├── change-column.test.js
├── create-schema.test.js
├── create-table.test.js
├── data-types.test.js
├── delete.test.js
├── enum.test.js
├── generateJoin.test.js
├── get-constraint-snippet.test.js
├── index.test.js
├── insert.test.js
├── json.test.js
├── offset-limit.test.js
├── order.test.js
├── remove-column.test.js
├── remove-constraint.test.js
├── select.test.js
├── show-constraints.test.js
├── update.test.js
└── where.test.js
├── support.js
├── transaction.test.js
└── utils.test.js
/.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 |
22 | [Makefile]
23 | indent_style = tabs
24 |
--------------------------------------------------------------------------------
/.esdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": " Sequelize | The node.js ORM for PostgreSQL, MySQL, SQLite and MSSQL",
3 | "source": "./lib",
4 | "destination": "./esdoc",
5 | "access": ["public"],
6 | "builtinExternal": false,
7 | "unexportIdentifier": true,
8 | "undocumentIdentifier": false,
9 | "includeSource": false,
10 | "coverage": false,
11 | "lint": false,
12 | "plugins": [
13 | {"name": "./docs/plugins/esdoc-sequelize.js"}
14 | ],
15 | "styles": [
16 | "./docs/css/style.css",
17 | "./docs/css/theme.css"
18 | ],
19 | "scripts": [
20 | "./docs/scripts/script.js"
21 | ],
22 | "manual": {
23 | "globalIndex": true,
24 | "index": "./docs/index.md",
25 | "asset": "./docs/images",
26 | "badge": false,
27 | "installation": [
28 | "./docs/getting-started.md",
29 | "./docs/usage.md"
30 | ],
31 | "tutorial": [
32 | "./docs/models-definition.md",
33 | "./docs/models-usage.md",
34 | "./docs/querying.md",
35 | "./docs/instances.md",
36 | "./docs/associations.md",
37 | "./docs/transactions.md",
38 | "./docs/scopes.md",
39 | "./docs/hooks.md",
40 | "./docs/raw-queries.md",
41 | "./docs/migrations.md",
42 | "./docs/upgrade-to-v4.md"
43 | ],
44 | "advanced": [
45 | "./docs/legacy.md"
46 | ],
47 | "faq": [
48 | "./docs/whos-using.md",
49 | "./docs/imprint.md"
50 | ]
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "mocha/no-exclusive-tests": "error",
4 | "mocha/no-skipped-tests": "warn",
5 |
6 | "array-bracket-spacing": "error",
7 | "comma-spacing": "error",
8 | "key-spacing": "error",
9 | "keyword-spacing": "error",
10 |
11 | "no-console": "off",
12 | "no-extra-parens": "warn",
13 | "valid-jsdoc": "off",
14 | "new-cap": [
15 | "warn",
16 | {
17 | "properties": false
18 | }
19 | ],
20 | "no-extra-boolean-cast": "warn",
21 | "strict": [
22 | "warn",
23 | "global"
24 | ],
25 | "no-var": "error",
26 | "prefer-const": "error",
27 | "semi": [
28 | "error",
29 | "always"
30 | ],
31 | "space-before-function-paren": [
32 | "warn",
33 | "never"
34 | ],
35 | "space-before-blocks": "error",
36 | "prefer-arrow-callback": "error",
37 | "arrow-parens": [
38 | "error",
39 | "as-needed"
40 | ],
41 | "comma-style": [
42 | "warn",
43 | "last"
44 | ],
45 | "no-bitwise": "off",
46 | "no-cond-assign": [
47 | "error",
48 | "except-parens"
49 | ],
50 | "curly": "off",
51 | "eqeqeq": "error",
52 | "no-extend-native": "error",
53 | "wrap-iife": [
54 | "error",
55 | "any"
56 | ],
57 | "indent": [
58 | "error",
59 | 2,
60 | {
61 | "SwitchCase": 1
62 | }
63 | ],
64 | "no-use-before-define": "off",
65 | "no-caller": "error",
66 | "no-undef": "error",
67 | "no-unused-vars": "error",
68 | "no-irregular-whitespace": "error",
69 | "max-depth": [
70 | "error",
71 | 8
72 | ],
73 | "quotes": [
74 | "error",
75 | "single",
76 | {
77 | "avoidEscape": true
78 | }
79 | ],
80 | "linebreak-style": "error",
81 | "no-loop-func": "warn",
82 | "object-shorthand": "error",
83 | "one-var-declaration-per-line": "warn",
84 | "comma-dangle": "warn",
85 | "no-shadow": "warn",
86 | "camelcase": "warn"
87 | },
88 | "parserOptions": {
89 | "ecmaVersion": 6,
90 | "sourceType": "script"
91 | },
92 | "plugins": [
93 | "mocha"
94 | ],
95 | "env": {
96 | "node": true,
97 | "mocha": true,
98 | "es6": true
99 | }
100 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
9 |
10 | ## What are you 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 | __Dialect version:__ XXX
28 | __Database version:__ XXX
29 | __Sequelize version:__ XXX
30 | __Tested with latest release:__ No (If yes, specify that version)
31 |
32 |
33 | **Note :** _Your issue may be ignored OR closed by maintainers if it's not tested against latest version OR does not follow issue template._
34 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
8 |
9 | ### Pull Request check-list
10 |
11 | _Please make sure to review and check all of these items:_
12 |
13 | - [ ] Does `npm run test` or `npm run test-DIALECT` pass with this change (including linting)?
14 | - [ ] Does the description below contain a link to an existing issue (Closes #[issue]) or a description of the issue you are solving?
15 | - [ ] Have you added new tests to prevent regressions?
16 | - [ ] Is a documentation update included (if this change modifies existing APIs, or introduces new ones)?
17 | - [ ] Did you follow the commit message conventions explained in [CONTRIBUTING.md](../CONTRIBUTING.md)?
18 |
19 |
20 |
21 | ### Description of change
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 |
2 | # Configuration for probot-stale - https://github.com/probot/stale
3 |
4 | # Number of days of inactivity before an issue becomes stale
5 | daysUntilStale: 100
6 |
7 | # Number of days of inactivity before a stale issue is closed
8 | daysUntilClose: 7
9 |
10 | # Issues with these labels will never be considered stale
11 | exemptLabels:
12 | - pinned
13 | - feature
14 | - documentation
15 | - bug
16 | - discussion
17 |
18 | # Label to use when marking an issue as stale
19 | staleLabel: stale
20 |
21 | # Comment to post when marking an issue as stale. Set to `false` to disable
22 | markComment: >
23 | This issue has been automatically marked as stale because it has not had
24 | recent activity. It will be closed if no further activity occurs.
25 | If this is still an issue, just leave a comment 🙂
26 |
27 | # Comment to post when closing a stale issue. Set to `false` to disable
28 | closeComment: false
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | test*.js
2 | *.swp
3 | .idea
4 | .DS_STORE
5 | node_modules
6 | npm-debug.log
7 | *~
8 | test/binary/tmp/*
9 | test/tmp/*
10 | test/dialects/sqlite/test.sqlite
11 | test/sqlite/test.sqlite
12 | coverage-*
13 | site
14 | docs/api/tmp.md
15 | ssce.js
16 | sscce.js
17 | coverage
18 | .vscode/
19 | *.sublime*
20 | esdoc
21 | package-lock.json
22 | yarn.lock
23 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | docs
2 | esdoc
3 | examples
4 | test
5 | README.md
6 | .watchr.js
7 | changelog.md
8 | Makefile
9 | coverage*
10 | .github
11 |
12 | appveyor-setup.ps1
13 | appveyor.yml
14 | codecov.yml
15 | docker-compose.yml
16 | mkdocs.yml
17 | .dockerignore
18 | .editorconfig
19 | .travis.yml
20 | package-lock.json
21 |
22 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | dist: trusty
3 |
4 | language: node_js
5 |
6 | branches:
7 | only:
8 | - master
9 | - v3
10 | - /^greenkeeper/.*$/
11 | except:
12 | - /^v\d+\.\d+\.\d+$/
13 |
14 | cache:
15 | directories:
16 | - $HOME/.npm
17 |
18 | env:
19 | global:
20 | # mysql info
21 | - SEQ_MYSQL_DB=sequelize_test
22 | - SEQ_MYSQL_USER=sequelize_test
23 | - SEQ_MYSQL_PW=sequelize_test
24 | - SEQ_MYSQL_HOST=127.0.0.1
25 | - SEQ_MYSQL_PORT=8999
26 | # postgres info
27 | - SEQ_PG_DB=sequelize_test
28 | - SEQ_PG_USER=sequelize_test
29 | - SEQ_PG_PW=sequelize_test
30 | - SEQ_PG_HOST=127.0.0.1
31 | - SEQ_PG_PORT=8998
32 |
33 | before_script:
34 | # mount ramdisk
35 | - "if [ $POSTGRES_VER ]; then sudo mkdir /mnt/sequelize-postgres-ramdisk; fi"
36 | - "if [ $POSTGRES_VER ]; then sudo mount -t ramfs tmpfs /mnt/sequelize-postgres-ramdisk; fi"
37 | - "if [ $MYSQL_VER ]; then sudo mkdir /mnt/sequelize-mysql-ramdisk; fi"
38 | - "if [ $MYSQL_VER ]; then sudo mount -t ramfs tmpfs /mnt/sequelize-mysql-ramdisk; fi"
39 |
40 | # setup docker
41 | - "if [ $POSTGRES_VER ] || [ $MYSQL_VER ]; then docker-compose up -d ${POSTGRES_VER} ${MYSQL_VER}; fi"
42 | - "if [ $MYSQL_VER ]; then docker run --link ${MYSQL_VER}:db -e CHECK_PORT=3306 -e CHECK_HOST=db --net sequelize_default giorgos/takis; fi"
43 | - "if [ $POSTGRES_VER ]; then docker run --link ${POSTGRES_VER}:db -e CHECK_PORT=5432 -e CHECK_HOST=db --net sequelize_default giorgos/takis; fi"
44 |
45 | script:
46 | - npm run lint
47 | - "if [ $COVERAGE ]; then npm run cover && bash <(curl -s https://codecov.io/bash) -f coverage/lcov.info; else npm run test; fi"
48 |
49 | jobs:
50 | include:
51 | - stage: test
52 | node_js: '4'
53 | env: DIALECT=sqlite COVERAGE=true
54 | - stage: test
55 | node_js: '4'
56 | sudo: required
57 | env: MYSQL_VER=mysql-57 DIALECT=mysql COVERAGE=true
58 | - stage: test
59 | node_js: '4'
60 | sudo: required
61 | env: POSTGRES_VER=postgres-95 DIALECT=postgres COVERAGE=true
62 | - stage: test
63 | node_js: '4'
64 | sudo: required
65 | env: POSTGRES_VER=postgres-95 DIALECT=postgres-native COVERAGE=true
66 | - stage: test
67 | node_js: '8'
68 | env: DIALECT=sqlite
69 | - stage: test
70 | node_js: '6'
71 | env: DIALECT=sqlite
72 | - stage: release
73 | node_js: '8'
74 | script:
75 | - npm run semantic-release
76 | before_deploy:
77 | - npm run docs
78 | deploy:
79 | provider: surge
80 | project: ./esdoc/
81 | domain: docs.sequelizejs.com
82 | skip_cleanup: true
83 |
84 | stages:
85 | - test
86 | - name: release
87 | if: branch = master AND type = push AND fork = false
88 |
--------------------------------------------------------------------------------
/CONTACT.md:
--------------------------------------------------------------------------------
1 | ## Contact Us
2 |
3 | In some cases you might want to reach the maintainers privately. These can be reporting a security vulnerability or any other specific cases.
4 | You can use the information below to contact maintainers directly. We will try to get back to you as soon as possible.
5 |
6 | ### Via Email
7 |
8 | - **Mick Hansen** mick.kasper.hansen@gmail.com
9 | - **Jan Aagaard Meier** janzeh@gmail.com
10 | - **Sushant Dhiman** sushantdhiman@outlook.com
11 |
12 | ### Via Slack
13 |
14 | Maintainer's usernames for [Sequelize Slack Channel](https://sequelize.slack.com)
15 |
16 | - **Mick Hansen** @mhansen
17 | - **Jan Aagaard Meier** @janmeier
18 | - **Sushant Dhiman** @sushantdhiman
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/CONTRIBUTING.DOCS.md:
--------------------------------------------------------------------------------
1 | # Documentation
2 |
3 | The sequelize documentation is written in a combination of markdown (articles and example based documentation) and [JSDoc](http://usejsdoc.org) (API reference generated from source code comments).
4 |
5 | All documentation is located in the `docs` folder.
6 |
7 | The documentation is rendered using [esdoc](http://esdoc.org) and continously deployed to [Surge](http://surge.sh). esdoc generates static HTML from the code comments.
8 |
9 | All pages in the documentation are defined in the `manual` section of `.esdoc.json`. Each page is given as a separate line:
10 |
11 | To view the docs locally run `npm run docs` and open the generated HTML in your favorite browser.
12 |
13 | ## Articles and example based docs
14 | Write markdown, and have fun :)
15 |
16 | ## API docs
17 | Change the source-code, and rerun `npm run docs` to see your changes.
18 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:6
2 |
3 | RUN apt-get install libpq-dev
4 |
5 | WORKDIR /sequelize
6 | VOLUME /sequelize
7 |
8 | COPY . /sequelize
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2014-present Sequelize contributors
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 |
--------------------------------------------------------------------------------
/appveyor-setup.ps1:
--------------------------------------------------------------------------------
1 |
2 | Set-Service sqlbrowser -StartupType auto
3 | Start-Service sqlbrowser
4 |
5 | [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null
6 | [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.SqlWmiManagement") | Out-Null
7 |
8 | $wmi = New-Object('Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer')
9 | $tcp = $wmi.GetSmoObject("ManagedComputer[@Name='${env:computername}']/ServerInstance[@Name='SQL2016']/ServerProtocol[@Name='Tcp']")
10 | $tcp.IsEnabled = $true
11 | $tcp.Alter()
12 |
13 | $wmi = New-Object('Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer')
14 | $ipall = $wmi.GetSmoObject("ManagedComputer[@Name='${env:computername}']/ServerInstance[@Name='SQL2016']/ServerProtocol[@Name='Tcp']/IPAddress[@Name='IPAll']")
15 | $port = $ipall.IPAddressProperties.Item("TcpDynamicPorts").Value
16 |
17 | $config = @{
18 | instanceName = "SQL2016"
19 | host = "localhost"
20 | username = "sa"
21 | password = "Password12!"
22 | port = $port
23 | database = "sequelize_test"
24 | dialectOptions = @{
25 | requestTimeout = 25000
26 | cryptoCredentialsDetails = @{
27 | ciphers = "RC4-MD5"
28 | }
29 | }
30 | pool = @{
31 | max = 5
32 | idle = 3000
33 | }
34 | }
35 |
36 | $json = $config | ConvertTo-Json -Depth 3
37 |
38 | # Create sequelize_test database
39 | sqlcmd -S "(local)" -U "sa" -P "Password12!" -d "master" -Q "CREATE DATABASE [sequelize_test]; ALTER DATABASE [sequelize_test] SET READ_COMMITTED_SNAPSHOT ON;"
40 |
41 | # cannot use Out-File because it outputs a BOM
42 | [IO.File]::WriteAllLines((Join-Path $pwd "test\config\mssql.json"), $json)
43 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: '{build}'
2 |
3 | platform:
4 | - x64
5 |
6 | services:
7 | - mssql2016
8 |
9 | shallow_clone: true
10 |
11 | cache:
12 | - node_modules
13 |
14 | environment:
15 | matrix:
16 | - {NODE_VERSION: 4, DIALECT: mssql, COVERAGE: true}
17 |
18 | install:
19 | - ps: Install-Product node $env:NODE_VERSION x64
20 | - ps: |
21 | $pkg = ConvertFrom-Json (Get-Content -Raw package.json)
22 | $pkg.devDependencies.PSObject.Properties.Remove('sqlite3')
23 | $pkg.devDependencies.PSObject.Properties.Remove('pg-native')
24 | ConvertTo-Json $pkg | Out-File package.json -Encoding UTF8
25 | - npm install
26 |
27 | build: off
28 |
29 | before_test:
30 | - ps: . .\appveyor-setup.ps1
31 |
32 | test_script:
33 | - 'IF "%COVERAGE%" == "true" (npm run cover) ELSE (npm test)'
34 |
35 | after_test:
36 | - ps: |
37 | $env:PATH = 'C:\Program Files\Git\usr\bin;' + $env:PATH
38 | if (Test-Path env:\COVERAGE) {
39 | Invoke-WebRequest -Uri 'https://codecov.io/bash' -OutFile codecov.sh
40 | bash codecov.sh -f "coverage\lcov.info"
41 | }
42 |
43 | branches:
44 | only:
45 | - master
46 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment:
2 | layout: "header, changes"
3 | behavior: default
4 |
5 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | sequelize:
5 | build: .
6 | links:
7 | - mysql-57
8 | - postgres-95
9 | volumes:
10 | - .:/sequelize
11 | environment:
12 | SEQ_DB: sequelize_test
13 | SEQ_USER: sequelize_test
14 | SEQ_PW: sequelize_test
15 |
16 | # PostgreSQL
17 | postgres-95:
18 | image: sushantdhiman/postgres:9.5
19 | environment:
20 | POSTGRES_USER: sequelize_test
21 | POSTGRES_PASSWORD: sequelize_test
22 | POSTGRES_DB: sequelize_test
23 | volumes:
24 | - /mnt/sequelize-postgres-ramdisk:/var/lib/postgresql/data
25 | ports:
26 | - "8998:5432"
27 | container_name: postgres-95
28 |
29 | # MySQL
30 | mysql-57:
31 | image: mysql:5.7
32 | environment:
33 | MYSQL_ROOT_PASSWORD: lollerskates
34 | MYSQL_DATABASE: sequelize_test
35 | MYSQL_USER: sequelize_test
36 | MYSQL_PASSWORD: sequelize_test
37 | volumes:
38 | - /mnt/sequelize-mysql-ramdisk:/var/lib/mysql
39 | ports:
40 | - "8999:3306"
41 | container_name: mysql-57
42 |
43 | # MSSQL
44 | mssql:
45 | image: microsoft/mssql-server-linux:latest
46 | environment:
47 | ACCEPT_EULA: "Y"
48 | SA_PASSWORD: yourStrong(!)Password
49 | ports:
50 | - "8997:1433"
51 | container_name: mssql
52 |
53 |
--------------------------------------------------------------------------------
/docs/ROUTER:
--------------------------------------------------------------------------------
1 | 301 /en/v3/ https://sequelize.readthedocs.io/en/v3/
2 | 301 /en/v3/:foo https://sequelize.readthedocs.io/en/v3/:foo
3 | 301 /en/v3/:foo/:bar https://sequelize.readthedocs.io/en/v3/:foo/:bar
4 | 301 /en/v3/:foo/:bar/:baz https://sequelize.readthedocs.io/en/v3/:foo/:bar/:baz
5 | 301 /en/v3/:foo/:bar/:baz/:quz https://sequelize.readthedocs.io/en/v3/:foo/:bar/:baz/:quz
6 |
7 | 301 /en/latest /
8 | 301 /en/latest/ /
9 |
10 | 301 /en/latest/docs/getting-started/ /manual/installation/getting-started.html
11 | 301 /en/latest/docs/:section/ /manual/tutorial/:section.html
12 |
13 | 301 /en/latest/api/sequelize/ /class/lib/sequelize.js~Sequelize.html
14 | 301 /en/latest/api/model/ /class/lib/model.js~Model.html
15 | 301 /en/latest/api/instance/ /class/lib/model.js~Model.html
16 | 301 /en/latest/api/associations/ /class/lib/associations/base.js~Association.html
17 | 301 /en/latest/api/associations/belongs-to/ /class/lib/associations/belongs-to.js~BelongsTo.html
18 | 301 /en/latest/api/associations/belongs-to-many/ /class/lib/associations/belongs-to-.many.js~BelongsToMany.html
19 | 301 /en/latest/api/associations/has-one/ /class/lib/associations/has-one.js~HasOne.html
20 | 301 /en/latest/api/associations/has-many/ /class/lib/associations/has-many.js~HasMany.html
21 | 301 /en/latest/api/transaction/ /class/lib/transaction.js~Transaction.html
22 | 301 /en/latest/api/datatypes/ /variable/index.html#static-variable-DataTypes
23 | 301 /en/latest/api/deferrable/ /variable/index.html#static-variable-Deferrable
24 | 301 /en/latest/api/errors/ /class/lib/errors/index.js~BaseError.html
25 |
--------------------------------------------------------------------------------
/docs/css/style.css:
--------------------------------------------------------------------------------
1 | @import url(https://fonts.googleapis.com/css?family=Titillium+Web);
2 |
3 | .algolia-autocomplete .algolia-docsearch-suggestion--category-header,
4 | .algolia-autocomplete .algolia-docsearch-suggestion--title,
5 | .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column,
6 | #docs-search-input {
7 | font-size: 16px !important;
8 | line-height: normal;
9 | }
10 |
11 | .algolia-autocomplete .algolia-docsearch-suggestion--wrapper {
12 | padding: 0 !important;
13 | }
14 |
15 | .docs-search-container {
16 | float: right;
17 | margin-right: 30px;
18 | }
19 |
20 | #docs-search-input {
21 | border: none;
22 | background: transparent;
23 | padding: 10px;
24 | }
25 |
26 | #docs-search-input, .docs-search-container {
27 | height: 40px;
28 | width: 300px;
29 | }
30 |
31 | /* ESDoc search */
32 | .search-box {
33 | display: none;
34 | }
35 |
36 | div.logo img {
37 | width: 200px;
38 | height: 200px;
39 | }
40 |
41 | div.sequelize {
42 | color: #399af3;
43 | font-size: 60px;
44 | font-family: 'Titillium Web', sans-serif;
45 | }
46 |
47 | .content {
48 | max-width: 1200px;
49 | }
50 |
51 | .navigation {
52 | overflow-x: hidden;
53 | }
54 |
55 | .manual-root .content img {
56 | box-shadow: none !important;
57 | max-width: 300px;
58 | }
59 |
60 | .manual-toc a {
61 | width: 240px;
62 | white-space: pre-wrap;
63 | }
64 |
65 | blockquote {
66 | background: #fafafa;
67 | border-left: 10px solid #ddd;
68 | margin: 1.5em 10px;
69 | padding: 0.5em 10px;
70 | }
71 |
72 | blockquote p {
73 | margin-bottom: 0;
74 | }
75 |
76 | h3#static-variable-DataTypes ~ div[data-ice="properties"] table.params tr td:nth-child(3) {
77 | display: none;
78 | }
79 |
80 | h3#static-variable-QueryTypes ~ div[data-ice="properties"] table.params tr td:nth-child(2),
81 | h3#static-variable-QueryTypes ~ div[data-ice="properties"] table.params tr td:nth-child(3),
82 | h3#static-variable-QueryTypes ~ div[data-ice="properties"] table.params tr td:nth-child(4) {
83 | display: none;
84 | }
85 |
86 | .manual-badge,
87 | .manual-cards {
88 | display: none !important;
89 | }
90 |
91 | a[href="source.html"],
92 | a[href^="file/lib/"] {
93 | display: none;
94 | }
95 |
96 | .manual-color:after {
97 | content: '' !important;
98 | }
99 |
100 | .slack-logo {
101 | height: 20px;
102 | position: relative;
103 | top: 3px;
104 | padding: 0 5px;
105 | }
106 |
107 | .header-logo {
108 | height: 25px;
109 | position: relative;
110 | top: 6px;
111 | }
112 |
113 | .center {
114 | text-align: center;
115 | }
116 |
117 | pre {
118 | white-space: pre-wrap;
119 | }
120 |
--------------------------------------------------------------------------------
/docs/css/theme.css:
--------------------------------------------------------------------------------
1 | /* Side Nav */
2 | .manual-color, .kind-class, .manual-color-faq {
3 | background-color: #6eb5f7;
4 | }
5 |
6 | .manual-color a, .kind-class, .manual-color-faq a {
7 | color: #ffffff !important;
8 | }
9 |
10 | .kind-variable, .manual-color-reference {
11 | background-color: #074278;
12 | }
13 |
14 | .kind-variable, .manual-color-reference a, .manual-color-faq a {
15 | color: #ffffff;
16 | }
17 |
18 | .manual-toc-root ul li {
19 | margin-top: 0.5em;
20 | margin-bottom: 0.5em;
21 | font-weight: normal !important;
22 | }
23 |
24 | .manual-toc .indent-h1 {
25 | margin: 0.5em 0 0 .5em;
26 | }
27 |
28 | /* Class Summay*/
29 | .summary thead tr {
30 | background-color: #074278;
31 | }
32 |
33 | .summary thead span a {
34 | font-weight: 600;
35 | }
36 |
37 | /* Params Table */
38 | table.params thead {
39 | background: none;
40 | color: inherit;
41 | font-weight: 600;
42 | }
43 |
44 | table.params ul {
45 | padding: 0;
46 | }
47 |
48 | table.params ul li {
49 | list-style: none;
50 | }
51 |
52 | table.params td {
53 | padding: 4px 8px;
54 | }
55 |
56 | table.params td:first-child {
57 | border-left: none;
58 | }
59 |
60 | table.params td:last-child {
61 | border-right: none;
62 | }
63 |
64 | table.params thead td {
65 | border-top: none;
66 | }
67 |
68 | table.params tbody tr:last-child td {
69 | border-bottom: none;
70 | }
71 |
72 | .informal {
73 | display: block;
74 | padding-left: 1.3em;
75 | border-left: 5px solid #dfdfdf;
76 | font-size: 1.1em;
77 | font-family: 'Kalam', cursive;
78 | margin: 22px 0;
79 | }
80 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/images/bitovi-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/images/bitovi-logo.png
--------------------------------------------------------------------------------
/docs/images/clevertech.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/images/clevertech.png
--------------------------------------------------------------------------------
/docs/images/connected-cars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/images/connected-cars.png
--------------------------------------------------------------------------------
/docs/images/filsh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/images/filsh.png
--------------------------------------------------------------------------------
/docs/images/logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/images/logo-small.png
--------------------------------------------------------------------------------
/docs/images/logo-snaplytics-green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/images/logo-snaplytics-green.png
--------------------------------------------------------------------------------
/docs/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/images/logo.png
--------------------------------------------------------------------------------
/docs/images/metamarkets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/images/metamarkets.png
--------------------------------------------------------------------------------
/docs/images/shutterstock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/images/shutterstock.png
--------------------------------------------------------------------------------
/docs/images/slack.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/images/walmart-labs-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/docs/images/walmart-labs-logo.png
--------------------------------------------------------------------------------
/docs/imprint.md:
--------------------------------------------------------------------------------
1 | # Imprint
2 |
3 | - Boring legal stuff for the rest of us.
4 | As there are people who are suing for fun and glory, you can find the respective information about the author of the page right here. Have fun reading ...
5 |
6 | ## AUTHOR(S)
7 |
8 | ```
9 | Main author:
10 |
11 | Sascha Depold
12 | Uhlandstr. 160
13 | 10719 Berlin
14 | sascha [at] depold [dot] com
15 | [plus] 49 152 [slash] 03878582
16 | ```
17 |
18 | ## INHALTLICHE VERANTWORTUNG
19 |
20 | ```
21 | Ich übernehme keine Haftung für ausgehende Links.
22 | Daher musst du dich bei Problemen an deren Betreiber wenden!
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 |
Sequelize
6 |
7 |
8 | [](https://travis-ci.org/sequelize/sequelize)
9 | [](https://npmjs.org/package/sequelize)
10 | [](https://github.com/sequelize/sequelize/releases)
11 |
12 | Sequelize is a promise-based ORM for Node.js v4 and up. It supports the dialects PostgreSQL, MySQL, SQLite and MSSQL and features solid transaction support, relations, read replication and more.
13 |
14 | ## Example usage
15 |
16 | ```js
17 | const Sequelize = require('sequelize');
18 | const sequelize = new Sequelize('database', 'username', 'password', {
19 | host: 'localhost',
20 | dialect: 'mysql'|'sqlite'|'postgres'|'mssql',
21 |
22 | pool: {
23 | max: 5,
24 | min: 0,
25 | acquire: 30000,
26 | idle: 10000
27 | },
28 |
29 | // SQLite only
30 | storage: 'path/to/database.sqlite',
31 |
32 | // http://docs.sequelizejs.com/manual/tutorial/querying.html#operators
33 | operatorsAliases: false
34 | });
35 |
36 | const User = sequelize.define('user', {
37 | username: Sequelize.STRING,
38 | birthday: Sequelize.DATE
39 | });
40 |
41 | sequelize.sync()
42 | .then(() => User.create({
43 | username: 'janedoe',
44 | birthday: new Date(1980, 6, 20)
45 | }))
46 | .then(jane => {
47 | console.log(jane.toJSON());
48 | });
49 | ```
50 |
51 | Please use [Getting Started](manual/installation/getting-started) to learn more. If you wish to learn about Sequelize API please use [API Reference](identifiers)
52 |
--------------------------------------------------------------------------------
/docs/legacy.md:
--------------------------------------------------------------------------------
1 | # Working with legacy tables
2 |
3 | While out of the box Sequelize will seem a bit opinionated it's trivial to both legacy and forward proof your application by defining (otherwise generated) table and field names.
4 |
5 | ## Tables
6 | ```js
7 | sequelize.define('user', {
8 |
9 | }, {
10 | tableName: 'users'
11 | });
12 | ```
13 |
14 | ## Fields
15 | ```js
16 | sequelize.define('modelName', {
17 | userId: {
18 | type: Sequelize.INTEGER,
19 | field: 'user_id'
20 | }
21 | });
22 | ```
23 |
24 | ## Primary keys
25 | Sequelize will assume your table has a `id` primary key property by default.
26 |
27 | To define your own primary key:
28 |
29 | ```js
30 | sequelize.define('collection', {
31 | uid: {
32 | type: Sequelize.INTEGER,
33 | primaryKey: true,
34 | autoIncrement: true // Automatically gets converted to SERIAL for postgres
35 | }
36 | });
37 |
38 | sequelize.define('collection', {
39 | uuid: {
40 | type: Sequelize.UUID,
41 | primaryKey: true
42 | }
43 | });
44 | ```
45 |
46 | And if your model has no primary key at all you can use `Model.removeAttribute('id');`
47 |
48 | ## Foreign keys
49 | ```js
50 | // 1:1
51 | Organization.belongsTo(User, {foreignKey: 'owner_id'});
52 | User.hasOne(Organization, {foreignKey: 'owner_id'});
53 |
54 | // 1:M
55 | Project.hasMany(Task, {foreignKey: 'tasks_pk'});
56 | Task.belongsTo(Project, {foreignKey: 'tasks_pk'});
57 |
58 | // N:M
59 | User.hasMany(Role, {through: 'user_has_roles', foreignKey: 'user_role_user_id'});
60 | Role.hasMany(User, {through: 'user_has_roles', foreignKey: 'roles_identifier'});
61 | ```
62 |
--------------------------------------------------------------------------------
/docs/plugins/esdoc-sequelize.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const cheerio = require('cheerio');
4 | const esdocConfig = require('../../.esdoc.json');
5 |
6 | exports.onHandleHTML = function(ev) {
7 | const $ = cheerio.load(ev.data.html);
8 |
9 | const $title = $('head title');
10 | if ($title.text().indexOf(esdocConfig.title) === -1) {
11 | $title.text($title.text() + ' | ' + esdocConfig.title);
12 | }
13 |
14 | const $header = $('header');
15 | $header.prepend('
');
16 | $('.repo-url-github').after('
Join us on Slack');
17 |
18 | // remove unnecessary scripts
19 | const scripts = ['script/search_index.js', 'script/search.js', 'script/inherited-summary.js', 'script/test-summary.js', 'script/inner-link.js'];
20 | for (const script of scripts) {
21 | $(`script[src="${script}"]`).remove();
22 | }
23 |
24 | // Algolia search
25 | if (process.env.ALGOLIA_API_KEY) {
26 | $('head').append('
');
27 | $header.append('
');
28 | $('body').append(`
29 |
30 |
39 | `);
40 | } else {
41 | console.log('Set ALGOLIA_API_KEY environment variable to enable Algolia search field');
42 | }
43 |
44 | ev.data.html = $.html();
45 | };
46 |
--------------------------------------------------------------------------------
/docs/scripts/script.js:
--------------------------------------------------------------------------------
1 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
2 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
3 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
4 | })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
5 |
6 | ga('create', 'UA-96831523-1', 'auto');
7 | ga('send', 'pageview');
8 |
--------------------------------------------------------------------------------
/docs/whos-using.md:
--------------------------------------------------------------------------------
1 | # Who's using sequelize?
2 |
3 | [](http://www.walmartlabs.com/)
4 |
5 | > ... we are avid users of sequelize (and have been for the past 18 months) (Feb 2017)
6 |
7 |
8 |
9 | [](https://snaplytics.io)
10 |
11 | > We've been using sequelize since we started in the beginning of 2015. We use it for our graphql servers (in connection with [graphql-sequelize](http://github.com/mickhansen/graphql-sequelize)), and for all our background workers.
12 |
13 |
14 |
15 | [](https://connectedcars.dk/)
16 |
17 |
18 |
19 | [](https://bitovi.com)
20 |
21 | > We have used Sequelize in enterprise projects for some of our Fortune 100 and Fortune 500 clients. It is used in deployments that are depended on by hundreds of millions of devices every year.
22 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * The entry point.
5 | *
6 | * @module Sequelize
7 | */
8 | module.exports = require('./lib/sequelize');
9 |
--------------------------------------------------------------------------------
/lib/associations/helpers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const _ = require('lodash');
4 |
5 | function checkNamingCollision(association) {
6 | if (association.source.rawAttributes.hasOwnProperty(association.as)) {
7 | throw new Error(
8 | 'Naming collision between attribute \'' + association.as +
9 | '\' and association \'' + association.as + '\' on model ' + association.source.name +
10 | '. To remedy this, change either foreignKey or as in your association definition'
11 | );
12 | }
13 | }
14 | exports.checkNamingCollision = checkNamingCollision;
15 |
16 | function addForeignKeyConstraints(newAttribute, source, target, options, key) {
17 | // FK constraints are opt-in: users must either set `foreignKeyConstraints`
18 | // on the association, or request an `onDelete` or `onUpdate` behaviour
19 |
20 | if (options.foreignKeyConstraint || options.onDelete || options.onUpdate) {
21 |
22 | // Find primary keys: composite keys not supported with this approach
23 | const primaryKeys = _.chain(source.rawAttributes).keys()
24 | .filter(key => source.rawAttributes[key].primaryKey)
25 | .map(key => source.rawAttributes[key].field || key).value();
26 |
27 | if (primaryKeys.length === 1) {
28 | if (source._schema) {
29 | newAttribute.references = {
30 | model: source.sequelize.getQueryInterface().QueryGenerator.addSchema({
31 | tableName: source.tableName,
32 | _schema: source._schema,
33 | _schemaDelimiter: source._schemaDelimiter
34 | })
35 | };
36 | } else {
37 | newAttribute.references = { model: source.tableName };
38 | }
39 |
40 | newAttribute.references.key = key || primaryKeys[0];
41 | newAttribute.onDelete = options.onDelete;
42 | newAttribute.onUpdate = options.onUpdate;
43 | }
44 | }
45 | }
46 | exports.addForeignKeyConstraints = addForeignKeyConstraints;
47 |
48 | /**
49 | * Mixin (inject) association methods to model prototype
50 | *
51 | * @private
52 | * @param {Object} Association instance
53 | * @param {Object} Model prototype
54 | * @param {Array} Method names to inject
55 | * @param {Object} Mapping between model and association method names
56 | */
57 | function mixinMethods(association, obj, methods, aliases) {
58 | aliases = aliases || {};
59 |
60 | for (const method of methods) {
61 | // don't override custom methods
62 | if (!obj[association.accessors[method]]) {
63 | const realMethod = aliases[method] || method;
64 |
65 | obj[association.accessors[method]] = function() {
66 | const instance = this;
67 | const args = [instance].concat(Array.from(arguments));
68 |
69 | return association[realMethod].apply(association, args);
70 | };
71 | }
72 | }
73 | }
74 | exports.mixinMethods = mixinMethods;
75 |
--------------------------------------------------------------------------------
/lib/associations/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Association = require('./base');
4 | Association.BelongsTo = require('./belongs-to');
5 | Association.HasOne = require('./has-one');
6 | Association.HasMany = require('./has-many');
7 | Association.BelongsToMany = require('./belongs-to-many');
8 |
9 | module.exports = Association;
10 | module.exports.default = Association;
11 | module.exports.Association = Association;
12 |
--------------------------------------------------------------------------------
/lib/dialects/abstract/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class AbstractDialect {}
4 |
5 | AbstractDialect.prototype.supports = {
6 | 'DEFAULT': true,
7 | 'DEFAULT VALUES': false,
8 | 'VALUES ()': false,
9 | 'LIMIT ON UPDATE': false,
10 | 'ON DUPLICATE KEY': true,
11 | 'ORDER NULLS': false,
12 | 'UNION': true,
13 | 'UNION ALL': true,
14 | /* What is the dialect's keyword for INSERT IGNORE */
15 | 'IGNORE': '',
16 |
17 | /* does the dialect support returning values for inserted/updated fields */
18 | returnValues: false,
19 |
20 | /* features specific to autoIncrement values */
21 | autoIncrement: {
22 | /* does the dialect require modification of insert queries when inserting auto increment fields */
23 | identityInsert: false,
24 |
25 | /* does the dialect support inserting default/null values for autoincrement fields */
26 | defaultValue: true,
27 |
28 | /* does the dialect support updating autoincrement fields */
29 | update: true
30 | },
31 | /* Do we need to say DEFAULT for bulk insert */
32 | bulkDefault: false,
33 | /* The dialect's words for INSERT IGNORE */
34 | ignoreDuplicates: '',
35 | /* Does the dialect support ON DUPLICATE KEY UPDATE */
36 | updateOnDuplicate: false,
37 | schemas: false,
38 | transactions: true,
39 | transactionOptions: {
40 | type: false
41 | },
42 | migrations: true,
43 | upserts: true,
44 | constraints: {
45 | restrict: true,
46 | addConstraint: true,
47 | dropConstraint: true,
48 | unique: true,
49 | default: false,
50 | check: true,
51 | foreignKey: true,
52 | primaryKey: true
53 | },
54 | index: {
55 | collate: true,
56 | length: false,
57 | parser: false,
58 | concurrently: false,
59 | type: false,
60 | using: true
61 | },
62 | joinTableDependent: true,
63 | groupedLimit: true,
64 | indexViaAlter: false,
65 | JSON: false,
66 | deferrableConstraints: false
67 | };
68 |
69 | module.exports = AbstractDialect;
70 | module.exports.AbstractDialect = AbstractDialect;
71 | module.exports.default = AbstractDialect;
72 |
--------------------------------------------------------------------------------
/lib/dialects/mssql/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const _ = require('lodash');
4 | const AbstractDialect = require('../abstract');
5 | const ConnectionManager = require('./connection-manager');
6 | const Query = require('./query');
7 | const QueryGenerator = require('./query-generator');
8 | const DataTypes = require('../../data-types').mssql;
9 |
10 | class MssqlDialect extends AbstractDialect {
11 | constructor(sequelize) {
12 | super();
13 | this.sequelize = sequelize;
14 | this.connectionManager = new ConnectionManager(this, sequelize);
15 | this.QueryGenerator = _.extend({}, QueryGenerator, {
16 | options: sequelize.options,
17 | _dialect: this,
18 | sequelize
19 | });
20 | }
21 | }
22 |
23 | MssqlDialect.prototype.supports = _.merge(_.cloneDeep(AbstractDialect.prototype.supports), {
24 | 'DEFAULT': true,
25 | 'DEFAULT VALUES': true,
26 | 'LIMIT ON UPDATE': true,
27 | 'ORDER NULLS': false,
28 | lock: false,
29 | transactions: true,
30 | migrations: false,
31 | upserts: true,
32 | returnValues: {
33 | output: true
34 | },
35 | schemas: true,
36 | autoIncrement: {
37 | identityInsert: true,
38 | defaultValue: false,
39 | update: false
40 | },
41 | constraints: {
42 | restrict: false,
43 | default: true
44 | },
45 | index: {
46 | collate: false,
47 | length: false,
48 | parser: false,
49 | type: true,
50 | using: false,
51 | where: true
52 | },
53 | NUMERIC: true,
54 | tmpTableTrigger: true
55 | });
56 |
57 | ConnectionManager.prototype.defaultVersion = '12.0.2000'; // SQL Server 2014 Express
58 | MssqlDialect.prototype.Query = Query;
59 | MssqlDialect.prototype.name = 'mssql';
60 | MssqlDialect.prototype.TICK_CHAR = '"';
61 | MssqlDialect.prototype.TICK_CHAR_LEFT = '[';
62 | MssqlDialect.prototype.TICK_CHAR_RIGHT = ']';
63 | MssqlDialect.prototype.DataTypes = DataTypes;
64 |
65 | module.exports = MssqlDialect;
66 |
--------------------------------------------------------------------------------
/lib/dialects/mssql/query-interface.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | Returns an object that treats MSSQL's inabilities to do certain queries.
5 |
6 | @class QueryInterface
7 | @static
8 | @private
9 | */
10 |
11 | /**
12 | A wrapper that fixes MSSQL's inability to cleanly remove columns from existing tables if they have a default constraint.
13 |
14 | @method removeColumn
15 | @for QueryInterface
16 |
17 | @param {String} tableName The name of the table.
18 | @param {String} attributeName The name of the attribute that we want to remove.
19 | @param {Object} options
20 | @param {Boolean|Function} [options.logging] A function that logs the sql queries, or false for explicitly not logging these queries
21 | @private
22 | */
23 | const removeColumn = function(tableName, attributeName, options) {
24 | options = Object.assign({ raw: true }, options || {});
25 |
26 | const findConstraintSql = this.QueryGenerator.getDefaultConstraintQuery(tableName, attributeName);
27 | return this.sequelize.query(findConstraintSql, options)
28 | .spread(results => {
29 | if (!results.length) {
30 | // No default constraint found -- we can cleanly remove the column
31 | return;
32 | }
33 | const dropConstraintSql = this.QueryGenerator.dropConstraintQuery(tableName, results[0].name);
34 | return this.sequelize.query(dropConstraintSql, options);
35 | })
36 | .then(() => {
37 | const findForeignKeySql = this.QueryGenerator.getForeignKeyQuery(tableName, attributeName);
38 | return this.sequelize.query(findForeignKeySql, options);
39 | })
40 | .spread(results => {
41 | if (!results.length) {
42 | // No foreign key constraints found, so we can remove the column
43 | return;
44 | }
45 | const dropForeignKeySql = this.QueryGenerator.dropForeignKeyQuery(tableName, results[0].constraint_name);
46 | return this.sequelize.query(dropForeignKeySql, options);
47 | })
48 | .then(() => {
49 | //Check if the current column is a primaryKey
50 | const primaryKeyConstraintSql = this.QueryGenerator.getPrimaryKeyConstraintQuery(tableName, attributeName);
51 | return this.sequelize.query(primaryKeyConstraintSql, options);
52 | })
53 | .spread(result => {
54 | if (!result.length) {
55 | return;
56 | }
57 | const dropConstraintSql = this.QueryGenerator.dropConstraintQuery(tableName, result[0].constraintName);
58 | return this.sequelize.query(dropConstraintSql, options);
59 | })
60 | .then(() => {
61 | const removeSql = this.QueryGenerator.removeColumnQuery(tableName, attributeName);
62 | return this.sequelize.query(removeSql, options);
63 | });
64 | };
65 |
66 | module.exports = {
67 | removeColumn
68 | };
69 |
--------------------------------------------------------------------------------
/lib/dialects/mssql/resource-lock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Promise = require('../../promise');
4 |
5 | function ResourceLock(resource) {
6 | this.resource = resource;
7 | this.previous = Promise.resolve(resource);
8 | }
9 |
10 | ResourceLock.prototype.unwrap = function() {
11 | return this.resource;
12 | };
13 |
14 | ResourceLock.prototype.lock = function() {
15 | const lock = this.previous;
16 | let resolve;
17 |
18 | this.previous = new Promise(r => {
19 | resolve = r;
20 | });
21 |
22 | return lock.disposer(resolve);
23 | };
24 |
25 | module.exports = ResourceLock;
26 |
--------------------------------------------------------------------------------
/lib/dialects/mysql/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const _ = require('lodash');
4 | const AbstractDialect = require('../abstract');
5 | const ConnectionManager = require('./connection-manager');
6 | const Query = require('./query');
7 | const QueryGenerator = require('./query-generator');
8 | const DataTypes = require('../../data-types').mysql;
9 |
10 | class MysqlDialect extends AbstractDialect {
11 | constructor(sequelize) {
12 | super();
13 | this.sequelize = sequelize;
14 | this.connectionManager = new ConnectionManager(this, sequelize);
15 | this.QueryGenerator = _.extend({}, QueryGenerator, {
16 | options: sequelize.options,
17 | _dialect: this,
18 | sequelize
19 | });
20 | }
21 | }
22 |
23 | MysqlDialect.prototype.supports = _.merge(_.cloneDeep(AbstractDialect.prototype.supports), {
24 | 'VALUES ()': true,
25 | 'LIMIT ON UPDATE': true,
26 | 'IGNORE': ' IGNORE',
27 | lock: true,
28 | forShare: 'LOCK IN SHARE MODE',
29 | index: {
30 | collate: false,
31 | length: true,
32 | parser: true,
33 | type: true,
34 | using: 1
35 | },
36 | constraints: {
37 | dropConstraint: false,
38 | check: false
39 | },
40 | ignoreDuplicates: ' IGNORE',
41 | updateOnDuplicate: true,
42 | indexViaAlter: true,
43 | NUMERIC: true,
44 | GEOMETRY: true,
45 | JSON: true,
46 | REGEXP: true
47 | });
48 |
49 | ConnectionManager.prototype.defaultVersion = '5.6.0';
50 | MysqlDialect.prototype.Query = Query;
51 | MysqlDialect.prototype.QueryGenerator = QueryGenerator;
52 | MysqlDialect.prototype.DataTypes = DataTypes;
53 | MysqlDialect.prototype.name = 'mysql';
54 | MysqlDialect.prototype.TICK_CHAR = '`';
55 | MysqlDialect.prototype.TICK_CHAR_LEFT = MysqlDialect.prototype.TICK_CHAR;
56 | MysqlDialect.prototype.TICK_CHAR_RIGHT = MysqlDialect.prototype.TICK_CHAR;
57 |
58 | module.exports = MysqlDialect;
59 |
--------------------------------------------------------------------------------
/lib/dialects/mysql/query-interface.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | Returns an object that treats MySQL's inabilities to do certain queries.
5 |
6 | @class QueryInterface
7 | @static
8 | @private
9 | */
10 |
11 | const _ = require('lodash');
12 | const UnknownConstraintError = require('../../errors').UnknownConstraintError;
13 |
14 | /**
15 | A wrapper that fixes MySQL's inability to cleanly remove columns from existing tables if they have a foreign key constraint.
16 |
17 | @method removeColumn
18 | @for QueryInterface
19 |
20 | @param {String} tableName The name of the table.
21 | @param {String} columnName The name of the attribute that we want to remove.
22 | @param {Object} options
23 | @private
24 | */
25 | function removeColumn(tableName, columnName, options) {
26 | options = options || {};
27 |
28 | return this.sequelize.query(
29 | this.QueryGenerator.getForeignKeyQuery(tableName, columnName),
30 | _.assign({ raw: true }, options)
31 | )
32 | .spread(results => {
33 | //Exclude primary key constraint
34 | if (!results.length || results[0].constraint_name === 'PRIMARY') {
35 | // No foreign key constraints found, so we can remove the column
36 | return;
37 | }
38 | return this.sequelize.query(
39 | this.QueryGenerator.dropForeignKeyQuery(tableName, results[0].constraint_name),
40 | _.assign({ raw: true }, options)
41 | );
42 | })
43 | .then(() => this.sequelize.query(
44 | this.QueryGenerator.removeColumnQuery(tableName, columnName),
45 | _.assign({ raw: true }, options)
46 | ));
47 | }
48 |
49 |
50 | function removeConstraint(tableName, constraintName, options) {
51 | const sql = this.QueryGenerator.showConstraintsQuery(tableName, constraintName);
52 |
53 | return this.sequelize.query(sql, Object.assign({}, options, { type: this.sequelize.QueryTypes.SHOWCONSTRAINTS }))
54 | .then(constraints => {
55 | const constraint = constraints[0];
56 | let query;
57 | if (constraint && constraint.constraintType) {
58 | if (constraint.constraintType === 'FOREIGN KEY') {
59 | query = this.QueryGenerator.dropForeignKeyQuery(tableName, constraintName);
60 | } else {
61 | query = this.QueryGenerator.removeIndexQuery(constraint.tableName, constraint.constraintName);
62 | }
63 | } else {
64 | throw new UnknownConstraintError(`Constraint ${constraintName} on table ${tableName} does not exist`);
65 | }
66 |
67 | return this.sequelize.query(query, options);
68 | });
69 | }
70 |
71 | exports.removeConstraint = removeConstraint;
72 | exports.removeColumn = removeColumn;
73 |
--------------------------------------------------------------------------------
/lib/dialects/parserStore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const stores = new Map();
4 |
5 | module.exports = dialect => {
6 |
7 | if (!stores.has(dialect)) {
8 | stores.set(dialect, new Map());
9 | }
10 |
11 | return {
12 | clear() {
13 | stores.get(dialect).clear();
14 | },
15 | refresh(dataType) {
16 | for (const type of dataType.types[dialect]) {
17 | stores.get(dialect).set(type, dataType.parse);
18 | }
19 | },
20 | get(type) {
21 | return stores.get(dialect).get(type);
22 | }
23 | };
24 | };
25 |
--------------------------------------------------------------------------------
/lib/dialects/postgres/hstore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const hstore = require('pg-hstore')({sanitize: true});
4 |
5 | function stringify(data) {
6 | if (data === null) return null;
7 | return hstore.stringify(data);
8 | }
9 | exports.stringify = stringify;
10 |
11 | function parse(value) {
12 | if (value === null) return null;
13 | return hstore.parse(value);
14 | }
15 | exports.parse = parse;
16 |
--------------------------------------------------------------------------------
/lib/dialects/postgres/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const _ = require('lodash');
4 | const AbstractDialect = require('../abstract');
5 | const ConnectionManager = require('./connection-manager');
6 | const Query = require('./query');
7 | const QueryGenerator = require('./query-generator');
8 | const DataTypes = require('../../data-types').postgres;
9 |
10 | class PostgresDialect extends AbstractDialect {
11 | constructor(sequelize) {
12 | super();
13 | this.sequelize = sequelize;
14 | this.connectionManager = new ConnectionManager(this, sequelize);
15 | this.QueryGenerator = _.extend({}, QueryGenerator, {
16 | options: sequelize.options,
17 | _dialect: this,
18 | sequelize
19 | });
20 | }
21 | }
22 |
23 | PostgresDialect.prototype.supports = _.merge(_.cloneDeep(AbstractDialect.prototype.supports), {
24 | 'DEFAULT VALUES': true,
25 | 'EXCEPTION': true,
26 | 'ON DUPLICATE KEY': false,
27 | 'ORDER NULLS': true,
28 | returnValues: {
29 | returning: true
30 | },
31 | bulkDefault: true,
32 | schemas: true,
33 | lock: true,
34 | lockOf: true,
35 | lockKey: true,
36 | lockOuterJoinFailure: true,
37 | forShare: 'FOR SHARE',
38 | index: {
39 | concurrently: true,
40 | using: 2,
41 | where: true
42 | },
43 | NUMERIC: true,
44 | ARRAY: true,
45 | RANGE: true,
46 | GEOMETRY: true,
47 | REGEXP: true,
48 | GEOGRAPHY: true,
49 | JSON: true,
50 | JSONB: true,
51 | HSTORE: true,
52 | deferrableConstraints: true,
53 | searchPath: true
54 | });
55 |
56 | ConnectionManager.prototype.defaultVersion = '9.4.0';
57 | PostgresDialect.prototype.Query = Query;
58 | PostgresDialect.prototype.DataTypes = DataTypes;
59 | PostgresDialect.prototype.name = 'postgres';
60 | PostgresDialect.prototype.TICK_CHAR = '"';
61 | PostgresDialect.prototype.TICK_CHAR_LEFT = PostgresDialect.prototype.TICK_CHAR;
62 | PostgresDialect.prototype.TICK_CHAR_RIGHT = PostgresDialect.prototype.TICK_CHAR;
63 |
64 | module.exports = PostgresDialect;
65 | module.exports.default = PostgresDialect;
66 | module.exports.PostgresDialect = PostgresDialect;
67 |
--------------------------------------------------------------------------------
/lib/dialects/postgres/range.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const _ = require('lodash');
4 |
5 | function stringifyRangeBound(bound) {
6 | if (bound === null) {
7 | return '' ;
8 | } else if (bound === Infinity || bound === -Infinity) {
9 | return bound.toString().toLowerCase();
10 | } else {
11 | return JSON.stringify(bound);
12 | }
13 | }
14 |
15 | function parseRangeBound(bound, parseType) {
16 | if (!bound) {
17 | return null;
18 | } else if (bound === 'infinity') {
19 | return Infinity;
20 | } else if (bound === '-infinity') {
21 | return -Infinity;
22 | } else {
23 | return parseType(bound);
24 | }
25 | }
26 |
27 | function stringify(data) {
28 | if (data === null) return null;
29 |
30 | if (!_.isArray(data)) throw new Error('range must be an array');
31 | if (!data.length) return 'empty';
32 | if (data.length !== 2) throw new Error('range array length must be 0 (empty) or 2 (lower and upper bounds)');
33 |
34 | if (data.hasOwnProperty('inclusive')) {
35 | if (data.inclusive === false) data.inclusive = [false, false];
36 | else if (!data.inclusive) data.inclusive = [true, false];
37 | else if (data.inclusive === true) data.inclusive = [true, true];
38 | } else {
39 | data.inclusive = [true, false];
40 | }
41 |
42 | _.each(data, (value, index) => {
43 | if (_.isObject(value)) {
44 | if (value.hasOwnProperty('inclusive')) data.inclusive[index] = !!value.inclusive;
45 | if (value.hasOwnProperty('value')) data[index] = value.value;
46 | }
47 | });
48 |
49 | const lowerBound = stringifyRangeBound(data[0]);
50 | const upperBound = stringifyRangeBound(data[1]);
51 |
52 | return (data.inclusive[0] ? '[' : '(') + lowerBound + ',' + upperBound + (data.inclusive[1] ? ']' : ')');
53 | }
54 | exports.stringify = stringify;
55 |
56 | function parse(value, parser) {
57 | if (value === null) return null;
58 | if (value === 'empty') {
59 | const empty = [];
60 | empty.inclusive = [];
61 | return empty;
62 | }
63 |
64 | let result = value
65 | .substring(1, value.length - 1)
66 | .split(',', 2);
67 |
68 | if (result.length !== 2) return value;
69 |
70 | result = result.map(value => parseRangeBound(value, parser));
71 |
72 | result.inclusive = [value[0] === '[', value[value.length - 1] === ']'];
73 |
74 | return result;
75 | }
76 | exports.parse = parse;
77 |
--------------------------------------------------------------------------------
/lib/dialects/sqlite/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const _ = require('lodash');
4 | const AbstractDialect = require('../abstract');
5 | const ConnectionManager = require('./connection-manager');
6 | const Query = require('./query');
7 | const QueryGenerator = require('./query-generator');
8 | const DataTypes = require('../../data-types').sqlite;
9 |
10 | class SqliteDialect extends AbstractDialect {
11 | constructor(sequelize) {
12 | super();
13 | this.sequelize = sequelize;
14 | this.connectionManager = new ConnectionManager(this, sequelize);
15 | this.QueryGenerator = _.extend({}, QueryGenerator, {
16 | options: sequelize.options,
17 | _dialect: this,
18 | sequelize
19 | });
20 | }
21 | }
22 |
23 | SqliteDialect.prototype.supports = _.merge(_.cloneDeep(AbstractDialect.prototype.supports), {
24 | 'DEFAULT': false,
25 | 'DEFAULT VALUES': true,
26 | 'UNION ALL': false,
27 | 'IGNORE': ' OR IGNORE',
28 | index: {
29 | using: false,
30 | where: true
31 | },
32 | transactionOptions: {
33 | type: true,
34 | autocommit: false
35 | },
36 | constraints: {
37 | addConstraint: false,
38 | dropConstraint: false
39 | },
40 | joinTableDependent: false,
41 | groupedLimit: false,
42 | ignoreDuplicates: ' OR IGNORE',
43 | JSON: true
44 | });
45 |
46 | ConnectionManager.prototype.defaultVersion = '3.8.0';
47 | SqliteDialect.prototype.Query = Query;
48 | SqliteDialect.prototype.DataTypes = DataTypes;
49 | SqliteDialect.prototype.name = 'sqlite';
50 | SqliteDialect.prototype.TICK_CHAR = '`';
51 | SqliteDialect.prototype.TICK_CHAR_LEFT = SqliteDialect.prototype.TICK_CHAR;
52 | SqliteDialect.prototype.TICK_CHAR_RIGHT = SqliteDialect.prototype.TICK_CHAR;
53 |
54 | module.exports = SqliteDialect;
55 | module.exports.SqliteDialect = SqliteDialect;
56 | module.exports.default = SqliteDialect;
57 |
--------------------------------------------------------------------------------
/lib/errors.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('./errors/index.js');
4 |
--------------------------------------------------------------------------------
/lib/model-manager.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Toposort = require('toposort-class');
4 | const _ = require('lodash');
5 |
6 | class ModelManager {
7 | constructor(sequelize) {
8 | this.models = [];
9 | this.sequelize = sequelize;
10 | }
11 |
12 | addModel(model) {
13 | this.models.push(model);
14 | this.sequelize.models[model.name] = model;
15 |
16 | return model;
17 | }
18 |
19 | removeModel(modelToRemove) {
20 | this.models = this.models.filter(model => model.name !== modelToRemove.name);
21 |
22 | delete this.sequelize.models[modelToRemove.name];
23 | }
24 |
25 | getModel(against, options) {
26 | options = _.defaults(options || {}, {
27 | attribute: 'name'
28 | });
29 |
30 | const model = this.models.filter(model => model[options.attribute] === against);
31 |
32 | return model ? model[0] : null;
33 | }
34 |
35 | get all() {
36 | return this.models;
37 | }
38 |
39 | /**
40 | * Iterate over Models in an order suitable for e.g. creating tables. Will
41 | * take foreign key constraints into account so that dependencies are visited
42 | * before dependents.
43 | * @private
44 | */
45 | forEachModel(iterator, options) {
46 | const models = {};
47 | const sorter = new Toposort();
48 | let sorted;
49 | let dep;
50 |
51 | options = _.defaults(options || {}, {
52 | reverse: true
53 | });
54 |
55 | for (const model of this.models) {
56 | let deps = [];
57 | let tableName = model.getTableName();
58 |
59 | if (_.isObject(tableName)) {
60 | tableName = tableName.schema + '.' + tableName.tableName;
61 | }
62 |
63 | models[tableName] = model;
64 |
65 | for (const attrName in model.rawAttributes) {
66 | if (model.rawAttributes.hasOwnProperty(attrName)) {
67 | const attribute = model.rawAttributes[attrName];
68 |
69 | if (attribute.references) {
70 | dep = attribute.references.model;
71 |
72 | if (_.isObject(dep)) {
73 | dep = dep.schema + '.' + dep.tableName;
74 | }
75 |
76 | deps.push(dep);
77 | }
78 | }
79 | }
80 |
81 | deps = deps.filter(dep => tableName !== dep);
82 |
83 | sorter.add(tableName, deps);
84 | }
85 |
86 | sorted = sorter.sort();
87 | if (options.reverse) {
88 | sorted = sorted.reverse();
89 | }
90 | for (const name of sorted) {
91 | iterator(models[name], name);
92 | }
93 | }
94 | }
95 |
96 | module.exports = ModelManager;
97 | module.exports.ModelManager = ModelManager;
98 | module.exports.default = ModelManager;
99 |
--------------------------------------------------------------------------------
/lib/promise.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Promise = require('bluebird').getNewLibraryCopy();
4 |
5 | module.exports = Promise;
6 | module.exports.Promise = Promise;
7 | module.exports.default = Promise;
8 |
--------------------------------------------------------------------------------
/lib/query-types.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * An enum of query types used by `sequelize.query`
5 | *
6 | * @see {@link Sequelize#query}
7 | *
8 | * @property SELECT
9 | * @property INSERT
10 | * @property UPDATE
11 | * @property BULKUPDATE
12 | * @property BULKDELETE
13 | * @property DELETE
14 | * @property UPSERT
15 | * @property VERSION
16 | * @property SHOWTABLES
17 | * @property SHOWINDEXES
18 | * @property DESCRIBE
19 | * @property RAW
20 | * @property FOREIGNKEYS
21 | * @property SHOWCONSTRAINTS
22 | */
23 | const QueryTypes = module.exports = { // eslint-disable-line
24 | SELECT: 'SELECT',
25 | INSERT: 'INSERT',
26 | UPDATE: 'UPDATE',
27 | BULKUPDATE: 'BULKUPDATE',
28 | BULKDELETE: 'BULKDELETE',
29 | DELETE: 'DELETE',
30 | UPSERT: 'UPSERT',
31 | VERSION: 'VERSION',
32 | SHOWTABLES: 'SHOWTABLES',
33 | SHOWINDEXES: 'SHOWINDEXES',
34 | DESCRIBE: 'DESCRIBE',
35 | RAW: 'RAW',
36 | FOREIGNKEYS: 'FOREIGNKEYS',
37 | SHOWCONSTRAINTS: 'SHOWCONSTRAINTS'
38 | };
39 |
--------------------------------------------------------------------------------
/lib/sql-string.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const dataTypes = require('./data-types');
4 | const util = require('util');
5 | const _ = require('lodash');
6 |
7 | function escape(val, timeZone, dialect, format) {
8 | let prependN = false;
9 | if (val === undefined || val === null) {
10 | return 'NULL';
11 | }
12 | switch (typeof val) {
13 | case 'boolean':
14 | // SQLite doesn't have true/false support. MySQL aliases true/false to 1/0
15 | // for us. Postgres actually has a boolean type with true/false literals,
16 | // but sequelize doesn't use it yet.
17 | if (dialect === 'sqlite' || dialect === 'mssql') {
18 | return +!!val;
19 | }
20 | return '' + !!val;
21 | case 'number':
22 | return val + '';
23 | case 'string':
24 | // In mssql, prepend N to all quoted vals which are originally a string (for
25 | // unicode compatibility)
26 | prependN = dialect === 'mssql';
27 | break;
28 | }
29 |
30 | if (val instanceof Date) {
31 | val = dataTypes[dialect].DATE.prototype.stringify(val, { timezone: timeZone });
32 | }
33 |
34 | if (Buffer.isBuffer(val)) {
35 | if (dataTypes[dialect].BLOB) {
36 | return dataTypes[dialect].BLOB.prototype.stringify(val);
37 | }
38 |
39 | return dataTypes.BLOB.prototype.stringify(val);
40 | }
41 |
42 | if (Array.isArray(val)) {
43 | const partialEscape = _.partial(escape, _, timeZone, dialect, format);
44 | if (dialect === 'postgres' && !format) {
45 | return dataTypes.ARRAY.prototype.stringify(val, {escape: partialEscape});
46 | }
47 | return val.map(partialEscape);
48 | }
49 |
50 | if (!val.replace) {
51 | throw new Error('Invalid value ' + util.inspect(val));
52 | }
53 |
54 | if (dialect === 'postgres' || dialect === 'sqlite' || dialect === 'mssql') {
55 | // http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
56 | // http://stackoverflow.com/q/603572/130598
57 | val = val.replace(/'/g, "''");
58 | } else {
59 | val = val.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, s => {
60 | switch (s) {
61 | case '\0': return '\\0';
62 | case '\n': return '\\n';
63 | case '\r': return '\\r';
64 | case '\b': return '\\b';
65 | case '\t': return '\\t';
66 | case '\x1a': return '\\Z';
67 | default: return '\\' + s;
68 | }
69 | });
70 | }
71 | return (prependN ? "N'" : "'") + val + "'";
72 | }
73 | exports.escape = escape;
74 |
75 | function format(sql, values, timeZone, dialect) {
76 | values = [].concat(values);
77 |
78 | if (typeof sql !== 'string') {
79 | throw new Error('Invalid SQL string provided: ' + sql);
80 | }
81 | return sql.replace(/\?/g, match => {
82 | if (!values.length) {
83 | return match;
84 | }
85 |
86 | return escape(values.shift(), timeZone, dialect, true);
87 | });
88 | }
89 | exports.format = format;
90 |
91 | function formatNamedParameters(sql, values, timeZone, dialect) {
92 | return sql.replace(/\:+(?!\d)(\w+)/g, (value, key) => {
93 | if ('postgres' === dialect && '::' === value.slice(0, 2)) {
94 | return value;
95 | }
96 |
97 | if (values[key] !== undefined) {
98 | return escape(values[key], timeZone, dialect, true);
99 | } else {
100 | throw new Error('Named parameter "' + value + '" has no value in the given object.');
101 | }
102 | });
103 | }
104 | exports.formatNamedParameters = formatNamedParameters;
105 |
--------------------------------------------------------------------------------
/lib/utils/inherits.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const util = require('util');
4 | const _ = require('lodash');
5 |
6 | /**
7 | * like util.inherits, but also copies over static properties
8 | * @private
9 | */
10 | function inherits(constructor, superConstructor) {
11 | util.inherits(constructor, superConstructor); // Instance (prototype) methods
12 | _.extend(constructor, superConstructor); // Static methods
13 | }
14 |
15 | module.exports = inherits;
16 | module.exports.inherits = inherits;
17 | module.exports.default = inherits;
18 |
--------------------------------------------------------------------------------
/lib/utils/logger.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Sequelize module for debug and deprecation messages.
5 | * It require a `context` for which messages will be printed.
6 | *
7 | * @module logging
8 | * @private
9 | */
10 |
11 | const depd = require('depd'),
12 | debug = require('debug'),
13 | _ = require('lodash');
14 |
15 | class Logger {
16 | constructor(config) {
17 |
18 | this.config = _.extend({
19 | context: 'sequelize',
20 | debug: true
21 | }, config || {});
22 |
23 | this.depd = depd(this.config.context);
24 | this.debug = debug(this.config.context);
25 | }
26 |
27 | deprecate(message) {
28 | this.depd(message);
29 | }
30 |
31 | debug(message) {
32 | this.config.debug && this.debug(message);
33 | }
34 |
35 | warn(message) {
36 | console.warn(`(${this.config.context}) Warning: ${message}`);
37 | }
38 |
39 | debugContext(childContext) {
40 | if (!childContext) {
41 | throw new Error('No context supplied to debug');
42 | }
43 | return debug([this.config.context, childContext].join(':'));
44 | }
45 | }
46 |
47 | module.exports = Logger;
48 |
--------------------------------------------------------------------------------
/lib/utils/parameter-validator.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const _ = require('lodash');
4 | const util = require('util');
5 | const Utils = require('../utils');
6 |
7 | function validateDeprecation(value, expectation, options) {
8 | if (!options.deprecated) {
9 | return;
10 | }
11 |
12 | const valid = value instanceof options.deprecated || Object.prototype.toString.call(value) === Object.prototype.toString.call(options.deprecated.call());
13 |
14 | if (valid) {
15 | const message = `${util.inspect(value)} should not be of type "${options.deprecated.name}"`;
16 | Utils.deprecate(options.deprecationWarning || message);
17 | }
18 |
19 | return valid;
20 | }
21 |
22 | function validate(value, expectation) {
23 | // the second part of this check is a workaround to deal with an issue that occurs in node-webkit when
24 | // using object literals. https://github.com/sequelize/sequelize/issues/2685
25 | if (value instanceof expectation || Object.prototype.toString.call(value) === Object.prototype.toString.call(expectation.call())) {
26 | return true;
27 | }
28 |
29 | throw new Error(`The parameter (value: ${value}) is no ${expectation.name}`);
30 | }
31 |
32 | function check(value, expectation, options) {
33 | options = _.extend({
34 | deprecated: false,
35 | index: null,
36 | method: null,
37 | optional: false
38 | }, options || {});
39 |
40 | if (!value && options.optional) {
41 | return true;
42 | }
43 |
44 | if (value === undefined) {
45 | throw new Error('No value has been passed.');
46 | }
47 |
48 | if (expectation === undefined) {
49 | throw new Error('No expectation has been passed.');
50 | }
51 |
52 | return false
53 | || validateDeprecation(value, expectation, options)
54 | || validate(value, expectation, options);
55 | }
56 |
57 | module.exports = check;
58 | module.exports.check = check;
59 | module.exports.default = check;
60 |
--------------------------------------------------------------------------------
/lib/utils/validator-extras.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const _ = require('lodash');
4 | const validator = _.cloneDeep(require('validator'));
5 | const moment = require('moment');
6 |
7 | const extensions = {
8 | extend(name, fn) {
9 | this[name] = fn;
10 |
11 | return this;
12 | },
13 | notEmpty(str) {
14 | return !str.match(/^[\s\t\r\n]*$/);
15 | },
16 | len(str, min, max) {
17 | return this.isLength(str, min, max);
18 | },
19 | isUrl(str) {
20 | return this.isURL(str);
21 | },
22 | isIPv6(str) {
23 | return this.isIP(str, 6);
24 | },
25 | isIPv4(str) {
26 | return this.isIP(str, 4);
27 | },
28 | notIn(str, values) {
29 | return !this.isIn(str, values);
30 | },
31 | regex(str, pattern, modifiers) {
32 | str += '';
33 | if (Object.prototype.toString.call(pattern).slice(8, -1) !== 'RegExp') {
34 | pattern = new RegExp(pattern, modifiers);
35 | }
36 | return str.match(pattern);
37 | },
38 | notRegex(str, pattern, modifiers) {
39 | return !this.regex(str, pattern, modifiers);
40 | },
41 | isDecimal(str) {
42 | return str !== '' && !!str.match(/^(?:-?(?:[0-9]+))?(?:\.[0-9]*)?(?:[eE][\+\-]?(?:[0-9]+))?$/);
43 | },
44 | min(str, val) {
45 | const number = parseFloat(str);
46 | return isNaN(number) || number >= val;
47 | },
48 | max(str, val) {
49 | const number = parseFloat(str);
50 | return isNaN(number) || number <= val;
51 | },
52 | not(str, pattern, modifiers) {
53 | return this.notRegex(str, pattern, modifiers);
54 | },
55 | contains(str, elem) {
56 | return str.indexOf(elem) >= 0 && !!elem;
57 | },
58 | notContains(str, elem) {
59 | return !this.contains(str, elem);
60 | },
61 | is(str, pattern, modifiers) {
62 | return this.regex(str, pattern, modifiers);
63 | }
64 | };
65 | exports.extensions = extensions;
66 |
67 | function extendModelValidations(modelInstance) {
68 | const extensions = {
69 | isImmutable(str, param, field) {
70 | return modelInstance.isNewRecord || modelInstance.dataValues[field] === modelInstance._previousDataValues[field];
71 | }
72 | };
73 |
74 | _.forEach(extensions, (extend, key) => {
75 | validator[key] = extend;
76 | });
77 | }
78 | exports.extendModelValidations = extendModelValidations;
79 |
80 | // Deprecate this.
81 | validator.notNull = function() {
82 | throw new Error('Warning "notNull" validation has been deprecated in favor of Schema based "allowNull"');
83 | };
84 |
85 | // https://github.com/chriso/validator.js/blob/6.2.0/validator.js
86 | _.forEach(extensions, (extend, key) => {
87 | validator[key] = extend;
88 | });
89 |
90 | // map isNull to isEmpty
91 | // https://github.com/chriso/validator.js/commit/e33d38a26ee2f9666b319adb67c7fc0d3dea7125
92 | validator.isNull = validator.isEmpty;
93 |
94 | // isDate removed in 7.0.0
95 | // https://github.com/chriso/validator.js/commit/095509fc707a4dc0e99f85131df1176ad6389fc9
96 | validator.isDate = function(dateString) {
97 | // avoid http://momentjs.com/guides/#/warnings/js-date/
98 | // by doing a preliminary check on `dateString`
99 | const parsed = Date.parse(dateString);
100 | if (isNaN(parsed)) {
101 | // fail if we can't parse it
102 | return false;
103 | } else {
104 | // otherwise convert to ISO 8601 as moment prefers
105 | // http://momentjs.com/docs/#/parsing/string/
106 | const date = new Date(parsed);
107 | return moment(date.toISOString()).isValid();
108 | }
109 | };
110 |
111 | exports.validator = validator;
112 |
--------------------------------------------------------------------------------
/sscce_template.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /*
4 | * Copy this file to ./sscce.js
5 | * Add code from issue
6 | * npm run sscce-{dialect}
7 | */
8 |
9 | const Sequelize = require('./index');
10 | const sequelize = require('./test/support').createSequelizeInstance();
11 |
--------------------------------------------------------------------------------
/test/config/.docker.env:
--------------------------------------------------------------------------------
1 | # Special ports needed for docker to prevent port conflicts
2 | SEQ_MYSQL_PORT=8999
3 | SEQ_MYSQL_USER=sequelize_test
4 | SEQ_MYSQL_PW=sequelize_test
5 | SEQ_PG_PORT=8998
6 | SEQ_PG_USER=sequelize_test
7 | SEQ_PG_PW=sequelize_test
8 | SEQ_MSSQL_PORT=8997
9 | SEQ_MSSQL_DB=master
10 | SEQ_MSSQL_USER=sa
11 | SEQ_MSSQL_PW=yourStrong(!)Password
12 |
--------------------------------------------------------------------------------
/test/config/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | let mssqlConfig;
5 | try {
6 | mssqlConfig = JSON.parse(fs.readFileSync(__dirname + '/mssql.json', 'utf8'));
7 | } catch (e) {
8 | // ignore
9 | }
10 |
11 | module.exports = {
12 | username: process.env.SEQ_USER || 'root',
13 | password: process.env.SEQ_PW || null,
14 | database: process.env.SEQ_DB || 'sequelize_test',
15 | host: process.env.SEQ_HOST || '127.0.0.1',
16 | pool: {
17 | max: process.env.SEQ_POOL_MAX || 5,
18 | idle: process.env.SEQ_POOL_IDLE || 30000
19 | },
20 |
21 | rand() {
22 | return parseInt(Math.random() * 999, 10);
23 | },
24 |
25 | mssql: mssqlConfig || {
26 | database: process.env.SEQ_MSSQL_DB || process.env.SEQ_DB || 'sequelize_test',
27 | username: process.env.SEQ_MSSQL_USER || process.env.SEQ_USER || 'sequelize',
28 | password: process.env.SEQ_MSSQL_PW || process.env.SEQ_PW || 'nEGkLma26gXVHFUAHJxcmsrK',
29 | host: process.env.SEQ_MSSQL_HOST || process.env.SEQ_HOST || '127.0.0.1',
30 | port: process.env.SEQ_MSSQL_PORT || process.env.SEQ_PORT || 1433,
31 | dialectOptions: {
32 | // big insert queries need a while
33 | requestTimeout: 60000
34 | },
35 | pool: {
36 | max: process.env.SEQ_MSSQL_POOL_MAX || process.env.SEQ_POOL_MAX || 5,
37 | idle: process.env.SEQ_MSSQL_POOL_IDLE || process.env.SEQ_POOL_IDLE || 3000
38 | }
39 | },
40 |
41 | //make idle time small so that tests exit promptly
42 | mysql: {
43 | database: process.env.SEQ_MYSQL_DB || process.env.SEQ_DB || 'sequelize_test',
44 | username: process.env.SEQ_MYSQL_USER || process.env.SEQ_USER || 'root',
45 | password: process.env.SEQ_MYSQL_PW || process.env.SEQ_PW || null,
46 | host: process.env.MYSQL_PORT_3306_TCP_ADDR || process.env.SEQ_MYSQL_HOST || process.env.SEQ_HOST || '127.0.0.1',
47 | port: process.env.MYSQL_PORT_3306_TCP_PORT || process.env.SEQ_MYSQL_PORT || process.env.SEQ_PORT || 3306,
48 | pool: {
49 | max: process.env.SEQ_MYSQL_POOL_MAX || process.env.SEQ_POOL_MAX || 5,
50 | idle: process.env.SEQ_MYSQL_POOL_IDLE || process.env.SEQ_POOL_IDLE || 3000
51 | }
52 | },
53 |
54 | sqlite: {
55 | },
56 |
57 | postgres: {
58 | database: process.env.SEQ_PG_DB || process.env.SEQ_DB || 'sequelize_test',
59 | username: process.env.SEQ_PG_USER || process.env.SEQ_USER || 'postgres',
60 | password: process.env.SEQ_PG_PW || process.env.SEQ_PW || 'postgres',
61 | host: process.env.POSTGRES_PORT_5432_TCP_ADDR || process.env.SEQ_PG_HOST || process.env.SEQ_HOST || '127.0.0.1',
62 | port: process.env.POSTGRES_PORT_5432_TCP_PORT || process.env.SEQ_PG_PORT || process.env.SEQ_PORT || 5432,
63 | pool: {
64 | max: process.env.SEQ_PG_POOL_MAX || process.env.SEQ_POOL_MAX || 5,
65 | idle: process.env.SEQ_PG_POOL_IDLE || process.env.SEQ_POOL_IDLE || 3000
66 | }
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/test/config/options.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | module.exports = {
6 | configFile: path.resolve('config', 'database.json'),
7 | migrationsPath: path.resolve('db', 'migrate')
8 | };
9 |
--------------------------------------------------------------------------------
/test/integration/assets/es6project.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | exports.default = function(sequelize, DataTypes) {
3 | return sequelize.define('Project' + parseInt(Math.random() * 9999999999999999), {
4 | name: DataTypes.STRING
5 | });
6 | };
7 |
--------------------------------------------------------------------------------
/test/integration/assets/project.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(sequelize, DataTypes) {
4 | return sequelize.define('Project' + parseInt(Math.random() * 9999999999999999), {
5 | name: DataTypes.STRING
6 | });
7 | };
8 |
--------------------------------------------------------------------------------
/test/integration/dialects/mssql/query-queue.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Promise = require('../../../../lib/promise'),
6 | DataTypes = require('../../../../lib/data-types'),
7 | Support = require('../../support'),
8 | dialect = Support.getTestDialect();
9 |
10 | if (dialect.match(/^mssql/)) {
11 | describe('[MSSQL Specific] Query Queue', () => {
12 | beforeEach(function() {
13 | const User = this.User = this.sequelize.define('User', {
14 | username: DataTypes.STRING
15 | });
16 |
17 | return this.sequelize.sync({ force: true }).then(() => {
18 | return User.create({ username: 'John'});
19 | });
20 | });
21 |
22 | it('should queue concurrent requests to a connection', function() {
23 | const User = this.User;
24 |
25 | return expect(this.sequelize.transaction(t => {
26 | return Promise.all([
27 | User.findOne({
28 | transaction: t
29 | }),
30 | User.findOne({
31 | transaction: t
32 | })
33 | ]);
34 | })).not.to.be.rejected;
35 | });
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/test/integration/dialects/mysql/errors.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai');
4 | const expect = chai.expect;
5 | const Support = require(__dirname + '/../../support');
6 | const dialect = Support.getTestDialect();
7 | const DataTypes = require(__dirname + '/../../../../lib/data-types');
8 |
9 | if (dialect === 'mysql') {
10 | describe('[MYSQL Specific] Errors', () => {
11 |
12 | const validateError = (promise, errClass, errValues) => {
13 | const wanted = Object.assign({}, errValues);
14 |
15 | return expect(promise).to.have.been.rejectedWith(errClass).then(() =>
16 | promise.catch(err => Object.keys(wanted).forEach(k => expect(err[k]).to.eql(wanted[k]))));
17 | };
18 |
19 | describe('ForeignKeyConstraintError', () => {
20 | beforeEach(function() {
21 | this.Task = this.sequelize.define('task', { title: DataTypes.STRING });
22 | this.User = this.sequelize.define('user', { username: DataTypes.STRING });
23 | this.UserTasks = this.sequelize.define('tasksusers', { userId: DataTypes.INTEGER, taskId: DataTypes.INTEGER });
24 |
25 | this.User.belongsToMany(this.Task, { onDelete: 'RESTRICT', through: 'tasksusers' });
26 | this.Task.belongsToMany(this.User, { onDelete: 'RESTRICT', through: 'tasksusers' });
27 |
28 | this.Task.belongsTo(this.User, { foreignKey: 'primaryUserId', as: 'primaryUsers' });
29 | });
30 |
31 | it('in context of DELETE restriction', function() {
32 | const self = this,
33 | ForeignKeyConstraintError = this.sequelize.ForeignKeyConstraintError;
34 |
35 | return this.sequelize.sync({ force: true }).bind({}).then(() => {
36 | return Promise.all([
37 | self.User.create({ id: 67, username: 'foo' }),
38 | self.Task.create({ id: 52, title: 'task' })
39 | ]);
40 | }).spread(function(user1, task1) {
41 | this.user1 = user1;
42 | this.task1 = task1;
43 | return user1.setTasks([task1]);
44 | }).then(function() {
45 | return Promise.all([
46 | validateError(this.user1.destroy(), ForeignKeyConstraintError, {
47 | fields: ['userId'],
48 | table: 'users',
49 | value: undefined,
50 | index: 'tasksusers_ibfk_1',
51 | reltype: 'parent'
52 | }),
53 | validateError(this.task1.destroy(), ForeignKeyConstraintError, {
54 | fields: ['taskId'],
55 | table: 'tasks',
56 | value: undefined,
57 | index: 'tasksusers_ibfk_2',
58 | reltype: 'parent'
59 | })
60 | ]);
61 | });
62 | });
63 |
64 | it('in context of missing relation', function() {
65 | const self = this,
66 | ForeignKeyConstraintError = this.sequelize.ForeignKeyConstraintError;
67 |
68 | return this.sequelize.sync({ force: true }).then(() =>
69 | validateError(self.Task.create({ title: 'task', primaryUserId: 5 }), ForeignKeyConstraintError, {
70 | fields: ['primaryUserId'],
71 | table: 'users',
72 | value: 5,
73 | index: 'tasks_ibfk_1',
74 | reltype: 'child'
75 | }));
76 | });
77 |
78 | });
79 | });
80 | }
81 |
--------------------------------------------------------------------------------
/test/integration/dialects/postgres/connection-manager.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../../support'),
6 | dialect = Support.getTestDialect(),
7 | DataTypes = require(__dirname + '/../../../../lib/data-types'),
8 | _ = require('lodash');
9 |
10 | if (dialect.match(/^postgres/)) {
11 | describe('[POSTGRES] Sequelize', () => {
12 | function checkTimezoneParsing(baseOptions) {
13 | const options = _.extend({}, baseOptions, { timezone: 'Asia/Kolkata', timestamps: true });
14 | const sequelize = Support.createSequelizeInstance(options);
15 |
16 | const tzTable = sequelize.define('tz_table', { foo: DataTypes.STRING });
17 | return tzTable.sync({force: true}).then(() => {
18 | return tzTable.create({foo: 'test'}).then(row => {
19 | expect(row).to.be.not.null;
20 | });
21 | });
22 | }
23 |
24 | it('should correctly parse the moment based timezone', function() {
25 | return checkTimezoneParsing(this.sequelize.options);
26 | });
27 |
28 | it('should correctly parse the moment based timezone while fetching hstore oids', function() {
29 | // reset oids so we need to refetch them
30 | DataTypes.HSTORE.types.postgres.oids = [];
31 | DataTypes.HSTORE.types.postgres.array_oids = [];
32 | return checkTimezoneParsing(this.sequelize.options);
33 | });
34 | });
35 | }
36 |
--------------------------------------------------------------------------------
/test/integration/dialects/postgres/error.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | DataTypes = require(__dirname + '/../../../../lib/data-types'),
6 | Support = require(__dirname + '/../../support'),
7 | Sequelize = Support.Sequelize,
8 | dialect = Support.getTestDialect(),
9 | _ = require('lodash');
10 |
11 | if (dialect.match(/^postgres/)) {
12 | const constraintName = 'overlap_period';
13 | beforeEach(function() {
14 | const self = this;
15 | this.Booking = self.sequelize.define('Booking', {
16 | roomNo: DataTypes.INTEGER,
17 | period: DataTypes.RANGE(DataTypes.DATE)
18 | });
19 | return self.Booking
20 | .sync({ force: true })
21 | .then(() => {
22 | return self.sequelize.query('ALTER TABLE "' + self.Booking.tableName + '" ADD CONSTRAINT ' + constraintName +
23 | ' EXCLUDE USING gist ("roomNo" WITH =, period WITH &&)');
24 | });
25 | });
26 |
27 | describe('[POSTGRES Specific] ExclusionConstraintError', () => {
28 |
29 | it('should contain error specific properties', () => {
30 | const errDetails = {
31 | message: 'Exclusion constraint error',
32 | constraint: 'constraint_name',
33 | fields: { 'field1': 1, 'field2': [123, 321] },
34 | table: 'table_name',
35 | parent: new Error('Test error')
36 | };
37 | const err = new Sequelize.ExclusionConstraintError(errDetails);
38 |
39 | _.each(errDetails, (value, key) => {
40 | expect(value).to.be.deep.equal(err[key]);
41 | });
42 | });
43 |
44 | it('should throw ExclusionConstraintError when "period" value overlaps existing', function() {
45 | const Booking = this.Booking;
46 |
47 | return Booking
48 | .create({
49 | roomNo: 1,
50 | guestName: 'Incognito Visitor',
51 | period: [new Date(2015, 0, 1), new Date(2015, 0, 3)]
52 | })
53 | .then(() => {
54 | return expect(Booking
55 | .create({
56 | roomNo: 1,
57 | guestName: 'Frequent Visitor',
58 | period: [new Date(2015, 0, 2), new Date(2015, 0, 5)]
59 | })).to.eventually.be.rejectedWith(Sequelize.ExclusionConstraintError);
60 | });
61 | });
62 |
63 | });
64 | }
65 |
--------------------------------------------------------------------------------
/test/integration/dialects/postgres/hstore.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../../support'),
6 | dialect = Support.getTestDialect(),
7 | hstore = require('../../../../lib/dialects/postgres/hstore');
8 |
9 | if (dialect.match(/^postgres/)) {
10 | describe('[POSTGRES Specific] hstore', () => {
11 | describe('stringify', () => {
12 | it('should handle empty objects correctly', () => {
13 | expect(hstore.stringify({ })).to.equal('');
14 | });
15 |
16 | it('should handle null values correctly', () => {
17 | expect(hstore.stringify({ null: null })).to.equal('"null"=>NULL');
18 | });
19 |
20 | it('should handle null values correctly', () => {
21 | expect(hstore.stringify({ foo: null })).to.equal('"foo"=>NULL');
22 | });
23 |
24 | it('should handle empty string correctly', () => {
25 | expect(hstore.stringify({foo: ''})).to.equal('"foo"=>\"\"');
26 | });
27 |
28 | it('should handle a string with backslashes correctly', () => {
29 | expect(hstore.stringify({foo: '\\'})).to.equal('"foo"=>"\\\\"');
30 | });
31 |
32 | it('should handle a string with double quotes correctly', () => {
33 | expect(hstore.stringify({foo: '""a"'})).to.equal('"foo"=>"\\"\\"a\\""');
34 | });
35 |
36 | it('should handle a string with single quotes correctly', () => {
37 | expect(hstore.stringify({foo: "''a'"})).to.equal('"foo"=>"\'\'\'\'a\'\'"');
38 | });
39 |
40 | it('should handle simple objects correctly', () => {
41 | expect(hstore.stringify({ test: 'value' })).to.equal('"test"=>"value"');
42 | });
43 |
44 | });
45 |
46 | describe('parse', () => {
47 | it('should handle a null object correctly', () => {
48 | expect(hstore.parse(null)).to.deep.equal(null);
49 | });
50 |
51 | it('should handle empty string correctly', () => {
52 | expect(hstore.parse('"foo"=>\"\"')).to.deep.equal({foo: ''});
53 | });
54 |
55 | it('should handle a string with double quotes correctly', () => {
56 | expect(hstore.parse('"foo"=>"\\\"\\\"a\\\""')).to.deep.equal({foo: '\"\"a\"'});
57 | });
58 |
59 | it('should handle a string with single quotes correctly', () => {
60 | expect(hstore.parse('"foo"=>"\'\'\'\'a\'\'"')).to.deep.equal({foo: "''a'"});
61 | });
62 |
63 | it('should handle a string with backslashes correctly', () => {
64 | expect(hstore.parse('"foo"=>"\\\\"')).to.deep.equal({foo: '\\'});
65 | });
66 |
67 | it('should handle empty objects correctly', () => {
68 | expect(hstore.parse('')).to.deep.equal({ });
69 | });
70 |
71 | it('should handle simple objects correctly', () => {
72 | expect(hstore.parse('"test"=>"value"')).to.deep.equal({ test: 'value' });
73 | });
74 |
75 | });
76 | describe('stringify and parse', () => {
77 | it('should stringify then parse back the same structure', () => {
78 | const testObj = {foo: 'bar', count: '1', emptyString: '', quotyString: '""', extraQuotyString: '"""a"""""', backslashes: '\\f023', moreBackslashes: '\\f\\0\\2\\1', backslashesAndQuotes: '\\"\\"uhoh"\\"', nully: null};
79 | expect(hstore.parse(hstore.stringify(testObj))).to.deep.equal(testObj);
80 | expect(hstore.parse(hstore.stringify(hstore.parse(hstore.stringify(testObj))))).to.deep.equal(testObj);
81 | });
82 | });
83 | });
84 | }
85 |
--------------------------------------------------------------------------------
/test/integration/dialects/sqlite/test.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/test/integration/dialects/sqlite/test.sqlite
--------------------------------------------------------------------------------
/test/integration/hooks/count.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | DataTypes = require(__dirname + '/../../../lib/data-types');
7 |
8 | describe(Support.getTestDialectTeaser('Hooks'), () => {
9 | beforeEach(function() {
10 | this.User = this.sequelize.define('User', {
11 | username: {
12 | type: DataTypes.STRING,
13 | allowNull: false
14 | },
15 | mood: {
16 | type: DataTypes.ENUM,
17 | values: ['happy', 'sad', 'neutral']
18 | }
19 | });
20 | return this.sequelize.sync({ force: true });
21 | });
22 |
23 | describe('#count', () => {
24 | beforeEach(function() {
25 | return this.User.bulkCreate([
26 | {username: 'adam', mood: 'happy'},
27 | {username: 'joe', mood: 'sad'},
28 | {username: 'joe', mood: 'happy'}
29 | ]);
30 | });
31 |
32 | describe('on success', () => {
33 | it('hook runs', function() {
34 | let beforeHook = false;
35 |
36 | this.User.beforeCount(() => {
37 | beforeHook = true;
38 | });
39 |
40 | return this.User.count().then(count => {
41 | expect(count).to.equal(3);
42 | expect(beforeHook).to.be.true;
43 | });
44 | });
45 |
46 | it('beforeCount hook can change options', function() {
47 | this.User.beforeCount(options => {
48 | options.where.username = 'adam';
49 | });
50 |
51 | return expect(this.User.count({where: {username: 'joe'}})).to.eventually.equal(1);
52 | });
53 | });
54 |
55 | describe('on error', () => {
56 | it('in beforeCount hook returns error', function() {
57 | this.User.beforeCount(() => {
58 | throw new Error('Oops!');
59 | });
60 |
61 | return expect(this.User.count({where: {username: 'adam'}})).to.be.rejectedWith('Oops!');
62 | });
63 | });
64 | });
65 |
66 | });
67 |
--------------------------------------------------------------------------------
/test/integration/hooks/destroy.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | DataTypes = require(__dirname + '/../../../lib/data-types'),
7 | sinon = require('sinon');
8 |
9 | describe(Support.getTestDialectTeaser('Hooks'), () => {
10 | beforeEach(function() {
11 | this.User = this.sequelize.define('User', {
12 | username: {
13 | type: DataTypes.STRING,
14 | allowNull: false
15 | },
16 | mood: {
17 | type: DataTypes.ENUM,
18 | values: ['happy', 'sad', 'neutral']
19 | }
20 | });
21 | return this.sequelize.sync({ force: true });
22 | });
23 |
24 | describe('#destroy', () => {
25 | describe('on success', () => {
26 | it('should run hooks', function() {
27 | const beforeHook = sinon.spy(),
28 | afterHook = sinon.spy();
29 |
30 | this.User.beforeDestroy(beforeHook);
31 | this.User.afterDestroy(afterHook);
32 |
33 | return this.User.create({username: 'Toni', mood: 'happy'}).then(user => {
34 | return user.destroy().then(() => {
35 | expect(beforeHook).to.have.been.calledOnce;
36 | expect(afterHook).to.have.been.calledOnce;
37 | });
38 | });
39 | });
40 | });
41 |
42 | describe('on error', () => {
43 | it('should return an error from before', function() {
44 | const beforeHook = sinon.spy(),
45 | afterHook = sinon.spy();
46 |
47 | this.User.beforeDestroy(() => {
48 | beforeHook();
49 | throw new Error('Whoops!');
50 | });
51 | this.User.afterDestroy(afterHook);
52 |
53 | return this.User.create({username: 'Toni', mood: 'happy'}).then(user => {
54 | return expect(user.destroy()).to.be.rejected.then(() => {
55 | expect(beforeHook).to.have.been.calledOnce;
56 | expect(afterHook).not.to.have.been.called;
57 | });
58 | });
59 | });
60 |
61 | it('should return an error from after', function() {
62 | const beforeHook = sinon.spy(),
63 | afterHook = sinon.spy();
64 |
65 | this.User.beforeDestroy(beforeHook);
66 | this.User.afterDestroy(() => {
67 | afterHook();
68 | throw new Error('Whoops!');
69 | });
70 |
71 | return this.User.create({username: 'Toni', mood: 'happy'}).then(user => {
72 | return expect(user.destroy()).to.be.rejected.then(() => {
73 | expect(beforeHook).to.have.been.calledOnce;
74 | expect(afterHook).to.have.been.calledOnce;
75 | });
76 | });
77 | });
78 | });
79 | });
80 |
81 | });
82 |
--------------------------------------------------------------------------------
/test/integration/hooks/restore.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | DataTypes = require(__dirname + '/../../../lib/data-types'),
7 | sinon = require('sinon');
8 |
9 | describe(Support.getTestDialectTeaser('Hooks'), () => {
10 | beforeEach(function() {
11 | this.User = this.sequelize.define('User', {
12 | username: {
13 | type: DataTypes.STRING,
14 | allowNull: false
15 | },
16 | mood: {
17 | type: DataTypes.ENUM,
18 | values: ['happy', 'sad', 'neutral']
19 | }
20 | });
21 |
22 | this.ParanoidUser = this.sequelize.define('ParanoidUser', {
23 | username: DataTypes.STRING,
24 | mood: {
25 | type: DataTypes.ENUM,
26 | values: ['happy', 'sad', 'neutral']
27 | }
28 | }, {
29 | paranoid: true
30 | });
31 |
32 | return this.sequelize.sync({ force: true });
33 | });
34 |
35 | describe('#restore', () => {
36 | describe('on success', () => {
37 | it('should run hooks', function() {
38 | const beforeHook = sinon.spy(),
39 | afterHook = sinon.spy();
40 |
41 | this.ParanoidUser.beforeRestore(beforeHook);
42 | this.ParanoidUser.afterRestore(afterHook);
43 |
44 | return this.ParanoidUser.create({username: 'Toni', mood: 'happy'}).then(user => {
45 | return user.destroy().then(() => {
46 | return user.restore().then(() => {
47 | expect(beforeHook).to.have.been.calledOnce;
48 | expect(afterHook).to.have.been.calledOnce;
49 | });
50 | });
51 | });
52 | });
53 | });
54 |
55 | describe('on error', () => {
56 | it('should return an error from before', function() {
57 | const beforeHook = sinon.spy(),
58 | afterHook = sinon.spy();
59 |
60 | this.ParanoidUser.beforeRestore(() => {
61 | beforeHook();
62 | throw new Error('Whoops!');
63 | });
64 | this.ParanoidUser.afterRestore(afterHook);
65 |
66 | return this.ParanoidUser.create({username: 'Toni', mood: 'happy'}).then(user => {
67 | return user.destroy().then(() => {
68 | return expect(user.restore()).to.be.rejected.then(() => {
69 | expect(beforeHook).to.have.been.calledOnce;
70 | expect(afterHook).not.to.have.been.called;
71 | });
72 | });
73 | });
74 | });
75 |
76 | it('should return an error from after', function() {
77 | const beforeHook = sinon.spy(),
78 | afterHook = sinon.spy();
79 |
80 | this.ParanoidUser.beforeRestore(beforeHook);
81 | this.ParanoidUser.afterRestore(() => {
82 | afterHook();
83 | throw new Error('Whoops!');
84 | });
85 |
86 | return this.ParanoidUser.create({username: 'Toni', mood: 'happy'}).then(user => {
87 | return user.destroy().then(() => {
88 | return expect(user.restore()).to.be.rejected.then(() => {
89 | expect(beforeHook).to.have.been.calledOnce;
90 | expect(afterHook).to.have.been.calledOnce;
91 | });
92 | });
93 | });
94 | });
95 | });
96 | });
97 |
98 | });
99 |
--------------------------------------------------------------------------------
/test/integration/hooks/upsert.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | DataTypes = require(__dirname + '/../../../lib/data-types'),
7 | sinon = require('sinon');
8 |
9 | if (Support.sequelize.dialect.supports.upserts) {
10 | describe(Support.getTestDialectTeaser('Hooks'), () => {
11 | beforeEach(function() {
12 | this.User = this.sequelize.define('User', {
13 | username: {
14 | type: DataTypes.STRING,
15 | allowNull: false,
16 | unique: true //Either Primary Key/Unique Keys should be passed to upsert
17 | },
18 | mood: {
19 | type: DataTypes.ENUM,
20 | values: ['happy', 'sad', 'neutral']
21 | }
22 | });
23 | return this.sequelize.sync({ force: true });
24 | });
25 |
26 | describe('#upsert', () => {
27 | describe('on success', () => {
28 | it('should run hooks', function() {
29 | const beforeHook = sinon.spy(),
30 | afterHook = sinon.spy();
31 |
32 | this.User.beforeUpsert(beforeHook);
33 | this.User.afterUpsert(afterHook);
34 |
35 | return this.User.upsert({username: 'Toni', mood: 'happy'}).then(() => {
36 | expect(beforeHook).to.have.been.calledOnce;
37 | expect(afterHook).to.have.been.calledOnce;
38 | });
39 | });
40 | });
41 |
42 | describe('on error', () => {
43 | it('should return an error from before', function() {
44 | const beforeHook = sinon.spy(),
45 | afterHook = sinon.spy();
46 |
47 | this.User.beforeUpsert(() => {
48 | beforeHook();
49 | throw new Error('Whoops!');
50 | });
51 | this.User.afterUpsert(afterHook);
52 |
53 | return expect(this.User.upsert({username: 'Toni', mood: 'happy'})).to.be.rejected.then(() => {
54 | expect(beforeHook).to.have.been.calledOnce;
55 | expect(afterHook).not.to.have.been.called;
56 | });
57 | });
58 |
59 | it('should return an error from after', function() {
60 | const beforeHook = sinon.spy(),
61 | afterHook = sinon.spy();
62 |
63 | this.User.beforeUpsert(beforeHook);
64 | this.User.afterUpsert(() => {
65 | afterHook();
66 | throw new Error('Whoops!');
67 | });
68 |
69 | return expect(this.User.upsert({username: 'Toni', mood: 'happy'})).to.be.rejected.then(() => {
70 | expect(beforeHook).to.have.been.calledOnce;
71 | expect(afterHook).to.have.been.calledOnce;
72 | });
73 | });
74 | });
75 |
76 | describe('preserves changes to values', () => {
77 | it('beforeUpsert', function() {
78 | let hookCalled = 0;
79 | const valuesOriginal = { mood: 'sad', username: 'leafninja' };
80 |
81 | this.User.beforeUpsert(values => {
82 | values.mood = 'happy';
83 | hookCalled++;
84 | });
85 |
86 | return this.User.upsert(valuesOriginal).then(() => {
87 | expect(valuesOriginal.mood).to.equal('happy');
88 | expect(hookCalled).to.equal(1);
89 | });
90 | });
91 | });
92 | });
93 | });
94 | }
95 |
--------------------------------------------------------------------------------
/test/integration/include/paranoid.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | sinon = require('sinon'),
6 | Support = require(__dirname + '/../support'),
7 | DataTypes = require(__dirname + '/../../../lib/data-types');
8 |
9 | describe(Support.getTestDialectTeaser('Paranoid'), () => {
10 |
11 | beforeEach(function( ) {
12 | const S = this.sequelize,
13 | DT = DataTypes,
14 |
15 | A = this.A = S.define('A', { name: DT.STRING }, { paranoid: true }),
16 | B = this.B = S.define('B', { name: DT.STRING }, { paranoid: true }),
17 | C = this.C = S.define('C', { name: DT.STRING }, { paranoid: true }),
18 | D = this.D = S.define('D', { name: DT.STRING }, { paranoid: true });
19 |
20 | A.belongsTo(B);
21 | A.belongsToMany(D, {through: 'a_d'});
22 | A.hasMany(C);
23 |
24 | B.hasMany(A);
25 | B.hasMany(C);
26 |
27 | C.belongsTo(A);
28 | C.belongsTo(B);
29 |
30 | D.belongsToMany(A, {through: 'a_d'});
31 |
32 | return S.sync({ force: true });
33 | });
34 |
35 | before(function() {
36 | this.clock = sinon.useFakeTimers();
37 | });
38 |
39 | after(function() {
40 | this.clock.restore();
41 | });
42 |
43 | it('paranoid with timestamps: false should be ignored / not crash', function() {
44 | const S = this.sequelize,
45 | Test = S.define('Test', {
46 | name: DataTypes.STRING
47 | }, {
48 | timestamps: false,
49 | paranoid: true
50 | });
51 |
52 | return S.sync({ force: true }).then(() => {
53 | return Test.findById(1);
54 | });
55 | });
56 |
57 | it('test if non required is marked as false', function( ) {
58 | const A = this.A,
59 | B = this.B,
60 | options = {
61 | include: [
62 | {
63 | model: B,
64 | required: false
65 | }
66 | ]
67 | };
68 |
69 | return A.find(options).then(() => {
70 | expect(options.include[0].required).to.be.equal(false);
71 | });
72 | });
73 |
74 | it('test if required is marked as true', function( ) {
75 | const A = this.A,
76 | B = this.B,
77 | options = {
78 | include: [
79 | {
80 | model: B,
81 | required: true
82 | }
83 | ]
84 | };
85 |
86 | return A.find(options).then(() => {
87 | expect(options.include[0].required).to.be.equal(true);
88 | });
89 | });
90 |
91 | it('should not load paranoid, destroyed instances, with a non-paranoid parent', function() {
92 | const X = this.sequelize.define('x', {
93 | name: DataTypes.STRING
94 | }, {
95 | paranoid: false
96 | });
97 |
98 | const Y = this.sequelize.define('y', {
99 | name: DataTypes.STRING
100 | }, {
101 | timestamps: true,
102 | paranoid: true
103 | });
104 |
105 | X.hasMany(Y);
106 |
107 | return this.sequelize.sync({ force: true}).bind(this).then(function() {
108 | return this.sequelize.Promise.all([
109 | X.create(),
110 | Y.create()
111 | ]);
112 | }).spread(function(x, y) {
113 | this.x = x;
114 | this.y = y;
115 |
116 | return x.addY(y);
117 | }).then(function() {
118 | return this.y.destroy();
119 | }).then(function() {
120 | //prevent CURRENT_TIMESTAMP to be same
121 | this.clock.tick(1000);
122 |
123 | return X.findAll({
124 | include: [Y]
125 | }).get(0);
126 | }).then(x => {
127 | expect(x.ys).to.have.length(0);
128 | });
129 | });
130 | });
131 |
--------------------------------------------------------------------------------
/test/integration/model/optimistic_locking.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require(__dirname + '/../support');
4 | const DataTypes = require(__dirname + '/../../../lib/data-types');
5 | const chai = require('chai');
6 | const expect = chai.expect;
7 |
8 | describe(Support.getTestDialectTeaser('Model'), () => {
9 | describe('optimistic locking', () => {
10 | let Account;
11 | beforeEach(function() {
12 | Account = this.sequelize.define('Account', {
13 | number: {
14 | type: DataTypes.INTEGER
15 | }
16 | }, {
17 | version: true
18 | });
19 | return Account.sync({force: true});
20 | });
21 |
22 | it('should increment the version on save', () => {
23 | return Account.create({number: 1}).then(account => {
24 | account.number += 1;
25 | expect(account.version).to.eq(0);
26 | return account.save();
27 | }).then(account => {
28 | expect(account.version).to.eq(1);
29 | });
30 | });
31 |
32 | it('should increment the version on update', () => {
33 | return Account.create({number: 1}).then(account => {
34 | expect(account.version).to.eq(0);
35 | return account.update({ number: 2 });
36 | }).then(account => {
37 | expect(account.version).to.eq(1);
38 | account.number += 1;
39 | return account.save();
40 | }).then(account => {
41 | expect(account.number).to.eq(3);
42 | expect(account.version).to.eq(2);
43 | });
44 | });
45 |
46 | it('prevents stale instances from being saved', () => {
47 | return expect(Account.create({number: 1}).then(accountA => {
48 | return Account.findById(accountA.id).then(accountB => {
49 | accountA.number += 1;
50 | return accountA.save().then(() => { return accountB; });
51 | });
52 | }).then(accountB => {
53 | accountB.number += 1;
54 | return accountB.save();
55 | })).to.eventually.be.rejectedWith(Support.Sequelize.OptimisticLockError);
56 | });
57 |
58 | it('increment() also increments the version', () => {
59 | return Account.create({number: 1}).then(account => {
60 | expect(account.version).to.eq(0);
61 | return account.increment('number', { by: 1} );
62 | }).then(account => {
63 | return account.reload();
64 | }).then(account => {
65 | expect(account.version).to.eq(1);
66 | });
67 | });
68 |
69 | it('decrement() also increments the version', () => {
70 | return Account.create({number: 1}).then(account => {
71 | expect(account.version).to.eq(0);
72 | return account.decrement('number', { by: 1} );
73 | }).then(account => {
74 | return account.reload();
75 | }).then(account => {
76 | expect(account.version).to.eq(1);
77 | });
78 | });
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/test/integration/model/scope.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | Sequelize = require('../../../index'),
5 | expect = chai.expect,
6 | Support = require(__dirname + '/../support');
7 |
8 | describe(Support.getTestDialectTeaser('Model'), () => {
9 | describe('scope', () => {
10 | beforeEach(function() {
11 | this.ScopeMe = this.sequelize.define('ScopeMe', {
12 | username: Sequelize.STRING,
13 | email: Sequelize.STRING,
14 | access_level: Sequelize.INTEGER,
15 | other_value: Sequelize.INTEGER
16 | }, {
17 | scopes: {
18 | lowAccess: {
19 | attributes: ['other_value', 'access_level'],
20 | where: {
21 | access_level: {
22 | lte: 5
23 | }
24 | }
25 | },
26 | withName: {
27 | attributes: ['username']
28 | }
29 | }
30 | });
31 |
32 | return this.sequelize.sync({force: true}).then(() => {
33 | const records = [
34 | {username: 'tony', email: 'tony@sequelizejs.com', access_level: 3, other_value: 7},
35 | {username: 'tobi', email: 'tobi@fakeemail.com', access_level: 10, other_value: 11},
36 | {username: 'dan', email: 'dan@sequelizejs.com', access_level: 5, other_value: 10},
37 | {username: 'fred', email: 'fred@foobar.com', access_level: 3, other_value: 7}
38 | ];
39 | return this.ScopeMe.bulkCreate(records);
40 | });
41 | });
42 |
43 | it('should be able to merge attributes as array', function() {
44 | return this.ScopeMe.scope('lowAccess', 'withName').findOne()
45 | .then(record => {
46 | expect(record.other_value).to.exist;
47 | expect(record.username).to.exist;
48 | expect(record.access_level).to.exist;
49 | });
50 | });
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/test/integration/model/scope/findAndCount.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | Sequelize = require('../../../../index'),
5 | expect = chai.expect,
6 | Support = require(__dirname + '/../../support');
7 |
8 | describe(Support.getTestDialectTeaser('Model'), () => {
9 | describe('scope', () => {
10 |
11 | describe('findAndCount', () => {
12 |
13 | beforeEach(function() {
14 | this.ScopeMe = this.sequelize.define('ScopeMe', {
15 | username: Sequelize.STRING,
16 | email: Sequelize.STRING,
17 | access_level: Sequelize.INTEGER,
18 | other_value: Sequelize.INTEGER
19 | }, {
20 | defaultScope: {
21 | where: {
22 | access_level: {
23 | gte: 5
24 | }
25 | },
26 | attributes: ['username', 'email', 'access_level']
27 | },
28 | scopes: {
29 | lowAccess: {
30 | where: {
31 | access_level: {
32 | lte: 5
33 | }
34 | }
35 | },
36 | withOrder: {
37 | order: ['username']
38 | }
39 | }
40 | });
41 |
42 | return this.sequelize.sync({force: true}).then(() => {
43 | const records = [
44 | {username: 'tony', email: 'tony@sequelizejs.com', access_level: 3, other_value: 7},
45 | {username: 'tobi', email: 'tobi@fakeemail.com', access_level: 10, other_value: 11},
46 | {username: 'dan', email: 'dan@sequelizejs.com', access_level: 5, other_value: 10},
47 | {username: 'fred', email: 'fred@foobar.com', access_level: 3, other_value: 7}
48 | ];
49 | return this.ScopeMe.bulkCreate(records);
50 | });
51 | });
52 |
53 | it('should apply defaultScope', function() {
54 | return this.ScopeMe.findAndCount().then(result => {
55 | expect(result.count).to.equal(2);
56 | expect(result.rows.length).to.equal(2);
57 | });
58 | });
59 |
60 | it('should be able to override default scope', function() {
61 | return this.ScopeMe.findAndCount({ where: { access_level: { gt: 5 }}})
62 | .then(result => {
63 | expect(result.count).to.equal(1);
64 | expect(result.rows.length).to.equal(1);
65 | });
66 | });
67 |
68 | it('should be able to unscope', function() {
69 | return this.ScopeMe.unscoped().findAndCount({ limit: 1 })
70 | .then(result => {
71 | expect(result.count).to.equal(4);
72 | expect(result.rows.length).to.equal(1);
73 | });
74 | });
75 |
76 | it('should be able to apply other scopes', function() {
77 | return this.ScopeMe.scope('lowAccess').findAndCount()
78 | .then(result => {
79 | expect(result.count).to.equal(3);
80 | });
81 | });
82 |
83 | it('should be able to merge scopes with where', function() {
84 | return this.ScopeMe.scope('lowAccess')
85 | .findAndCount({ where: { username: 'dan'}}).then(result => {
86 | expect(result.count).to.equal(1);
87 | });
88 | });
89 |
90 | it('should ignore the order option if it is found within the scope', function() {
91 | return this.ScopeMe.scope('withOrder').findAndCount()
92 | .then(result => {
93 | expect(result.count).to.equal(4);
94 | });
95 | });
96 | });
97 | });
98 | });
99 |
--------------------------------------------------------------------------------
/test/integration/model/update.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require(__dirname + '/../support');
4 | const DataTypes = require(__dirname + '/../../../lib/data-types');
5 | const chai = require('chai');
6 | const expect = chai.expect;
7 | const current = Support.sequelize;
8 | const _ = require('lodash');
9 |
10 | describe(Support.getTestDialectTeaser('Model'), () => {
11 | describe('update', () => {
12 | beforeEach(function() {
13 | this.Account = this.sequelize.define('Account', {
14 | ownerId: {
15 | type: DataTypes.INTEGER,
16 | allowNull: false,
17 | field: 'owner_id'
18 | },
19 | name: {
20 | type: DataTypes.STRING
21 | }
22 | });
23 | return this.Account.sync({force: true});
24 | });
25 |
26 | it('should only update the passed fields', function() {
27 | return this.Account
28 | .create({ ownerId: 2 })
29 | .then(account => this.Account.update({
30 | name: Math.random().toString()
31 | }, {
32 | where: {
33 | id: account.get('id')
34 | }
35 | }));
36 | });
37 |
38 |
39 | if (_.get(current.dialect.supports, 'returnValues.returning')) {
40 | it('should return the updated record', function() {
41 | return this.Account.create({ ownerId: 2 }).then(account => {
42 | return this.Account.update({ name: 'FooBar' }, {
43 | where: {
44 | id: account.get('id')
45 | },
46 | returning: true
47 | }).spread((count, accounts) => {
48 | const firstAcc = accounts[0];
49 | expect(firstAcc.ownerId).to.be.equal(2);
50 | expect(firstAcc.name).to.be.equal('FooBar');
51 | });
52 | });
53 | });
54 | }
55 |
56 | if (current.dialect.supports['LIMIT ON UPDATE']) {
57 | it('should only update one row', function() {
58 | return this.Account.create({
59 | ownerId: 2,
60 | name: 'Account Name 1'
61 | })
62 | .then(() => {
63 | return this.Account.create({
64 | ownerId: 2,
65 | name: 'Account Name 2'
66 | });
67 | })
68 | .then(() => {
69 | return this.Account.create({
70 | ownerId: 2,
71 | name: 'Account Name 3'
72 | });
73 | })
74 | .then(() => {
75 | const options = {
76 | where: {
77 | ownerId: 2
78 | },
79 | limit: 1
80 | };
81 | return this.Account.update({ name: 'New Name' }, options);
82 | })
83 | .then(account => {
84 | expect(account[0]).to.equal(1);
85 | });
86 | });
87 | }
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/test/integration/pool.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai');
4 | const expect = chai.expect;
5 | const Support = require(__dirname + '/support');
6 | const dialect = Support.getTestDialect();
7 | const sinon = require('sinon');
8 | const Sequelize = Support.Sequelize;
9 |
10 | describe(Support.getTestDialectTeaser('Pooling'), function() {
11 | if (dialect === 'sqlite') return;
12 |
13 | beforeEach(() => {
14 | this.sinon = sinon.sandbox.create();
15 | });
16 |
17 | afterEach(() => {
18 | this.sinon.restore();
19 | });
20 |
21 | it('should reject when unable to acquire connection in given time', () => {
22 | this.testInstance = new Sequelize('localhost', 'ffd', 'dfdf', {
23 | dialect,
24 | databaseVersion: '1.2.3',
25 | pool: {
26 | acquire: 1000 //milliseconds
27 | }
28 | });
29 |
30 | this.sinon.stub(this.testInstance.connectionManager, '_connect')
31 | .returns(new Sequelize.Promise(() => {}));
32 |
33 | return expect(this.testInstance.authenticate())
34 | .to.eventually.be.rejectedWith('ResourceRequest timed out');
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/integration/replication.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai');
4 | const expect = chai.expect;
5 | const Support = require(__dirname + '/support');
6 | const DataTypes = require(__dirname + '/../../lib/data-types');
7 | const dialect = Support.getTestDialect();
8 | const sinon = require('sinon');
9 |
10 | describe(Support.getTestDialectTeaser('Replication'), function() {
11 | if (dialect === 'sqlite') return;
12 |
13 | let sandbox;
14 | let readSpy, writeSpy;
15 |
16 | beforeEach(() => {
17 | sandbox = sinon.sandbox.create();
18 |
19 | this.sequelize = Support.getSequelizeInstance(null, null, null, {
20 | replication: {
21 | write: Support.getConnectionOptions(),
22 | read: [Support.getConnectionOptions()]
23 | }
24 | });
25 |
26 | expect(this.sequelize.connectionManager.pool.write).to.be.ok;
27 | expect(this.sequelize.connectionManager.pool.read).to.be.ok;
28 |
29 | this.User = this.sequelize.define('User', {
30 | firstName: {
31 | type: DataTypes.STRING,
32 | field: 'first_name'
33 | }
34 | });
35 |
36 | return this.User.sync({force: true})
37 | .then(() => {
38 | readSpy = sandbox.spy(this.sequelize.connectionManager.pool.read, 'acquire');
39 | writeSpy = sandbox.spy(this.sequelize.connectionManager.pool.write, 'acquire');
40 | });
41 | });
42 |
43 | afterEach(() => {
44 | sandbox.restore();
45 | });
46 |
47 | function expectReadCalls() {
48 | chai.expect(readSpy.callCount).least(1);
49 | chai.expect(writeSpy.notCalled).eql(true);
50 | }
51 |
52 | function expectWriteCalls() {
53 | chai.expect(writeSpy.callCount).least(1);
54 | chai.expect(readSpy.notCalled).eql(true);
55 | }
56 |
57 | it('should be able to make a write', () => {
58 | return this.User.create({
59 | firstName: Math.random().toString()
60 | }).then(expectWriteCalls);
61 | });
62 |
63 | it('should be able to make a read', () => {
64 | return this.User.findAll().then(expectReadCalls);
65 | });
66 |
67 | it('should run read-only transactions on the replica', () => {
68 | return this.sequelize.transaction({readOnly: true}, transaction => {
69 | return this.User.findAll({transaction});
70 | }).then(expectReadCalls);
71 | });
72 |
73 | it('should run non-read-only transactions on the primary', () => {
74 | return this.sequelize.transaction(transaction => {
75 | return this.User.findAll({transaction});
76 | }).then(expectWriteCalls);
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/test/integration/schema.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/support'),
6 | DataTypes = require(__dirname + '/../../lib/data-types');
7 |
8 | describe(Support.getTestDialectTeaser('Schema'), () => {
9 | beforeEach(function() {
10 | return this.sequelize.createSchema('testschema');
11 | });
12 |
13 | afterEach(function() {
14 | return this.sequelize.dropSchema('testschema');
15 | });
16 |
17 | beforeEach(function() {
18 | this.User = this.sequelize.define('User', {
19 | aNumber: { type: DataTypes.INTEGER }
20 | }, {
21 | schema: 'testschema'
22 | });
23 |
24 | return this.User.sync({ force: true });
25 | });
26 |
27 | it('supports increment', function() {
28 | return this.User.create({ aNumber: 1 }).then(user => {
29 | return user.increment('aNumber', { by: 3 });
30 | }).then(result => {
31 | return result.reload();
32 | }).then(user => {
33 | expect(user).to.be.ok;
34 | expect(user.aNumber).to.be.equal(4);
35 | });
36 | });
37 |
38 | it('supports decrement', function() {
39 | return this.User.create({ aNumber: 10 }).then(user => {
40 | return user.decrement('aNumber', { by: 3 });
41 | }).then(result => {
42 | return result.reload();
43 | }).then(user => {
44 | expect(user).to.be.ok;
45 | expect(user.aNumber).to.be.equal(7);
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/integration/sequelize/log.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | sinon = require('sinon'),
5 | expect = chai.expect,
6 | Support = require(__dirname + '/../support'),
7 | dialect = Support.getTestDialect();
8 |
9 | describe(Support.getTestDialectTeaser('Sequelize'), () => {
10 | describe('log', () => {
11 | beforeEach(function() {
12 | this.spy = sinon.spy(console, 'log');
13 | });
14 |
15 | afterEach(() => {
16 | console.log.restore();
17 | });
18 |
19 | describe('with disabled logging', () => {
20 | beforeEach(function() {
21 | this.sequelize = new Support.Sequelize('db', 'user', 'pw', { dialect, logging: false });
22 | });
23 |
24 | it('does not call the log method of the logger', function() {
25 | this.sequelize.log();
26 | expect(this.spy.calledOnce).to.be.false;
27 | });
28 | });
29 |
30 | describe('with default logging options', () => {
31 | beforeEach(function() {
32 | this.sequelize = new Support.Sequelize('db', 'user', 'pw', { dialect });
33 | });
34 |
35 | describe('called with no arguments', () => {
36 | it('calls the log method', function() {
37 | this.sequelize.log();
38 | expect(this.spy.calledOnce).to.be.true;
39 | });
40 |
41 | it('logs an empty string as info event', function() {
42 | this.sequelize.log('');
43 | expect(this.spy.calledOnce).to.be.true;
44 | });
45 | });
46 |
47 | describe('called with one argument', () => {
48 | it('logs the passed string as info event', function() {
49 | this.sequelize.log('my message');
50 | expect(this.spy.withArgs('my message').calledOnce).to.be.true;
51 | });
52 | });
53 |
54 | describe('called with more than two arguments', () => {
55 | it('passes the arguments to the logger', function() {
56 | this.sequelize.log('error', 'my message', 1, { a: 1 });
57 | expect(this.spy.withArgs('error', 'my message', 1, { a: 1 }).calledOnce).to.be.true;
58 | });
59 | });
60 | });
61 |
62 | describe('with a custom function for logging', () => {
63 | beforeEach(function() {
64 | this.spy = sinon.spy();
65 | this.sequelize = new Support.Sequelize('db', 'user', 'pw', { dialect, logging: this.spy });
66 | });
67 |
68 | it('calls the custom logger method', function() {
69 | this.sequelize.log('om nom');
70 | expect(this.spy.calledOnce).to.be.true;
71 | });
72 |
73 | it('calls the custom logger method with options', function() {
74 | const message = 'om nom';
75 | const timeTaken = 5;
76 | const options = {correlationId: 'ABC001'};
77 | this.sequelize.log(message, timeTaken, options);
78 | expect(this.spy.withArgs(message, timeTaken, options).calledOnce).to.be.true;
79 | });
80 |
81 | });
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/test/integration/support.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require('../support');
4 |
5 | beforeEach(function() {
6 | this.sequelize.test.trackRunningQueries();
7 | return Support.clearDatabase(this.sequelize);
8 | });
9 |
10 | afterEach(function() {
11 | try {
12 | this.sequelize.test.verifyNoRunningQueries();
13 | } catch (err) {
14 | err.message += ' in '+this.currentTest.fullTitle();
15 | throw err;
16 | }
17 | });
18 |
19 | module.exports = Support;
20 |
--------------------------------------------------------------------------------
/test/integration/timezone.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/support'),
6 | dialect = Support.getTestDialect(),
7 | Sequelize = require(__dirname + '/../../index'),
8 | Promise = Sequelize.Promise;
9 |
10 | if (dialect !== 'sqlite') {
11 | // Sqlite does not support setting timezone
12 |
13 | describe(Support.getTestDialectTeaser('Timezone'), () => {
14 | beforeEach(function() {
15 | this.sequelizeWithTimezone = Support.createSequelizeInstance({
16 | timezone: '+07:00'
17 | });
18 | this.sequelizeWithNamedTimezone = Support.createSequelizeInstance({
19 | timezone: 'America/New_York'
20 | });
21 | });
22 |
23 | it('returns the same value for current timestamp', function() {
24 | let now = 'now()';
25 | const startQueryTime = Date.now();
26 |
27 | if (dialect === 'mssql') {
28 | now = 'GETDATE()';
29 | }
30 |
31 | const query = 'SELECT ' + now + ' as now';
32 | return Promise.all([
33 | this.sequelize.query(query, { type: this.sequelize.QueryTypes.SELECT }),
34 | this.sequelizeWithTimezone.query(query, { type: this.sequelize.QueryTypes.SELECT })
35 | ]).spread((now1, now2) => {
36 | const elapsedQueryTime = Date.now() - startQueryTime + 1001;
37 | expect(now1[0].now.getTime()).to.be.closeTo(now2[0].now.getTime(), elapsedQueryTime);
38 | });
39 | });
40 |
41 | if (dialect === 'mysql') {
42 | it('handles existing timestamps', function() {
43 | const NormalUser = this.sequelize.define('user', {}),
44 | TimezonedUser = this.sequelizeWithTimezone.define('user', {});
45 |
46 | return this.sequelize.sync({ force: true }).bind(this).then(() => {
47 | return NormalUser.create({});
48 | }).then(function(normalUser) {
49 | this.normalUser = normalUser;
50 | return TimezonedUser.findById(normalUser.id);
51 | }).then(function(timezonedUser) {
52 | // Expect 7 hours difference, in milliseconds.
53 | // This difference is expected since two instances, configured for each their timezone is trying to read the same timestamp
54 | // this test does not apply to PG, since it stores the timezone along with the timestamp.
55 | expect(this.normalUser.createdAt.getTime() - timezonedUser.createdAt.getTime()).to.be.closeTo(60 * 60 * 7 * 1000, 1000);
56 | });
57 | });
58 |
59 | it('handles named timezones', function() {
60 | const NormalUser = this.sequelize.define('user', {}),
61 | TimezonedUser = this.sequelizeWithNamedTimezone.define('user', {});
62 |
63 | return this.sequelize.sync({ force: true }).bind(this).then(() => {
64 | return TimezonedUser.create({});
65 | }).then(timezonedUser => {
66 | return Promise.all([
67 | NormalUser.findById(timezonedUser.id),
68 | TimezonedUser.findById(timezonedUser.id)
69 | ]);
70 | }).spread((normalUser, timezonedUser) => {
71 | // Expect 5 hours difference, in milliseconds, +/- 1 hour for DST
72 | expect(normalUser.createdAt.getTime() - timezonedUser.createdAt.getTime()).to.be.closeTo(60 * 60 * 4 * 1000 * -1, 60 * 60 * 1000);
73 | });
74 | });
75 | }
76 | });
77 | }
78 |
--------------------------------------------------------------------------------
/test/integration/tmp/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/test/integration/tmp/.gitkeep
--------------------------------------------------------------------------------
/test/integration/trigger.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | Sequelize = require('../../index'),
5 | expect = chai.expect,
6 | Support = require(__dirname + '/../support'),
7 | current = Support.sequelize;
8 |
9 | if (current.dialect.supports.tmpTableTrigger) {
10 | describe(Support.getTestDialectTeaser('Model'), () => {
11 | describe('trigger', () => {
12 | let User;
13 | const triggerQuery = 'create trigger User_ChangeTracking on [users] for insert,update, delete \n' +
14 | 'as\n' +
15 | 'SET NOCOUNT ON\n' +
16 | 'if exists(select 1 from inserted)\n' +
17 | 'begin\n' +
18 | 'select * from inserted\n' +
19 | 'end\n' +
20 | 'if exists(select 1 from deleted)\n' +
21 | 'begin\n' +
22 | 'select * from deleted\n' +
23 | 'end\n';
24 |
25 | beforeEach(function() {
26 | User = this.sequelize.define('user', {
27 | username: {
28 | type: Sequelize.STRING,
29 | field: 'user_name'
30 | }
31 | }, {
32 | hasTrigger: true
33 | });
34 |
35 | return User.sync({force: true}).bind(this).then(function() {
36 | return this.sequelize.query(triggerQuery, {type: this.sequelize.QueryTypes.RAW});
37 | });
38 | });
39 |
40 | it('should return output rows after insert', () => {
41 | return User.create({
42 | username: 'triggertest'
43 | }).then(() => {
44 | return expect(User.find({username: 'triggertest'})).to.eventually.have.property('username').which.equals('triggertest');
45 | });
46 | });
47 |
48 | it('should return output rows after instance update', () => {
49 | return User.create({
50 | username: 'triggertest'
51 | }).then(user => {
52 | user.username = 'usernamechanged';
53 | return user.save();
54 | })
55 | .then(() => {
56 | return expect(User.find({username: 'usernamechanged'})).to.eventually.have.property('username').which.equals('usernamechanged');
57 | });
58 | });
59 |
60 | it('should return output rows after Model update', () => {
61 | return User.create({
62 | username: 'triggertest'
63 | }).then(user => {
64 | return User.update({
65 | username: 'usernamechanged'
66 | }, {
67 | where: {
68 | id: user.get('id')
69 | }
70 | });
71 | })
72 | .then(() => {
73 | return expect(User.find({username: 'usernamechanged'})).to.eventually.have.property('username').which.equals('usernamechanged');
74 | });
75 | });
76 |
77 | it('should successfully delete with a trigger on the table', () => {
78 | return User.create({
79 | username: 'triggertest'
80 | }).then(user => {
81 | return user.destroy();
82 | }).then(() => {
83 | return expect(User.find({username: 'triggertest'})).to.eventually.be.null;
84 | });
85 | });
86 | });
87 | });
88 | }
89 |
--------------------------------------------------------------------------------
/test/integration/vectors.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Sequelize = require('../../index'),
6 | Support = require(__dirname + '/support');
7 |
8 | chai.should();
9 |
10 | describe(Support.getTestDialectTeaser('Vectors'), () => {
11 | it('should not allow insert backslash', function() {
12 | const Student = this.sequelize.define('student', {
13 | name: Sequelize.STRING
14 | }, {
15 | tableName: 'student'
16 | });
17 |
18 | return Student.sync({force: true}).then(() => {
19 | return Student.create({
20 | name: 'Robert\\\'); DROP TABLE "students"; --'
21 | }).then(result => {
22 | expect(result.get('name')).to.equal('Robert\\\'); DROP TABLE "students"; --');
23 | return Student.findAll();
24 | }).then(result => {
25 | expect(result[0].name).to.equal('Robert\\\'); DROP TABLE "students"; --');
26 | });
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/test/tmp/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/sequelize/fea0ca730741ceb80a0a1a937432b5425b9760ed/test/tmp/.gitkeep
--------------------------------------------------------------------------------
/test/unit/associations/association.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai');
4 | const expect = chai.expect;
5 | const Support = require(__dirname + '/../support');
6 | const current = Support.sequelize;
7 | const AssociationError = require(__dirname + '/../../../lib/errors').AssociationError;
8 |
9 | describe(Support.getTestDialectTeaser('belongsTo'), () => {
10 | it('should throw an AssociationError when two associations have the same alias', () => {
11 | const User = current.define('User');
12 | const Task = current.define('Task');
13 |
14 | User.belongsTo(Task, { as: 'task' });
15 | const errorFunction = User.belongsTo.bind(User, Task, { as: 'task' });
16 | const errorMessage = 'You have used the alias task in two separate associations. Aliased associations must have unique aliases.';
17 | expect(errorFunction).to.throw(AssociationError, errorMessage);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/unit/associations/belongs-to.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | _ = require('lodash'),
6 | Support = require(__dirname + '/../support'),
7 | current = Support.sequelize;
8 |
9 | describe(Support.getTestDialectTeaser('belongsTo'), () => {
10 | it('should not override custom methods with association mixin', () => {
11 | const methods = {
12 | getTask: 'get',
13 | setTask: 'set',
14 | createTask: 'create'
15 | };
16 | const User = current.define('User');
17 | const Task = current.define('Task');
18 |
19 | _.each(methods, (alias, method) => {
20 | User.prototype[method] = function() {
21 | const realMethod = this.constructor.associations.task[alias];
22 | expect(realMethod).to.be.a('function');
23 | return realMethod;
24 | };
25 | });
26 |
27 | User.belongsTo(Task, { as: 'task' });
28 |
29 | const user = User.build();
30 |
31 | _.each(methods, (alias, method) => {
32 | expect(user[method]()).to.be.a('function');
33 | });
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/test/unit/associations/dont-modify-options.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | DataTypes = require(__dirname + '/../../../lib/data-types'),
7 | Sequelize = require('../../../index');
8 |
9 | describe(Support.getTestDialectTeaser('associations'), () => {
10 | describe('Test options.foreignKey', () => {
11 | beforeEach(function() {
12 |
13 | this.A = this.sequelize.define('A', {
14 | id: {
15 | type: DataTypes.CHAR(20),
16 | primaryKey: true
17 | }
18 | });
19 | this.B = this.sequelize.define('B', {
20 | id: {
21 | type: Sequelize.CHAR(20),
22 | primaryKey: true
23 | }
24 | });
25 | this.C = this.sequelize.define('C', {});
26 | });
27 |
28 | it('should not be overwritten for belongsTo', function() {
29 | const reqValidForeignKey = { foreignKey: { allowNull: false }};
30 | this.A.belongsTo(this.B, reqValidForeignKey);
31 | this.A.belongsTo(this.C, reqValidForeignKey);
32 | expect(this.A.rawAttributes.CId.type).to.deep.equal(this.C.rawAttributes.id.type);
33 | });
34 | it('should not be overwritten for belongsToMany', function() {
35 | const reqValidForeignKey = { foreignKey: { allowNull: false }, through: 'ABBridge'};
36 | this.B.belongsToMany(this.A, reqValidForeignKey);
37 | this.A.belongsTo(this.C, reqValidForeignKey);
38 | expect(this.A.rawAttributes.CId.type).to.deep.equal(this.C.rawAttributes.id.type);
39 | });
40 | it('should not be overwritten for hasOne', function() {
41 | const reqValidForeignKey = { foreignKey: { allowNull: false }};
42 | this.B.hasOne(this.A, reqValidForeignKey);
43 | this.A.belongsTo(this.C, reqValidForeignKey);
44 | expect(this.A.rawAttributes.CId.type).to.deep.equal(this.C.rawAttributes.id.type);
45 | });
46 | it('should not be overwritten for hasMany', function() {
47 | const reqValidForeignKey = { foreignKey: { allowNull: false }};
48 | this.B.hasMany(this.A, reqValidForeignKey);
49 | this.A.belongsTo(this.C, reqValidForeignKey);
50 | expect(this.A.rawAttributes.CId.type).to.deep.equal(this.C.rawAttributes.id.type);
51 | });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/test/unit/associations/has-one.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | _ = require('lodash'),
6 | Support = require(__dirname + '/../support'),
7 | DataTypes = require(__dirname + '/../../../lib/data-types'),
8 | current = Support.sequelize;
9 |
10 | describe(Support.getTestDialectTeaser('hasOne'), () => {
11 | it('properly use the `as` key to generate foreign key name', () => {
12 | const User = current.define('User', { username: DataTypes.STRING }),
13 | Task = current.define('Task', { title: DataTypes.STRING });
14 |
15 | User.hasOne(Task);
16 | expect(Task.rawAttributes.UserId).not.to.be.empty;
17 |
18 | User.hasOne(Task, {as: 'Shabda'});
19 | expect(Task.rawAttributes.ShabdaId).not.to.be.empty;
20 | });
21 |
22 | it('should not override custom methods with association mixin', () => {
23 | const methods = {
24 | getTask: 'get',
25 | setTask: 'set',
26 | createTask: 'create'
27 | };
28 | const User = current.define('User');
29 | const Task = current.define('Task');
30 |
31 | _.each(methods, (alias, method) => {
32 | User.prototype[method] = function() {
33 | const realMethod = this.constructor.associations.task[alias];
34 | expect(realMethod).to.be.a('function');
35 | return realMethod;
36 | };
37 | });
38 |
39 | User.hasOne(Task, { as: 'task' });
40 |
41 | const user = User.build();
42 |
43 | _.each(methods, (alias, method) => {
44 | expect(user[method]()).to.be.a('function');
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/test/unit/connection-manager.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | sinon = require('sinon'),
5 | expect = chai.expect,
6 | Support = require(__dirname + '/support'),
7 | Sequelize = require(__dirname + '/../../index'),
8 | ConnectionManager = require(__dirname + '/../../lib/dialects/abstract/connection-manager'),
9 | Promise = Sequelize.Promise;
10 |
11 | describe('connection manager', () => {
12 | describe('_connect', () => {
13 | beforeEach(function() {
14 | this.sinon = sinon.sandbox.create();
15 | this.connection = {};
16 |
17 | this.dialect = {
18 | connectionManager: {
19 | connect: this.sinon.stub().returns(Promise.resolve(this.connection))
20 | }
21 | };
22 |
23 | this.sequelize = Support.createSequelizeInstance();
24 | });
25 |
26 | afterEach(function() {
27 | this.sinon.restore();
28 | });
29 |
30 | it('should resolve connection on dialect connection manager', function() {
31 | const connection = {};
32 | this.dialect.connectionManager.connect.returns(Promise.resolve(connection));
33 |
34 | const connectionManager = new ConnectionManager(this.dialect, this.sequelize);
35 |
36 | const config = {};
37 |
38 | return expect(connectionManager._connect(config)).to.eventually.equal(connection).then(() => {
39 | expect(this.dialect.connectionManager.connect).to.have.been.calledWith(config);
40 | });
41 | });
42 |
43 | it('should let beforeConnect hook modify config', function() {
44 | const username = Math.random().toString(),
45 | password = Math.random().toString();
46 |
47 | this.sequelize.beforeConnect(config => {
48 | config.username = username;
49 | config.password = password;
50 | return config;
51 | });
52 |
53 | const connectionManager = new ConnectionManager(this.dialect, this.sequelize);
54 |
55 | return connectionManager._connect({}).then(() => {
56 | expect(this.dialect.connectionManager.connect).to.have.been.calledWith({
57 | username,
58 | password
59 | });
60 | });
61 | });
62 |
63 | it('should call afterConnect', function() {
64 | const spy = sinon.spy();
65 | this.sequelize.afterConnect(spy);
66 |
67 | const connectionManager = new ConnectionManager(this.dialect, this.sequelize);
68 |
69 | return connectionManager._connect({}).then(() => {
70 | expect(spy.callCount).to.equal(1);
71 | expect(spy.firstCall.args[0]).to.equal(this.connection);
72 | expect(spy.firstCall.args[1]).to.eql({});
73 | });
74 | });
75 | });
76 | });
77 |
--------------------------------------------------------------------------------
/test/unit/dialects/mssql/connection-manager.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Sequelize = require(__dirname + '/../../../../index'),
6 | tedious = require('tedious'),
7 | sinon = require('sinon'),
8 | connectionStub = sinon.stub(tedious, 'Connection');
9 |
10 | connectionStub.returns({on() {}});
11 |
12 | describe('[MSSQL] Connection Manager', () => {
13 | let instance,
14 | config;
15 | beforeEach(() => {
16 | config = {
17 | dialect: 'mssql',
18 | database: 'none',
19 | username: 'none',
20 | password: 'none',
21 | host: 'localhost',
22 | port: 2433,
23 | pool: {},
24 | dialectOptions: {
25 | domain: 'TEST.COM'
26 | }
27 | };
28 | instance = new Sequelize(config.database
29 | , config.username
30 | , config.password
31 | , config);
32 | });
33 |
34 | it('connectionManager._connect() Does not delete `domain` from config.dialectOptions',
35 | () => {
36 | expect(config.dialectOptions.domain).to.equal('TEST.COM');
37 | instance.dialect.connectionManager._connect(config);
38 | expect(config.dialectOptions.domain).to.equal('TEST.COM');
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/test/unit/dialects/mssql/query.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const Query = require(path.resolve('./lib/dialects/mssql/query.js'));
5 | const Support = require(path.resolve('./test/support'));
6 | const sequelize = Support.sequelize;
7 | const sinon = require('sinon');
8 | const expect = require('chai').expect;
9 | const tedious = require('tedious');
10 | const tediousIsolationLevel = tedious.ISOLATION_LEVEL;
11 | const connectionStub = { beginTransaction: () => {}, lib: tedious };
12 |
13 | let sandbox, query;
14 |
15 | describe('[MSSQL]', () => {
16 | describe('beginTransaction', () => {
17 | beforeEach(() => {
18 | sandbox = sinon.sandbox.create();
19 | const options = {
20 | transaction: { name: 'transactionName' },
21 | isolationLevel: 'REPEATABLE_READ',
22 | logging: false
23 | };
24 | sandbox.stub(connectionStub, 'beginTransaction').callsFake(cb => {
25 | cb();
26 | });
27 | query = new Query(connectionStub, sequelize, options);
28 | });
29 |
30 | it('should call beginTransaction with correct arguments', () => {
31 | return query._run(connectionStub, 'BEGIN TRANSACTION')
32 | .then(() => {
33 | expect(connectionStub.beginTransaction.called).to.equal(true);
34 | expect(connectionStub.beginTransaction.args[0][1]).to.equal('transactionName');
35 | expect(connectionStub.beginTransaction.args[0][2]).to.equal(tediousIsolationLevel.REPEATABLE_READ);
36 | });
37 | });
38 |
39 | afterEach(() => {
40 | sandbox.restore();
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/unit/dialects/mssql/resource-lock.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const ResourceLock = require('../../../../lib/dialects/mssql/resource-lock'),
4 | Promise = require('../../../../lib/promise'),
5 | assert = require('assert');
6 |
7 | describe('[MSSQL Specific] ResourceLock', () => {
8 | it('should process requests serially', () => {
9 | const expected = {};
10 | const lock = new ResourceLock(expected);
11 | let last = 0;
12 |
13 | function validateResource(actual) {
14 | assert.equal(actual, expected);
15 | }
16 |
17 | return Promise.all([
18 | Promise.using(lock.lock(), resource => {
19 | validateResource(resource);
20 | assert.equal(last, 0);
21 | last = 1;
22 |
23 | return Promise.delay(15);
24 | }),
25 | Promise.using(lock.lock(), resource => {
26 | validateResource(resource);
27 | assert.equal(last, 1);
28 | last = 2;
29 | }),
30 | Promise.using(lock.lock(), resource => {
31 | validateResource(resource);
32 | assert.equal(last, 2);
33 | last = 3;
34 |
35 | return Promise.delay(5);
36 | })
37 | ]);
38 | });
39 |
40 | it('should still return resource after failure', () => {
41 | const expected = {};
42 | const lock = new ResourceLock(expected);
43 |
44 | function validateResource(actual) {
45 | assert.equal(actual, expected);
46 | }
47 |
48 | return Promise.all([
49 | Promise.using(lock.lock(), resource => {
50 | validateResource(resource);
51 |
52 | throw new Error('unexpected error');
53 | }).catch(() => {}),
54 | Promise.using(lock.lock(), validateResource)
55 | ]);
56 | });
57 |
58 | it('should be able to.lock resource without waiting on lock', () => {
59 | const expected = {};
60 | const lock = new ResourceLock(expected);
61 |
62 | assert.equal(lock.unwrap(), expected);
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/test/unit/dialects/mysql/errors.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai');
4 | const expect = chai.expect;
5 | const Support = require(__dirname + '/../../support');
6 | const dialect = Support.getTestDialect();
7 | const queryProto = Support.sequelize.dialect.Query.prototype;
8 |
9 | if (dialect === 'mysql') {
10 | describe('[MYSQL Specific] ForeignKeyConstraintError - error message parsing', () => {
11 | it('FK Errors with ` quotation char are parsed correctly', () => {
12 | const fakeErr = new Error('Cannot delete or update a parent row: a foreign key constraint fails (`table`.`brothers`, CONSTRAINT `brothers_ibfk_1` FOREIGN KEY (`personId`) REFERENCES `people` (`id`) ON UPDATE CASCADE).');
13 |
14 | fakeErr.code = 1451;
15 |
16 | const parsedErr = queryProto.formatError(fakeErr);
17 |
18 | expect(parsedErr).to.be.instanceOf(Support.sequelize.ForeignKeyConstraintError);
19 | expect(parsedErr.parent).to.equal(fakeErr);
20 | expect(parsedErr.reltype).to.equal('parent');
21 | expect(parsedErr.table).to.equal('people');
22 | expect(parsedErr.fields).to.be.an('array').to.deep.equal(['personId']);
23 | expect(parsedErr.value).to.be.undefined;
24 | expect(parsedErr.index).to.equal('brothers_ibfk_1');
25 | });
26 |
27 | it('FK Errors with " quotation char are parsed correctly', () => {
28 | const fakeErr = new Error('Cannot delete or update a parent row: a foreign key constraint fails ("table"."brothers", CONSTRAINT "brothers_ibfk_1" FOREIGN KEY ("personId") REFERENCES "people" ("id") ON UPDATE CASCADE).');
29 |
30 | fakeErr.code = 1451;
31 |
32 | const parsedErr = queryProto.formatError(fakeErr);
33 |
34 | expect(parsedErr).to.be.instanceOf(Support.sequelize.ForeignKeyConstraintError);
35 | expect(parsedErr.parent).to.equal(fakeErr);
36 | expect(parsedErr.reltype).to.equal('parent');
37 | expect(parsedErr.table).to.equal('people');
38 | expect(parsedErr.fields).to.be.an('array').to.deep.equal(['personId']);
39 | expect(parsedErr.value).to.be.undefined;
40 | expect(parsedErr.index).to.equal('brothers_ibfk_1');
41 | });
42 | });
43 | }
44 |
--------------------------------------------------------------------------------
/test/unit/errors.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const errors = require('../../lib/errors');
4 | const expect = require('chai').expect;
5 |
6 | describe('errors', () => {
7 | it('should maintain stack trace with message', () => {
8 | const errorsWithMessage = [
9 | 'BaseError', 'ValidationError', 'UnknownConstraintError', 'InstanceError',
10 | 'EmptyResultError', 'EagerLoadingError', 'AssociationError', 'QueryError'
11 | ];
12 |
13 | errorsWithMessage.forEach(errorName => {
14 | function throwError() {
15 | throw new errors[errorName]('this is a message');
16 | }
17 | let err;
18 | try {
19 | throwError();
20 | } catch (error) {
21 | err = error;
22 | }
23 | expect(err).to.exist;
24 | const stackParts = err.stack.split('\n');
25 | const fullErrorName = 'Sequelize' + errorName;
26 | expect(stackParts[0]).to.equal(fullErrorName + ': this is a message');
27 | expect(stackParts[1]).to.match(/^ at throwError \(.*errors.test.js:\d+:\d+\)$/);
28 | });
29 | });
30 |
31 | it('should maintain stack trace without message', () => {
32 | const errorsWithoutMessage = [
33 | 'ConnectionError', 'ConnectionRefusedError', 'ConnectionTimedOutError',
34 | 'AccessDeniedError', 'HostNotFoundError', 'HostNotReachableError', 'InvalidConnectionError'
35 | ];
36 |
37 | errorsWithoutMessage.forEach(errorName => {
38 | function throwError() {
39 | throw new errors[errorName](null);
40 | }
41 | let err;
42 | try {
43 | throwError();
44 | } catch (error) {
45 | err = error;
46 | }
47 | expect(err).to.exist;
48 | const stackParts = err.stack.split('\n');
49 |
50 | const fullErrorName = 'Sequelize' + errorName;
51 | expect(stackParts[0]).to.equal(fullErrorName);
52 | expect(stackParts[1]).to.match(/^ at throwError \(.*errors.test.js:\d+:\d+\)$/);
53 | });
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/test/unit/increment.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | Sequelize = Support.Sequelize;
8 |
9 | describe(Support.getTestDialectTeaser('Model'), () => {
10 | describe('increment', () => {
11 | describe('options tests', () => {
12 | const Model = current.define('User', {
13 | id: {
14 | type: Sequelize.BIGINT,
15 | primaryKey: true,
16 | autoIncrement: true
17 | },
18 | count: Sequelize.BIGINT
19 | });
20 |
21 | it('should reject if options are missing', () => {
22 | return expect(() => Model.increment(['id', 'count']))
23 | .to.throw('Missing where attribute in the options parameter');
24 | });
25 |
26 | it('should reject if options.where are missing', () => {
27 | return expect(() => Model.increment(['id', 'count'], { by: 10}))
28 | .to.throw('Missing where attribute in the options parameter');
29 | });
30 | });
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/test/unit/instance/build.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | DataTypes = require(__dirname + '/../../../lib/data-types'),
7 | current = Support.sequelize;
8 |
9 | describe(Support.getTestDialectTeaser('Instance'), () => {
10 | describe('build', () => {
11 | it('should populate NOW default values', () => {
12 | const Model = current.define('Model', {
13 | created_time: {
14 | type: DataTypes.DATE,
15 | allowNull: true,
16 | defaultValue: DataTypes.NOW
17 | },
18 | updated_time: {
19 | type: DataTypes.DATE,
20 | allowNull: true,
21 | defaultValue: DataTypes.NOW
22 | },
23 | ip: {
24 | type: DataTypes.STRING,
25 | validate: {
26 | isIP: true
27 | }
28 | },
29 | ip2: {
30 | type: DataTypes.STRING,
31 | validate: {
32 | isIP: {
33 | msg: 'test'
34 | }
35 | }
36 | }
37 | }, {
38 | timestamp: false
39 | }),
40 | instance = Model.build({ip: '127.0.0.1', ip2: '0.0.0.0'});
41 |
42 | expect(instance.get('created_time')).to.be.ok;
43 | expect(instance.get('created_time')).to.be.an.instanceof(Date);
44 |
45 | expect(instance.get('updated_time')).to.be.ok;
46 | expect(instance.get('updated_time')).to.be.an.instanceof(Date);
47 |
48 | return instance.validate();
49 | });
50 |
51 | it('should populate explicitly undefined UUID primary keys', () => {
52 | const Model = current.define('Model', {
53 | id: {
54 | type: DataTypes.UUID,
55 | primaryKey: true,
56 | allowNull: false,
57 | defaultValue: DataTypes.UUIDV4
58 | }
59 | }),
60 | instance = Model.build({
61 | id: undefined
62 | });
63 |
64 | expect(instance.get('id')).not.to.be.undefined;
65 | expect(instance.get('id')).to.be.ok;
66 | });
67 |
68 | it('should populate undefined columns with default value', () => {
69 | const Model = current.define('Model', {
70 | number1: {
71 | type: DataTypes.INTEGER,
72 | defaultValue: 1
73 | },
74 | number2: {
75 | type: DataTypes.INTEGER,
76 | defaultValue: 2
77 | }
78 | }),
79 | instance = Model.build({
80 | number1: undefined
81 | });
82 |
83 | expect(instance.get('number1')).not.to.be.undefined;
84 | expect(instance.get('number1')).to.equal(1);
85 | expect(instance.get('number2')).not.to.be.undefined;
86 | expect(instance.get('number2')).to.equal(2);
87 | });
88 |
89 | it('should clone the default values', () => {
90 | const Model = current.define('Model', {
91 | data: {
92 | type: DataTypes.JSONB,
93 | defaultValue: { foo: 'bar' }
94 | }
95 | }),
96 | instance = Model.build();
97 | instance.data.foo = 'biz';
98 |
99 | expect(instance.get('data')).to.eql({ foo: 'biz' });
100 | expect(Model.build().get('data')).to.eql({ foo: 'bar' });
101 | });
102 | });
103 | });
104 |
--------------------------------------------------------------------------------
/test/unit/instance/decrement.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | Sequelize = Support.Sequelize,
8 | sinon = require('sinon');
9 |
10 | describe(Support.getTestDialectTeaser('Instance'), () => {
11 | describe('decrement', () => {
12 | describe('options tests', () => {
13 | let stub, instance;
14 | const Model = current.define('User', {
15 | id: {
16 | type: Sequelize.BIGINT,
17 | primaryKey: true,
18 | autoIncrement: true
19 | }
20 | });
21 |
22 | before(() => {
23 | stub = sinon.stub(current, 'query').returns(
24 | Sequelize.Promise.resolve({
25 | _previousDataValues: {id: 3},
26 | dataValues: {id: 1}
27 | })
28 | );
29 | });
30 |
31 | after(() => {
32 | stub.restore();
33 | });
34 |
35 | it('should allow decrements even if options are not given', () => {
36 | instance = Model.build({id: 3}, {isNewRecord: false});
37 | expect(() => {
38 | instance.decrement(['id']);
39 | }).to.not.throw();
40 | });
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/unit/instance/destroy.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | Sequelize = Support.Sequelize,
8 | sinon = require('sinon');
9 |
10 | describe(Support.getTestDialectTeaser('Instance'), () => {
11 | describe('destroy', () => {
12 | describe('options tests', () => {
13 | let stub, instance;
14 | const Model = current.define('User', {
15 | id: {
16 | type: Sequelize.BIGINT,
17 | primaryKey: true,
18 | autoIncrement: true
19 | }
20 | });
21 |
22 | before(() => {
23 | stub = sinon.stub(current, 'query').returns(
24 | Sequelize.Promise.resolve({
25 | _previousDataValues: {},
26 | dataValues: {id: 1}
27 | })
28 | );
29 | });
30 |
31 | after(() => {
32 | stub.restore();
33 | });
34 |
35 | it('should allow destroies even if options are not given', () => {
36 | instance = Model.build({id: 1}, {isNewRecord: false});
37 | expect(() => {
38 | instance.destroy();
39 | }).to.not.throw();
40 | });
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/unit/instance/get.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | sinon = require('sinon'),
5 | expect = chai.expect,
6 | Support = require(__dirname + '/../support'),
7 | DataTypes = require(__dirname + '/../../../lib/data-types'),
8 | current = Support.sequelize;
9 |
10 | describe(Support.getTestDialectTeaser('Instance'), () => {
11 | describe('get', () => {
12 | beforeEach(function() {
13 | this.getSpy = sinon.spy();
14 | this.User = current.define('User', {
15 | name: {
16 | type: DataTypes.STRING,
17 | get: this.getSpy
18 | }
19 | });
20 | });
21 |
22 | it('invokes getter if raw: false', function() {
23 | this.User.build().get('name');
24 |
25 | expect(this.getSpy).to.have.been.called;
26 | });
27 |
28 | it('does not invoke getter if raw: true', function() {
29 | expect(this.getSpy, { raw: true }).not.to.have.been.called;
30 | });
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/test/unit/instance/increment.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | Sequelize = Support.Sequelize,
8 | sinon = require('sinon');
9 |
10 | describe(Support.getTestDialectTeaser('Instance'), () => {
11 | describe('increment', () => {
12 | describe('options tests', () => {
13 | let stub, instance;
14 | const Model = current.define('User', {
15 | id: {
16 | type: Sequelize.BIGINT,
17 | primaryKey: true,
18 | autoIncrement: true
19 | }
20 | });
21 |
22 | before(() => {
23 | stub = sinon.stub(current, 'query').returns(
24 | Sequelize.Promise.resolve({
25 | _previousDataValues: {id: 1},
26 | dataValues: {id: 3}
27 | })
28 | );
29 | });
30 |
31 | after(() => {
32 | stub.restore();
33 | });
34 |
35 | it('should allow increments even if options are not given', () => {
36 | instance = Model.build({id: 1}, {isNewRecord: false});
37 | expect(() => {
38 | instance.increment(['id']);
39 | }).to.not.throw();
40 | });
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/unit/instance/is-soft-deleted.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | DataTypes = require(__dirname + '/../../../lib/data-types'),
8 | Sequelize = Support.Sequelize,
9 | moment = require('moment');
10 |
11 | describe(Support.getTestDialectTeaser('Instance'), () => {
12 | describe('isSoftDeleted', () => {
13 | beforeEach(function() {
14 | const User = current.define('User', {
15 | name: DataTypes.STRING,
16 | birthdate: DataTypes.DATE,
17 | meta: DataTypes.JSON,
18 | deletedAt: {
19 | type: Sequelize.DATE
20 | }
21 | });
22 |
23 | const ParanoidUser = current.define('User', {
24 | name: DataTypes.STRING,
25 | birthdate: DataTypes.DATE,
26 | meta: DataTypes.JSON,
27 | deletedAt: {
28 | type: Sequelize.DATE
29 | }
30 | }, {
31 | paranoid: true
32 | });
33 |
34 | this.paranoidUser = ParanoidUser.build({
35 | name: 'a'
36 | }, {
37 | isNewRecord: false,
38 | raw: true
39 | });
40 |
41 | this.user = User.build({
42 | name: 'a'
43 | }, {
44 | isNewRecord: false,
45 | raw: true
46 | });
47 | });
48 |
49 | it('should not throw if paranoid is set to true', function() {
50 | expect(() => {
51 | this.paranoidUser.isSoftDeleted();
52 | }).to.not.throw();
53 | });
54 |
55 | it('should throw if paranoid is set to false', function() {
56 | expect(() => {
57 | this.user.isSoftDeleted();
58 | }).to.throw('Model is not paranoid');
59 | });
60 |
61 | it('should return false if the soft-delete property is the same as ' +
62 | 'the default value', function() {
63 | this.paranoidUser.setDataValue('deletedAt', null);
64 | expect(this.paranoidUser.isSoftDeleted()).to.be.false;
65 | });
66 |
67 | it('should return false if the soft-delete property is set to a date in ' +
68 | 'the future', function() {
69 | this.paranoidUser.setDataValue('deletedAt', moment().add(5, 'days').format());
70 | expect(this.paranoidUser.isSoftDeleted()).to.be.false;
71 | });
72 |
73 | it('should return true if the soft-delete property is set to a date ' +
74 | 'before now', function() {
75 | this.paranoidUser.setDataValue('deletedAt', moment().subtract(5, 'days').format());
76 | expect(this.paranoidUser.isSoftDeleted()).to.be.true;
77 | });
78 | });
79 | });
80 |
--------------------------------------------------------------------------------
/test/unit/instance/previous.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai');
4 | const expect = chai.expect;
5 | const Support = require(__dirname + '/../support');
6 | const DataTypes = require(__dirname + '/../../../lib/data-types');
7 | const current = Support.sequelize;
8 |
9 | describe(Support.getTestDialectTeaser('Instance'), () => {
10 | describe('previous', () => {
11 | it('should return correct previous value', () => {
12 | const Model = current.define('Model', {
13 | text: DataTypes.STRING,
14 | textCustom: {
15 | type: DataTypes.STRING,
16 | set(val) {
17 | this.setDataValue('textCustom', val);
18 | },
19 | get() {
20 | this.getDataValue('textCustom');
21 | }
22 | }
23 | });
24 |
25 | const instance = Model.build({ text: 'a', textCustom: 'abc' });
26 | expect(instance.previous('text')).to.be.not.ok;
27 | expect(instance.previous('textCustom')).to.be.not.ok;
28 |
29 | instance.set('text', 'b');
30 | instance.set('textCustom', 'def');
31 |
32 | expect(instance.previous('text')).to.be.equal('a');
33 | expect(instance.previous('textCustom')).to.be.equal('abc');
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/unit/instance/reload.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | Sequelize = Support.Sequelize,
8 | sinon = require('sinon');
9 |
10 | describe(Support.getTestDialectTeaser('Instance'), () => {
11 | describe('reload', () => {
12 | describe('options tests', () => {
13 | let stub, instance;
14 | const Model = current.define('User', {
15 | id: {
16 | type: Sequelize.BIGINT,
17 | primaryKey: true,
18 | autoIncrement: true
19 | },
20 | deletedAt: {
21 | type: Sequelize.DATE
22 | }
23 | }, {
24 | paranoid: true
25 | });
26 |
27 | before(() => {
28 | stub = sinon.stub(current, 'query').returns(
29 | Sequelize.Promise.resolve({
30 | _previousDataValues: {id: 1},
31 | dataValues: {id: 2}
32 | })
33 | );
34 | });
35 |
36 | after(() => {
37 | stub.restore();
38 | });
39 |
40 | it('should allow reloads even if options are not given', () => {
41 | instance = Model.build({id: 1}, {isNewRecord: false});
42 | expect(() => {
43 | instance.reload();
44 | }).to.not.throw();
45 | });
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/unit/instance/restore.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | Sequelize = Support.Sequelize,
8 | sinon = require('sinon');
9 |
10 | describe(Support.getTestDialectTeaser('Instance'), () => {
11 | describe('restore', () => {
12 | describe('options tests', () => {
13 | let stub, instance;
14 | const Model = current.define('User', {
15 | id: {
16 | type: Sequelize.BIGINT,
17 | primaryKey: true,
18 | autoIncrement: true
19 | },
20 | deletedAt: {
21 | type: Sequelize.DATE
22 | }
23 | }, {
24 | paranoid: true
25 | });
26 |
27 | before(() => {
28 | stub = sinon.stub(current, 'query').returns(
29 | Sequelize.Promise.resolve([{
30 | _previousDataValues: {id: 1},
31 | dataValues: {id: 2}
32 | }, 1])
33 | );
34 | });
35 |
36 | after(() => {
37 | stub.restore();
38 | });
39 |
40 | it('should allow restores even if options are not given', () => {
41 | instance = Model.build({id: 1}, {isNewRecord: false});
42 | expect(() => {
43 | instance.restore();
44 | }).to.not.throw();
45 | });
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/unit/instance/save.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | Sequelize = Support.Sequelize,
8 | sinon = require('sinon');
9 |
10 | describe(Support.getTestDialectTeaser('Instance'), () => {
11 | describe('save', () => {
12 | it('should disallow saves if no primary key values is present', () => {
13 | const Model = current.define('User', {
14 |
15 | }),
16 | instance = Model.build({}, {isNewRecord: false});
17 |
18 | expect(() => {
19 | instance.save();
20 | }).to.throw();
21 | });
22 |
23 | describe('options tests', () => {
24 | let stub, instance;
25 | const Model = current.define('User', {
26 | id: {
27 | type: Sequelize.BIGINT,
28 | primaryKey: true,
29 | autoIncrement: true
30 | }
31 | });
32 |
33 | before(() => {
34 | stub = sinon.stub(current, 'query').returns(
35 | Sequelize.Promise.resolve([{
36 | _previousDataValues: {},
37 | dataValues: {id: 1}
38 | }, 1])
39 | );
40 | });
41 |
42 | after(() => {
43 | stub.restore();
44 | });
45 |
46 | it('should allow saves even if options are not given', () => {
47 | instance = Model.build({});
48 | expect(() => {
49 | instance.save();
50 | }).to.not.throw();
51 | });
52 | });
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/unit/instance/to-json.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | DataTypes = require(__dirname + '/../../../lib/data-types'),
7 | current = Support.sequelize;
8 |
9 | describe(Support.getTestDialectTeaser('Instance'), () => {
10 | describe('toJSON', () => {
11 | it('returns copy of json', () => {
12 | const User = current.define('User', {
13 | name: DataTypes.STRING
14 | });
15 | const user = User.build({ name: 'my-name' });
16 | const json1 = user.toJSON();
17 | expect(json1).to.have.property('name').and.be.equal('my-name');
18 |
19 | // remove value from json and ensure it's not changed in the instance
20 | delete json1.name;
21 |
22 | const json2 = user.toJSON();
23 | expect(json2).to.have.property('name').and.be.equal('my-name');
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/test/unit/model/bulkcreate.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | sinon = require('sinon'),
6 | Support = require(__dirname + '/../support'),
7 | DataTypes = require('../../../lib/data-types'),
8 | current = Support.sequelize,
9 | Promise = current.Promise;
10 |
11 | describe(Support.getTestDialectTeaser('Model'), () => {
12 | describe('bulkCreate', () => {
13 | before(function() {
14 | this.Model = current.define('model', {
15 | accountId: {
16 | type: DataTypes.INTEGER(11).UNSIGNED,
17 | allowNull: false,
18 | field: 'account_id'
19 | }
20 | }, { timestamps: false });
21 |
22 | this.stub = sinon.stub(current.getQueryInterface(), 'bulkInsert').returns(Promise.resolve([]));
23 | });
24 |
25 | afterEach(function() {
26 | this.stub.reset();
27 | });
28 |
29 | after(function() {
30 | this.stub.restore();
31 | });
32 |
33 | describe('validations', () => {
34 | it('should not fail for renamed fields', function() {
35 | return this.Model.bulkCreate([
36 | { accountId: 42 }
37 | ], { validate: true }).then(() => {
38 | expect(this.stub.getCall(0).args[1]).to.deep.equal([
39 | { account_id: 42, id: null }
40 | ]);
41 | });
42 | });
43 | });
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/test/unit/model/count.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | sinon = require('sinon'),
8 | DataTypes = require(__dirname + '/../../../lib/data-types'),
9 | Promise = require('bluebird');
10 |
11 | describe(Support.getTestDialectTeaser('Model'), () => {
12 | describe('method count', () => {
13 | before(() => {
14 | this.oldFindAll = current.Model.findAll;
15 | this.oldAggregate = current.Model.aggregate;
16 |
17 | current.Model.findAll = sinon.stub().returns(Promise.resolve());
18 |
19 | this.User = current.define('User', {
20 | username: DataTypes.STRING,
21 | age: DataTypes.INTEGER
22 | });
23 | this.Project = current.define('Project', {
24 | name: DataTypes.STRING
25 | });
26 |
27 | this.User.hasMany(this.Project);
28 | this.Project.belongsTo(this.User);
29 | });
30 |
31 | after(() => {
32 | current.Model.findAll = this.oldFindAll;
33 | current.Model.aggregate = this.oldAggregate;
34 | });
35 |
36 | beforeEach(() => {
37 | this.stub = current.Model.aggregate = sinon.stub().returns(Promise.resolve());
38 | });
39 |
40 | describe('should pass the same options to model.aggregate as findAndCount', () => {
41 | it('with includes', () => {
42 | const queryObject = {
43 | include: [this.Project]
44 | };
45 | return this.User.count(queryObject)
46 | .then(() => this.User.findAndCount(queryObject))
47 | .then(() => {
48 | const count = this.stub.getCall(0).args;
49 | const findAndCount = this.stub.getCall(1).args;
50 | expect(count).to.eql(findAndCount);
51 | });
52 | });
53 |
54 | it('attributes should be stripped in case of findAndCount', () => {
55 | const queryObject = {
56 | attributes: ['username']
57 | };
58 | return this.User.count(queryObject)
59 | .then(() => this.User.findAndCount(queryObject))
60 | .then(() => {
61 | const count = this.stub.getCall(0).args;
62 | const findAndCount = this.stub.getCall(1).args;
63 | expect(count).not.to.eql(findAndCount);
64 | count[2].attributes = undefined;
65 | expect(count).to.eql(findAndCount);
66 | });
67 | });
68 | });
69 |
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/test/unit/model/define.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | DataTypes = require('../../../lib/data-types'),
7 | current = Support.sequelize;
8 |
9 | describe(Support.getTestDialectTeaser('Model'), () => {
10 | describe('define', () => {
11 | it('should allow custom timestamps with underscored: true', () => {
12 | const Model = current.define('User', {}, {
13 | createdAt: 'createdAt',
14 | updatedAt: 'updatedAt',
15 | timestamps: true,
16 | underscored: true
17 | });
18 |
19 | expect(Model.rawAttributes).to.haveOwnProperty('createdAt');
20 | expect(Model.rawAttributes).to.haveOwnProperty('updatedAt');
21 |
22 | expect(Model._timestampAttributes.createdAt).to.equal('createdAt');
23 | expect(Model._timestampAttributes.updatedAt).to.equal('updatedAt');
24 |
25 | expect(Model.rawAttributes).not.to.have.property('created_at');
26 | expect(Model.rawAttributes).not.to.have.property('updated_at');
27 | });
28 |
29 | it('should throw when id is added but not marked as PK', () => {
30 | expect(() => {
31 | current.define('foo', {
32 | id: DataTypes.INTEGER
33 | });
34 | }).to.throw("A column called 'id' was added to the attributes of 'foos' but not marked with 'primaryKey: true'");
35 |
36 | expect(() => {
37 | current.define('bar', {
38 | id: {
39 | type: DataTypes.INTEGER
40 | }
41 | });
42 | }).to.throw("A column called 'id' was added to the attributes of 'bars' but not marked with 'primaryKey: true'");
43 | });
44 | it('should defend against null or undefined "unique" attributes', () => {
45 | expect(() => {
46 | current.define('baz', {
47 | foo: {
48 | type: DataTypes.STRING,
49 | unique: null
50 | },
51 | bar: {
52 | type: DataTypes.STRING,
53 | unique: undefined
54 | },
55 | bop: {
56 | type: DataTypes.DATE
57 | }
58 | });
59 | }).not.to.throw();
60 | });
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/test/unit/model/destroy.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | sinon = require('sinon'),
8 | Promise = current.Promise,
9 | DataTypes = require('../../../lib/data-types'),
10 | _ = require('lodash');
11 |
12 | describe(Support.getTestDialectTeaser('Model'), () => {
13 |
14 | describe('method destroy', () => {
15 | const User = current.define('User', {
16 | name: DataTypes.STRING,
17 | secretValue: DataTypes.INTEGER
18 | });
19 |
20 | before(function() {
21 | this.stubDelete = sinon.stub(current.getQueryInterface(), 'bulkDelete').callsFake(() => {
22 | return Promise.resolve([]);
23 | });
24 | });
25 |
26 | beforeEach(function() {
27 | this.deloptions = {where: {secretValue: '1'}};
28 | this.cloneOptions = _.clone(this.deloptions);
29 | this.stubDelete.reset();
30 | });
31 |
32 | afterEach(function() {
33 | delete this.deloptions;
34 | delete this.cloneOptions;
35 | });
36 |
37 | after(function() {
38 | this.stubDelete.restore();
39 | });
40 |
41 | it('can detect complexe objects', () => {
42 | const Where = function() { this.secretValue = '1'; };
43 |
44 | expect(() => {
45 | User.destroy({where: new Where()});
46 | }).to.throw();
47 |
48 | });
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/test/unit/model/find-create-find.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | UniqueConstraintError = require(__dirname + '/../../../lib/errors').UniqueConstraintError,
7 | current = Support.sequelize,
8 | sinon = require('sinon'),
9 | Promise = require('bluebird');
10 |
11 | describe(Support.getTestDialectTeaser('Model'), () => {
12 | describe('findCreateFind', () => {
13 | const Model = current.define('Model', {});
14 |
15 | beforeEach(function() {
16 | this.sinon = sinon.sandbox.create();
17 | });
18 |
19 | afterEach(function() {
20 | this.sinon.restore();
21 | });
22 |
23 | it('should return the result of the first find call if not empty', function() {
24 | const result = {},
25 | where = {prop: Math.random().toString()},
26 | findSpy = this.sinon.stub(Model, 'findOne').returns(Promise.resolve(result));
27 |
28 | return expect(Model.findCreateFind({
29 | where
30 | })).to.eventually.eql([result, false]).then(() => {
31 | expect(findSpy).to.have.been.calledOnce;
32 | expect(findSpy.getCall(0).args[0].where).to.equal(where);
33 | });
34 | });
35 |
36 | it('should create if first find call is empty', function() {
37 | const result = {},
38 | where = {prop: Math.random().toString()},
39 | createSpy = this.sinon.stub(Model, 'create').returns(Promise.resolve(result));
40 |
41 | this.sinon.stub(Model, 'findOne').returns(Promise.resolve(null));
42 |
43 | return expect(Model.findCreateFind({
44 | where
45 | })).to.eventually.eql([result, true]).then(() => {
46 | expect(createSpy).to.have.been.calledWith(where);
47 | });
48 | });
49 |
50 | it('should do a second find if create failed do to unique constraint', function() {
51 | const result = {},
52 | where = {prop: Math.random().toString()},
53 | findSpy = this.sinon.stub(Model, 'findOne');
54 |
55 | this.sinon.stub(Model, 'create').callsFake(() => {
56 | return Promise.reject(new UniqueConstraintError());
57 | });
58 |
59 | findSpy.onFirstCall().returns(Promise.resolve(null));
60 | findSpy.onSecondCall().returns(Promise.resolve(result));
61 |
62 | return expect(Model.findCreateFind({
63 | where
64 | })).to.eventually.eql([result, false]).then(() => {
65 | expect(findSpy).to.have.been.calledTwice;
66 | expect(findSpy.getCall(1).args[0].where).to.equal(where);
67 | });
68 | });
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/test/unit/model/find-or-create.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | cls = require('continuation-local-storage'),
8 | sinon = require('sinon'),
9 | stub = sinon.stub,
10 | Promise = require('bluebird');
11 |
12 | describe(Support.getTestDialectTeaser('Model'), () => {
13 |
14 | describe('method findOrCreate', () => {
15 |
16 | before(() => {
17 | current.constructor.useCLS(cls.createNamespace('sequelize'));
18 | });
19 |
20 | after(() => {
21 | delete current.constructor._cls;
22 | });
23 |
24 | beforeEach(function() {
25 | this.User = current.define('User', {}, {
26 | name: 'John'
27 | });
28 |
29 | this.transactionStub = stub(this.User.sequelize, 'transaction');
30 | this.transactionStub.returns(new Promise(() => {}));
31 |
32 | this.clsStub = stub(current.constructor._cls, 'get');
33 | this.clsStub.returns({ id: 123 });
34 | });
35 |
36 | afterEach(function() {
37 | this.transactionStub.restore();
38 | this.clsStub.restore();
39 | });
40 |
41 | it('should use transaction from cls if available', function() {
42 |
43 | const options = {
44 | where: {
45 | name: 'John'
46 | }
47 | };
48 |
49 | this.User.findOrCreate(options);
50 |
51 | expect(this.clsStub.calledOnce).to.equal(true, 'expected to ask for transaction');
52 | });
53 |
54 | it('should not use transaction from cls if provided as argument', function() {
55 |
56 | const options = {
57 | where: {
58 | name: 'John'
59 | },
60 | transaction: { id: 123 }
61 | };
62 |
63 | this.User.findOrCreate(options);
64 |
65 | expect(this.clsStub.called).to.equal(false);
66 | });
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/test/unit/model/findone.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | sinon = require('sinon'),
8 | DataTypes = require(__dirname + '/../../../lib/data-types'),
9 | Promise = require('bluebird');
10 |
11 | describe(Support.getTestDialectTeaser('Model'), () => {
12 | describe('method findOne', () => {
13 | before(function() {
14 | this.oldFindAll = current.Model.findAll;
15 | });
16 | after(function() {
17 | current.Model.findAll = this.oldFindAll;
18 | });
19 |
20 | beforeEach(function() {
21 | this.stub = current.Model.findAll = sinon.stub().returns(Promise.resolve());
22 | });
23 |
24 | describe('should not add limit when querying on a primary key', () => {
25 | it('with id primary key', function() {
26 | const Model = current.define('model');
27 |
28 | return Model.findOne({ where: { id: 42 }}).bind(this).then(function() {
29 | expect(this.stub.getCall(0).args[0]).to.be.an('object').not.to.have.property('limit');
30 | });
31 | });
32 |
33 | it('with custom primary key', function() {
34 | const Model = current.define('model', {
35 | uid: {
36 | type: DataTypes.INTEGER,
37 | primaryKey: true,
38 | autoIncrement: true
39 | }
40 | });
41 |
42 | return Model.findOne({ where: { uid: 42 }}).bind(this).then(function() {
43 | expect(this.stub.getCall(0).args[0]).to.be.an('object').not.to.have.property('limit');
44 | });
45 | });
46 |
47 | it('with blob primary key', function() {
48 | const Model = current.define('model', {
49 | id: {
50 | type: DataTypes.BLOB,
51 | primaryKey: true,
52 | autoIncrement: true
53 | }
54 | });
55 |
56 | return Model.findOne({ where: { id: new Buffer('foo') }}).bind(this).then(function() {
57 | expect(this.stub.getCall(0).args[0]).to.be.an('object').not.to.have.property('limit');
58 | });
59 | });
60 | });
61 |
62 | it('should add limit when using { $ gt on the primary key', function() {
63 | const Model = current.define('model');
64 |
65 | return Model.findOne({ where: { id: { $gt: 42 }}}).bind(this).then(function() {
66 | expect(this.stub.getCall(0).args[0]).to.be.an('object').to.have.property('limit');
67 | });
68 | });
69 |
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/test/unit/model/helpers.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai');
4 | const expect = chai.expect;
5 | const Support = require(__dirname + '/../support');
6 | const current = Support.sequelize;
7 |
8 | describe(Support.getTestDialectTeaser('Model'), () => {
9 | describe('hasAlias', () => {
10 | beforeEach(function() {
11 | this.User = current.define('user');
12 | this.Task = current.define('task');
13 | });
14 |
15 | it('returns true if a model has an association with the specified alias', function() {
16 | this.Task.belongsTo(this.User, { as: 'owner'});
17 | expect(this.Task.hasAlias('owner')).to.equal(true);
18 | });
19 |
20 | it('returns false if a model does not have an association with the specified alias', function() {
21 | this.Task.belongsTo(this.User, { as: 'owner'});
22 | expect(this.Task.hasAlias('notOwner')).to.equal(false);
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/unit/model/indexes.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | DataTypes = require(__dirname + '/../../../lib/data-types');
8 |
9 | describe(Support.getTestDialectTeaser('Model'), () => {
10 | describe('indexes', () => {
11 | it('should automatically set a gin index for JSONB indexes', () => {
12 | const Model = current.define('event', {
13 | eventData: {
14 | type: DataTypes.JSONB,
15 | index: true,
16 | field: 'data'
17 | }
18 | });
19 |
20 | expect(Model.rawAttributes.eventData.index).not.to.equal(true);
21 | expect(Model.options.indexes.length).to.equal(1);
22 | expect(Model.options.indexes[0].fields).to.eql(['data']);
23 | expect(Model.options.indexes[0].using).to.equal('gin');
24 | });
25 |
26 | it('should set the unique property when type is unique', () => {
27 | const Model = current.define('m', {}, {
28 | indexes: [
29 | {
30 | type: 'unique'
31 | },
32 | {
33 | type: 'UNIQUE'
34 | }
35 | ]
36 | });
37 |
38 | expect(Model.options.indexes[0].unique).to.eql(true);
39 | expect(Model.options.indexes[1].unique).to.eql(true);
40 | });
41 |
42 | it('should not set rawAttributes when indexes are defined via options', () => {
43 | const User = current.define('User', {
44 | username: DataTypes.STRING
45 | }, {
46 | indexes: [{
47 | unique: true,
48 | fields: ['username']
49 | }]
50 | });
51 |
52 | expect(User.rawAttributes.username.unique).to.be.undefined;
53 | });
54 |
55 | it('should not set rawAttributes when composite unique indexes are defined via options', () => {
56 | const User = current.define('User', {
57 | name: DataTypes.STRING,
58 | address: DataTypes.STRING
59 | }, {
60 | indexes: [{
61 | unique: 'users_name_address',
62 | fields: ['name', 'address']
63 | }]
64 | });
65 |
66 | expect(User.rawAttributes.name.unique).to.be.undefined;
67 | expect(User.rawAttributes.address.unique).to.be.undefined;
68 | });
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/test/unit/model/overwriting-builtins.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../../support'),
6 | DataTypes = require(__dirname + '/../../../lib/data-types');
7 |
8 | describe(Support.getTestDialectTeaser('Model'), () => {
9 |
10 | describe('not breaking built-ins', () => {
11 | it('it should not break instance.set by defining a model set attribute', function() {
12 | const User = this.sequelize.define('OverWrittenKeys', {
13 | set: DataTypes.STRING
14 | });
15 |
16 | const user = User.build({set: 'A'});
17 | expect(user.get('set')).to.equal('A');
18 | user.set('set', 'B');
19 | expect(user.get('set')).to.equal('B');
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/unit/model/removeAttribute.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | _ = require('lodash'),
8 | DataTypes = require(__dirname + '/../../../lib/data-types');
9 |
10 | describe(Support.getTestDialectTeaser('Model'), () => {
11 | describe('removeAttribute', () => {
12 | it('should support removing the primary key', () => {
13 | const Model = current.define('m', {
14 | name: DataTypes.STRING
15 | });
16 |
17 | expect(Model.primaryKeyAttribute).not.to.be.undefined;
18 | expect(_.size(Model.primaryKeys)).to.equal(1);
19 |
20 | Model.removeAttribute('id');
21 |
22 | expect(Model.primaryKeyAttribute).to.be.undefined;
23 | expect(_.size(Model.primaryKeys)).to.equal(0);
24 | });
25 |
26 | it('should not add undefined attribute after removing primary key', () => {
27 | const Model = current.define('m', {
28 | name: DataTypes.STRING
29 | });
30 |
31 | Model.removeAttribute('id');
32 |
33 | const instance = Model.build();
34 | expect(instance.dataValues).not.to.include.keys('undefined');
35 | });
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/test/unit/model/schema.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize;
7 |
8 | describe(Support.getTestDialectTeaser('Model') + 'Schemas', () => {
9 | if (current.dialect.supports.schemas) {
10 | const Project = current.define('project'),
11 | Company = current.define('company', {}, {
12 | schema: 'default',
13 | schemaDelimiter: '&'
14 | });
15 |
16 | describe('schema', () => {
17 | it('should work with no default schema', () => {
18 | expect(Project._schema).to.be.null;
19 | });
20 |
21 | it('should apply default schema from define', () => {
22 | expect(Company._schema).to.equal('default');
23 | });
24 |
25 | it('should be able to override the default schema', () => {
26 | expect(Company.schema('newSchema')._schema).to.equal('newSchema');
27 | });
28 |
29 | it('should be able nullify schema', () => {
30 | expect(Company.schema(null)._schema).to.be.null;
31 | });
32 |
33 | it('should support multiple, coexistent schema models', () => {
34 | const schema1 = Company.schema('schema1'),
35 | schema2 = Company.schema('schema1');
36 |
37 | expect(schema1._schema).to.equal('schema1');
38 | expect(schema2._schema).to.equal('schema1');
39 | });
40 | });
41 |
42 | describe('schema delimiter', () => {
43 | it('should work with no default schema delimiter', () => {
44 | expect(Project._schemaDelimiter).to.equal('');
45 | });
46 |
47 | it('should apply default schema delimiter from define', () => {
48 | expect(Company._schemaDelimiter).to.equal('&');
49 | });
50 |
51 | it('should be able to override the default schema delimiter', () => {
52 | expect(Company.schema(Company._schema, '^')._schemaDelimiter).to.equal('^');
53 | });
54 |
55 | it('should support multiple, coexistent schema delimiter models', () => {
56 | const schema1 = Company.schema(Company._schema, '$'),
57 | schema2 = Company.schema(Company._schema, '#');
58 |
59 | expect(schema1._schemaDelimiter).to.equal('$');
60 | expect(schema2._schemaDelimiter).to.equal('#');
61 | });
62 | });
63 | }
64 | });
65 |
--------------------------------------------------------------------------------
/test/unit/model/update.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | sinon = require('sinon'),
8 | Promise = current.Promise,
9 | DataTypes = require('../../../lib/data-types'),
10 | _ = require('lodash');
11 |
12 | describe(Support.getTestDialectTeaser('Model'), () => {
13 | describe('method update', () => {
14 | before(function() {
15 | this.User = current.define('User', {
16 | name: DataTypes.STRING,
17 | secretValue: DataTypes.INTEGER
18 | });
19 | });
20 |
21 | beforeEach(function() {
22 | this.stubUpdate = sinon.stub(current.getQueryInterface(), 'bulkUpdate').returns(Promise.resolve([]));
23 | this.updates = { name: 'Batman', secretValue: '7' };
24 | this.cloneUpdates = _.clone(this.updates);
25 | });
26 |
27 | afterEach(function() {
28 | this.stubUpdate.restore();
29 | });
30 |
31 | afterEach(function() {
32 | delete this.updates;
33 | delete this.cloneUpdates;
34 | });
35 |
36 | describe('properly clones input values', () => {
37 | it('with default options', function() {
38 | return this.User.update(this.updates, { where: { secretValue: '1' } }).then(() => {
39 | expect(this.updates).to.be.deep.eql(this.cloneUpdates);
40 | });
41 | });
42 |
43 | it('when using fields option', function() {
44 | return this.User.update(this.updates, { where: { secretValue: '1' }, fields: ['name'] }).then(() => {
45 | expect(this.updates).to.be.deep.eql(this.cloneUpdates);
46 | });
47 | });
48 | });
49 |
50 | it('can detect complexe objects', function() {
51 | const Where = function() { this.secretValue = '1'; };
52 |
53 | expect(() => {
54 | this.User.update(this.updates, { where: new Where() });
55 | }).to.throw();
56 | });
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/test/unit/model/upsert.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/../support'),
6 | current = Support.sequelize,
7 | sinon = require('sinon'),
8 | Promise = current.Promise,
9 | DataTypes = require('../../../lib/data-types');
10 |
11 | describe(Support.getTestDialectTeaser('Model'), () => {
12 |
13 | if (current.dialect.supports.upserts) {
14 | describe('method upsert', () => {
15 | const self = this;
16 | const User = current.define('User', {
17 | name: DataTypes.STRING,
18 | virtualValue: {
19 | type: DataTypes.VIRTUAL,
20 | set(val) {
21 | return this.value = val;
22 | },
23 | get() {
24 | return this.value;
25 | }
26 | },
27 | value: DataTypes.STRING,
28 | secretValue: {
29 | type: DataTypes.INTEGER,
30 | allowNull: false
31 | },
32 | createdAt: {
33 | type: DataTypes.DATE,
34 | field: 'created_at'
35 | }
36 | });
37 |
38 | const UserNoTime = current.define('UserNoTime', {
39 | name: DataTypes.STRING
40 | }, {
41 | timestamps: false
42 | });
43 |
44 | before(function() {
45 | this.query = current.query;
46 | current.query = sinon.stub().returns(Promise.resolve());
47 |
48 | self.stub = sinon.stub(current.getQueryInterface(), 'upsert').callsFake(() => {
49 | return User.build({});
50 | });
51 | });
52 |
53 | beforeEach(() => {
54 | self.stub.reset();
55 | });
56 |
57 | after(function() {
58 | current.query = this.query;
59 | self.stub.restore();
60 | });
61 |
62 |
63 | it('skip validations for missing fields', () => {
64 | return expect(User.upsert({
65 | name: 'Grumpy Cat'
66 | })).not.to.be.rejectedWith(current.ValidationError);
67 | });
68 |
69 | it('creates new record with correct field names', () => {
70 | return User
71 | .upsert({
72 | name: 'Young Cat',
73 | virtualValue: 999
74 | })
75 | .then(() => {
76 | expect(Object.keys(self.stub.getCall(0).args[1])).to.deep.equal([
77 | 'name', 'value', 'created_at', 'updatedAt'
78 | ]);
79 | });
80 | });
81 |
82 | it('creates new record with timestamps disabled', () => {
83 | return UserNoTime
84 | .upsert({
85 | name: 'Young Cat'
86 | })
87 | .then(() => {
88 | expect(Object.keys(self.stub.getCall(0).args[1])).to.deep.equal([
89 | 'name'
90 | ]);
91 | });
92 | });
93 |
94 | it('updates all changed fields by default', () => {
95 | return User
96 | .upsert({
97 | name: 'Old Cat',
98 | virtualValue: 111
99 | })
100 | .then(() => {
101 | expect(Object.keys(self.stub.getCall(0).args[2])).to.deep.equal([
102 | 'name', 'value', 'updatedAt'
103 | ]);
104 | });
105 | });
106 | });
107 | }
108 | });
109 |
--------------------------------------------------------------------------------
/test/unit/promise.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | expect = chai.expect,
5 | Support = require(__dirname + '/support'),
6 | Sequelize = Support.Sequelize,
7 | Promise = Sequelize.Promise,
8 | Bluebird = require('bluebird');
9 |
10 | describe('Promise', () => {
11 | it('should be an independent copy of bluebird library', () => {
12 | expect(Promise.prototype.then).to.be.a('function');
13 | expect(Promise).to.not.equal(Bluebird);
14 | expect(Promise.prototype).to.not.equal(Bluebird.prototype);
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/test/unit/query.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai'),
4 | sinon = require('sinon'),
5 | expect = chai.expect,
6 | Support = require(__dirname + '/support'),
7 | Sequelize = Support.Sequelize,
8 | Promise = Sequelize.Promise,
9 | current = Support.sequelize;
10 |
11 | describe('sequelize.query', () => {
12 | it('connection should be released only once when retry fails', () => {
13 | const getConnectionStub = sinon.stub(current.connectionManager, 'getConnection').callsFake(() => {
14 | return Promise.resolve({});
15 | });
16 | const releaseConnectionStub = sinon.stub(current.connectionManager, 'releaseConnection').callsFake(() => {
17 | return Promise.resolve();
18 | });
19 | const queryStub = sinon.stub(current.dialect.Query.prototype, 'run').callsFake(() => {
20 | return Promise.reject(new Error('wrong sql'));
21 | });
22 | return current.query('THIS IS A WRONG SQL', {
23 | retry: {
24 | max: 2,
25 | // retry for all errors
26 | match: null
27 | }
28 | })
29 | .catch(() => {})
30 | .finally(() => {
31 | expect(releaseConnectionStub).have.been.calledOnce;
32 | queryStub.restore();
33 | getConnectionStub.restore();
34 | releaseConnectionStub.restore();
35 | });
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/test/unit/sql/add-column.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require(__dirname + '/../support'),
4 | DataTypes = require('../../../lib/data-types'),
5 | expectsql = Support.expectsql,
6 | current = Support.sequelize,
7 | sql = current.dialect.QueryGenerator;
8 |
9 |
10 | if (current.dialect.name === 'mysql') {
11 | describe(Support.getTestDialectTeaser('SQL'), () => {
12 | describe('addColumn', () => {
13 |
14 | const Model = current.define('users', {
15 | id: {
16 | type: DataTypes.INTEGER,
17 | primaryKey: true,
18 | autoIncrement: true
19 | }
20 | }, { timestamps: false });
21 |
22 | it('properly generate alter queries', () => {
23 | return expectsql(sql.addColumnQuery(Model.getTableName(), 'level_id', current.normalizeAttribute({
24 | type: DataTypes.FLOAT,
25 | allowNull: false
26 | })), {
27 | mysql: 'ALTER TABLE `users` ADD `level_id` FLOAT NOT NULL;'
28 | });
29 | });
30 |
31 | it('properly generate alter queries for foreign keys', () => {
32 | return expectsql(sql.addColumnQuery(Model.getTableName(), 'level_id', current.normalizeAttribute({
33 | type: DataTypes.INTEGER,
34 | references: {
35 | model: 'level',
36 | key: 'id'
37 | },
38 | onUpdate: 'cascade',
39 | onDelete: 'cascade'
40 | })), {
41 | mysql: 'ALTER TABLE `users` ADD `level_id` INTEGER, ADD CONSTRAINT `users_level_id_foreign_idx` FOREIGN KEY (`level_id`) REFERENCES `level` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;'
42 | });
43 | });
44 |
45 | it('properly generate alter queries with FIRST', () => {
46 | return expectsql(sql.addColumnQuery(Model.getTableName(), 'test_added_col_first', current.normalizeAttribute({
47 | type: DataTypes.STRING,
48 | first: true
49 | })), {
50 | mysql: 'ALTER TABLE `users` ADD `test_added_col_first` VARCHAR(255) FIRST;'
51 | });
52 | });
53 | });
54 | });
55 | }
56 |
--------------------------------------------------------------------------------
/test/unit/sql/change-column.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require(__dirname + '/../support'),
4 | DataTypes = require('../../../lib/data-types'),
5 | expectsql = Support.expectsql,
6 | sinon = require('sinon'),
7 | current = Support.sequelize,
8 | Promise = current.Promise;
9 |
10 |
11 | if (current.dialect.name !== 'sqlite') {
12 | describe(Support.getTestDialectTeaser('SQL'), () => {
13 | describe('changeColumn', () => {
14 |
15 | const Model = current.define('users', {
16 | id: {
17 | type: DataTypes.INTEGER,
18 | primaryKey: true,
19 | autoIncrement: true
20 | },
21 | level_id: {
22 | type: DataTypes.INTEGER
23 | }
24 | }, { timestamps: false });
25 |
26 | before(function() {
27 |
28 | this.stub = sinon.stub(current, 'query').callsFake(sql => {
29 | return Promise.resolve(sql);
30 | });
31 | });
32 |
33 | beforeEach(function() {
34 | this.stub.resetHistory();
35 | });
36 |
37 | after(function() {
38 | this.stub.restore();
39 | });
40 |
41 | it('properly generate alter queries', () => {
42 | return current.getQueryInterface().changeColumn(Model.getTableName(), 'level_id', {
43 | type: DataTypes.FLOAT,
44 | allowNull: false
45 | }).then(sql => {
46 | expectsql(sql, {
47 | mssql: 'ALTER TABLE [users] ALTER COLUMN [level_id] FLOAT NOT NULL;',
48 | mysql: 'ALTER TABLE `users` CHANGE `level_id` `level_id` FLOAT NOT NULL;',
49 | postgres: 'ALTER TABLE "users" ALTER COLUMN "level_id" SET NOT NULL;ALTER TABLE "users" ALTER COLUMN "level_id" DROP DEFAULT;ALTER TABLE "users" ALTER COLUMN "level_id" TYPE FLOAT;'
50 | });
51 | });
52 | });
53 |
54 | it('properly generate alter queries for foreign keys', () => {
55 | return current.getQueryInterface().changeColumn(Model.getTableName(), 'level_id', {
56 | type: DataTypes.INTEGER,
57 | references: {
58 | model: 'level',
59 | key: 'id'
60 | },
61 | onUpdate: 'cascade',
62 | onDelete: 'cascade'
63 | }).then(sql => {
64 | expectsql(sql, {
65 | mssql: 'ALTER TABLE [users] ADD CONSTRAINT [level_id_foreign_idx] FOREIGN KEY ([level_id]) REFERENCES [level] ([id]) ON DELETE CASCADE;',
66 | mysql: 'ALTER TABLE `users` ADD CONSTRAINT `users_level_id_foreign_idx` FOREIGN KEY (`level_id`) REFERENCES `level` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;',
67 | postgres: 'ALTER TABLE "users" ADD CONSTRAINT "level_id_foreign_idx" FOREIGN KEY ("level_id") REFERENCES "level" ("id") ON DELETE CASCADE ON UPDATE CASCADE;'
68 | });
69 | });
70 | });
71 |
72 | });
73 | });
74 | }
75 |
--------------------------------------------------------------------------------
/test/unit/sql/create-schema.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require(__dirname + '/../support');
4 | const expectsql = Support.expectsql;
5 | const current = Support.sequelize;
6 | const sql = current.dialect.QueryGenerator;
7 |
8 | describe(Support.getTestDialectTeaser('SQL'), () => {
9 | if (current.dialect.name === 'postgres') {
10 | describe('dropSchema', () => {
11 | it('IF EXISTS', () => {
12 | expectsql(sql.dropSchema('foo'), {
13 | postgres: 'DROP SCHEMA IF EXISTS foo CASCADE;'
14 | });
15 | });
16 | });
17 |
18 | describe('createSchema', () => {
19 | before(function() {
20 | this.version = current.options.databaseVersion;
21 | });
22 |
23 | after(function() {
24 | current.options.databaseVersion = this.version;
25 | });
26 |
27 | it('9.2.0 or above', () => {
28 | current.options.databaseVersion = '9.2.0';
29 | expectsql(sql.createSchema('foo'), {
30 | postgres: 'CREATE SCHEMA IF NOT EXISTS foo;'
31 | });
32 | });
33 |
34 | it('below 9.2.0', () => {
35 | current.options.databaseVersion = '9.0.0';
36 | expectsql(sql.createSchema('foo'), {
37 | postgres: 'CREATE SCHEMA foo;'
38 | });
39 | });
40 | });
41 | }
42 | });
43 |
--------------------------------------------------------------------------------
/test/unit/sql/offset-limit.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require(__dirname + '/../support'),
4 | util = require('util'),
5 | expectsql = Support.expectsql,
6 | current = Support.sequelize,
7 | sql = current.dialect.QueryGenerator;
8 |
9 | // Notice: [] will be replaced by dialect specific tick/quote character when there is not dialect specific expectation but only a default expectation
10 |
11 | suite(Support.getTestDialectTeaser('SQL'), () => {
12 | suite('offset/limit', () => {
13 | const testsql = function(options, expectation) {
14 | const model = options.model;
15 |
16 | test(util.inspect(options, {depth: 2}), () => {
17 | return expectsql(
18 | sql.addLimitAndOffset(
19 | options,
20 | model
21 | ),
22 | expectation
23 | );
24 | });
25 | };
26 |
27 | testsql({
28 | limit: 10, //when no order by present, one is automagically prepended, test its existence
29 | model: {primaryKeyField: 'id', name: 'tableRef'}
30 | }, {
31 | default: ' LIMIT 10',
32 | mssql: ' ORDER BY [tableRef].[id] OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY'
33 | });
34 |
35 | testsql({
36 | limit: 10,
37 | order: [
38 | ['email', 'DESC'] // for MSSQL
39 | ]
40 | }, {
41 | default: ' LIMIT 10',
42 | mssql: ' OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY'
43 | });
44 |
45 | testsql({
46 | limit: 10,
47 | offset: 20,
48 | order: [
49 | ['email', 'DESC'] // for MSSQL
50 | ]
51 | }, {
52 | default: ' LIMIT 20, 10',
53 | postgres: ' LIMIT 10 OFFSET 20',
54 | mssql: ' OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY'
55 | });
56 |
57 | testsql({
58 | limit: "';DELETE FROM user",
59 | order: [
60 | ['email', 'DESC'] // for MSSQL
61 | ]
62 | }, {
63 | default: " LIMIT ''';DELETE FROM user'",
64 | mysql: " LIMIT '\\';DELETE FROM user'",
65 | mssql: " OFFSET 0 ROWS FETCH NEXT N''';DELETE FROM user' ROWS ONLY"
66 | });
67 |
68 | testsql({
69 | limit: 10,
70 | offset: "';DELETE FROM user",
71 | order: [
72 | ['email', 'DESC'] // for MSSQL
73 | ]
74 | }, {
75 | sqlite: " LIMIT ''';DELETE FROM user', 10",
76 | postgres: " LIMIT 10 OFFSET ''';DELETE FROM user'",
77 | mysql: " LIMIT '\\';DELETE FROM user', 10",
78 | mssql: " OFFSET N''';DELETE FROM user' ROWS FETCH NEXT 10 ROWS ONLY"
79 | });
80 | });
81 | });
82 |
--------------------------------------------------------------------------------
/test/unit/sql/remove-column.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require(__dirname + '/../support'),
4 | expectsql = Support.expectsql,
5 | current = Support.sequelize,
6 | sql = current.dialect.QueryGenerator;
7 |
8 | // Notice: [] will be replaced by dialect specific tick/quote character when there is not dialect specific expectation but only a default expectation
9 |
10 | if (current.dialect.name !== 'sqlite') {
11 | suite(Support.getTestDialectTeaser('SQL'), () => {
12 | suite('removeColumn', () => {
13 | test('schema', () => {
14 | expectsql(sql.removeColumnQuery({
15 | schema: 'archive',
16 | tableName: 'user'
17 | }, 'email'), {
18 | mssql: 'ALTER TABLE [archive].[user] DROP COLUMN [email];',
19 | mysql: 'ALTER TABLE `archive.user` DROP `email`;',
20 | postgres: 'ALTER TABLE "archive"."user" DROP COLUMN "email";'
21 | });
22 | });
23 | });
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/test/unit/sql/remove-constraint.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require(__dirname + '/../support');
4 | const current = Support.sequelize;
5 | const expectsql = Support.expectsql;
6 | const sql = current.dialect.QueryGenerator;
7 |
8 | if (current.dialect.supports.constraints.dropConstraint) {
9 | describe(Support.getTestDialectTeaser('SQL'), () => {
10 | describe('removeConstraint', () => {
11 | it('naming', () => {
12 | expectsql(sql.removeConstraintQuery('myTable', 'constraint_name'), {
13 | default: 'ALTER TABLE [myTable] DROP CONSTRAINT [constraint_name]'
14 | });
15 | });
16 | });
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/test/unit/sql/show-constraints.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require(__dirname + '/../support');
4 | const current = Support.sequelize;
5 | const expectsql = Support.expectsql;
6 | const sql = current.dialect.QueryGenerator;
7 |
8 | describe(Support.getTestDialectTeaser('SQL'), () => {
9 | describe('showConstraint', () => {
10 | it('naming', () => {
11 | expectsql(sql.showConstraintsQuery('myTable'), {
12 | mssql: "EXEC sp_helpconstraint @objname = N'[myTable]';",
13 | postgres: 'SELECT constraint_catalog AS "constraintCatalog", constraint_schema AS "constraintSchema", constraint_name AS "constraintName", table_catalog AS "tableCatalog", table_schema AS "tableSchema", table_name AS "tableName", constraint_type AS "constraintType", is_deferrable AS "isDeferrable", initially_deferred AS "initiallyDeferred" from INFORMATION_SCHEMA.table_constraints WHERE table_name=\'myTable\';',
14 | mysql: "SELECT CONSTRAINT_CATALOG AS constraintCatalog, CONSTRAINT_NAME AS constraintName, CONSTRAINT_SCHEMA AS constraintSchema, CONSTRAINT_TYPE AS constraintType, TABLE_NAME AS tableName, TABLE_SCHEMA AS tableSchema from INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE table_name='myTable';",
15 | default: "SELECT sql FROM sqlite_master WHERE tbl_name='myTable';"
16 | });
17 | });
18 |
19 | it('should add constraint_name to where clause if passed in case of mysql', () => {
20 | expectsql(sql.showConstraintsQuery('myTable', 'myConstraintName'), {
21 | mssql: "EXEC sp_helpconstraint @objname = N'[myTable]';",
22 | postgres: 'SELECT constraint_catalog AS "constraintCatalog", constraint_schema AS "constraintSchema", constraint_name AS "constraintName", table_catalog AS "tableCatalog", table_schema AS "tableSchema", table_name AS "tableName", constraint_type AS "constraintType", is_deferrable AS "isDeferrable", initially_deferred AS "initiallyDeferred" from INFORMATION_SCHEMA.table_constraints WHERE table_name=\'myTable\';',
23 | mysql: "SELECT CONSTRAINT_CATALOG AS constraintCatalog, CONSTRAINT_NAME AS constraintName, CONSTRAINT_SCHEMA AS constraintSchema, CONSTRAINT_TYPE AS constraintType, TABLE_NAME AS tableName, TABLE_SCHEMA AS tableSchema from INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE table_name='myTable' AND constraint_name = 'myConstraintName';",
24 | default: "SELECT sql FROM sqlite_master WHERE tbl_name='myTable' AND sql LIKE '%myConstraintName%';"
25 | });
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/test/unit/sql/update.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Support = require(__dirname + '/../support'),
4 | DataTypes = require(__dirname + '/../../../lib/data-types'),
5 | expectsql = Support.expectsql,
6 | current = Support.sequelize,
7 | sql = current.dialect.QueryGenerator;
8 |
9 | // Notice: [] will be replaced by dialect specific tick/quote character when there is not dialect specific expectation but only a default expectation
10 |
11 | describe(Support.getTestDialectTeaser('SQL'), () => {
12 | describe('update', () => {
13 | it('with temp table for trigger', () => {
14 | const User = Support.sequelize.define('user', {
15 | username: {
16 | type: DataTypes.STRING,
17 | field: 'user_name'
18 | }
19 | }, {
20 | timestamps: false,
21 | hasTrigger: true
22 | });
23 |
24 | const options = {
25 | returning: true,
26 | hasTrigger: true
27 | };
28 | expectsql(sql.updateQuery(User.tableName, {user_name: 'triggertest'}, {id: 2}, options, User.rawAttributes),
29 | {
30 | mssql: 'declare @tmp table ([id] INTEGER,[user_name] NVARCHAR(255));UPDATE [users] SET [user_name]=N\'triggertest\' OUTPUT INSERTED.[id],INSERTED.[user_name] into @tmp WHERE [id] = 2;select * from @tmp',
31 | postgres: 'UPDATE "users" SET "user_name"=\'triggertest\' WHERE "id" = 2 RETURNING *',
32 | default: "UPDATE `users` SET `user_name`=\'triggertest\' WHERE `id` = 2"
33 | });
34 | });
35 |
36 |
37 | it('Works with limit', () => {
38 | const User = Support.sequelize.define('User', {
39 | username: {
40 | type: DataTypes.STRING
41 | },
42 | userId: {
43 | type: DataTypes.INTEGER
44 | }
45 | }, {
46 | timestamps: false
47 | });
48 |
49 | expectsql(sql.updateQuery(User.tableName, { username: 'new.username' }, { username: 'username' }, { limit: 1 }), {
50 | mssql: "UPDATE TOP(1) [Users] SET [username]=N'new.username' OUTPUT INSERTED.* WHERE [username] = N'username'",
51 | mysql: "UPDATE `Users` SET `username`='new.username' WHERE `username` = 'username' LIMIT 1",
52 | sqlite: "UPDATE `Users` SET `username`='new.username' WHERE rowid IN (SELECT rowid FROM `Users` WHERE `username` = 'username' LIMIT 1)",
53 | default: "UPDATE [Users] SET [username]='new.username' WHERE [username] = 'username'"
54 | });
55 | });
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/test/unit/support.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('../support');
4 |
--------------------------------------------------------------------------------
/test/unit/transaction.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require('chai');
4 | const expect = chai.expect;
5 | const sinon = require('sinon');
6 | const Support = require(__dirname + '/support');
7 | const Sequelize = Support.Sequelize;
8 | const dialect = Support.getTestDialect();
9 | const current = Support.sequelize;
10 |
11 | describe('Transaction', function() {
12 | before(() => {
13 | this.stub = sinon.stub(current, 'query').returns(Sequelize.Promise.resolve({}));
14 |
15 | this.stubConnection = sinon.stub(current.connectionManager, 'getConnection')
16 | .returns(Sequelize.Promise.resolve({
17 | uuid: 'ssfdjd-434fd-43dfg23-2d',
18 | close() {}
19 | }));
20 |
21 | this.stubRelease = sinon.stub(current.connectionManager, 'releaseConnection')
22 | .returns(Sequelize.Promise.resolve());
23 | });
24 |
25 | beforeEach(() => {
26 | this.stub.resetHistory();
27 | this.stubConnection.resetHistory();
28 | this.stubRelease.resetHistory();
29 | });
30 |
31 | after(() => {
32 | this.stub.restore();
33 | this.stubConnection.restore();
34 | });
35 |
36 | it('should run auto commit query only when needed', () => {
37 | const expectations = {
38 | all: [
39 | 'START TRANSACTION;'
40 | ],
41 | sqlite: [
42 | 'BEGIN DEFERRED TRANSACTION;'
43 | ],
44 | mssql: [
45 | 'BEGIN TRANSACTION;'
46 | ]
47 | };
48 | return current.transaction(() => {
49 | expect(this.stub.args.map(arg => arg[0])).to.deep.equal(expectations[dialect] || expectations.all);
50 | return Sequelize.Promise.resolve();
51 | });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------