From bf5f1a53201b40dec09b79c5c3379d41fccc7e33 Mon Sep 17 00:00:00 2001 From: Nicola Marcacci Rossi Date: Tue, 8 Aug 2023 19:23:57 +0200 Subject: [PATCH 1/2] break: Drop support for node 18 --- .github/workflows/release.yml | 2 +- .github/workflows/testing.yml | 1 - .gitignore | 2 + .nvmrc | 2 +- docker-compose.yml | 17 ++ package-lock.json | 220 +++++++++++++++++- package.json | 11 +- src/migrations/generate.ts | 18 +- src/permissions/check.ts | 2 +- src/permissions/generate.ts | 14 +- src/resolvers/filters.ts | 6 +- src/resolvers/mutations.ts | 31 +-- src/resolvers/node.ts | 2 +- src/resolvers/resolver.ts | 4 +- src/resolvers/utils.ts | 4 +- .../__snapshots__/generate.spec.ts.snap | 6 + tests/__snapshots__/query.spec.ts.snap | 22 ++ .../__snapshots__/resolve.spec.ts.snap | 0 tests/{unit => }/generate.spec.ts | 4 +- tests/query.spec.ts | 28 +++ tests/{unit => }/resolve.spec.ts | 10 +- tests/utils/database/knex.ts | 19 ++ tests/utils/database/schema.ts | 51 ++++ tests/utils/database/seed.ts | 53 +++++ tests/utils/generate-migration.ts | 35 +++ tests/{unit/utils.ts => utils/models.ts} | 21 +- tests/utils/server.ts | 108 +++++++++ tsconfig.json | 6 +- 28 files changed, 638 insertions(+), 61 deletions(-) create mode 100644 docker-compose.yml rename tests/{unit => }/__snapshots__/generate.spec.ts.snap (97%) create mode 100644 tests/__snapshots__/query.spec.ts.snap rename tests/{unit => }/__snapshots__/resolve.spec.ts.snap (100%) rename tests/{unit => }/generate.spec.ts (58%) create mode 100644 tests/query.spec.ts rename tests/{unit => }/resolve.spec.ts (92%) create mode 100644 tests/utils/database/knex.ts create mode 100644 tests/utils/database/schema.ts create mode 100644 tests/utils/database/seed.ts create mode 100644 tests/utils/generate-migration.ts rename tests/{unit/utils.ts => utils/models.ts} (90%) create mode 100644 tests/utils/server.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b929fae..734a4b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: submodules: true - uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 20 - run: npm run bootstrap - run: npm run build - name: semantic release diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index f424aa8..233c43d 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -14,7 +14,6 @@ jobs: fail-fast: true matrix: version: - - 18 - 20 name: test and build package (node ${{ matrix.version }}) runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index c734b70..58bdf6e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ yarn-error.log* *.tgz .env + +/tmp diff --git a/.nvmrc b/.nvmrc index 3c03207..209e3ef 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18 +20 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c60c2d8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: '3.4' +services: + postgres: + image: postgres:13-alpine + shm_size: 1gb + environment: + POSTGRES_DB: postgres + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + POSTGRES_HOST_AUTH_METHOD: trust + TZ: 'Europe/Zurich' + ports: + - '5432:5432' + networks: + default: + aliases: + - postgres.local diff --git a/package-lock.json b/package-lock.json index 510be49..06ef3bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,15 +29,18 @@ "del-cli": "5.0.0", "esbuild": "0.18.19", "eslint": "8.46.0", + "graphql-request": "^6.1.0", "jest": "29.6.2", "mock-knex": "0.4.12", + "pg": "^8.11.2", "prettier": "2.8.8", + "simple-git": "^3.19.1", "ts-jest": "29.1.1", "ts-node": "10.9.1", "typescript": "5.1.6" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1864,6 +1867,21 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true + }, "node_modules/@nicolo-ribaudo/semver-v6": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", @@ -2983,6 +3001,15 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -3319,6 +3346,15 @@ "cti": "dist/cti.js" } }, + "node_modules/cross-fetch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "dev": true, + "dependencies": { + "node-fetch": "^2.6.12" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4788,6 +4824,19 @@ "node": ">= 10.x" } }, + "node_modules/graphql-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", + "integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==", + "dev": true, + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0", + "cross-fetch": "^3.1.5" + }, + "peerDependencies": { + "graphql": "14 - 16" + } + }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -6884,6 +6933,12 @@ "node": ">=6" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6968,11 +7023,102 @@ "node": ">=8" } }, + "node_modules/pg": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.2.tgz", + "integrity": "sha512-l4rmVeV8qTIrrPrIR3kZQqBgSN93331s9i6wiUiLOSk0Q7PmUxZD/m1rQI622l3NfqBby9Ar5PABfS/SulfieQ==", + "dev": true, + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "dev": true, + "optional": true + }, "node_modules/pg-connection-string": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "dev": true, + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==", + "dev": true + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dev": true, + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pg/node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", + "dev": true + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dev": true, + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7064,6 +7210,45 @@ "node": ">=8" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7673,6 +7858,21 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-git": { + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.19.1.tgz", + "integrity": "sha512-Ck+rcjVaE1HotraRAS8u/+xgTvToTuoMkT9/l9lvuP5jftwnYUp6DwuJzsKErHgfyRk8IB8pqGHWEbM3tLgV1w==", + "dev": true, + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -7742,6 +7942,15 @@ "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -8510,6 +8719,15 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index cebee3c..79fe59a 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "main": "dist/cjs/index.cjs", "types": "dist/esm/index.d.ts", "engines": { - "node": ">=18" + "node": ">=20" }, "sideEffecs": false, "scripts": { @@ -17,8 +17,10 @@ "generate:index-files": "cti create ./src --excludes types --withoutbackup", "lint": "eslint src", "lint:fix": "eslint src --fix", - "test": "npm run lint && npm run test:unit && npm run build", - "test:unit": "jest tests/unit --no-cache --no-watchman", + "deps": "docker-compose up", + "test": "npm run lint && npm run test:api && npm run build", + "test:api": "jest tests --no-cache --no-watchman", + "generate-migration": "esbuild tests/utils/generate-migration.ts --bundle --platform=node --outdir=tmp --out-extension:.js=.cjs --format=cjs --packages=external && node tmp/generate-migration.cjs", "clean": "del-cli dist/**", "prebuild": "npm run clean", "build": "npm run build:esm && npm run build:cjs", @@ -55,9 +57,12 @@ "del-cli": "5.0.0", "esbuild": "0.18.19", "eslint": "8.46.0", + "graphql-request": "^6.1.0", "jest": "29.6.2", "mock-knex": "0.4.12", + "pg": "^8.11.2", "prettier": "2.8.8", + "simple-git": "^3.19.1", "ts-jest": "29.1.1", "ts-node": "10.9.1", "typescript": "5.1.6" diff --git a/src/migrations/generate.ts b/src/migrations/generate.ts index d3cd102..10dd6f0 100644 --- a/src/migrations/generate.ts +++ b/src/migrations/generate.ts @@ -80,7 +80,7 @@ export class MigrationGenerator { this.renameTable(model.name, model.oldName!); }); tables[tables.indexOf(model.oldName)] = model.name; - this.columns[model.name] = this.columns[model.oldName]!; + this.columns[model.name] = this.columns[model.oldName]; delete this.columns[model.oldName]; if (model.updatable) { @@ -99,7 +99,7 @@ export class MigrationGenerator { }); }); tables[tables.indexOf(`${model.oldName}Revision`)] = `${model.name}Revision`; - this.columns[`${model.name}Revision`] = this.columns[`${model.oldName}Revision`]!; + this.columns[`${model.name}Revision`] = this.columns[`${model.oldName}Revision`]; delete this.columns[`${model.oldName}Revision`]; } } @@ -131,7 +131,7 @@ export class MigrationGenerator { model, model.fields.filter( ({ name, relation, raw, foreignKey }) => - !raw && !this.columns[model.name]!.some((col) => col.name === (foreignKey || (relation ? `${name}Id` : name))) + !raw && !this.columns[model.name].some((col) => col.name === (foreignKey || (relation ? `${name}Id` : name))) ), up, down @@ -139,7 +139,7 @@ export class MigrationGenerator { // Update fields const existingFields = model.fields.filter(({ name, relation, nonNull }) => { - const col = this.columns[model.name]!.find((col) => col.name === (relation ? `${name}Id` : name)); + const col = this.columns[model.name].find((col) => col.name === (relation ? `${name}Id` : name)); if (!col) { return false; } @@ -197,7 +197,7 @@ export class MigrationGenerator { ({ name, relation, raw, foreignKey, updatable }) => !raw && updatable && - !this.columns[revisionTable]!.some((col) => col.name === (foreignKey || (relation ? `${name}Id` : name))) + !this.columns[revisionTable].some((col) => col.name === (foreignKey || (relation ? `${name}Id` : name))) ); this.createRevisionFields(model, missingRevisionFields, up, down); @@ -208,7 +208,7 @@ export class MigrationGenerator { !raw && !updatable && foreignKey !== 'id' && - this.columns[revisionTable]!.some((col) => col.name === (foreignKey || (relation ? `${name}Id` : name))) + this.columns[revisionTable].some((col) => col.name === (foreignKey || (relation ? `${name}Id` : name))) ); this.createRevisionFields(model, revisionFieldsToRemove, down, up); } @@ -219,7 +219,7 @@ export class MigrationGenerator { if (tables.includes(model.name)) { this.createFields( model, - model.fields.filter(({ name, deleted }) => deleted && this.columns[model.name]!.some((col) => col.name === name)), + model.fields.filter(({ name, deleted }) => deleted && this.columns[model.name].some((col) => col.name === name)), down, up ); @@ -363,7 +363,7 @@ export class MigrationGenerator { this.column( field, { alter: true }, - summonByName(this.columns[model.name]!, field.relation ? `${field.name}Id` : field.name) + summonByName(this.columns[model.name], field.relation ? `${field.name}Id` : field.name) ); } }); @@ -389,7 +389,7 @@ export class MigrationGenerator { this.column( field, { alter: true }, - summonByName(this.columns[model.name]!, field.relation ? `${field.name}Id` : field.name) + summonByName(this.columns[model.name], field.relation ? `${field.name}Id` : field.name) ); } }); diff --git a/src/permissions/check.ts b/src/permissions/check.ts index 11aaae3..d047a93 100644 --- a/src/permissions/check.ts +++ b/src/permissions/check.ts @@ -194,7 +194,7 @@ const permissionLinkQuery = ( ) => { const aliases = new AliasGenerator(); let alias = aliases.getShort(); - const { type, me, where } = links[0]!; + const { type, me, where } = links[0]; // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here subQuery.from(`${type} as ${alias}`); if (me) { diff --git a/src/permissions/generate.ts b/src/permissions/generate.ts index 1f5dfa6..3b74a4b 100644 --- a/src/permissions/generate.ts +++ b/src/permissions/generate.ts @@ -71,7 +71,7 @@ export const generatePermissions = (models: Models, config: PermissionsConfig) = rolePermissions[type] = {}; for (const action of ACTIONS) { if (action === 'READ' || action in block) { - rolePermissions[type]![action] = true; + rolePermissions[type][action] = true; } } } @@ -95,7 +95,7 @@ export const generatePermissions = (models: Models, config: PermissionsConfig) = }; const addPermissions = (models: Models, permissions: RolePermissions, links: PermissionLink[], block: PermissionsBlock) => { - const { type } = links[links.length - 1]!; + const { type } = links[links.length - 1]; const model = summonByName(models, type); for (const action of ACTIONS) { @@ -103,11 +103,11 @@ const addPermissions = (models: Models, permissions: RolePermissions, links: Per if (!permissions[type]) { permissions[type] = {}; } - if (!permissions[type]![action]) { - permissions[type]![action] = []; + if (!permissions[type][action]) { + permissions[type][action] = []; } - if (permissions[type]![action] !== true) { - (permissions[type]![action] as PermissionStack).push(links); + if (permissions[type][action] !== true) { + (permissions[type][action] as PermissionStack).push(links); } } } @@ -123,7 +123,7 @@ const addPermissions = (models: Models, permissions: RolePermissions, links: Per reverse: true, }; } else { - const field = model.reverseRelationsByName[relation]!; + const field = model.reverseRelationsByName[relation]; if (!field) { throw new Error(`Relation ${relation} in model ${model.name} does not exist.`); diff --git a/src/resolvers/filters.ts b/src/resolvers/filters.ts index 9b02c4d..bcc1dfb 100644 --- a/src/resolvers/filters.ts +++ b/src/resolvers/filters.ts @@ -72,12 +72,12 @@ const applyWhere = (node: WhereNode, where: Where, ops: Ops, const specialFilter = key.match(/^(\w+)_(\w+)$/); if (specialFilter) { const [, actualKey, filter] = specialFilter; - if (!SPECIAL_FILTERS[filter!]) { + if (!SPECIAL_FILTERS[filter]) { // Should not happen throw new Error(`Invalid filter ${key}.`); } ops.push((query) => - query.whereRaw(SPECIAL_FILTERS[filter!]!, [`${node.shortTableAlias}.${actualKey}`, value as string]) + query.whereRaw(SPECIAL_FILTERS[filter], [`${node.shortTableAlias}.${actualKey}`, value as string]) ); continue; } @@ -154,7 +154,7 @@ const applyOrderBy = (node: FieldResolverNode, orderBy: OrderBy, query: Knex.Que throw new UserInputError(`You need to specify exactly 1 value to order by for each orderBy entry.`); } const key = keys[0]; - const value = vals[key!]; + const value = vals[key]; // Simple field // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here diff --git a/src/resolvers/mutations.ts b/src/resolvers/mutations.ts index 9fdc08e..e2e856c 100644 --- a/src/resolvers/mutations.ts +++ b/src/resolvers/mutations.ts @@ -12,7 +12,7 @@ export const mutationResolver = async (_parent: any, args: any, partialCtx: Cont return await partialCtx.knex.transaction(async (knex) => { const [, mutation, modelName] = it(info.fieldName.match(/^(create|update|delete|restore)(.+)$/)); const ctx = { ...partialCtx, knex, info, aliases: new AliasGenerator() }; - const model = summonByName(ctx.models, modelName!); + const model = summonByName(ctx.models, modelName); switch (mutation) { case 'create': return await create(model, args, ctx); @@ -110,17 +110,20 @@ const del = async (model: Model, { where, dryRun }: { where: any; dryRun: boolea return; } - if (dryRun) { - if (!(currentModel.name in toDelete)) { - toDelete[currentModel.name] = {}; - } - toDelete[currentModel.name]![entity.id] = entity[currentModel.displayField || 'id'] || entity.id; - } else { + if (!(currentModel.name in toDelete)) { + toDelete[currentModel.name] = {}; + } + if (entity.id in toDelete[currentModel.name]) { + return; + } + entity[currentModel.displayField || 'id'] || entity.id; + + if (!dryRun) { const normalizedInput = { deleted: true, deletedAt: ctx.now, deletedById: ctx.user.id }; const data = { prev: entity, input: {}, normalizedInput, next: { ...entity, ...normalizedInput } }; if (ctx.mutationHook) { beforeHooks.push(async () => { - await ctx.mutationHook!(currentModel, 'delete', 'before', data, ctx); + await ctx.mutationHook(currentModel, 'delete', 'before', data, ctx); }); } mutations.push(async () => { @@ -129,7 +132,7 @@ const del = async (model: Model, { where, dryRun }: { where: any; dryRun: boolea }); if (ctx.mutationHook) { afterHooks.push(async () => { - await ctx.mutationHook!(currentModel, 'delete', 'after', data, ctx); + await ctx.mutationHook(currentModel, 'delete', 'after', data, ctx); }); } } @@ -148,13 +151,13 @@ const del = async (model: Model, { where, dryRun }: { where: any; dryRun: boolea if (!toUnlink[descendantModel.name]) { toUnlink[descendantModel.name] = {}; } - if (!toUnlink[descendantModel.name]![descendant.id]) { - toUnlink[descendantModel.name]![descendant.id] = { + if (!toUnlink[descendantModel.name][descendant.id]) { + toUnlink[descendantModel.name][descendant.id] = { display: descendant[descendantModel.displayField || 'id'] || entity.id, fields: [], }; } - toUnlink[descendantModel.name]![descendant.id]!.fields.push(name); + toUnlink[descendantModel.name][descendant.id].fields.push(name); } else { mutations.push(async () => { await ctx @@ -225,7 +228,7 @@ const restore = async (model: Model, { where }: { where: any }, ctx: FullContext const data = { prev: relatedEntity, input: {}, normalizedInput, next: { ...relatedEntity, ...normalizedInput } }; if (ctx.mutationHook) { beforeHooks.push(async () => { - await ctx.mutationHook!(model, 'restore', 'before', data, ctx); + await ctx.mutationHook(model, 'restore', 'before', data, ctx); }); } mutations.push(async () => { @@ -234,7 +237,7 @@ const restore = async (model: Model, { where }: { where: any }, ctx: FullContext }); if (ctx.mutationHook) { afterHooks.push(async () => { - await ctx.mutationHook!(model, 'restore', 'after', data, ctx); + await ctx.mutationHook(model, 'restore', 'after', data, ctx); }); } diff --git a/src/resolvers/node.ts b/src/resolvers/node.ts index ec89c30..e1b6e3f 100644 --- a/src/resolvers/node.ts +++ b/src/resolvers/node.ts @@ -132,7 +132,7 @@ export const getFragmentSpreads = (node: ResolverNode) => node.selectionSet.filter(isFragmentSpreadNode).map((subNode) => getResolverNode({ ctx: node.ctx, - node: node.ctx.info.fragments[subNode.name.value]!, + node: node.ctx.info.fragments[subNode.name.value], tableAlias: node.tableAlias, baseTypeDefinition: node.baseTypeDefinition, typeName: node.model.name, diff --git a/src/resolvers/resolver.ts b/src/resolvers/resolver.ts index 4d5b6b8..6a33274 100644 --- a/src/resolvers/resolver.ts +++ b/src/resolvers/resolver.ts @@ -165,7 +165,7 @@ const applySubQueries = async ( if (!entriesById[entry[ID_ALIAS]]) { entriesById[entry[ID_ALIAS]] = []; } - entriesById[entry[ID_ALIAS]!]!.push(entry); + entriesById[entry[ID_ALIAS]].push(entry); } const ids = Object.keys(entriesById); @@ -189,7 +189,7 @@ const applySubQueries = async ( const children = hydrate(subNode, rawChildren); for (const child of children) { - for (const entry of entriesById[child[foreignKey] as string]!) { + for (const entry of entriesById[child[foreignKey] as string]) { if (isList) { (entry[fieldName] as Entry[]).push(cloneDeep(child)); } else { diff --git a/src/resolvers/utils.ts b/src/resolvers/utils.ts index dc19b33..1c2ea75 100644 --- a/src/resolvers/utils.ts +++ b/src/resolvers/utils.ts @@ -72,13 +72,13 @@ export function hydrate( outer: for (const [column, value] of Object.entries(entry)) { let current = res; const shortParts = column.split('__'); - const fieldName = shortParts.pop()!; + const fieldName = shortParts.pop(); const columnWithoutField = shortParts.join('__'); const longColumn = node.ctx.aliases.getLong(columnWithoutField); const longColumnWithoutRoot = longColumn.replace(new RegExp(`^${tableAlias}(__)?`), ''); const allParts = [tableAlias, ...(longColumnWithoutRoot ? longColumnWithoutRoot.split('__') : []), fieldName]; for (let i = 0; i < allParts.length - 1; i++) { - const part = allParts[i]!; + const part = allParts[i]; if (!current[part]) { const idField = [node.ctx.aliases.getShort(allParts.slice(0, i + 1).join('__')), ID_ALIAS].join('__'); diff --git a/tests/unit/__snapshots__/generate.spec.ts.snap b/tests/__snapshots__/generate.spec.ts.snap similarity index 97% rename from tests/unit/__snapshots__/generate.spec.ts.snap rename to tests/__snapshots__/generate.spec.ts.snap index 5f97314..74fe72e 100644 --- a/tests/unit/__snapshots__/generate.spec.ts.snap +++ b/tests/__snapshots__/generate.spec.ts.snap @@ -38,6 +38,11 @@ type Query { manyObjects(where: SomeObjectWhere, search: String, orderBy: [SomeObjectOrderBy!], limit: Int, offset: Int): [SomeObject!]! } +enum Role { + ADMIN + USER +} + enum SomeEnum { A B @@ -89,6 +94,7 @@ scalar Upload type User { id: ID! username: String + role: Role createdManyObjects(where: SomeObjectWhere, search: String, orderBy: [SomeObjectOrderBy!], limit: Int, offset: Int): [SomeObject!]! updatedManyObjects(where: SomeObjectWhere, search: String, orderBy: [SomeObjectOrderBy!], limit: Int, offset: Int): [SomeObject!]! deletedManyObjects(where: SomeObjectWhere, search: String, orderBy: [SomeObjectOrderBy!], limit: Int, offset: Int): [SomeObject!]! diff --git a/tests/__snapshots__/query.spec.ts.snap b/tests/__snapshots__/query.spec.ts.snap new file mode 100644 index 0000000..992df95 --- /dev/null +++ b/tests/__snapshots__/query.spec.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`query can be executed 1`] = ` +{ + "manyObjects": [ + { + "another": { + "id": "226a20e8-5c18-4423-99ca-eb0df6ff4fdd", + "manyObjects": [ + { + "field": null, + "id": "604ab55d-ec3e-4857-9f27-219158f80e64", + }, + ], + }, + "field": null, + "id": "604ab55d-ec3e-4857-9f27-219158f80e64", + "xyz": 1, + }, + ], +} +`; diff --git a/tests/unit/__snapshots__/resolve.spec.ts.snap b/tests/__snapshots__/resolve.spec.ts.snap similarity index 100% rename from tests/unit/__snapshots__/resolve.spec.ts.snap rename to tests/__snapshots__/resolve.spec.ts.snap diff --git a/tests/unit/generate.spec.ts b/tests/generate.spec.ts similarity index 58% rename from tests/unit/generate.spec.ts rename to tests/generate.spec.ts index feede93..15829cd 100644 --- a/tests/unit/generate.spec.ts +++ b/tests/generate.spec.ts @@ -1,5 +1,5 @@ -import { printSchemaFromModels } from '../../src/generate'; -import { rawModels } from './utils'; +import { printSchemaFromModels } from '../src'; +import { rawModels } from './utils/models'; describe('generate', () => { it('generates a schema', () => { diff --git a/tests/query.spec.ts b/tests/query.spec.ts new file mode 100644 index 0000000..7738077 --- /dev/null +++ b/tests/query.spec.ts @@ -0,0 +1,28 @@ +import { gql } from '../src'; +import { ANOTHER_ID, SOME_ID } from './utils/database/seed'; +import { withServer } from './utils/server'; + +describe('query', () => { + it('can be executed', async () => { + await withServer(async (request) => { + expect( + await request(gql` + query SomeQuery { + manyObjects(where: { another: { id: "${ANOTHER_ID}" } }, orderBy: [{ xyz: DESC }]) { + id + field + xyz + another { + id + manyObjects(where: { id: "${SOME_ID}" }) { + id + field + } + } + } + } + `) + ).toMatchSnapshot(); + }); + }); +}); diff --git a/tests/unit/resolve.spec.ts b/tests/resolve.spec.ts similarity index 92% rename from tests/unit/resolve.spec.ts rename to tests/resolve.spec.ts index 6a5b6ce..02e3934 100644 --- a/tests/unit/resolve.spec.ts +++ b/tests/resolve.spec.ts @@ -1,12 +1,12 @@ import { makeExecutableSchema } from '@graphql-tools/schema'; import { execute, parse, Source } from 'graphql'; import knex from 'knex'; -import { gql } from '../../src/client/gql'; -import { Context } from '../../src/context'; -import { generate } from '../../src/generate'; -import { getResolvers } from '../../src/resolvers'; -import { models, permissions, rawModels } from './utils'; import { DateTime } from 'luxon'; +import { gql } from '../src/client/gql'; +import { Context } from '../src/context'; +import { generate } from '../src/generate'; +import { getResolvers } from '../src/resolvers'; +import { models, permissions, rawModels } from './utils/models'; const test = async (operationName: string, query: string, variableValues: object, responses: unknown[]) => { const knexInstance = knex({ diff --git a/tests/utils/database/knex.ts b/tests/utils/database/knex.ts new file mode 100644 index 0000000..fef26a5 --- /dev/null +++ b/tests/utils/database/knex.ts @@ -0,0 +1,19 @@ +import knex from 'knex'; + +export const getKnex = (database = 'postgres') => + knex({ + client: 'postgresql', + connection: { + host: 'localhost', + database, + user: 'postgres', + password: 'password', + }, + migrations: { + tableName: 'knex_migrations', + }, + pool: { + min: 0, + max: 30, + }, + }); diff --git a/tests/utils/database/schema.ts b/tests/utils/database/schema.ts new file mode 100644 index 0000000..b463166 --- /dev/null +++ b/tests/utils/database/schema.ts @@ -0,0 +1,51 @@ +import { Knex } from 'knex'; + +export const setupSchema = async (knex: Knex) => { + await knex.raw(`CREATE TYPE "someEnum" AS ENUM ('A','B','C')`); + + await knex.raw(`CREATE TYPE "role" AS ENUM ('ADMIN','USER')`); + + await knex.schema.createTable('User', (table) => { + table.uuid('id').notNullable().primary(); + table.string('username', undefined).nullable(); + table + .enum('role', null as any, { + useNative: true, + existingType: true, + enumName: 'role', + }) + .nullable(); + }); + + await knex.schema.createTable('AnotherObject', (table) => { + table.uuid('id').notNullable().primary(); + }); + + await knex.schema.createTable('SomeObject', (table) => { + table.uuid('id').notNullable().primary(); + table.string('field', undefined).nullable(); + table.uuid('anotherId').notNullable(); + table.foreign('anotherId').references('id').inTable('AnotherObject'); + table.decimal('list', 1, 1).notNullable(); + table.integer('xyz').notNullable(); + table.timestamp('createdAt').notNullable(); + table.uuid('createdById').notNullable(); + table.foreign('createdById').references('id').inTable('User'); + table.timestamp('updatedAt').notNullable(); + table.uuid('updatedById').notNullable(); + table.foreign('updatedById').references('id').inTable('User'); + table.boolean('deleted').notNullable().defaultTo(false); + table.timestamp('deletedAt').nullable(); + table.uuid('deletedById').nullable(); + table.foreign('deletedById').references('id').inTable('User'); + }); + + await knex.schema.createTable('SomeObjectRevision', (table) => { + table.uuid('id').notNullable().primary(); + table.uuid('someObjectId').notNullable(); + table.uuid('createdById').notNullable(); + table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now(0)); + table.boolean('deleted').notNullable(); + table.integer('xyz').notNullable(); + }); +}; diff --git a/tests/utils/database/seed.ts b/tests/utils/database/seed.ts new file mode 100644 index 0000000..d9b3330 --- /dev/null +++ b/tests/utils/database/seed.ts @@ -0,0 +1,53 @@ +import { Knex } from 'knex'; +import { DateTime } from 'luxon'; +import { summonByName } from '../../../src'; +import { models } from '../models'; + +export const ADMIN_ID = '04e45b48-04cf-4b38-bb25-b9af5ae0b2c4'; + +export const SOME_ID = '604ab55d-ec3e-4857-9f27-219158f80e64'; +export const ANOTHER_ID = '226a20e8-5c18-4423-99ca-eb0df6ff4fdd'; + +export const seed = { + User: [ + { + id: ADMIN_ID, + username: 'admin', + role: 'ADMIN', + }, + ], + AnotherObject: [ + { + id: ANOTHER_ID, + }, + ], + SomeObject: [ + { + id: SOME_ID, + anotherId: ANOTHER_ID, + list: 0, + xyz: 1, + }, + ], +}; + +export const setupSeed = async (knex: Knex) => { + const now = DateTime.now(); + for (const [table, entities] of Object.entries(seed)) { + const model = summonByName(models, table); + await knex.batchInsert( + table, + entities.map((entity, i) => ({ + ...entity, + ...(model.creatable && { + createdAt: now.plus({ second: i }), + createdById: ADMIN_ID, + }), + ...(model.updatable && { + updatedAt: now.plus({ second: i }), + updatedById: ADMIN_ID, + }), + })) + ); + } +}; diff --git a/tests/utils/generate-migration.ts b/tests/utils/generate-migration.ts new file mode 100644 index 0000000..c760c9f --- /dev/null +++ b/tests/utils/generate-migration.ts @@ -0,0 +1,35 @@ +import { writeFileSync } from 'fs'; +import { simpleGit } from 'simple-git'; +import { MigrationGenerator } from '../../src/migrations/generate'; +import { getKnex } from './database/knex'; +import { rawModels } from './models'; + +const git = simpleGit(); + +const getDate = () => { + const date = new Date(); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + + return `${year}${month}${day}${hours}${minutes}${seconds}`; +}; + +const writeMigration = async () => { + const name = process.argv[2] || (await git.branch()).current.split('/').pop(); + + const knex = getKnex(); + + try { + const migrations = await new MigrationGenerator(knex, rawModels).generate(); + + writeFileSync(`tmp/${getDate()}_${name}.ts`, migrations); + } finally { + await knex.destroy(); + } +}; + +void writeMigration(); diff --git a/tests/unit/utils.ts b/tests/utils/models.ts similarity index 90% rename from tests/unit/utils.ts rename to tests/utils/models.ts index 243eb18..f579148 100644 --- a/tests/unit/utils.ts +++ b/tests/utils/models.ts @@ -8,6 +8,11 @@ export const rawModels: RawModels = [ type: 'enum', values: ['A', 'B', 'C'], }, + { + name: 'Role', + type: 'enum', + values: ['ADMIN', 'USER'], + }, { name: 'SomeRawObject', @@ -23,8 +28,17 @@ export const rawModels: RawModels = [ name: 'username', type: 'String', }, + { + name: 'role', + type: 'Role', + }, ], }, + { + type: 'object', + name: 'AnotherObject', + fields: [], + }, { type: 'object', name: 'SomeObject', @@ -51,6 +65,8 @@ export const rawModels: RawModels = [ { name: 'list', type: 'Float', + scale: 1, + precision: 1, nonNull: true, list: true, args: [{ name: 'magic', type: 'Boolean' }], @@ -66,11 +82,6 @@ export const rawModels: RawModels = [ }, ], }, - { - type: 'object', - name: 'AnotherObject', - fields: [], - }, ]; export const models = getModels(rawModels); diff --git a/tests/utils/server.ts b/tests/utils/server.ts new file mode 100644 index 0000000..a396bd2 --- /dev/null +++ b/tests/utils/server.ts @@ -0,0 +1,108 @@ +import { makeExecutableSchema } from '@graphql-tools/schema'; +import { execute, parse, Source, TypedQueryDocumentNode } from 'graphql'; +import graphqlRequest, { RequestDocument, Variables } from 'graphql-request'; +import { createServer, RequestListener } from 'http'; +import { Knex } from 'knex'; +import { DateTime } from 'luxon'; +import { Context } from '../../src/context'; +import { generate } from '../../src/generate'; +import { getResolvers } from '../../src/resolvers'; +import { getKnex } from './database/knex'; +import { setupSchema } from './database/schema'; +import { ADMIN_ID, setupSeed } from './database/seed'; +import { models, permissions, rawModels } from './models'; + +const MIN_PORT = 49152; +const MAX_PORT = 65535; + +export const withServer = async ( + cb: ( + request: (document: RequestDocument | TypedQueryDocumentNode, ...variablesAndRequestHeaders: any) => Promise, + knex: Knex + ) => Promise +) => { + // eslint-disable-next-line prefer-const + let handler: RequestListener; + let port: number; + const server = createServer((req, res) => handler(req, res)); + + // eslint-disable-next-line no-constant-condition + while (true) { + try { + port = Math.floor(Math.random() * (MAX_PORT - MIN_PORT + 1)) + MIN_PORT; + await new Promise((res, rej) => server.listen(port, res).once('error', rej)); + break; + } catch (e) { + console.error(e); + } + } + + const rootKnex = getKnex(); + const dbName = `test_${port}`; + await rootKnex.raw('DROP DATABASE IF EXISTS ?? WITH (FORCE)', dbName); + await rootKnex.raw(`CREATE DATABASE ?? OWNER ??`, [dbName, 'postgres']); + + const knex = getKnex(dbName); + + try { + await setupSchema(knex); + await setupSeed(knex); + + handler = async (req, res) => { + const user = await knex('User').where({ id: ADMIN_ID }).first(); + + const typeDefs = generate(rawModels); + const contextValue: Context = { + req, + knex, + document: typeDefs, + locale: 'en', + locales: ['en'], + user, + rawModels, + models, + permissions, + now: DateTime.fromISO('2020-01-01T00:00:00.000Z'), + }; + const { + query, + variables: variableValues, + operationName, + } = await new Promise((res) => { + const chunks: any = []; + req + .on('data', (chunk) => { + chunks.push(chunk); + }) + .on('end', () => { + res(JSON.parse(Buffer.concat(chunks).toString())); + }); + }); + const result = await execute({ + schema: makeExecutableSchema({ + typeDefs, + resolvers: getResolvers(models), + }), + document: parse(new Source(query, 'GraphQL request')), + contextValue, + variableValues, + operationName, + }); + + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify(result)); + }; + + const request = ( + document: RequestDocument | TypedQueryDocumentNode, + ...variablesAndRequestHeaders: any + ) => graphqlRequest(`http://localhost:${port}`, document, ...variablesAndRequestHeaders); + + await cb(request, knex); + } finally { + server.close(); + await knex.destroy(); + await rootKnex.raw('DROP DATABASE IF EXISTS ?? WITH (FORCE)', dbName); + await rootKnex.destroy(); + } +}; diff --git a/tsconfig.json b/tsconfig.json index 97522e6..da189e3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,13 @@ { - "include": ["./src/**/*.ts", "./src/**/*.tsx"], + "include": ["./src/**/*.ts", "./src/**/*.tsx", "./tests/**/*.ts"], "exclude": ["node_modules", "dist"], "compilerOptions": { "outDir": "./dist/esm", - "rootDir": "./src", + "rootDir": "./", "sourceMap": true, "declaration": true, "target": "esnext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true - } + }, } From 8ea1d9c7a6a04b5bac1713771457e1f6d7c26c72 Mon Sep 17 00:00:00 2001 From: Nicola Marcacci Rossi Date: Wed, 9 Aug 2023 09:36:28 +0200 Subject: [PATCH 2/2] fixes --- .github/workflows/testing.yml | 4 +++- package.json | 5 +++-- tests/{ => api}/__snapshots__/query.spec.ts.snap | 0 tests/{ => api}/query.spec.ts | 6 +++--- tests/{ => unit}/__snapshots__/generate.spec.ts.snap | 0 tests/{ => unit}/__snapshots__/resolve.spec.ts.snap | 0 tests/{ => unit}/generate.spec.ts | 4 ++-- tests/{ => unit}/resolve.spec.ts | 10 +++++----- tsconfig.json | 4 ++-- 9 files changed, 18 insertions(+), 15 deletions(-) rename tests/{ => api}/__snapshots__/query.spec.ts.snap (100%) rename tests/{ => api}/query.spec.ts (80%) rename tests/{ => unit}/__snapshots__/generate.spec.ts.snap (100%) rename tests/{ => unit}/__snapshots__/resolve.spec.ts.snap (100%) rename tests/{ => unit}/generate.spec.ts (59%) rename tests/{ => unit}/resolve.spec.ts (91%) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 233c43d..0a2265a 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -23,7 +23,9 @@ jobs: with: node-version: ${{ matrix.version }} - run: npm ci - - run: npm test + - run: npm run lint + - run: npm run test:unit + - run: npm run build test_all: if: ${{ always() }} diff --git a/package.json b/package.json index 79fe59a..0b51764 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,9 @@ "lint": "eslint src", "lint:fix": "eslint src --fix", "deps": "docker-compose up", - "test": "npm run lint && npm run test:api && npm run build", - "test:api": "jest tests --no-cache --no-watchman", + "test": "npm run lint && npm run test:unit && npm run test:api && npm run build", + "test:unit": "jest tests/unit --no-cache --no-watchman", + "test:api": "jest tests/api --no-cache --no-watchman", "generate-migration": "esbuild tests/utils/generate-migration.ts --bundle --platform=node --outdir=tmp --out-extension:.js=.cjs --format=cjs --packages=external && node tmp/generate-migration.cjs", "clean": "del-cli dist/**", "prebuild": "npm run clean", diff --git a/tests/__snapshots__/query.spec.ts.snap b/tests/api/__snapshots__/query.spec.ts.snap similarity index 100% rename from tests/__snapshots__/query.spec.ts.snap rename to tests/api/__snapshots__/query.spec.ts.snap diff --git a/tests/query.spec.ts b/tests/api/query.spec.ts similarity index 80% rename from tests/query.spec.ts rename to tests/api/query.spec.ts index 7738077..148ff7e 100644 --- a/tests/query.spec.ts +++ b/tests/api/query.spec.ts @@ -1,6 +1,6 @@ -import { gql } from '../src'; -import { ANOTHER_ID, SOME_ID } from './utils/database/seed'; -import { withServer } from './utils/server'; +import { gql } from '../../src'; +import { ANOTHER_ID, SOME_ID } from '../utils/database/seed'; +import { withServer } from '../utils/server'; describe('query', () => { it('can be executed', async () => { diff --git a/tests/__snapshots__/generate.spec.ts.snap b/tests/unit/__snapshots__/generate.spec.ts.snap similarity index 100% rename from tests/__snapshots__/generate.spec.ts.snap rename to tests/unit/__snapshots__/generate.spec.ts.snap diff --git a/tests/__snapshots__/resolve.spec.ts.snap b/tests/unit/__snapshots__/resolve.spec.ts.snap similarity index 100% rename from tests/__snapshots__/resolve.spec.ts.snap rename to tests/unit/__snapshots__/resolve.spec.ts.snap diff --git a/tests/generate.spec.ts b/tests/unit/generate.spec.ts similarity index 59% rename from tests/generate.spec.ts rename to tests/unit/generate.spec.ts index 15829cd..c3b0111 100644 --- a/tests/generate.spec.ts +++ b/tests/unit/generate.spec.ts @@ -1,5 +1,5 @@ -import { printSchemaFromModels } from '../src'; -import { rawModels } from './utils/models'; +import { printSchemaFromModels } from '../../src'; +import { rawModels } from '../utils/models'; describe('generate', () => { it('generates a schema', () => { diff --git a/tests/resolve.spec.ts b/tests/unit/resolve.spec.ts similarity index 91% rename from tests/resolve.spec.ts rename to tests/unit/resolve.spec.ts index 02e3934..711db4d 100644 --- a/tests/resolve.spec.ts +++ b/tests/unit/resolve.spec.ts @@ -2,11 +2,11 @@ import { makeExecutableSchema } from '@graphql-tools/schema'; import { execute, parse, Source } from 'graphql'; import knex from 'knex'; import { DateTime } from 'luxon'; -import { gql } from '../src/client/gql'; -import { Context } from '../src/context'; -import { generate } from '../src/generate'; -import { getResolvers } from '../src/resolvers'; -import { models, permissions, rawModels } from './utils/models'; +import { gql } from '../../src/client/gql'; +import { Context } from '../../src/context'; +import { generate } from '../../src/generate'; +import { getResolvers } from '../../src/resolvers'; +import { models, permissions, rawModels } from '../utils/models'; const test = async (operationName: string, query: string, variableValues: object, responses: unknown[]) => { const knexInstance = knex({ diff --git a/tsconfig.json b/tsconfig.json index da189e3..d8d4aee 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,9 @@ { - "include": ["./src/**/*.ts", "./src/**/*.tsx", "./tests/**/*.ts"], + "include": ["./src/**/*.ts", "./src/**/*.tsx"], "exclude": ["node_modules", "dist"], "compilerOptions": { "outDir": "./dist/esm", - "rootDir": "./", + "rootDir": "./src", "sourceMap": true, "declaration": true, "target": "esnext",