Skip to content

Commit 61af760

Browse files
committed
fix(ports): reuse canonical multi-address probe
1 parent 928816a commit 61af760

1 file changed

Lines changed: 13 additions & 40 deletions

File tree

src/cli/program.force.test.ts

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,10 @@ vi.mock("node:child_process", async () => {
99
};
1010
});
1111

12-
const tryListenOnPortMock = vi.hoisted(() => vi.fn());
12+
const probePortUsageMock = vi.hoisted(() => vi.fn());
1313

1414
vi.mock("../infra/ports-probe.js", () => ({
15-
tryListenOnPort: (...args: unknown[]) => tryListenOnPortMock(...args),
16-
probePortUsage: async (port: number) => {
17-
let sawUnknown = false;
18-
for (const host of ["127.0.0.1", "0.0.0.0", "::1", "::"]) {
19-
try {
20-
await tryListenOnPortMock({ port, host, exclusive: true });
21-
} catch (err: unknown) {
22-
const code = (err as { code?: unknown }).code;
23-
if (code === "EADDRINUSE") {
24-
return "busy";
25-
}
26-
if (code === "EADDRNOTAVAIL" || code === "EAFNOSUPPORT") {
27-
continue;
28-
}
29-
sawUnknown = true;
30-
}
31-
}
32-
return sawUnknown ? "unknown" : "free";
33-
},
15+
probePortUsage: (...args: unknown[]) => probePortUsageMock(...args),
3416
}));
3517

3618
import { execFileSync } from "node:child_process";
@@ -51,10 +33,8 @@ describe("gateway --force helpers", () => {
5133
vi.clearAllMocks();
5234
originalKill = process.kill.bind(process);
5335
originalPlatform = process.platform;
54-
tryListenOnPortMock.mockReset();
55-
tryListenOnPortMock.mockRejectedValue(
56-
Object.assign(new Error("in use"), { code: "EADDRINUSE" }),
57-
);
36+
probePortUsageMock.mockReset();
37+
probePortUsageMock.mockResolvedValue("busy");
5838
// Pin to linux so all lsof tests are platform-invariant.
5939
Object.defineProperty(process, "platform", { value: "linux", configurable: true });
6040
});
@@ -83,7 +63,7 @@ describe("gateway --force helpers", () => {
8363
});
8464

8565
it("skips lsof when the port is already bindable", async () => {
86-
tryListenOnPortMock.mockResolvedValue(undefined);
66+
probePortUsageMock.mockResolvedValue("free");
8767

8868
const result = await forceFreePortAndWait(18789, { timeoutMs: 500, intervalMs: 100 });
8969

@@ -217,9 +197,7 @@ describe("gateway --force helpers", () => {
217197
}
218198
return "18789/tcp: 4242\n";
219199
});
220-
tryListenOnPortMock
221-
.mockRejectedValueOnce(Object.assign(new Error("in use"), { code: "EADDRINUSE" }))
222-
.mockResolvedValue(undefined);
200+
probePortUsageMock.mockResolvedValueOnce("busy").mockResolvedValue("free");
223201

224202
const result = await forceFreePortAndWait(18789, { timeoutMs: 500, intervalMs: 100 });
225203

@@ -249,15 +227,12 @@ describe("gateway --force helpers", () => {
249227
return "";
250228
});
251229

252-
const busyErr = Object.assign(new Error("in use"), { code: "EADDRINUSE" });
253-
// Each probePortUsage call probes up to 4 hosts; the last check needs all
254-
// hosts free, so use permanent mockResolvedValue instead of once.
255-
tryListenOnPortMock
256-
.mockRejectedValueOnce(busyErr)
257-
.mockRejectedValueOnce(busyErr)
258-
.mockRejectedValueOnce(busyErr)
259-
.mockRejectedValueOnce(busyErr)
260-
.mockResolvedValue(undefined);
230+
probePortUsageMock
231+
.mockResolvedValueOnce("busy")
232+
.mockResolvedValueOnce("busy")
233+
.mockResolvedValueOnce("busy")
234+
.mockResolvedValueOnce("busy")
235+
.mockResolvedValue("free");
261236

262237
const promise = forceFreePortAndWait(18789, {
263238
timeoutMs: 300,
@@ -279,9 +254,7 @@ describe("gateway --force helpers", () => {
279254

280255
it("throws when lsof is unavailable and fuser is missing", async () => {
281256
// An inconclusive four-host probe must continue into the cleanup tools.
282-
tryListenOnPortMock.mockRejectedValue(
283-
Object.assign(new Error("probe failed"), { code: "EIO" }),
284-
);
257+
probePortUsageMock.mockResolvedValue("unknown");
285258
(execFileSync as unknown as Mock).mockImplementation((cmd: string) => {
286259
const err = new Error(`spawnSync ${cmd} ENOENT`) as NodeJS.ErrnoException;
287260
err.code = "ENOENT";

0 commit comments

Comments
 (0)