From a8ce25ff1f8edb61ade5a9ae3749843e41a20a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zo=C3=A9?= Date: Sun, 10 Apr 2022 16:00:30 +0200 Subject: [PATCH] refactor: modernize SSCCE (#218) * refactor: modernize * refactor: modernize sscce * feat: add docker images * build: update ci * refactor: remove .idea * refactor: update tsconfig/gitignore * build: use node 14 * build: disable linter on sscce file * docs: document the need to install pg-hstore if using DataTypes.HSTORE * docs: update documentation * feat: run against Sequelize 6 & 7 on node 10 to 16 * fix: make start:x scripts work in cjs * fix: ignore internal ts error * ci: dedupe CI runs * docs: update references to sscce.ts --- .eslintrc.json | 26 +-- .github/workflows/ci.yml | 47 ++-- .gitignore | 2 +- package.json | 63 +++--- readme.md | 214 +++++++++++++++---- setup/.eslintrc.json | 5 - setup/check-database-running.ts | 10 + setup/{ci-db-configs.js => ci-db-configs.ts} | 14 +- setup/create-sequelize-instance.ts | 15 ++ setup/global-adjusts.js | 53 ----- setup/global-adjusts.ts | 25 +++ setup/lib-replacements.js | 60 ------ setup/lib-replacements.ts | 67 ++++++ setup/logging.js | 21 -- setup/logging.ts | 54 +++++ setup/mariadb/10.3/docker-compose.yml | 20 ++ setup/mariadb/10.3/start.sh | 16 ++ setup/mariadb/10.3/stop.sh | 8 + setup/mssql/2019/docker-compose.yml | 18 ++ setup/mssql/2019/start.sh | 16 ++ setup/mssql/2019/stop.sh | 8 + setup/mysql/8.0/docker-compose.yml | 21 ++ setup/mysql/8.0/start.sh | 16 ++ setup/mysql/8.0/stop.sh | 8 + setup/postgres/10/docker-compose.yml | 19 ++ setup/postgres/10/start.sh | 16 ++ setup/postgres/10/stop.sh | 8 + setup/prepare-ci.sh | 13 +- setup/runner.js | 41 ---- setup/runner.ts | 82 +++++++ setup/sequelize-version.cjs | 5 + setup/sequelize-version.js | 1 - setup/ts-setup.js | 6 - setup/ts-setup.ts | 8 + setup/wait-until-healthy.sh | 20 ++ setup/wrap-options.js | 33 --- setup/wrap-options.ts | 33 +++ src/sscce-sequelize-6.ts | 41 ++++ src/sscce-sequelize-7.ts | 41 ++++ src/sscce.js | 36 ---- src/sscce.ts | 40 ---- src/utils/create-sequelize-instance.d.ts | 5 - src/utils/create-sequelize-instance.js | 8 - src/utils/log.d.ts | 3 - src/utils/log.js | 9 - tsconfig.json | 32 +-- 46 files changed, 843 insertions(+), 464 deletions(-) delete mode 100644 setup/.eslintrc.json create mode 100644 setup/check-database-running.ts rename setup/{ci-db-configs.js => ci-db-configs.ts} (79%) create mode 100644 setup/create-sequelize-instance.ts delete mode 100644 setup/global-adjusts.js create mode 100644 setup/global-adjusts.ts delete mode 100644 setup/lib-replacements.js create mode 100644 setup/lib-replacements.ts delete mode 100644 setup/logging.js create mode 100644 setup/logging.ts create mode 100644 setup/mariadb/10.3/docker-compose.yml create mode 100755 setup/mariadb/10.3/start.sh create mode 100755 setup/mariadb/10.3/stop.sh create mode 100644 setup/mssql/2019/docker-compose.yml create mode 100755 setup/mssql/2019/start.sh create mode 100755 setup/mssql/2019/stop.sh create mode 100644 setup/mysql/8.0/docker-compose.yml create mode 100755 setup/mysql/8.0/start.sh create mode 100755 setup/mysql/8.0/stop.sh create mode 100644 setup/postgres/10/docker-compose.yml create mode 100755 setup/postgres/10/start.sh create mode 100755 setup/postgres/10/stop.sh delete mode 100644 setup/runner.js create mode 100644 setup/runner.ts create mode 100644 setup/sequelize-version.cjs delete mode 100644 setup/sequelize-version.js delete mode 100644 setup/ts-setup.js create mode 100644 setup/ts-setup.ts create mode 100755 setup/wait-until-healthy.sh delete mode 100644 setup/wrap-options.js create mode 100644 setup/wrap-options.ts create mode 100644 src/sscce-sequelize-6.ts create mode 100644 src/sscce-sequelize-7.ts delete mode 100644 src/sscce.js delete mode 100644 src/sscce.ts delete mode 100644 src/utils/create-sequelize-instance.d.ts delete mode 100644 src/utils/create-sequelize-instance.js delete mode 100644 src/utils/log.d.ts delete mode 100644 src/utils/log.js diff --git a/.eslintrc.json b/.eslintrc.json index 623e1f0ee..f6cf40bba 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,17 +1,19 @@ { - "env": { - "es6": true, - "node": true - }, - "parserOptions": { - "ecmaVersion": 10 - }, "extends": [ - "eslint:recommended", - "plugin:node/recommended" + "@ephys/eslint-config-typescript", + "@ephys/eslint-config-typescript/node" ], "rules": { - "no-unused-vars": "off", - "node/no-unpublished-require": "off" - } + "unicorn/prefer-node-protocol": "off", + "no-console": "off", + "import/order": "off", + "prefer-object-has-own": "off", + "unicorn/prefer-at": "off", + "@typescript-eslint/no-unused-expressions": "off", + + // we still have to support node 10 while Sequelize 6 remains alive + "unicorn/prefer-module": "off", + "unicorn/prefer-top-level-await": "off" + }, + "ignorePatterns": ["src/*"] } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3a9c0acc..c45963caf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ name: CI -on: [push, pull_request] +on: [pull_request] env: SEQ_DB: sequelize_test @@ -11,28 +11,30 @@ jobs: strategy: fail-fast: false matrix: - combination: ['v5', 'v6', 'v6 with TS'] - name: SQLite (${{ matrix.combination }}) + # node 10 is the minimum supported version for Sequelize 6 + # node 14 is the minimum supported version for Sequelize 7 + # node 16 is latest LTS (to keep updated) + node: [10, 14, 16] + name: SQLite (sequelize 6 & 7, node ${{ matrix.node }}) runs-on: ubuntu-latest env: DIALECT: sqlite - CI_COMBINATION: ${{ matrix.combination }} steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: - node-version: 12.x + node-version: 14.x - run: /bin/bash ./setup/prepare-ci.sh - name: Execute SSCCE - run: npm run ci + run: npm run _test test-postgres: strategy: fail-fast: false matrix: postgres-version: [9.5, 10] # Does not work with 12 native: [true, false] - combination: ['v5', 'v6', 'v6 with TS'] - name: Postgres ${{ matrix.postgres-version }}${{ matrix.native && ' (native)' || '' }} (${{ matrix.combination }}) + node: [10, 14, 16] + name: Postgres ${{ matrix.postgres-version }}${{ matrix.native && ' (native)' || '' }} (sequelize 6 & 7, node ${{ matrix.node }}) runs-on: ubuntu-latest services: postgres: @@ -46,25 +48,24 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 env: DIALECT: ${{ matrix.native && 'postgres-native' || 'postgres' }} - CI_COMBINATION: ${{ matrix.combination }} steps: - run: PGPASSWORD=sequelize_test psql -h localhost -p 5432 -U sequelize_test sequelize_test -c '\l' - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3 with: - node-version: 12.x + node-version: ${{ matrix.node }} - run: /bin/bash ./setup/prepare-ci.sh - name: Execute SSCCE - run: npm run ci + run: npm run _test test-mysql-mariadb: strategy: fail-fast: false matrix: + node: [10, 14, 16] db: - '{ name: "MySQL 5.7", image: "mysql:5.7", dialect: "mysql" }' - '{ name: "MariaDB 10.3", image: "mariadb:10.3", dialect: "mariadb" }' - combination: ['v5', 'v6', 'v6 with TS'] - name: ${{ fromJson(matrix.db).name }} (${{ matrix.combination }}) + name: ${{ fromJson(matrix.db).name }} (node ${{ matrix.node }}) runs-on: ubuntu-latest services: mysql: @@ -79,23 +80,22 @@ jobs: options: --health-cmd="mysqladmin -usequelize_test -psequelize_test status" --health-interval 10s --health-timeout 5s --health-retries 5 --tmpfs /var/lib/mysql:rw env: DIALECT: ${{ fromJson(matrix.db).dialect }} - CI_COMBINATION: ${{ matrix.combination }} steps: - run: mysql --host 127.0.0.1 --port 3306 -uroot -psequelize_test -e "GRANT ALL ON *.* TO 'sequelize_test'@'%' with grant option; FLUSH PRIVILEGES;" - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3 with: - node-version: 12.x + node-version: ${{ matrix.node }} - run: /bin/bash ./setup/prepare-ci.sh - name: Execute SSCCE - run: npm run ci + run: npm run _test test-mssql: strategy: fail-fast: false matrix: + node: [10, 14, 16] mssql-version: [2017, 2019] - combination: ['v5', 'v6', 'v6 with TS'] - name: MSSQL ${{ matrix.mssql-version }} (${{ matrix.combination }}) + name: MSSQL ${{ matrix.mssql-version }} (sequelize 6 & 7, node ${{ matrix.node }}) runs-on: ubuntu-latest services: mssql: @@ -113,13 +113,12 @@ jobs: --health-retries 10 env: DIALECT: mssql - CI_COMBINATION: ${{ matrix.combination }} steps: - run: /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "Password12!" -Q "CREATE DATABASE sequelize_test; ALTER DATABASE sequelize_test SET READ_COMMITTED_SNAPSHOT ON;" - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3 with: - node-version: 12.x + node-version: ${{ matrix.node }} - run: /bin/bash ./setup/prepare-ci.sh - name: Execute SSCCE - run: npm run ci + run: npm run _test diff --git a/.gitignore b/.gitignore index 5898ed419..ccef16f17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ node_modules npm-debug.log -ts-dist yarn.lock +.idea diff --git a/package.json b/package.json index eca4e7033..7b085ecf8 100644 --- a/package.json +++ b/package.json @@ -1,50 +1,57 @@ { - "name": "sequelize-sscce", - "description": "SSCCE repository for Sequelize", - "version": "0.0.0-development", + "private": true, "engines": { "node": ">=10.0.0" }, - "private": true, + "type": "commonjs", "license": "MIT", "dependencies": { "chai": "^4", "chai-as-promised": "^7", "chai-datetime": "^1", - "chalk": "^2", - "cross-env": "^5", - "fs-jetpack": "^2", - "is-plain-object": "^3", - "json-stringify-safe": "^5", - "lodash.defaults": "^4", + "chalk": "^4.1.2", + "cross-env": "^7", + "fs-jetpack": "^4", "sequelize": "^6", - "sinon": "^9", + "@sequelize/core": "^7.0.0-alpha.10", + "sinon": "^13", "sinon-chai": "^3" }, "scripts": { - "start": "cross-env LOCAL_SSCCE=true DIALECT=sqlite node setup/runner.js", - "ts-prep": "del-cli ts-dist && tsc && node setup/ts-setup.js", - "ts": "npm run ts-prep && cross-env LOCAL_TS_RUN=true npm start", - "do-replace": "node setup/lib-replacements.js --do", - "undo-replace": "node setup/lib-replacements.js --undo", - "ci": "node setup/runner.js", - "lint": "eslint ." + "_test": "ts-node setup/runner.ts", + "test:sqlite": "cross-env DIALECT=sqlite npm run _test", + "test:postgres": "cross-env DIALECT=postgres npm run _test", + "test:postgres-native": "cross-env DIALECT=postgres-native npm run _test", + "test:mariadb": "cross-env DIALECT=mariadb npm run _test", + "test:mysql": "cross-env DIALECT=mysql npm run _test", + "test:mssql": "cross-env DIALECT=mssql npm run _test", + "start:mariadb": "bash setup/mariadb/10.3/start.sh", + "start:mysql": "bash setup/mysql/8.0/start.sh", + "start:postgres": "bash setup/postgres/10/start.sh", + "start:mssql": "bash setup/mssql/2019/start.sh", + "stop:mariadb": "bash setup/mariadb/10.3/stop.sh", + "stop:mysql": "bash setup/mysql/8.0/stop.sh", + "stop:postgres": "bash setup/postgres/10/stop.sh", + "stop:mssql": "bash setup/mssql/2019/stop.sh", + "do-replace": "node setup/lib-replacements.ts --do", + "undo-replace": "node setup/lib-replacements.ts --undo", + "lint": "eslint . --fix" }, "devDependencies": { - "@types/bluebird": "*", + "@ephys/eslint-config-typescript": "^14.1.1", "@types/chai": "^4", "@types/chai-as-promised": "^7", - "@types/chai-datetime": "^0.0.36", + "@types/chai-datetime": "^0.0.37", + "@types/lodash": "^4.14.178", "@types/node": "^10", - "@types/sinon": "^9", + "@types/sinon": "^10", "@types/sinon-chai": "^3", "@types/validator": "*", - "@typescript-eslint/eslint-plugin": "2.16.0", - "@typescript-eslint/parser": "2.16.0", - "del-cli": "^3", - "eslint": "^6", - "eslint-plugin-node": "^10", - "sqlite3": "^4", - "typescript": "~4.1" + "del-cli": "^4", + "eslint": "^8", + "lodash": "^4.17.21", + "sqlite3": "^5", + "ts-node": "^10.4.0", + "typescript": "~4.5" } } diff --git a/readme.md b/readme.md index 946304c12..80d5554ee 100644 --- a/readme.md +++ b/readme.md @@ -1,14 +1,150 @@ # Sequelize Quick SSCCE Base Repository -Use this repository to quickly create an [SSCCE](http://www.sscce.org/) for your issue! This way your issue will be much easier to investigate. +Use this repository to create an [SSCCE](http://www.sscce.org/) for your issue! It will greatly help us figure out what is going wrong and your issue +will be much easier to investigate. -By using this repository, you won't have to worry about setting up any database. You don't need to install anything, you don't need docker, you don't need to spend time configuring a development environment to create your SSCCE. Everything is already set up for you. +## Method 1: Create your SSCCE locally (preferred) -**You just [write your code](src/sscce.js) and it works, directly from GitHub!** +### Step 1 - Install this repository locally -## Step 1 - Create the SSCCE +Start by [Forking this repository](https://docs.github.com/en/get-started/quickstart/fork-a-repo), +then [clone it on your machine](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository). -Go to the [src/sscce.js](https://github.com/papb/sequelize-sscce/blob/master/src/sscce.js) file in this repository, and click the edit button (a pencil). +Run `npm install` or `yarn` to install the necessary dependencies. + +### Step 1 - Create the SSCCE + +You now need to create a failing test that accurately represents the issue you're experiencing. + +If you want to test against Sequelize 6, modify [src/sscce-sequelize-6.ts](./src/sscce-sequelize-6.ts). +If you want to test against Sequelize 7, modify [src/sscce-sequelize-7.ts](./src/sscce-sequelize-7.ts). + +If you are using a version older than Sequelize 6, unfortunately these versions are now EOL and won't receive support. +You should consider migrating to Sequelize 6. + +Remember the [SSCCE rules](http://www.sscce.org/). It should be: + +- *Short (Small)* - It should not include anything that is not relevant to your issue. +- *Self Contained* - Ensure everything is included, ready to go. +- *Correct* - It should demonstrate the problem you're encountering (i.e. the sscce should fail, but with the right error). +- *Example* - Displays the problem we are trying to solve. + +### Step 2 - Run the SSCCE + +Use one of our npm scripts to run your SSCCE. + +Running with a dialect other than sqlite will require installing an extra package +& having a database running. For convenience, we provide docker containers for each database. + +You can also opt to open a pull request, and run the code directly in our CI. + +#### sqlite + +The easiest of the bunch. Simply run: + +```shell +npm run test:sqlite +``` + +#### postgres + +You'll need to install the `pg` package and have a postgres database running. + +```shell +# Do this only once. +npm install pg +# or +npm install pg-native + +# if you need to use DataTypes.HSTORE, you also need this dependency +npm install pg-hstore + +# Start the postgres database using docker. +# Requires docker. You can also run your own database if you prefer. +npm run start:postgres + +# run the sscce! +npm run test:postgres +# or +npm run test:postgres-native + +# Remember to stop the docker container once you're done. +npm run stop:postgres +``` + +#### mariadb + +```shell +# Do this only once. +npm install mariadb + +# Start the mariadb database using docker. +# Requires docker. You can also run your own database if you prefer. +npm run start:mariadb + +# run the sscce! +npm run test:mariadb + +# Remember to stop the docker container once you're done. +npm run stop:mariadb +``` + +#### mysql + +```shell +# Do this only once. +npm install mysql2 + +# Start the mysql database using docker. +# Requires docker. You can also run your own database if you prefer. +npm run start:mysql + +# run the sscce! +npm run test:mysql + +# Remember to stop the docker container once you're done. +npm run stop:mysql +``` + +#### mssql (SQL Server) + +```shell +# Do this only once. +npm install tedious + +# Start the mssql database using docker. +# Requires docker. You can also run your own database if you prefer. +npm run start:mssql + +# run the sscce! +npm run test:mssql + +# Remember to stop the docker container once you're done. +npm run stop:mssql +``` + +### Step 3 - Commit your SSCCE & sent it to us + +Open an issue on the [main sequelize repo](https://github.com/sequelize/sequelize/) describing +your problem and include a link to your SSCCE in it. + +You can also open a PR of your fork to [this repository](https://github.com/sequelize/sequelize-sscce), +this way your SSCCE will be run on our CI and will continue existing even if you delete your fork. + +## Method 2: Create your SSCCE on GitHub + +By using this method, you won't have to worry about setting up any database. +You don't need to install anything, you don't need docker, you don't need to spend time configuring a development environment to create your SSCCE. +Everything is already set up for you in our GitHub actions. + +**You just write your code and it works, directly from GitHub!** + +### Step 1 - Create the SSCCE + +If you want to test against Sequelize 6, go to this file: [src/sscce-sequelize-6.ts](./src/sscce-sequelize-6.ts). +If you want to test against Sequelize 7, go to this file: [src/sscce-sequelize-7.ts](./src/sscce-sequelize-7.ts). + +Then click the edit button (a pencil). Since this is not your repository, a fork will be automatically created for you to perform your edit. You will see this message: @@ -18,7 +154,7 @@ Just create your SSCCE in that file, and commit it to your fork:
-## Step 2 - Run your SSCCE with GitHub Actions +### Step 2 - Run your SSCCE with GitHub Actions This step is **extremely easy**. Now that you have commited your SSCCE to your fork, just open a Pull Request (don't worry, *I won't accept it!*): @@ -26,62 +162,56 @@ This step is **extremely easy**. Now that you have commited your SSCCE to your f The idea here is that once you open the pull request, GitHub Actions will automatically execute it for you, since I have it configured in the main repository. I won't accept the pull request, since the goal is just to have your code executed. -It will run your SSCCE and show show a green checkmark (or a red X) next to the commit: +It will run your SSCCE and show a green checkmark (or a red X) next to the commit:
-## What if you want to make some changes to the SSCCE? - -Just add more commits on top of it, in your fork, and your PR will be updated automatically, and the SSCCE will be executed again. +## FAQ -## I don't want to open a pull request for this +### What if you want to make some changes to the SSCCE? -Why not? This repository was created exactly for this purpose. Please feel free to use it. +Just add more commits on top of it, in your fork, and your PR will be updated automatically, and the SSCCE will be executed again. -However, if you prefer, you can also have your SSCCE executed in your own fork, via GitHub actions. +### I don't want to open a pull request for this -## Creating a dialect-specific SSCCE +You don't have to! If you've opted for method 1, you can just add a link to your forked repository in your issue. -By default, your SSCCE will be executed on all dialects. If you only want a specific dialect, you can check the `process.env.DIALECT` variable. For example, if you only want to run your SSCCE for postgres, you can add the following at the beginning of your SSCCE code: +However, opening a pull request will ensure the SSCCE continues to exist even if you delete your fork. Less clutter in your repository list! -```js -if (process.env.DIALECT !== "postgres") return; -// The rest of the SSCCE goes here... -``` +### Creating a dialect-specific SSCCE -## Enabling specific postgres extensions +By default, your SSCCE will be executed on all dialects. If you only want a specific dialect, +you can remove dialects you don't need to test on from the `testingOnDialects` variable in your `sscce-sequelize-x.ts` file. -If your issue needs a postgres extension such as `uuid-ossp`, you should enable it at the beginning of your SSCCE: +For example, if you only want to run your SSCCE for postgres, change the following line: -```js -await sequelize.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`); -// The rest of the SSCCE goes here... +```typescript +// if your issue is dialect specific, remove the dialects you don't need to test on. +export const testingOnDialects = new Set(['mssql', 'sqlite', 'mysql', 'mariadb', 'postgres', 'postgres-native']); ``` -## Creating a SSCCE with TypeScript +to this: -Just use the [src/sscce.ts](src/sscce.ts) file instead of the [src/sscce.js](src/sscce.js) file. That's it, super easy. Also works directly from GitHub. - -## Running the SSCCE locally - -This repository also comes with built-in support for running your SSCCE locally on SQLite: - -* Clone this repository -* `npm install` -* Edit the `src/sscce.js` as you like -* `npm start` +```typescript +// if your issue is dialect specific, remove the dialects you don't need to test on. +export const testingOnDialects = new Set(['postgres']); +``` -The above will run your SSCCE locally on SQLite, with no need for any other extra setup. +### Enabling specific postgres extensions -There is no local support for other dialects; this is harder because requires setting up local databases. It is doable, but out of scope for this repository. [Learn more](https://github.com/sequelize/sequelize/blob/master/CONTRIBUTING.md#3-database). +If your issue needs a postgres extension such as `uuid-ossp`, you should enable it at the beginning of your SSCCE: -### Running the SSCCE locally for TypeScript +```js +export async function run() { + const sequelize = createSequelize6Instance(); -Do the same as above, except: + // add it here, after createSequelizeInstance: + await sequelize.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`); -* Edit the `src/sscce.ts` file instead of the `src/sscce.js` file -* Run `npm run ts` instead of `npm start` + // The rest of the SSCCE goes here... +} +``` ## License -MIT (c) Pedro Augusto de Paula Barbosa +MIT (c) Pedro Augusto de Paula Barbosa & The Sequelize Team diff --git a/setup/.eslintrc.json b/setup/.eslintrc.json deleted file mode 100644 index 0eb0592cd..000000000 --- a/setup/.eslintrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "rules": { - "no-unused-vars": "error" - } -} diff --git a/setup/check-database-running.ts b/setup/check-database-running.ts new file mode 100644 index 000000000..6cc6b5ba1 --- /dev/null +++ b/setup/check-database-running.ts @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +import { createSequelize6Instance } from './create-sequelize-instance'; + +const sequelize = createSequelize6Instance(); + +(async () => { + await sequelize.authenticate(); + await sequelize.close(); +})(); diff --git a/setup/ci-db-configs.js b/setup/ci-db-configs.ts similarity index 79% rename from setup/ci-db-configs.js rename to setup/ci-db-configs.ts index 170d61211..d308c69fe 100644 --- a/setup/ci-db-configs.js +++ b/setup/ci-db-configs.ts @@ -1,15 +1,13 @@ -'use strict'; - const host = 'localhost'; const username = 'sequelize_test'; const password = 'sequelize_test'; const database = 'sequelize_test'; const pool = { max: 5, - idle: 3000 // make idle time small so that tests exit promptly + idle: 3000, // make idle time small so that tests exit promptly }; -module.exports = { +export const CiDbConfigs = { mysql: { host, username, password, database, port: 3306, pool }, mariadb: { host, username, password, database, port: 3306, pool }, @@ -28,8 +26,8 @@ module.exports = { dialectOptions: { options: { encrypt: false, - requestTimeout: 25000 - } - } - } + requestTimeout: 25_000, + }, + }, + }, }; diff --git a/setup/create-sequelize-instance.ts b/setup/create-sequelize-instance.ts new file mode 100644 index 000000000..1ec6d6438 --- /dev/null +++ b/setup/create-sequelize-instance.ts @@ -0,0 +1,15 @@ +import type { Options as Sequelize6Options } from 'sequelize'; +import { Sequelize as Sequelize6 } from 'sequelize'; +import type { Options as Sequelize7Options, Sequelize as Sequelize7 } from '@sequelize/core'; +import { wrapOptions } from './wrap-options'; + +export function createSequelize6Instance(options?: Sequelize6Options): Sequelize6 { + return new Sequelize6(wrapOptions(options)); +} + +export function createSequelize7Instance(options?: Sequelize7Options): Sequelize7 { + // not compatible with node 10 + const { Sequelize: Sequelize7Constructor } = require('@sequelize/core'); + // @ts-expect-error -- wrapOptions expect sequelize 6. + return new Sequelize7Constructor(wrapOptions(options)); +} diff --git a/setup/global-adjusts.js b/setup/global-adjusts.js deleted file mode 100644 index b113e0d00..000000000 --- a/setup/global-adjusts.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -// Enable same settings to chai from Sequelize main repo -const chai = require('chai'); -chai.use(require('chai-datetime')); -chai.use(require('chai-as-promised')); -chai.use(require('sinon-chai')); -chai.config.includeStack = true; -chai.should(); - -const Sequelize = require('sequelize'); -const chalk = require('chalk'); - -process.on('uncaughtException', e => { - console.error('An unhandled exception occurred:'); - throw e; -}); -process.on("unhandledRejection", e => { - console.error('An unhandled rejection occurred:'); - throw e; -}); - -const sequelizeVersion = require('./sequelize-version'); - -if (sequelizeVersion.startsWith('v5')) { - Sequelize.Promise.onPossiblyUnhandledRejection(e => { - console.error('An unhandled rejection occurred:'); - throw e; - }); - Sequelize.Promise.longStackTraces(); -} - -const colors = [ - 'red', - 'green', - 'blue', - 'yellow', - 'gray', - 'darkRed', - 'darkYellow', - 'purple', - 'pink', - 'orange', - 'cyan', - 'gold' -]; - -for (const color of colors) { - console[color] = (...args) => console.log.apply(console, args.map(arg => { - if (typeof arg !== "string") return arg; - return chalk.keyword(color)(arg); - })); -} diff --git a/setup/global-adjusts.ts b/setup/global-adjusts.ts new file mode 100644 index 000000000..67df7b9a6 --- /dev/null +++ b/setup/global-adjusts.ts @@ -0,0 +1,25 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiDatetime from 'chai-datetime'; +import sinonChai from 'sinon-chai'; + +// Enable same settings to chai from Sequelize main repo +chai.use(chaiDatetime); +chai.use(chaiAsPromised); +chai.use(sinonChai); +chai.config.includeStack = true; +chai.should(); + +process.on('uncaughtException', e => { + console.error('An unhandled exception occurred:'); + console.error(e); + + process.exit(1); +}); + +process.on('unhandledRejection', e => { + console.error('An unhandled rejection occurred:'); + console.error(e); + + process.exit(1); +}); diff --git a/setup/lib-replacements.js b/setup/lib-replacements.js deleted file mode 100644 index bab9bd661..000000000 --- a/setup/lib-replacements.js +++ /dev/null @@ -1,60 +0,0 @@ -'use strict'; - -require('./global-adjusts'); - -const jetpack = require('fs-jetpack').cwd(__dirname); -const { relative } = require('path'); -const replacementsSourcePath = "./../src/lib-replacements"; -const replacementsTargetPath = "./../node_modules/sequelize/lib"; -const replacementsTargetBackupPath = "./../node_modules/sequelize/sequelize-sscce-lib-backup"; - -async function ensureLibBackup() { - if (await jetpack.existsAsync(replacementsTargetBackupPath) === "dir") { - return; - } - await jetpack.copyAsync(replacementsTargetPath, replacementsTargetBackupPath); -} - -async function undoReplacements() { - if (await jetpack.existsAsync(replacementsTargetBackupPath) !== "dir") { - console.yellow('No replacements to be undone.'); - return; - } - console.blue('Undoing replacements...'); - await jetpack.removeAsync(replacementsTargetPath); - await jetpack.moveAsync(replacementsTargetBackupPath, replacementsTargetPath); - console.green('Success!'); -} - -async function doReplacements() { - const isEmpty = (await jetpack.listAsync(replacementsSourcePath)).length === 0; - if (isEmpty) { - console.yellow("No source code replacements to apply."); - return; - } - console.blue(`Doing replacements...`); - await ensureLibBackup(); - await jetpack.copyAsync(replacementsSourcePath, replacementsTargetPath, { - overwrite(sourceInspectData/*, destinationInspectData*/) { - const path = relative( - jetpack.path(replacementsSourcePath), - sourceInspectData.absolutePath - ); - console.blue(` - Overwriting "${path}"`); - return true; - } - }); - console.green('Success!'); -} - -async function run() { - if (process.argv[2] === "--do") { - await doReplacements(); - } else if (process.argv[2] === "--undo") { - await undoReplacements(); - } else { - throw new Error('Invalid call to setup/lib-replacements.js'); - } -} - -run(); diff --git a/setup/lib-replacements.ts b/setup/lib-replacements.ts new file mode 100644 index 000000000..9564a2aed --- /dev/null +++ b/setup/lib-replacements.ts @@ -0,0 +1,67 @@ +import './global-adjusts.js'; +import { relative } from 'path'; +import Jetpack from 'fs-jetpack'; +import { logBlue, logGreen, logYellow } from './logging.js'; + +const jetpack = Jetpack.cwd(__dirname); +const replacementsSourcePath = './../src/lib-replacements'; +const replacementsTargetPath = './../node_modules/sequelize/lib'; +const replacementsTargetBackupPath = './../node_modules/sequelize/sequelize-sscce-lib-backup'; + +async function ensureLibBackup() { + if (await jetpack.existsAsync(replacementsTargetBackupPath) === 'dir') { + return; + } + + await jetpack.copyAsync(replacementsTargetPath, replacementsTargetBackupPath); +} + +async function undoReplacements() { + if (await jetpack.existsAsync(replacementsTargetBackupPath) !== 'dir') { + logYellow('No replacements to be undone.'); + + return; + } + + logBlue('Undoing replacements...'); + await jetpack.removeAsync(replacementsTargetPath); + await jetpack.moveAsync(replacementsTargetBackupPath, replacementsTargetPath); + logGreen('Success!'); +} + +async function doReplacements() { + const result = await jetpack.listAsync(replacementsSourcePath); + const isEmpty = result == null || result.length === 0; + if (isEmpty) { + logYellow('No source code replacements to apply.'); + + return; + } + + logBlue(`Doing replacements...`); + await ensureLibBackup(); + await jetpack.copyAsync(replacementsSourcePath, replacementsTargetPath, { + overwrite(sourceInspectData/* , destinationInspectData */) { + const path = relative( + jetpack.path(replacementsSourcePath), + sourceInspectData.absolutePath!, + ); + logBlue(` - Overwriting "${path}"`); + + return true; + }, + }); + logGreen('Success!'); +} + +async function run() { + if (process.argv[2] === '--do') { + await doReplacements(); + } else if (process.argv[2] === '--undo') { + await undoReplacements(); + } else { + throw new Error('Invalid call to setup/lib-replacements.js'); + } +} + +void run(); diff --git a/setup/logging.js b/setup/logging.js deleted file mode 100644 index 5a27a7c28..000000000 --- a/setup/logging.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -const isPlainObject = require('is-plain-object'); -const chalk = require('chalk'); - -function isOptionsObject(arg) { - return arg && isPlainObject(arg) && Object.prototype.hasOwnProperty.call(arg, 'logging'); -} - -module.exports = function(...args) { - args = args.filter(x => x !== undefined); - if (args.length === 0) return; - if (isOptionsObject(args[args.length - 1])) args.pop(); - args[0] = chalk.gray(args[0]); - if (args.length === 2) { - // If benchmarking option is enabled, the logging will have a - // second argument last argument which is the elapsed time - args[1] = chalk.blue(`[Elapsed time: ${args[1]} ms]`); - } - console.log('[Sequelize]', ...args, '\n'); -}; diff --git a/setup/logging.ts b/setup/logging.ts new file mode 100644 index 000000000..ffe7289ec --- /dev/null +++ b/setup/logging.ts @@ -0,0 +1,54 @@ +import isPlainObject from 'lodash/isPlainObject.js'; +import type { Chalk } from 'chalk'; +import chalk from 'chalk'; +import type { Options } from 'sequelize'; + +function isOptionsObject(arg: any): arg is Options { + return arg && isPlainObject(arg) && Object.prototype.hasOwnProperty.call(arg, 'logging'); +} + +export function log(...args: any[]): void { + args = args.filter(x => x !== undefined); + if (args.length === 0) { + return; + } + + if (isOptionsObject(args[args.length - 1])) { + args.pop(); + } + + args[0] = chalk.gray(args[0]); + if (args.length === 2) { + // If benchmarking option is enabled, the logging will have a + // second argument last argument which is the elapsed time + args[1] = chalk.blue(`[Elapsed time: ${args[1]} ms]`); + } + + console.log('[Sequelize]', ...args, '\n'); +} + +export function logRed(...args: any[]): void { + return logColor(chalk.red, ...args); +} + +export function logBlue(...args: any[]): void { + return logColor(chalk.blue, ...args); +} + +export function logGreen(...args: any[]): void { + return logColor(chalk.green, ...args); +} + +export function logYellow(...args: any[]): void { + return logColor(chalk.yellow, ...args); +} + +function logColor(color: Chalk, ...args: any[]) { + return console.log(...args.map(arg => { + if (typeof arg !== 'string') { + return arg; + } + + return color(arg); + })); +} diff --git a/setup/mariadb/10.3/docker-compose.yml b/setup/mariadb/10.3/docker-compose.yml new file mode 100644 index 000000000..3cc679d0f --- /dev/null +++ b/setup/mariadb/10.3/docker-compose.yml @@ -0,0 +1,20 @@ +services: + mariadb-103: + container_name: sequelize-mariadb-103 + image: mariadb:10.3 + environment: + MYSQL_DATABASE: sequelize_test + MYSQL_USER: sequelize_test + MYSQL_PASSWORD: sequelize_test + MYSQL_ROOT_PASSWORD: sequelize_test + ports: + - 3306:3306 + healthcheck: + test: ["CMD", "mysqladmin", "-usequelize_test", "-psequelize_test", "status"] + interval: 3s + timeout: 1s + retries: 10 + +networks: + default: + name: sequelize-mariadb-103-network diff --git a/setup/mariadb/10.3/start.sh b/setup/mariadb/10.3/start.sh new file mode 100755 index 000000000..295d9bd26 --- /dev/null +++ b/setup/mariadb/10.3/start.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -Eeuxo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" # https://stackoverflow.com/a/17744637 + + +docker-compose -p sequelize-mariadb-103 down --remove-orphans +docker-compose -p sequelize-mariadb-103 up -d + +./../../wait-until-healthy.sh sequelize-mariadb-103 + +docker exec sequelize-mariadb-103 \ + mysql --host 127.0.0.1 --port 3306 -uroot -psequelize_test -e "GRANT ALL ON *.* TO 'sequelize_test'@'%' with grant option; FLUSH PRIVILEGES;" + +DIALECT=mariadb node --loader ts-node/esm ../../check-database-running.ts + +echo "Local MariaDB-10.3 instance is ready for Sequelize tests." diff --git a/setup/mariadb/10.3/stop.sh b/setup/mariadb/10.3/stop.sh new file mode 100755 index 000000000..e2629c115 --- /dev/null +++ b/setup/mariadb/10.3/stop.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -Eeuxo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" # https://stackoverflow.com/a/17744637 + + +docker-compose -p sequelize-mariadb-103 down --remove-orphans + +echo "Local MariaDB-10.3 instance stopped (if it was running)." diff --git a/setup/mssql/2019/docker-compose.yml b/setup/mssql/2019/docker-compose.yml new file mode 100644 index 000000000..beae992f7 --- /dev/null +++ b/setup/mssql/2019/docker-compose.yml @@ -0,0 +1,18 @@ +services: + mssql-2019: + container_name: sequelize-mssql-2019 + image: mcr.microsoft.com/mssql/server:2019-latest + environment: + ACCEPT_EULA: Y + SA_PASSWORD: Password12! + ports: + - 1433:1433 + healthcheck: + test: ["CMD", "/opt/mssql-tools/bin/sqlcmd", "-S", "localhost", "-U", "SA", "-P", "Password12!", "-l", "30", "-Q", "SELECT 1"] + interval: 3s + timeout: 1s + retries: 10 + +networks: + default: + name: sequelize-mssql-2019-network diff --git a/setup/mssql/2019/start.sh b/setup/mssql/2019/start.sh new file mode 100755 index 000000000..9478c38d7 --- /dev/null +++ b/setup/mssql/2019/start.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -Eeuxo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" # https://stackoverflow.com/a/17744637 + + +docker-compose -p sequelize-mssql-2019 down --remove-orphans +docker-compose -p sequelize-mssql-2019 up -d + +./../../wait-until-healthy.sh sequelize-mssql-2019 + +docker exec sequelize-mssql-2019 \ + /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "Password12!" -Q "CREATE DATABASE sequelize_test; ALTER DATABASE sequelize_test SET READ_COMMITTED_SNAPSHOT ON;" + +DIALECT=mssql node --loader ts-node/esm ../../check-database-running.ts + +echo "Local MSSQL-2019 instance is ready for Sequelize tests." diff --git a/setup/mssql/2019/stop.sh b/setup/mssql/2019/stop.sh new file mode 100755 index 000000000..0c8d73b3f --- /dev/null +++ b/setup/mssql/2019/stop.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -Eeuxo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" # https://stackoverflow.com/a/17744637 + + +docker-compose -p sequelize-mssql-2019 down --remove-orphans + +echo "Local MSSQL-2019 instance stopped (if it was running)." diff --git a/setup/mysql/8.0/docker-compose.yml b/setup/mysql/8.0/docker-compose.yml new file mode 100644 index 000000000..552b2225a --- /dev/null +++ b/setup/mysql/8.0/docker-compose.yml @@ -0,0 +1,21 @@ +services: + mysql-80: + container_name: sequelize-mysql-80 + image: mysql:8.0 + environment: + MYSQL_DATABASE: sequelize_test + MYSQL_USER: sequelize_test + MYSQL_PASSWORD: sequelize_test + MYSQL_ROOT_PASSWORD: sequelize_test + ports: + - 3306:3306 + # tmpfs: /var/lib/mysql:rw + healthcheck: + test: ["CMD", "mysqladmin", "-usequelize_test", "-psequelize_test", "status"] + interval: 3s + timeout: 1s + retries: 10 + +networks: + default: + name: sequelize-mysql-80-network diff --git a/setup/mysql/8.0/start.sh b/setup/mysql/8.0/start.sh new file mode 100755 index 000000000..36132fe74 --- /dev/null +++ b/setup/mysql/8.0/start.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -Eeuxo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" # https://stackoverflow.com/a/17744637 + + +docker-compose -p sequelize-mysql-80 down --remove-orphans +docker-compose -p sequelize-mysql-80 up -d + +../../wait-until-healthy.sh sequelize-mysql-80 + +docker exec sequelize-mysql-80 \ + mysql --host 127.0.0.1 --port 3306 -uroot -psequelize_test -e "GRANT ALL ON *.* TO 'sequelize_test'@'%' with grant option; FLUSH PRIVILEGES;" + +DIALECT=mysql node --loader ts-node/esm ../../check-database-running.ts + +echo "Local MySQL-8.0 instance is ready for Sequelize tests." diff --git a/setup/mysql/8.0/stop.sh b/setup/mysql/8.0/stop.sh new file mode 100755 index 000000000..a5a6492ca --- /dev/null +++ b/setup/mysql/8.0/stop.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -Eeuxo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" # https://stackoverflow.com/a/17744637 + + +docker-compose -p sequelize-mysql-80 down --remove-orphans + +echo "Local MySQL-8.0 instance stopped (if it was running)." diff --git a/setup/postgres/10/docker-compose.yml b/setup/postgres/10/docker-compose.yml new file mode 100644 index 000000000..e869f7d1a --- /dev/null +++ b/setup/postgres/10/docker-compose.yml @@ -0,0 +1,19 @@ +services: + postgres-10: + container_name: sequelize-postgres-10 + image: sushantdhiman/postgres:10 + environment: + POSTGRES_USER: sequelize_test + POSTGRES_PASSWORD: sequelize_test + POSTGRES_DB: sequelize_test + ports: + - 5432:5432 + healthcheck: + test: ["CMD", "pg_isready", "-U", "sequelize_test"] + interval: 3s + timeout: 1s + retries: 10 + +networks: + default: + name: sequelize-postgres-10-network diff --git a/setup/postgres/10/start.sh b/setup/postgres/10/start.sh new file mode 100755 index 000000000..23b9b034f --- /dev/null +++ b/setup/postgres/10/start.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -Eeuxo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" # https://stackoverflow.com/a/17744637 + + +docker-compose -p sequelize-postgres-10 down --remove-orphans +docker-compose -p sequelize-postgres-10 up -d + +./../../wait-until-healthy.sh sequelize-postgres-10 + +# docker exec sequelize-postgres-10 \ +# bash -c "export PGPASSWORD=sequelize_test && psql -h localhost -p 5432 -U sequelize_test sequelize_test -c '\l'" + +DIALECT=postgres node --loader ts-node/esm ../../check-database-running.ts + +echo "Local Postgres-10 instance is ready for Sequelize tests." diff --git a/setup/postgres/10/stop.sh b/setup/postgres/10/stop.sh new file mode 100755 index 000000000..907d20745 --- /dev/null +++ b/setup/postgres/10/stop.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -Eeuxo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" # https://stackoverflow.com/a/17744637 + + +docker-compose -p sequelize-postgres-10 down --remove-orphans + +echo "Local Postgres-10 instance stopped (if it was running)." diff --git a/setup/prepare-ci.sh b/setup/prepare-ci.sh index 2ac1d0280..fa5e95740 100644 --- a/setup/prepare-ci.sh +++ b/setup/prepare-ci.sh @@ -1,13 +1,6 @@ #!/bin/bash -c -if [ "$CI_COMBINATION" = "v6 with TS" ]; then - npm i; -else - if [ $CI_COMBINATION = "v5" ]; then - npm i --save sequelize@^5 - fi - npm i --production; # Install faster -fi +npm i; if [ "$DIALECT" = "postgres" ]; then npm i pg@^7 pg-hstore@^2 pg-types@^2; @@ -22,7 +15,3 @@ elif [ "$DIALECT" = "sqlite" ]; then elif [ "$DIALECT" = "mssql" ]; then npm i tedious@^6 fi - -if [ "$CI_COMBINATION" = "v6 with TS" ]; then - npm run ts-prep; -fi diff --git a/setup/runner.js b/setup/runner.js deleted file mode 100644 index c1afc2797..000000000 --- a/setup/runner.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -require('./global-adjusts'); - -const sequelizeVersion = require('./sequelize-version'); - -const USE_TS = process.env.LOCAL_TS_RUN || process.env.CI_COMBINATION === 'v6 with TS'; - -async function run() { - let heading = `Running SSCCE for ${process.env.DIALECT.toUpperCase()} with Sequelize ${sequelizeVersion}`; - if (USE_TS) heading += ' with TypeScript'; - heading = `===== ${heading} =====`; - - console.blue('\n' + '-'.repeat(heading.length)); - console.blue(heading); - console.blue('-'.repeat(heading.length) + '\n'); - - if (process.env.LOCAL_SSCCE) { - console.gold('Warning: running the SSCCE locally will use SQLite only. To run your SSCCE in all dialects, just configure Travis CI / AppVeyor in your GitHub repository.\n'); - } - - if (USE_TS) { - const { run } = require('./../ts-dist/sscce'); // eslint-disable-line - await run(); - } else { - await require('./../src/sscce')(); - } -} - -(async () => { - try { - await run(); - console.log('\n' + '-'.repeat(40) + '\n'); - console.green('SSCCE done without errors!'); - } catch (e) { - console.red(e); - console.log('\n' + '-'.repeat(40) + '\n'); - console.red('SSCCE done with error (see above).'); - process.exit(1); // eslint-disable-line no-process-exit - } -})(); diff --git a/setup/runner.ts b/setup/runner.ts new file mode 100644 index 000000000..1cdb5b345 --- /dev/null +++ b/setup/runner.ts @@ -0,0 +1,82 @@ +#!/usr/bin/env node + +// @ts-expect-error - it's fine for this to be "any" +import versions from './sequelize-version.cjs'; +import './global-adjusts'; +import assert from 'assert'; +import { existsSync } from 'fs'; +import { logBlue, logGreen, logRed } from './logging'; + +const majorNodeVersion = Number.parseInt(process.version.slice(1), 10); + +async function wrappedRun() { + const dialect = process.env.DIALECT; + assert(dialect, 'Must provide DIALECT environment variable. Use one of the `test:` npm scripts available. (e.g. `npm run test:sqlite`)'); + + let failed = false; + + if (existsSync(`${__dirname}/../src/sscce-sequelize-6.ts`)) { + let heading = `Running SSCCE for ${dialect.toUpperCase()} with Sequelize ${versions.sequelize6}`; + heading = `===== ${heading} =====`; + + logBlue(`\n${'-'.repeat(heading.length)}`); + logBlue(heading); + logBlue(`${'-'.repeat(heading.length)}\n`); + + const { run, testingOnDialects } = require('../src/sscce-sequelize-6'); + if (!testingOnDialects.has(process.env.DIALECT!)) { + logRed(`Skipping dialect ${process.env.DIALECT} as it has been omitted from 'testingOnDialects'`); + + return; + } + + try { + await run(); + } catch (error) { + logRed('Sequelize 6 test failed'); + logRed(error); + failed = true; + } + } + + if (existsSync(`${__dirname}/../src/sscce-sequelize-7.ts`)) { + let heading = `Running SSCCE for ${dialect.toUpperCase()} with Sequelize ${versions.sequelize7}`; + heading = `===== ${heading} =====`; + + logBlue(`\n${'-'.repeat(heading.length)}`); + logBlue(heading); + logBlue(`${'-'.repeat(heading.length)}\n`); + + if (majorNodeVersion >= 14) { + const { run, testingOnDialects } = require('../src/sscce-sequelize-7'); + if (!testingOnDialects.has(process.env.DIALECT!)) { + logRed(`Skipping dialect ${process.env.DIALECT} as it has been omitted from 'testingOnDialects'`); + + return; + } + + try { + await run(); + } catch (error) { + logRed('Sequelize 7 test failed'); + logRed(error); + failed = true; + } + } else { + logRed(`Current node version ${process.version} is insufficient for Sequelize 7, skipping this test`); + } + } + + if (failed) { + console.log(`\n${'-'.repeat(40)}\n`); + logRed('SSCCE done with error (see above).'); + process.exit(1); + } + + console.log(`\n${'-'.repeat(40)}\n`); + logGreen('SSCCE done without errors!'); +} + +(async () => { + await wrappedRun(); +})(); diff --git a/setup/sequelize-version.cjs b/setup/sequelize-version.cjs new file mode 100644 index 000000000..9c622c843 --- /dev/null +++ b/setup/sequelize-version.cjs @@ -0,0 +1,5 @@ +// this file is a .cjs file because ESM can't import JSON quite yet +module.exports = { + sequelize6: require('sequelize/package.json').version.replace(/^v?/, 'v'), + sequelize7: require('@sequelize/core/package.json').version.replace(/^v?/, 'v'), +}; diff --git a/setup/sequelize-version.js b/setup/sequelize-version.js deleted file mode 100644 index f002255e4..000000000 --- a/setup/sequelize-version.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('sequelize/package.json').version.replace(/^v?/, 'v'); diff --git a/setup/ts-setup.js b/setup/ts-setup.js deleted file mode 100644 index c87b6bb9b..000000000 --- a/setup/ts-setup.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -require('./global-adjusts'); - -const jetpack = require('fs-jetpack').cwd(__dirname); -jetpack.copy('./../src/utils', './../ts-dist/utils'); diff --git a/setup/ts-setup.ts b/setup/ts-setup.ts new file mode 100644 index 000000000..8bac1d909 --- /dev/null +++ b/setup/ts-setup.ts @@ -0,0 +1,8 @@ +import './global-adjusts.js'; + +import { cwd } from 'fs-jetpack'; +import path from 'path'; +import url from 'url'; + +const jetpack = cwd(path.dirname(url.fileURLToPath(import.meta.url))); +jetpack.copy('./../src/utils', './../ts-dist/utils'); diff --git a/setup/wait-until-healthy.sh b/setup/wait-until-healthy.sh new file mode 100755 index 000000000..80815069b --- /dev/null +++ b/setup/wait-until-healthy.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +if [ "$#" -ne 1 ]; then + >&2 echo "Please provide the container name or hash" + exit 1 +fi + +for _ in {1..50} +do + state=$(docker inspect -f '{{ .State.Health.Status }}' $1 2>&1) + return_code=$? + if [ ${return_code} -eq 0 ] && [ "$state" == "healthy" ]; then + echo "$1 is healthy!" + exit 0 + fi + sleep 0.4 +done + +>&2 echo "Timeout of 20s exceeded when waiting for container to be healthy: $1" +exit 1 diff --git a/setup/wrap-options.js b/setup/wrap-options.js deleted file mode 100644 index 519121895..000000000 --- a/setup/wrap-options.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -const defaults = require('lodash.defaults'); -const CIDBConfigs = require('./ci-db-configs'); -const logging = require('./logging'); - -module.exports = function wrapOptions(options) { - if (!process.env.DIALECT) throw new Error('Dialect is not defined! Aborting.'); - const isPostgresNative = process.env.DIALECT === 'postgres-native'; - const dialect = isPostgresNative ? 'postgres' : process.env.DIALECT; - - const config = CIDBConfigs[dialect]; - - options = options || {}; - options.dialect = dialect; - if (isPostgresNative) options.native = true; - - defaults(options, { - logging, - database: config.database, - username: config.username, - password: config.password, - host: config.host, - port: config.port, - pool: config.pool, - storage: config.storage, - dialectOptions: config.dialectOptions || {} - }); - - options.__isOptionsObject__ = true; - - return options; -}; diff --git a/setup/wrap-options.ts b/setup/wrap-options.ts new file mode 100644 index 000000000..e0f950acb --- /dev/null +++ b/setup/wrap-options.ts @@ -0,0 +1,33 @@ +import defaults from 'lodash/defaults.js'; +import { CiDbConfigs } from './ci-db-configs'; +import { log } from './logging'; +import type { Dialect, Options } from 'sequelize'; + +export function wrapOptions(options: Options = {}) { + if (!process.env.DIALECT) { + throw new Error('Dialect is not defined! Aborting.'); + } + + const isPostgresNative = process.env.DIALECT === 'postgres-native'; + const dialect = (isPostgresNative ? 'postgres' : process.env.DIALECT) as Dialect; + + // this fails in the CI due to mismatch between Sequelize 6 & 7. Should be resolved once we drop Sequelize 6. + // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error + // @ts-ignore + const config = CiDbConfigs[dialect]; + + options.dialect = dialect; + if (isPostgresNative) { + options.native = true; + } + + defaults(options, { + logging: log, + ...config, + }); + + // @ts-expect-error + options.__isOptionsObject__ = true; + + return options; +} diff --git a/src/sscce-sequelize-6.ts b/src/sscce-sequelize-6.ts new file mode 100644 index 000000000..c90761b96 --- /dev/null +++ b/src/sscce-sequelize-6.ts @@ -0,0 +1,41 @@ +import { DataTypes, Model } from 'sequelize'; +import { createSequelize6Instance } from '../setup/create-sequelize-instance'; +import { expect } from 'chai'; +import sinon from 'sinon'; + +// if your issue is dialect specific, remove the dialects you don't need to test on. +export const testingOnDialects = new Set(['mssql', 'sqlite', 'mysql', 'mariadb', 'postgres', 'postgres-native']); + +// You can delete this file if you don't want your SSCCE to be tested against Sequelize 6 + +// Your SSCCE goes inside this function. +export async function run() { + // This function should be used instead of `new Sequelize()`. + // It applies the config for your SSCCE to work on CI. + const sequelize = createSequelize6Instance({ + logQueryParameters: true, + benchmark: true, + define: { + // For less clutter in the SSCCE + timestamps: false, + }, + }); + + class Foo extends Model {} + + Foo.init({ + name: DataTypes.TEXT, + }, { + sequelize, + modelName: 'Foo', + }); + + // You can use sinon and chai assertions directly in your SSCCE. + const spy = sinon.spy(); + sequelize.afterBulkSync(() => spy()); + await sequelize.sync({ force: true }); + expect(spy).to.have.been.called; + + console.log(await Foo.create({ name: 'TS foo' })); + expect(await Foo.count()).to.equal(1); +} diff --git a/src/sscce-sequelize-7.ts b/src/sscce-sequelize-7.ts new file mode 100644 index 000000000..861b9fdea --- /dev/null +++ b/src/sscce-sequelize-7.ts @@ -0,0 +1,41 @@ +import { DataTypes, Model } from '@sequelize/core'; +import { createSequelize7Instance } from '../setup/create-sequelize-instance'; +import { expect } from 'chai'; +import sinon from 'sinon'; + +// if your issue is dialect specific, remove the dialects you don't need to test on. +export const testingOnDialects = new Set(['mssql', 'sqlite', 'mysql', 'mariadb', 'postgres', 'postgres-native']); + +// You can delete this file if you don't want your SSCCE to be tested against Sequelize 7 + +// Your SSCCE goes inside this function. +export async function run() { + // This function should be used instead of `new Sequelize()`. + // It applies the config for your SSCCE to work on CI. + const sequelize = createSequelize7Instance({ + logQueryParameters: true, + benchmark: true, + define: { + // For less clutter in the SSCCE + timestamps: false, + }, + }); + + class Foo extends Model {} + + Foo.init({ + name: DataTypes.TEXT, + }, { + sequelize, + modelName: 'Foo', + }); + + // You can use sinon and chai assertions directly in your SSCCE. + const spy = sinon.spy(); + sequelize.afterBulkSync(() => spy()); + await sequelize.sync({ force: true }); + expect(spy).to.have.been.called; + + console.log(await Foo.create({ name: 'TS foo' })); + expect(await Foo.count()).to.equal(1); +} diff --git a/src/sscce.js b/src/sscce.js deleted file mode 100644 index 45c7f8e53..000000000 --- a/src/sscce.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -// Require the necessary things from Sequelize -const { Sequelize, Op, Model, DataTypes } = require('sequelize'); - -// This function should be used instead of `new Sequelize()`. -// It applies the config for your SSCCE to work on CI. -const createSequelizeInstance = require('./utils/create-sequelize-instance'); - -// This is an utility logger that should be preferred over `console.log()`. -const log = require('./utils/log'); - -// You can use sinon and chai assertions directly in your SSCCE if you want. -const sinon = require('sinon'); -const { expect } = require('chai'); - -// Your SSCCE goes inside this function. -module.exports = async function() { - const sequelize = createSequelizeInstance({ - logQueryParameters: true, - benchmark: true, - define: { - timestamps: false // For less clutter in the SSCCE - } - }); - - const Foo = sequelize.define('Foo', { name: DataTypes.TEXT }); - - const spy = sinon.spy(); - sequelize.afterBulkSync(() => spy()); - await sequelize.sync(); - expect(spy).to.have.been.called; - - log(await Foo.create({ name: 'foo' })); - expect(await Foo.count()).to.equal(1); -}; diff --git a/src/sscce.ts b/src/sscce.ts deleted file mode 100644 index de41ca4b7..000000000 --- a/src/sscce.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Require the necessary things from Sequelize -import { Sequelize, Op, Model, DataTypes } from 'sequelize'; - -// This function should be used instead of `new Sequelize()`. -// It applies the config for your SSCCE to work on CI. -import createSequelizeInstance = require('./utils/create-sequelize-instance'); - -// This is an utility logger that should be preferred over `console.log()`. -import log = require('./utils/log'); - -// You can use sinon and chai assertions directly in your SSCCE if you want. -import sinon = require('sinon'); -import { expect } from 'chai'; - -// Your SSCCE goes inside this function. -export async function run() { - const sequelize = createSequelizeInstance({ - logQueryParameters: true, - benchmark: true, - define: { - timestamps: false // For less clutter in the SSCCE - } - }); - - class Foo extends Model {}; - Foo.init({ - name: DataTypes.TEXT - }, { - sequelize, - modelName: 'Foo' - }); - - const spy = sinon.spy(); - sequelize.afterBulkSync(() => spy()); - await sequelize.sync(); - expect(spy).to.have.been.called; - - log(await Foo.create({ name: 'TS foo' })); - expect(await Foo.count()).to.equal(1); -} diff --git a/src/utils/create-sequelize-instance.d.ts b/src/utils/create-sequelize-instance.d.ts deleted file mode 100644 index c93ae4d05..000000000 --- a/src/utils/create-sequelize-instance.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Sequelize, Options } from "sequelize/types"; - -declare function createSequelizeInstance(options: Options): Sequelize; - -export = createSequelizeInstance; diff --git a/src/utils/create-sequelize-instance.js b/src/utils/create-sequelize-instance.js deleted file mode 100644 index 93cb91266..000000000 --- a/src/utils/create-sequelize-instance.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -const wrapOptions = require('./../../setup/wrap-options'); -const { Sequelize } = require('sequelize'); - -module.exports = function createSequelizeInstance(options) { - return new Sequelize(wrapOptions(options)); -}; diff --git a/src/utils/log.d.ts b/src/utils/log.d.ts deleted file mode 100644 index 2f1ccc0b4..000000000 --- a/src/utils/log.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare function log(...args: any): void; - -export = log; diff --git a/src/utils/log.js b/src/utils/log.js deleted file mode 100644 index 93d373c55..000000000 --- a/src/utils/log.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -const jsonStringifySafe = require('json-stringify-safe'); - -module.exports = function log(...args) { - console.log.apply(console, args.map(arg => { - return jsonStringifySafe(arg, null, 2); - })); -}; diff --git a/tsconfig.json b/tsconfig.json index 87ed13288..b17064f0e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,18 @@ { - "include": [ - "src/sscce.ts" - ], - "compilerOptions": { - "outDir": "ts-dist", - "target": "es2018", // Node.js 10 - "module": "commonjs", - "moduleResolution": "node", - "resolveJsonModule": true, - "declaration": true, - "pretty": true, - "newLine": "lf", - "stripInternal": true, - "strict": true, - "skipLibCheck": true - } + "include": [ + "src/*", + "setup/*" + ], + "compilerOptions": { + "target": "es2021", + "module": "CommonJS", + "moduleResolution": "Node", + "esModuleInterop": true, + "resolveJsonModule": true, + "newLine": "lf", + "stripInternal": true, + "strict": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true + } }