From a2479efe75298aa849a8784a27e8645e66fe1fd1 Mon Sep 17 00:00:00 2001 From: VITOR PEREIRA Date: Mon, 8 Jul 2024 08:54:32 +0000 Subject: [PATCH] makes `npm test` more robust with `async-retry` and `orchestrator.js` --- jest.config.js | 2 + package-lock.json | 133 ++++++++++++++++++ package.json | 6 +- .../integration/api/v1/migrations/get.test.js | 10 +- .../api/v1/migrations/post.test.js | 10 +- tests/integration/api/v1/status/get.test.js | 6 + tests/orchestrator.js | 24 ++++ 7 files changed, 179 insertions(+), 12 deletions(-) create mode 100644 tests/orchestrator.js diff --git a/jest.config.js b/jest.config.js index 3bfd689..8956645 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,8 +8,10 @@ const nextJest = require('next/jest'); const createJestConfig = nextJest({ dir: ".", }); + const jestConfig = createJestConfig({ moduleDirectories: ["node_modules", ""], + testTimeout: 60000, }); module.exports = jestConfig \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a3e54da..e886f77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "async-retry": "^1.3.3", "dotenv": "^16.4.4", "dotenv-expand": "^11.0.6", "next": "^13.1.6", @@ -18,6 +19,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { + "concurrently": "^8.2.2", "jest": "^29.6.2", "prettier": "^2.8.8" } @@ -608,6 +610,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", @@ -1512,6 +1526,14 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "dependencies": { + "retry": "0.13.1" + } + }, "node_modules/babel-jest": { "version": "29.6.2", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.2.tgz", @@ -1826,6 +1848,48 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1846,6 +1910,22 @@ "node": ">= 8" } }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3070,6 +3150,12 @@ "node": ">=8" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3779,6 +3865,12 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -3834,6 +3926,23 @@ "node": ">=10" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -3872,6 +3981,15 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -3920,6 +4038,12 @@ "source-map": "^0.6.0" } }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -4100,6 +4224,15 @@ "node": ">=8.0" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", diff --git a/package.json b/package.json index 6d7524f..eb559d1 100644 --- a/package.json +++ b/package.json @@ -10,15 +10,16 @@ "services:down": "docker compose -f infra/compose.yaml down", "lint:check": "prettier --check .", "lint:fix": "prettier --write .", - "test": "jest --runInBand", + "test": "npm run services:up && concurrently -n next,jest --hide next -k -s command-jest \"next dev\" \"jest --runInBand\"", "test:watch": "jest --watchAll --runInBand", "migration:create": "node-pg-migrate -m infra/migrations create", "migration:up": "node-pg-migrate -m infra/migrations --envPath .env.development up", - "wait-for-postgres":"node infra/scripts/wait-for-postgres.js" + "wait-for-postgres": "node infra/scripts/wait-for-postgres.js" }, "author": "", "license": "MIT", "dependencies": { + "async-retry": "^1.3.3", "dotenv": "^16.4.4", "dotenv-expand": "^11.0.6", "next": "^13.1.6", @@ -28,6 +29,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { + "concurrently": "^8.2.2", "jest": "^29.6.2", "prettier": "^2.8.8" } diff --git a/tests/integration/api/v1/migrations/get.test.js b/tests/integration/api/v1/migrations/get.test.js index 0b45bdb..a3c918e 100644 --- a/tests/integration/api/v1/migrations/get.test.js +++ b/tests/integration/api/v1/migrations/get.test.js @@ -1,10 +1,10 @@ import database from 'infra/database.js' +import orchestrator from "tests/orchestrator"; -beforeAll(cleanDatabase); - -async function cleanDatabase(){ - await database.query("drop schema public cascade; create schema public"); -} +beforeAll(async () =>{ + await orchestrator.waitForAllServices() + await database.query("drop schema public cascade; create schema public"); +}) test("GET to /api/v1/migrations should return 200", async () => { const response = await fetch("http://localhost:3000/api/v1/migrations"); diff --git a/tests/integration/api/v1/migrations/post.test.js b/tests/integration/api/v1/migrations/post.test.js index 509c242..dbba31c 100644 --- a/tests/integration/api/v1/migrations/post.test.js +++ b/tests/integration/api/v1/migrations/post.test.js @@ -1,10 +1,10 @@ import database from 'infra/database.js' +import orchestrator from "tests/orchestrator"; -beforeAll(cleanDatabase); - -async function cleanDatabase(){ - await database.query("drop schema public cascade; create schema public"); -} +beforeAll(async () =>{ + await orchestrator.waitForAllServices() + await database.query("drop schema public cascade; create schema public"); +}) test("POST to /api/v1/migrations should return 200", async () => { const response1 = await fetch("http://localhost:3000/api/v1/migrations", { diff --git a/tests/integration/api/v1/status/get.test.js b/tests/integration/api/v1/status/get.test.js index 324f4d7..2d29e29 100644 --- a/tests/integration/api/v1/status/get.test.js +++ b/tests/integration/api/v1/status/get.test.js @@ -1,3 +1,9 @@ +import orchestrator from "tests/orchestrator"; + +beforeAll(async () =>{ + await orchestrator.waitForAllServices() +}) + test("GET to /api/v1/status should return 200", async () => { const response = await fetch("http://localhost:3000/api/v1/status"); expect(response.status).toBe(200); diff --git a/tests/orchestrator.js b/tests/orchestrator.js new file mode 100644 index 0000000..f464ae5 --- /dev/null +++ b/tests/orchestrator.js @@ -0,0 +1,24 @@ +import retry from "async-retry" + +async function waitForAllServices(){ + await waitForWebServer(); + + async function waitForWebServer(){ + return retry(fetchStatusPage, { + retries:100, + maxTimeout: 1000 + }) + } + + async function fetchStatusPage(bail, tryNumber){ + const response = await fetch("http://localhost:3000/api/v1/status"); + + if(response.status !== 200){ + throw Error(); + } + } +} + +export default { + waitForAllServices +} \ No newline at end of file