Skip to content

Commit 0cdd472

Browse files
authored
chore(test): Test Remix and NextJS SDKs on Node 20. (#8577)
Made updates about test server termination. Integrated [`http-terminator`](https://www.npmjs.com/package/http-terminator) as closing all sockets / cancelling pending requests and such produced plenty of maintenance code. Also, found out that [`portfinder`](https://www.npmjs.com/package/portfinder) does not work well in Node 20, and causes flakiness. Removed it from Remix and NextJS tests, as it can be replaced with `app.listen(0, ...)`.
1 parent 07b9d2e commit 0cdd472

File tree

8 files changed

+130
-32
lines changed

8 files changed

+130
-32
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ jobs:
430430
strategy:
431431
fail-fast: false
432432
matrix:
433-
node: [10, 12, 14, 16, 18]
433+
node: [10, 12, 14, 16, 18, 20]
434434
steps:
435435
- name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }})
436436
uses: actions/checkout@v3
@@ -706,7 +706,7 @@ jobs:
706706
strategy:
707707
fail-fast: false
708708
matrix:
709-
node: [14, 16, 18]
709+
node: [14, 16, 18, 20]
710710
remix: [1, 2]
711711
steps:
712712
- name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }})

packages/nextjs/test/integration/test/server/utils/helpers.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { getPortPromise } from 'portfinder';
21
import { TestEnv } from '../../../../../../node-integration-tests/utils';
32
import * as http from 'http';
43
import * as path from 'path';
54
import { createServer, Server } from 'http';
65
import { parse } from 'url';
76
import next from 'next';
7+
import { AddressInfo } from 'net';
88

99
// Type not exported from NextJS
1010
// @ts-ignore
@@ -24,9 +24,10 @@ export const createNextServer = async config => {
2424
});
2525
};
2626

27-
export const startServer = async (server: Server, port: string | number) => {
28-
return new Promise(resolve => {
29-
server.listen(port || 0, () => {
27+
export const startServer = async (server: Server) => {
28+
return new Promise<{ server: http.Server; url: string }>(resolve => {
29+
server.listen(0, () => {
30+
const port = (server.address() as AddressInfo).port;
3031
const url = `http://localhost:${port}`;
3132
resolve({ server, url });
3233
});
@@ -39,7 +40,6 @@ export class NextTestEnv extends TestEnv {
3940
}
4041

4142
public static async init(): Promise<NextTestEnv> {
42-
const port = await getPortPromise();
4343
const server = await createNextServer({
4444
dev: false,
4545
dir: path.resolve(__dirname, '../../..'),
@@ -50,8 +50,8 @@ export class NextTestEnv extends TestEnv {
5050
conf: path.resolve(__dirname, '../../next.config.js'),
5151
});
5252

53-
await startServer(server, port);
53+
const { url } = await startServer(server);
5454

55-
return new NextTestEnv(server, `http://localhost:${port}`);
55+
return new NextTestEnv(server, url);
5656
}
5757
}

packages/node-integration-tests/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"cors": "^2.8.5",
3232
"express": "^4.17.3",
3333
"graphql": "^16.3.0",
34+
"http-terminator": "^3.2.0",
3435
"mongodb": "^3.7.3",
3536
"mongodb-memory-server-global": "^7.6.3",
3637
"mysql": "^2.18.1",

packages/node-integration-tests/utils/index.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ import type { AxiosRequestConfig } from 'axios';
66
import axios from 'axios';
77
import type { Express } from 'express';
88
import type * as http from 'http';
9+
import type { HttpTerminator } from 'http-terminator';
10+
import { createHttpTerminator } from 'http-terminator';
911
import type { AddressInfo } from 'net';
1012
import nock from 'nock';
1113
import * as path from 'path';
1214

15+
const NODE_VERSION = parseSemver(process.versions.node).major;
16+
1317
export type TestServerConfig = {
1418
url: string;
1519
server: http.Server;
@@ -40,7 +44,6 @@ export type DataCollectorOptions = {
4044
* @return {*} {jest.Describe}
4145
*/
4246
export const conditionalTest = (allowedVersion: { min?: number; max?: number }): jest.Describe => {
43-
const NODE_VERSION = parseSemver(process.versions.node).major;
4447
if (!NODE_VERSION) {
4548
return describe.skip;
4649
}
@@ -123,10 +126,12 @@ async function makeRequest(
123126

124127
export class TestEnv {
125128
private _axiosConfig: AxiosRequestConfig | undefined = undefined;
129+
private _terminator: HttpTerminator;
126130

127131
public constructor(public readonly server: http.Server, public readonly url: string) {
128132
this.server = server;
129133
this.url = url;
134+
this._terminator = createHttpTerminator({ server: this.server, gracefulTerminationTimeout: 0 });
130135
}
131136

132137
/**
@@ -244,12 +249,20 @@ export class TestEnv {
244249
// Ex: Remix scope bleed tests.
245250
nock.cleanAll();
246251

247-
this.server.close(() => {
248-
resolve(envelopes);
249-
});
252+
// Abort all pending requests to nock to prevent hanging / flakes.
253+
// See: https://github.com/nock/nock/issues/1118#issuecomment-544126948
254+
nock.abortPendingRequests();
255+
256+
this._closeServer()
257+
.catch(e => {
258+
logger.warn(e);
259+
})
260+
.finally(() => {
261+
resolve(envelopes);
262+
});
263+
} else {
264+
resolve(envelopes);
250265
}
251-
252-
resolve(envelopes);
253266
}
254267

255268
return true;
@@ -291,12 +304,14 @@ export class TestEnv {
291304

292305
nock.cleanAll();
293306

294-
this.server.close(() => {
307+
void this._closeServer().then(() => {
295308
resolve(reqCount);
296309
});
297-
298-
resolve(reqCount);
299310
}, options.timeout || 1000);
300311
});
301312
}
313+
314+
private _closeServer(): Promise<void> {
315+
return this._terminator.terminate();
316+
}
302317
}

packages/remix/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@
3939
"devDependencies": {
4040
"@remix-run/node": "^1.4.3",
4141
"@remix-run/react": "^1.4.3",
42-
"@types/express": "^4.17.14",
43-
"portfinder": "^1.0.28"
42+
"@types/express": "^4.17.14"
4443
},
4544
"peerDependencies": {
4645
"@remix-run/node": "1.x",

packages/remix/test/integration/test/server/action.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada
3838
cookies: expect.any(Object),
3939
headers: {
4040
'user-agent': expect.any(String),
41-
host: 'localhost:8000',
41+
host: expect.stringContaining('localhost:'),
4242
},
4343
},
4444
});
@@ -114,7 +114,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada
114114
cookies: expect.any(Object),
115115
headers: {
116116
'user-agent': expect.any(String),
117-
host: 'localhost:8000',
117+
host: expect.stringContaining('localhost:'),
118118
},
119119
},
120120
});
@@ -134,7 +134,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada
134134
cookies: expect.any(Object),
135135
headers: {
136136
'user-agent': expect.any(String),
137-
host: 'localhost:8000',
137+
host: expect.stringContaining('localhost:'),
138138
},
139139
},
140140
});
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import express from 'express';
22
import { createRequestHandler } from '@remix-run/express';
3-
import { getPortPromise } from 'portfinder';
43
import { wrapExpressCreateRequestHandler } from '@sentry/remix';
54
import { TestEnv } from '../../../../../../node-integration-tests/utils';
65
import * as http from 'http';
6+
import { AddressInfo } from 'net';
77

88
export * from '../../../../../../node-integration-tests/utils';
99

@@ -16,18 +16,18 @@ export class RemixTestEnv extends TestEnv {
1616
const requestHandlerFactory =
1717
adapter === 'express' ? wrapExpressCreateRequestHandler(createRequestHandler) : createRequestHandler;
1818

19-
const port = await getPortPromise();
20-
19+
let serverPort;
2120
const server = await new Promise<http.Server>(resolve => {
2221
const app = express();
2322

2423
app.all('*', requestHandlerFactory({ build: require('../../../build') }));
2524

26-
const server = app.listen(port, () => {
25+
const server = app.listen(0, () => {
26+
serverPort = (server.address() as AddressInfo).port;
2727
resolve(server);
2828
});
2929
});
3030

31-
return new RemixTestEnv(server, `http://localhost:${port}`);
31+
return new RemixTestEnv(server, `http://localhost:${serverPort}`);
3232
}
3333
}

0 commit comments

Comments
 (0)