From 382d916f36035ad9e68069802c26b6beb3712cb1 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Tue, 18 Jul 2023 12:17:18 +0100 Subject: [PATCH 01/15] test(remix): Test Remix SDK on Node 20. --- .github/workflows/build.yml | 2 +- .../node-integration-tests/utils/index.ts | 32 ++++++++++++++----- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5523a861eee..0c024683bd4e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -706,7 +706,7 @@ jobs: strategy: fail-fast: false matrix: - node: [14, 16, 18] + node: [14, 16, 18, 20] remix: [1, 2] steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index 88120853ee69..467dc4d2179c 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -10,6 +10,8 @@ import type { AddressInfo } from 'net'; import nock from 'nock'; import * as path from 'path'; +const NODE_VERSION = parseSemver(process.versions.node).major; + export type TestServerConfig = { url: string; server: http.Server; @@ -40,7 +42,6 @@ export type DataCollectorOptions = { * @return {*} {jest.Describe} */ export const conditionalTest = (allowedVersion: { min?: number; max?: number }): jest.Describe => { - const NODE_VERSION = parseSemver(process.versions.node).major; if (!NODE_VERSION) { return describe.skip; } @@ -127,6 +128,16 @@ export class TestEnv { public constructor(public readonly server: http.Server, public readonly url: string) { this.server = server; this.url = url; + + // We need to destroy the socket after the response has been sent + // to prevent the server.close (called inside nock interceptor) from hanging in tests. + // Otherwise the tests may timeout. (Happening on Node 20) + // See: https://github.com/nodejs/node/issues/2642 + this.server.on('request', (req, res) => { + res.on('finish', () => { + req.socket.end(); + }); + }); } /** @@ -244,9 +255,7 @@ export class TestEnv { // Ex: Remix scope bleed tests. nock.cleanAll(); - this.server.close(() => { - resolve(envelopes); - }); + this._closeServer(); } resolve(envelopes); @@ -291,12 +300,19 @@ export class TestEnv { nock.cleanAll(); - this.server.close(() => { - resolve(reqCount); - }); - + this._closeServer(); resolve(reqCount); }, options.timeout || 1000); }); } + + private _closeServer(): void { + this.server.close(() => { + // @ts-ignore closeAllConnections() is only available from Node v18.2.0 + if (NODE_VERSION >= 18 && this.server.closeAllConnections) { + // @ts-ignore (Only available in Node 18+) + this.server.closeAllConnections(); + } + }); + } } From 1b8f3a4e9af7d6544296f0f49aaf2aed5399ac3a Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Tue, 18 Jul 2023 16:25:56 +0100 Subject: [PATCH 02/15] Increase timeout to 10 seconds. --- packages/remix/test/integration/jest.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/remix/test/integration/jest.config.js b/packages/remix/test/integration/jest.config.js index d3173100f47f..f571a906f5e3 100644 --- a/packages/remix/test/integration/jest.config.js +++ b/packages/remix/test/integration/jest.config.js @@ -6,4 +6,5 @@ module.exports = { testPathIgnorePatterns: [`${__dirname}/test/client`], detectOpenHandles: true, forceExit: true, + testTimeout: 10000, }; From f7f73a5805ae08d12ee5752c3c9b6c4b751d6a2b Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Tue, 18 Jul 2023 17:08:35 +0100 Subject: [PATCH 03/15] Resolve after server is closed. --- .../node-integration-tests/utils/index.ts | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index 467dc4d2179c..fb3cc7cbf708 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -255,10 +255,12 @@ export class TestEnv { // Ex: Remix scope bleed tests. nock.cleanAll(); - this._closeServer(); + void this._closeServer().then(() => { + resolve(envelopes); + }); + } else { + resolve(envelopes); } - - resolve(envelopes); } return true; @@ -300,19 +302,24 @@ export class TestEnv { nock.cleanAll(); - this._closeServer(); - resolve(reqCount); + void this._closeServer().then(() => { + resolve(reqCount); + }); }, options.timeout || 1000); }); } - private _closeServer(): void { - this.server.close(() => { - // @ts-ignore closeAllConnections() is only available from Node v18.2.0 - if (NODE_VERSION >= 18 && this.server.closeAllConnections) { - // @ts-ignore (Only available in Node 18+) - this.server.closeAllConnections(); - } + private _closeServer(): Promise { + return new Promise(resolve => { + this.server.close(() => { + // @ts-ignore closeAllConnections() is only available from Node v18.2.0 + if (NODE_VERSION >= 18 && this.server.closeAllConnections) { + // @ts-ignore (Only available in Node 18+) + this.server.closeAllConnections(); + } + + resolve(); + }); }); } } From ec6b8b13b9bd83cf7b5245d4e1f578361e5e4df5 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Wed, 19 Jul 2023 12:16:46 +0100 Subject: [PATCH 04/15] Increase `statusText` test timeouts. --- packages/remix/test/integration/test/server/action.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/remix/test/integration/test/server/action.test.ts b/packages/remix/test/integration/test/server/action.test.ts index a2a4632ba962..3b36557c5210 100644 --- a/packages/remix/test/integration/test/server/action.test.ts +++ b/packages/remix/test/integration/test/server/action.test.ts @@ -263,7 +263,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada ], }, }); - }); + }, 30000); it('handles a thrown `json()` error response without `statusText`', async () => { const env = await RemixTestEnv.init(adapter); @@ -318,7 +318,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada ], }, }); - }); + }, 30000); it('handles a thrown `json()` error response with string body', async () => { const env = await RemixTestEnv.init(adapter); From 1eca3e2af57268fd19788beb4585eb0cde5819f7 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Wed, 19 Jul 2023 15:53:15 +0000 Subject: [PATCH 05/15] Abort pending nock requests to prevent flakes. --- packages/node-integration-tests/utils/index.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index fb3cc7cbf708..92d1204a2ec7 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -255,9 +255,17 @@ export class TestEnv { // Ex: Remix scope bleed tests. nock.cleanAll(); - void this._closeServer().then(() => { - resolve(envelopes); - }); + // Abort all pending requests to nock to prevent hanging / flakes. + // See: https://github.com/nock/nock/issues/1118#issuecomment-544126948 + nock.abortPendingRequests(); + + this._closeServer() + .catch(e => { + logger.warn(e); + }) + .finally(() => { + resolve(envelopes); + }); } else { resolve(envelopes); } From 269862696e5bcffccd78155b47f6d01a920722ab Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Wed, 19 Jul 2023 16:22:01 +0000 Subject: [PATCH 06/15] Unref test server on close. --- packages/node-integration-tests/utils/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index 92d1204a2ec7..3215cb5eea29 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -326,6 +326,10 @@ export class TestEnv { this.server.closeAllConnections(); } + if (this.server.listening) { + this.server.unref(); + } + resolve(); }); }); From 26c31bd8cc675c995baf22f74f62066869c4b24e Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Wed, 19 Jul 2023 22:44:33 +0000 Subject: [PATCH 07/15] Revert increased timeouts. --- packages/remix/test/integration/test/server/action.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/remix/test/integration/test/server/action.test.ts b/packages/remix/test/integration/test/server/action.test.ts index 3b36557c5210..a2a4632ba962 100644 --- a/packages/remix/test/integration/test/server/action.test.ts +++ b/packages/remix/test/integration/test/server/action.test.ts @@ -263,7 +263,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada ], }, }); - }, 30000); + }); it('handles a thrown `json()` error response without `statusText`', async () => { const env = await RemixTestEnv.init(adapter); @@ -318,7 +318,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada ], }, }); - }, 30000); + }); it('handles a thrown `json()` error response with string body', async () => { const env = await RemixTestEnv.init(adapter); From 88e7ed21594742847a85bc1b0e693231b395f91e Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Wed, 19 Jul 2023 22:45:17 +0000 Subject: [PATCH 08/15] Run server-side tests in band. --- packages/remix/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remix/package.json b/packages/remix/package.json index 58bca014a549..2422a55c5a9e 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -76,7 +76,7 @@ "test:integration:clean": "(cd test/integration && rimraf .cache node_modules build)", "test:integration:client": "yarn playwright install-deps && yarn playwright test test/integration/test/client/", "test:integration:client:ci": "yarn test:integration:client --browser='all' --reporter='line'", - "test:integration:server": "export NODE_OPTIONS='--stack-trace-limit=25' && jest --config=test/integration/jest.config.js test/integration/test/server/", + "test:integration:server": "export NODE_OPTIONS='--stack-trace-limit=25' && jest --runInBand --config=test/integration/jest.config.js test/integration/test/server/", "test:unit": "jest", "test:watch": "jest --watch", "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" From c1de6fcf5744762140a3fa42b38aeb9d55ec08ed Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 20 Jul 2023 00:47:41 +0000 Subject: [PATCH 09/15] Emit 'close' event from server. --- packages/node-integration-tests/utils/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index 3215cb5eea29..2527fc18274c 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -332,6 +332,8 @@ export class TestEnv { resolve(); }); + + setImmediate(() => this.server.emit('close')); }); } } From 91ea12019d689aa89952dd7173be475de3d91283 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 20 Jul 2023 08:37:16 +0000 Subject: [PATCH 10/15] Use `http-terminator` for complete server termination. --- packages/node-integration-tests/package.json | 1 + .../node-integration-tests/utils/index.ts | 19 +--- yarn.lock | 89 ++++++++++++++++++- 3 files changed, 89 insertions(+), 20 deletions(-) diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 4d43ca35883d..5e7127f1adb5 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -31,6 +31,7 @@ "cors": "^2.8.5", "express": "^4.17.3", "graphql": "^16.3.0", + "http-terminator": "^3.2.0", "mongodb": "^3.7.3", "mongodb-memory-server-global": "^7.6.3", "mysql": "^2.18.1", diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index 2527fc18274c..abd5c04a688a 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -6,6 +6,7 @@ import type { AxiosRequestConfig } from 'axios'; import axios from 'axios'; import type { Express } from 'express'; import type * as http from 'http'; +import { createHttpTerminator } from 'http-terminator'; import type { AddressInfo } from 'net'; import nock from 'nock'; import * as path from 'path'; @@ -318,22 +319,6 @@ export class TestEnv { } private _closeServer(): Promise { - return new Promise(resolve => { - this.server.close(() => { - // @ts-ignore closeAllConnections() is only available from Node v18.2.0 - if (NODE_VERSION >= 18 && this.server.closeAllConnections) { - // @ts-ignore (Only available in Node 18+) - this.server.closeAllConnections(); - } - - if (this.server.listening) { - this.server.unref(); - } - - resolve(); - }); - - setImmediate(() => this.server.emit('close')); - }); + return createHttpTerminator({ server: this.server }).terminate(); } } diff --git a/yarn.lock b/yarn.lock index 197a789d7ceb..4c7f3f22025a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6104,7 +6104,7 @@ ajv@8.6.2: require-from-string "^2.0.2" uri-js "^4.2.2" -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.11.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -7853,6 +7853,11 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +boolean@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" + integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== + bower-config@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/bower-config/-/bower-config-1.4.3.tgz#3454fecdc5f08e7aa9cc6d556e492be0669689ae" @@ -10798,6 +10803,11 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" +delay@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" + integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -13129,6 +13139,16 @@ fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@2.x, fast-json-stab resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-json-stringify@^2.7.10: + version "2.7.13" + resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-2.7.13.tgz#277aa86c2acba4d9851bd6108ed657aa327ed8c0" + integrity sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA== + dependencies: + ajv "^6.11.0" + deepmerge "^4.2.2" + rfdc "^1.2.0" + string-similarity "^4.0.1" + fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -13141,6 +13161,13 @@ fast-ordered-set@^1.0.0: dependencies: blank-object "^1.0.1" +fast-printf@^1.6.9: + version "1.6.9" + resolved "https://registry.yarnpkg.com/fast-printf/-/fast-printf-1.6.9.tgz#212f56570d2dc8ccdd057ee93d50dd414d07d676" + integrity sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg== + dependencies: + boolean "^3.1.4" + fast-sourcemap-concat@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-sourcemap-concat/-/fast-sourcemap-concat-2.1.0.tgz#12dd36bfc38c804093e4bd1de61dd6216f574211" @@ -14323,6 +14350,13 @@ globals@^9.18.0: resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== +globalthis@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + globalyzer@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" @@ -15104,6 +15138,16 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http-terminator@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/http-terminator/-/http-terminator-3.2.0.tgz#bc158d2694b733ca4fbf22a35065a81a609fb3e9" + integrity sha512-JLjck1EzPaWjsmIf8bziM3p9fgR1Y3JoUKAkyYEbZmFrIvJM6I8vVJfBGWlEtV9IWOvzNnaTtjuwZeBY2kwB4g== + dependencies: + delay "^5.0.0" + p-wait-for "^3.2.0" + roarr "^7.0.4" + type-fest "^2.3.3" + https-browserify@1.0.0, https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -20607,7 +20651,7 @@ p-timeout@^2.0.1: dependencies: p-finally "^1.0.0" -p-timeout@^3.1.0, p-timeout@^3.2.0: +p-timeout@^3.0.0, p-timeout@^3.1.0, p-timeout@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== @@ -20629,6 +20673,13 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +p-wait-for@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-wait-for/-/p-wait-for-3.2.0.tgz#640429bcabf3b0dd9f492c31539c5718cb6a3f1f" + integrity sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA== + dependencies: + p-timeout "^3.0.0" + p-waterfall@2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" @@ -23753,7 +23804,7 @@ rework@1.0.1: convert-source-map "^0.3.3" css "^2.0.0" -rfdc@^1.1.4, rfdc@^1.3.0: +rfdc@^1.1.4, rfdc@^1.2.0, rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== @@ -23811,6 +23862,18 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +roarr@^7.0.4: + version "7.15.0" + resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.15.0.tgz#09b792f0cd31b4a7f91030bb1c47550ceec98ee4" + integrity sha512-CV9WefQfUXTX6wr8CrEMhfNef3sjIt9wNhE/5PNu4tNWsaoDNDXqq+OGn/RW9A1UPb0qc7FQlswXRaJJJsqn8A== + dependencies: + boolean "^3.1.4" + fast-json-stringify "^2.7.10" + fast-printf "^1.6.9" + globalthis "^1.0.2" + safe-stable-stringify "^2.4.1" + semver-compare "^1.0.0" + rollup-plugin-cleanup@3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/rollup-plugin-cleanup/-/rollup-plugin-cleanup-3.2.1.tgz#8cbc92ecf58babd7c210051929797f137bbf777c" @@ -23999,6 +24062,11 @@ safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz#34694bd8a30575b7f94792aa51527551bd733d61" integrity sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA== +safe-stable-stringify@^2.4.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -24182,6 +24250,11 @@ selfsigned@^1.10.7, selfsigned@^1.10.8: dependencies: node-forge "^0.10.0" +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== + semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -25268,6 +25341,11 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-similarity@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b" + integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ== + string-template@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" @@ -26598,6 +26676,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^2.3.3: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" From bc9e21092e4af9d2d781118a26657400ccae514b Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 20 Jul 2023 08:37:53 +0000 Subject: [PATCH 11/15] Use `portfinder` as late as possible. --- .../test/integration/test/server/utils/helpers.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/remix/test/integration/test/server/utils/helpers.ts b/packages/remix/test/integration/test/server/utils/helpers.ts index a2cfedca0f94..3eaacd64f792 100644 --- a/packages/remix/test/integration/test/server/utils/helpers.ts +++ b/packages/remix/test/integration/test/server/utils/helpers.ts @@ -1,6 +1,6 @@ import express from 'express'; import { createRequestHandler } from '@remix-run/express'; -import { getPortPromise } from 'portfinder'; +import { getPort, getPortPromise } from 'portfinder'; import { wrapExpressCreateRequestHandler } from '@sentry/remix'; import { TestEnv } from '../../../../../../node-integration-tests/utils'; import * as http from 'http'; @@ -16,18 +16,20 @@ export class RemixTestEnv extends TestEnv { const requestHandlerFactory = adapter === 'express' ? wrapExpressCreateRequestHandler(createRequestHandler) : createRequestHandler; - const port = await getPortPromise(); - + let serverPort; const server = await new Promise(resolve => { const app = express(); app.all('*', requestHandlerFactory({ build: require('../../../build') })); - const server = app.listen(port, () => { - resolve(server); + getPort((_, port) => { + serverPort = port; + const server = app.listen(port, () => { + resolve(server); + }); }); }); - return new RemixTestEnv(server, `http://localhost:${port}`); + return new RemixTestEnv(server, `http://localhost:${serverPort}`); } } From b4534685bdd8f882c8acf25188089003776a24c5 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 20 Jul 2023 09:29:32 +0000 Subject: [PATCH 12/15] Update `http-terminator` usage. --- packages/node-integration-tests/utils/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index abd5c04a688a..1f4b530c3e77 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -6,6 +6,7 @@ import type { AxiosRequestConfig } from 'axios'; import axios from 'axios'; import type { Express } from 'express'; import type * as http from 'http'; +import type { HttpTerminator } from 'http-terminator'; import { createHttpTerminator } from 'http-terminator'; import type { AddressInfo } from 'net'; import nock from 'nock'; @@ -125,10 +126,12 @@ async function makeRequest( export class TestEnv { private _axiosConfig: AxiosRequestConfig | undefined = undefined; + private _terminator: HttpTerminator; public constructor(public readonly server: http.Server, public readonly url: string) { this.server = server; this.url = url; + this._terminator = createHttpTerminator({ server: this.server, gracefulTerminationTimeout: 0 }); // We need to destroy the socket after the response has been sent // to prevent the server.close (called inside nock interceptor) from hanging in tests. @@ -319,6 +322,6 @@ export class TestEnv { } private _closeServer(): Promise { - return createHttpTerminator({ server: this.server }).terminate(); + return this._terminator.terminate(); } } From 470c7cb2fce05d22ddd2c278fd43190e09cec7b8 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 20 Jul 2023 11:29:07 +0000 Subject: [PATCH 13/15] Remove `portfinder`. --- packages/remix/package.json | 5 ++--- .../remix/test/integration/test/server/action.test.ts | 6 +++--- .../test/integration/test/server/utils/helpers.ts | 10 ++++------ yarn.lock | 2 +- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/remix/package.json b/packages/remix/package.json index 2422a55c5a9e..c276be3943e6 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -39,8 +39,7 @@ "devDependencies": { "@remix-run/node": "^1.4.3", "@remix-run/react": "^1.4.3", - "@types/express": "^4.17.14", - "portfinder": "^1.0.28" + "@types/express": "^4.17.14" }, "peerDependencies": { "@remix-run/node": "1.x", @@ -69,7 +68,7 @@ "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "yarn test:unit", "test:integration": "run-s test:integration:v1 test:integration:v2", - "test:integration:v1": "run-s test:integration:clean test:integration:prepare test:integration:client test:integration:server", + "test:integration:v1": "run-s test:integration:prepare test:integration:client test:integration:server", "test:integration:v2": "export REMIX_VERSION=2 && run-s test:integration:v1", "test:integration:ci": "run-s test:integration:clean test:integration:prepare test:integration:client:ci test:integration:server", "test:integration:prepare": "(cd test/integration && yarn)", diff --git a/packages/remix/test/integration/test/server/action.test.ts b/packages/remix/test/integration/test/server/action.test.ts index a2a4632ba962..edf94f20e253 100644 --- a/packages/remix/test/integration/test/server/action.test.ts +++ b/packages/remix/test/integration/test/server/action.test.ts @@ -38,7 +38,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada cookies: expect.any(Object), headers: { 'user-agent': expect.any(String), - host: 'localhost:8000', + host: expect.stringContaining('localhost:'), }, }, }); @@ -114,7 +114,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada cookies: expect.any(Object), headers: { 'user-agent': expect.any(String), - host: 'localhost:8000', + host: expect.stringContaining('localhost:'), }, }, }); @@ -134,7 +134,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada cookies: expect.any(Object), headers: { 'user-agent': expect.any(String), - host: 'localhost:8000', + host: expect.stringContaining('localhost:'), }, }, }); diff --git a/packages/remix/test/integration/test/server/utils/helpers.ts b/packages/remix/test/integration/test/server/utils/helpers.ts index 3eaacd64f792..7042940d6c14 100644 --- a/packages/remix/test/integration/test/server/utils/helpers.ts +++ b/packages/remix/test/integration/test/server/utils/helpers.ts @@ -1,9 +1,9 @@ import express from 'express'; import { createRequestHandler } from '@remix-run/express'; -import { getPort, getPortPromise } from 'portfinder'; import { wrapExpressCreateRequestHandler } from '@sentry/remix'; import { TestEnv } from '../../../../../../node-integration-tests/utils'; import * as http from 'http'; +import { AddressInfo } from 'net'; export * from '../../../../../../node-integration-tests/utils'; @@ -22,11 +22,9 @@ export class RemixTestEnv extends TestEnv { app.all('*', requestHandlerFactory({ build: require('../../../build') })); - getPort((_, port) => { - serverPort = port; - const server = app.listen(port, () => { - resolve(server); - }); + const server = app.listen(0, () => { + serverPort = (server.address() as AddressInfo).port; + resolve(server); }); }); diff --git a/yarn.lock b/yarn.lock index 4c7f3f22025a..f188bedfcdf3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21328,7 +21328,7 @@ pnp-webpack-plugin@1.6.4, pnp-webpack-plugin@^1.6.4: dependencies: ts-pnp "^1.1.6" -portfinder@^1.0.26, portfinder@^1.0.28, portfinder@^1.0.29: +portfinder@^1.0.26, portfinder@^1.0.29: version "1.0.32" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg== From 6ae5375a49e32c3729f74160c9766d3b18c56d7e Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 20 Jul 2023 16:39:01 +0000 Subject: [PATCH 14/15] Cleanup --- packages/node-integration-tests/utils/index.ts | 10 ---------- packages/remix/package.json | 4 ++-- packages/remix/test/integration/jest.config.js | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index 1f4b530c3e77..3bca893c34b3 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -132,16 +132,6 @@ export class TestEnv { this.server = server; this.url = url; this._terminator = createHttpTerminator({ server: this.server, gracefulTerminationTimeout: 0 }); - - // We need to destroy the socket after the response has been sent - // to prevent the server.close (called inside nock interceptor) from hanging in tests. - // Otherwise the tests may timeout. (Happening on Node 20) - // See: https://github.com/nodejs/node/issues/2642 - this.server.on('request', (req, res) => { - res.on('finish', () => { - req.socket.end(); - }); - }); } /** diff --git a/packages/remix/package.json b/packages/remix/package.json index c276be3943e6..c5eb4a5e9cda 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -68,14 +68,14 @@ "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "yarn test:unit", "test:integration": "run-s test:integration:v1 test:integration:v2", - "test:integration:v1": "run-s test:integration:prepare test:integration:client test:integration:server", + "test:integration:v1": "run-s test:integration:clean test:integration:prepare test:integration:client test:integration:server", "test:integration:v2": "export REMIX_VERSION=2 && run-s test:integration:v1", "test:integration:ci": "run-s test:integration:clean test:integration:prepare test:integration:client:ci test:integration:server", "test:integration:prepare": "(cd test/integration && yarn)", "test:integration:clean": "(cd test/integration && rimraf .cache node_modules build)", "test:integration:client": "yarn playwright install-deps && yarn playwright test test/integration/test/client/", "test:integration:client:ci": "yarn test:integration:client --browser='all' --reporter='line'", - "test:integration:server": "export NODE_OPTIONS='--stack-trace-limit=25' && jest --runInBand --config=test/integration/jest.config.js test/integration/test/server/", + "test:integration:server": "export NODE_OPTIONS='--stack-trace-limit=25' && jest --config=test/integration/jest.config.js test/integration/test/server/", "test:unit": "jest", "test:watch": "jest --watch", "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" diff --git a/packages/remix/test/integration/jest.config.js b/packages/remix/test/integration/jest.config.js index f571a906f5e3..d3173100f47f 100644 --- a/packages/remix/test/integration/jest.config.js +++ b/packages/remix/test/integration/jest.config.js @@ -6,5 +6,4 @@ module.exports = { testPathIgnorePatterns: [`${__dirname}/test/client`], detectOpenHandles: true, forceExit: true, - testTimeout: 10000, }; From 5f6316b7c897dc33b0f34ea8055c0ef0ce8307d8 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Sat, 22 Jul 2023 11:13:41 +0000 Subject: [PATCH 15/15] Remove `portfinder` from NextJS tests. --- .github/workflows/build.yml | 2 +- .../test/integration/test/server/utils/helpers.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0c024683bd4e..438c7152defa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -430,7 +430,7 @@ jobs: strategy: fail-fast: false matrix: - node: [10, 12, 14, 16, 18] + node: [10, 12, 14, 16, 18, 20] steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v3 diff --git a/packages/nextjs/test/integration/test/server/utils/helpers.ts b/packages/nextjs/test/integration/test/server/utils/helpers.ts index c7213785493f..efc8c144eee2 100644 --- a/packages/nextjs/test/integration/test/server/utils/helpers.ts +++ b/packages/nextjs/test/integration/test/server/utils/helpers.ts @@ -1,10 +1,10 @@ -import { getPortPromise } from 'portfinder'; import { TestEnv } from '../../../../../../node-integration-tests/utils'; import * as http from 'http'; import * as path from 'path'; import { createServer, Server } from 'http'; import { parse } from 'url'; import next from 'next'; +import { AddressInfo } from 'net'; // Type not exported from NextJS // @ts-ignore @@ -24,9 +24,10 @@ export const createNextServer = async config => { }); }; -export const startServer = async (server: Server, port: string | number) => { - return new Promise(resolve => { - server.listen(port || 0, () => { +export const startServer = async (server: Server) => { + return new Promise<{ server: http.Server; url: string }>(resolve => { + server.listen(0, () => { + const port = (server.address() as AddressInfo).port; const url = `http://localhost:${port}`; resolve({ server, url }); }); @@ -39,7 +40,6 @@ export class NextTestEnv extends TestEnv { } public static async init(): Promise { - const port = await getPortPromise(); const server = await createNextServer({ dev: false, dir: path.resolve(__dirname, '../../..'), @@ -50,8 +50,8 @@ export class NextTestEnv extends TestEnv { conf: path.resolve(__dirname, '../../next.config.js'), }); - await startServer(server, port); + const { url } = await startServer(server); - return new NextTestEnv(server, `http://localhost:${port}`); + return new NextTestEnv(server, url); } }