Skip to content

Commit 808b9b4

Browse files
committed
WIP: Bisect to debug get-port error
1 parent 5bcec2c commit 808b9b4

File tree

4 files changed

+89
-17
lines changed

4 files changed

+89
-17
lines changed

.github/workflows/ci.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,45 @@
11
name: CI
22
on: [push, pull_request]
33
jobs:
4+
# Temporary: bisect which port-finding mode causes flaky abort test
5+
bisect-port-finding:
6+
name: "Bisect: ${{ matrix.port-mode }}"
7+
runs-on: ubuntu-latest
8+
strategy:
9+
fail-fast: false
10+
matrix:
11+
port-mode: [simple, multi-host, get-port, get-port-preloaded]
12+
13+
steps:
14+
- uses: actions/checkout@v6
15+
16+
- uses: actions/setup-node@v6
17+
with:
18+
node-version: '22.x'
19+
check-latest: true
20+
cache: 'npm'
21+
cache-dependency-path: 'package.json'
22+
23+
- run: npm install
24+
- run: npm run build
25+
26+
# Run the node tests 3 times to catch flakes
27+
- name: "Run 1"
28+
run: npm run test:node
29+
env:
30+
PORT_FIND_MODE: ${{ matrix.port-mode }}
31+
NODE_OPTIONS: '--no-experimental-strip-types'
32+
- name: "Run 2"
33+
run: npm run test:node
34+
env:
35+
PORT_FIND_MODE: ${{ matrix.port-mode }}
36+
NODE_OPTIONS: '--no-experimental-strip-types'
37+
- name: "Run 3"
38+
run: npm run test:node
39+
env:
40+
PORT_FIND_MODE: ${{ matrix.port-mode }}
41+
NODE_OPTIONS: '--no-experimental-strip-types'
42+
443
build:
544
name: Build & test
645
runs-on: ubuntu-latest

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"postbuild:doc": "touch ./typedoc/.nojekyll",
6767
"test": "npm run build && npm run test:node",
6868
"test:node": "NODE_EXTRA_CA_CERTS=./test/fixtures/test-ca.pem TS_NODE_FILES=true mocha -r ts-node/register 'test/**/*.spec.ts'",
69+
"test:node:bisect": "npm run build && echo '=== simple ===' && PORT_FIND_MODE=simple npm run test:node && echo '=== multi-host ===' && PORT_FIND_MODE=multi-host npm run test:node && echo '=== get-port ===' && PORT_FIND_MODE=get-port npm run test:node && echo '=== get-port-preloaded ===' && PORT_FIND_MODE=get-port-preloaded npm run test:node",
6970
"test:browser": "npm run with-admin -- karma start",
7071
"test:browser:debug": "npm run with-admin -- karma start --single-run=false --browsers ChromeWithCert",
7172
"test:perf": "NODE_EXTRA_CA_CERTS=./test/fixtures/test-ca.pem TS_NODE_FILES=true mocha -r ts-node/register 'test/performance/*.perf.ts'",

src/server/mockttp-server.ts

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Buffer } from 'buffer';
22
import * as stream from 'stream';
33
import * as fs from 'fs';
44
import * as net from "net";
5+
import * as os from "os";
56
import * as tls from "tls";
67
import * as http from "http";
78
import * as http2 from "http2";
@@ -103,19 +104,59 @@ import { SocksServerOptions } from "./socks-server";
103104

104105
const serverPortCheckMutex = new Mutex();
105106

107+
// Toggle this to test different port-finding strategies:
108+
// Toggle this to test different port-finding strategies:
109+
// - 'simple': single-host check (default, no deps)
110+
// - 'multi-host': check all local interfaces (like get-port does)
111+
// - 'get-port': use actual get-port package (dynamic import)
112+
// - 'get-port-preloaded': pre-import get-port at module load time (matches c59e07d)
113+
const PORT_FIND_MODE = (process.env.PORT_FIND_MODE || 'simple') as
114+
'simple' | 'multi-host' | 'get-port' | 'get-port-preloaded';
115+
116+
// Pre-load get-port at module load time (like the static import in c59e07d)
117+
const preloadedGetPort = PORT_FIND_MODE === 'get-port-preloaded'
118+
? require('get-port') as { default: Function, portNumbers: Function }
119+
: null;
120+
106121
async function findFreePort(startPort: number, endPort: number): Promise<number> {
122+
if (PORT_FIND_MODE === 'get-port') {
123+
const { default: getPort, portNumbers } = await import('get-port');
124+
return getPort({ port: portNumbers(startPort, endPort) });
125+
}
126+
127+
if (PORT_FIND_MODE === 'get-port-preloaded') {
128+
return preloadedGetPort!.default({ port: preloadedGetPort!.portNumbers(startPort, endPort) });
129+
}
130+
131+
const hosts: Array<string | undefined> = PORT_FIND_MODE === 'multi-host'
132+
? (() => {
133+
const results: Array<string | undefined> = [undefined, '0.0.0.0'];
134+
for (const iface of Object.values(os.networkInterfaces())) {
135+
for (const config of iface!) results.push(config.address);
136+
}
137+
return results;
138+
})()
139+
: [undefined]; // 'simple' mode: just default host
140+
107141
for (let port = startPort; port <= endPort; port++) {
108142
try {
109-
await new Promise<void>((resolve, reject) => {
110-
const server = net.createServer();
111-
server.unref();
112-
server.once('error', reject);
113-
server.once('listening', () => server.close(() => resolve()));
114-
server.listen(port);
115-
});
143+
for (const host of hosts) {
144+
await new Promise<void>((resolve, reject) => {
145+
const server = net.createServer();
146+
server.unref();
147+
server.once('error', reject);
148+
server.once('listening', () => server.close(() => resolve()));
149+
if (host !== undefined) {
150+
server.listen(port, host);
151+
} else {
152+
server.listen(port);
153+
}
154+
});
155+
}
116156
return port;
117157
} catch (e: any) {
118-
if (e.code !== 'EADDRINUSE' && e.code !== 'EACCES') throw e;
158+
if (e.code !== 'EADDRINUSE' && e.code !== 'EACCES' &&
159+
e.code !== 'EADDRNOTAVAIL' && e.code !== 'EINVAL') throw e;
119160
}
120161
}
121162
throw new Error(`No open ports between ${startPort} and ${endPort}`);

test/integration/subscriptions/response-events.spec.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -327,15 +327,6 @@ describe("Response subscriptions", () => {
327327
describe("Abort subscriptions", () => {
328328
let server = getLocal();
329329

330-
// DEBUG: Test whether get-port's presence causes flaky abort test
331-
before(async function () {
332-
this.timeout(30000);
333-
const { default: getPort } = await import('get-port');
334-
console.log('[DEBUG] Calling getPort() 50 times to simulate full suite usage...');
335-
for (let i = 0; i < 50; i++) await getPort();
336-
console.log('[DEBUG] Done. lockedPorts should now have 50 entries.');
337-
});
338-
339330
beforeEach(() => server.start());
340331
afterEach(() => server.stop());
341332

0 commit comments

Comments
 (0)