From 57e89d8b0ede84b33ccb0621ed989f3cac5797dc Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy <344842+roodboi@users.noreply.github.com> Date: Sat, 7 Nov 2026 12:15:05 -0500 Subject: [PATCH 1/2] test(native): tolerate a reused port in the loopback probe test The free-port half of the loopback probe test reprobed the exact port it had just released. A parallel test can bind that ephemeral port first, and a loaded runner can time out the connect; both read as in use. The test now accepts the first of several freshly released ports the probe clears. Refs HACK-1946 Co-Authored-By: Claude Opus 5.5 --- packages/runtime-core/src/provider/publication.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/provider/publication.rs b/packages/runtime-core/src/provider/publication.rs index fe5a034b0..e69eccd14 100644 --- a/packages/runtime-core/src/provider/publication.rs +++ b/packages/runtime-core/src/provider/publication.rs @@ -854,7 +854,16 @@ mod port_probe_tests { "published_port_in_use" ); drop(exact); - require_loopback_port_free(port).unwrap(); + // Parallel tests can take a just-released ephemeral port, and a loaded + // runner can time out the probe's connect. Either reads as in use, so + // accept the first of several freshly released ports the probe clears. + let freed = (0..20).any(|_| { + let probe = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap(); + let port = probe.local_addr().unwrap().port(); + drop(probe); + require_loopback_port_free(port).is_ok() + }); + assert!(freed, "the probe never accepted a released loopback port"); // A wildcard listener would share loopback traffic under SO_REUSEADDR. let wildcard = TcpListener::bind((Ipv4Addr::UNSPECIFIED, 0)).unwrap(); let port = wildcard.local_addr().unwrap().port(); From 51c649fd91ae05c7ed4a5fc7e77337326c83e045 Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy <344842+roodboi@users.noreply.github.com> Date: Sat, 7 Nov 2026 13:17:39 -0500 Subject: [PATCH 2/2] test: give the non-interactive doctor --fix tests a 30 s budget Three tests in doctor-fix-noninteractive used Bun's 5 s default while the rest of the file already allows 30 s. A loaded CI runner finished the stale-daemon-pid case about 60 ms past 5 s. Refs HACK-1721 Co-Authored-By: Claude Opus 5.5 --- tests/doctor-fix-noninteractive.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/doctor-fix-noninteractive.test.ts b/tests/doctor-fix-noninteractive.test.ts index 3f5c443f2..764081500 100644 --- a/tests/doctor-fix-noninteractive.test.ts +++ b/tests/doctor-fix-noninteractive.test.ts @@ -248,7 +248,7 @@ test("doctor --fix under HACK_NO_INTERACTIVE clears a stale daemon pid without p expect(runCalls).toEqual( expect.arrayContaining([expect.arrayContaining(["daemon", "clear"])]) ); -}); +}, 30_000); test("doctor --fix under HACK_NO_INTERACTIVE skips macOS keychain repair and reports it", async () => { const projectRoot = await createProjectWithStaleDaemonPid(); @@ -273,7 +273,7 @@ test("doctor --fix under HACK_NO_INTERACTIVE skips macOS keychain repair and rep if (skippedNote !== undefined) { expect(skippedNote).not.toContain("Repair macOS host TLS trust"); } -}); +}, 30_000); test("doctor --fix never calls the raw clack confirm prompt under HACK_NO_INTERACTIVE", async () => { const projectRoot = await createProjectWithStaleDaemonPid(); @@ -291,7 +291,7 @@ test("doctor --fix never calls the raw clack confirm prompt under HACK_NO_INTERA // proves every prompt site in the --fix path routed through confirmSafe's // non-interactive branch instead of the raw clack prompt. expect(code).toBe(0); -}); +}, 30_000); test("doctor --fix creates a missing .hack/.gitignore and keeps user lines on a repair (HACK-1278)", async () => { const projectRoot = await createProjectWithStaleDaemonPid();