From 50c7ed3e8f788f857eafc24b43469a0c5173418a Mon Sep 17 00:00:00 2001 From: dbarr5 Date: Wed, 19 Aug 2026 07:31:46 -0400 Subject: [PATCH] test(doctor): assert the hanging-backend property instead of a stopwatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "a hanging backend cannot stall the fast report" injects clients that never resolve, sets timeoutMs to 50, and then asserted: assert.ok(Date.now() - started < 500); That measures the machine, not the behaviour. The 50ms timeout can fire exactly as designed and the assertion still fails because the runner was busy. Observed failing on windows-latest at 689ms, and locally at 1818ms under full-suite load while passing in isolation at 82ms. Replaced with the property the test exists to defend: a probe fed by a hanging client must never come back claiming it verified anything. agent.transport, auth.credential, agent.catalog and mcp.broker are named explicitly rather than filtered on an axis, because local checks like workspace.git legitimately do verify in this fixture — nothing about them touches the backend that is hanging. The wall clock is still bounded, but as a hang detector rather than a stopwatch: 30s distinguishes "returned" from "awaited forever", which is the failure the test was written to catch. A slow runner no longer registers as a bug. Stronger than what it replaces, not weaker. Mutation-checked: making notChecked return a verified axis fails this test along with two others (5 pass / 3 fail); restoring gives 8 / 8. The old assertion passed that mutation untouched — it never looked at a single axis, so a false green would have gone through while a busy runner did not. Gates at this commit: npm run typecheck exit 0 npm test 922 pass / 0 fail --- test/diagnostics.test.ts | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/diagnostics.test.ts b/test/diagnostics.test.ts index 92eae6b..3ee5a7f 100644 --- a/test/diagnostics.test.ts +++ b/test/diagnostics.test.ts @@ -212,8 +212,33 @@ test("a hanging backend cannot stall the fast report", async () => { const report = await diagnosticReport(ctx, { dependencies: { memoryRoots: roots, mcpStore: store, mcpClient: hanging, timeoutMs: 50 }, }); - assert.ok(Date.now() - started < 500); + + // What must hold is that the report CAME BACK and told the truth about the + // probes it could not complete. The previous assertion was a 500ms wall-clock + // budget, which measured the machine rather than the behaviour: a loaded CI + // runner blows it even when the 50ms timeout fired exactly as designed + // (observed on windows-latest at 689ms, and locally at 1818ms under full-suite + // load while passing in isolation at 82ms). assert.equal(report.mode, "fast"); + + // A probe fed by one of the hanging clients must never come back claiming it + // verified anything. Named explicitly rather than filtered on an axis: local + // checks like workspace.git legitimately verify here, because nothing about + // them touches the backend that is hanging. + const BACKEND_FED = ["agent.transport", "auth.credential", "agent.catalog", "mcp.broker"]; + const probed = report.checks.filter((check) => BACKEND_FED.includes(check.id)); + assert.equal(probed.length, BACKEND_FED.length, "every backend-fed check should be present in the report"); + for (const check of probed) { + assert.notEqual(check.verified.state, "yes", `${check.id} cannot be verified against a hanging backend`); + } + + // Still bound the wall clock, but as a hang detector rather than a stopwatch. + // The failure this guards against is an unbounded await, which never returns + // at all; any finite margin distinguishes that from a slow runner. + assert.ok( + Date.now() - started < 30_000, + "the fast report must be bounded by its own timeout, not by the backend", + ); }); test("--live renders the live report, never the fast one relabelled", async () => {