Skip to content

Commit 774eb26

Browse files
committed
test(cli): hold the busy port with an HTTP neighbour that answers, so the ping fails fast and the holder releases
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NQ4sTujBVMFFGmdXcRmdd5
1 parent bc2d71e commit 774eb26

1 file changed

Lines changed: 38 additions & 4 deletions

File tree

‎packages/cli/test/dev-connector-env-parity.integration.test.ts‎

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
* now runs the child's walk before it compiles and exports the port it lands
6363
* on (`portFlagEnv`).
6464
*
65-
* So the third boot here holds a port for its whole life (`holdPort`), asks
65+
* So the third boot here holds a port for its whole life (an HTTP neighbour
66+
* answering 503, see {@link holdPortWithNeighbour}), asks
6667
* `os dev -p` for it, and reads the shifted port out of the child's own
6768
* banner. The served and the materialized `baseUrl` must both name that port,
6869
* and a flow pinging the app through the connector (`fxp_ping`, the fixture's
@@ -73,7 +74,8 @@
7374
* `bin/run.js` with `NODE_ENV` unset, the shipped entry resolving commands from
7475
* `dist/` (`requireBuiltCli`), in its own process group (`dev` supervises a
7576
* `serve` grandchild). Each boot asks for a NON-ZERO port drawn with
76-
* `reservePort()` (the busy-port boot holds its own with `holdPort()`), because that number is the subject: the config must read
77+
* `reservePort()` (the busy-port boot holds its own instead), because that
78+
* number is the subject: the config must read
7779
* the port the flag names, before the boot, and a kernel-assigned `-p 0` read
7880
* back afterwards cannot be known to the config (the draw is ledgered in
7981
* `released-port-production-spawn.pin.test.ts`). A lost race on that draw is
@@ -84,6 +86,7 @@
8486

8587
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
8688
import { spawn, type ChildProcess } from 'node:child_process';
89+
import { createServer as createHttpServer } from 'node:http';
8790
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
8891
import { tmpdir } from 'node:os';
8992
import { join, resolve } from 'node:path';
@@ -93,7 +96,6 @@ import {
9396
childEnv,
9497
E2E_AUTH_SECRET,
9598
E2E_SECRET_KEY,
96-
holdPort,
9799
portContentionError,
98100
portDriftError,
99101
probeThroughChild,
@@ -302,6 +304,38 @@ function boot(
302304
});
303305
}
304306

307+
/**
308+
* Hold a port for a whole boot with a neighbour that ANSWERS: an HTTP server
309+
* replying 503 to everything, as another app on that port would.
310+
*
311+
* ⛔ Not `holdPort()` from the shared helper. Its bare socket never answers, so
312+
* a ping dialing it waits out the handler's timeout, and its `release()` then
313+
* waits forever on the connection the ping left behind (measured: the socket
314+
* sat in CLOSE_WAIT and the hook timed out at 600 s).
315+
*/
316+
function holdPortWithNeighbour(): Promise<{ port: number; release: () => Promise<void> }> {
317+
return new Promise((resolveHold, rejectHold) => {
318+
const server = createHttpServer((_req, res) => {
319+
res.writeHead(503, { connection: 'close' }).end('not this app');
320+
});
321+
server.on('error', rejectHold);
322+
server.listen(0, '0.0.0.0', () => {
323+
const address = server.address();
324+
if (address === null || typeof address === 'string') {
325+
rejectHold(new Error(`listen(0) produced no numeric address: ${String(address)}`));
326+
return;
327+
}
328+
resolveHold({
329+
port: address.port,
330+
release: () => new Promise<void>((done) => {
331+
server.closeAllConnections();
332+
server.close(() => done());
333+
}),
334+
});
335+
});
336+
});
337+
}
338+
305339
async function stopGroup(child: ChildProcess): Promise<void> {
306340
if (child.pid === undefined || child.exitCode !== null || child.signalCode !== null) return;
307341
await new Promise<void>((done) => {
@@ -415,7 +449,7 @@ beforeAll(async () => {
415449
}
416450

417451
// `os dev -p N` with N held for the whole boot: the server shifts off it.
418-
const held = await holdPort();
452+
const held = await holdPortWithNeighbour();
419453
try {
420454
live = await boot(
421455
'os dev -p (held)',

0 commit comments

Comments
 (0)