Skip to content

Commit fcb0b8e

Browse files
codeslakeclaude
andcommitted
test(holder-handover): carry launcher stderr on the startup-timeout assertion
The startup assertion at proxy-holder-handover.test.mjs:597 ("the holder never came up, so nothing was measured") is the one CI hit on a flake (forge run 33986651010, job 101361343490, head 3e22f57): 25366.84ms, matching the 25s startup deadline exactly. Unlike the two assertions right below it in the same test (the standby-armed check and the post-kill carries check), it did not pass the launcher stderr captured in `err` into its message, so the run that hit it left no line anywhere saying WHY the holder never bound. Fix: pass `err` into the same message the other two assertions already use. Reproduced deterministically (not the rare freePort() race the CI flake needs): CACHE_FIX_PROXY_BIND is not on this test file's own env delete-list, so it is inherited by the launcher it spawns. Pointing it at a TEST-NET-3 address (RFC 5737, never assigned to a real host) makes bindFailed() in bin/claude-via-proxy.mjs fire immediately and settle(1), producing the same "holder never came up" shape on demand. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 0e7aa80 commit fcb0b8e

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { describe, it } from "node:test";
2+
import assert from "node:assert/strict";
3+
import { spawnSync } from "node:child_process";
4+
import { fileURLToPath } from "node:url";
5+
import { dirname, join } from "node:path";
6+
7+
const testFile = join(dirname(fileURLToPath(import.meta.url)), "proxy-holder-handover.test.mjs");
8+
9+
describe("diagnostic evidence when a holder never comes up", () => {
10+
// A REAL, DETERMINISTIC repro of "the holder never came up" rather than the
11+
// rare freePort() race the actual flake needs. proxy-holder-handover's own
12+
// env-building does not delete CACHE_FIX_PROXY_BIND, so it is inherited
13+
// straight into the launcher it spawns; pointing it at a TEST-NET-3 address
14+
// (RFC 5737: reserved for documentation, never assigned to a real host) makes
15+
// the launcher's bindFailed() path fire immediately and settle(1) — the same
16+
// "holder never came up" shape the CI flake hit, on demand instead of by luck.
17+
it("keeps the launcher's stderr on the startup-timeout assertion, not just the constant message", () => {
18+
const env = { ...process.env, CACHE_FIX_PROXY_BIND: "203.0.113.1" };
19+
// This file itself runs under `node --test`, which sets NODE_TEST_CONTEXT
20+
// on its own process; inherited by the child, node's test runner reads it
21+
// as "already inside a --test run" and silently skips running the file
22+
// instead of executing it ("run() is being called recursively").
23+
delete env.NODE_TEST_CONTEXT;
24+
const r = spawnSync(process.execPath,
25+
["--test", "--test-reporter", "tap",
26+
"--test-name-pattern", "no hop is configured", testFile],
27+
{ env, encoding: "utf8", timeout: 90_000 });
28+
const out = r.stdout + r.stderr;
29+
const at = out.indexOf("the holder never came up");
30+
assert.ok(at >= 0, `expected the forced startup failure to fire:\n${out}`);
31+
const block = out.slice(at, at + 500);
32+
assert.match(block, /cache-fix\] cannot bind/,
33+
`the startup assertion's failure message dropped the launcher's stderr:\n${block}`);
34+
});
35+
});

test/proxy-holder-handover.test.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,8 @@ describe("holder handover (SIGUSR2)", () => {
594594
const up = Date.now() + 25_000;
595595
let body = await probe(port);
596596
while (body.startsWith("ERR:") && Date.now() < up) body = await probe(port);
597-
assert.equal(body, "ok", "the holder never came up, so nothing was measured");
597+
assert.equal(body, "ok",
598+
`the holder never came up, so nothing was measured. Launcher stderr: ${JSON.stringify(err.slice(-300))}`);
598599
assert.equal(await carries(), "pong", "premise: the live proxy must carry a CONNECT");
599600

600601
// Kill the supervisor AND the proxy, and nothing else. Killing the standby

0 commit comments

Comments
 (0)