From c2b79aa462af6a7eaff26124f39473417f2a5ff0 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Wed, 14 Oct 2026 19:10:22 +0800 Subject: [PATCH 1/3] test(doctor): separate Hopper availability and version assertions --- .../cli/doctorHopperDiscovery.test.ts | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/tests/boundary/cli/doctorHopperDiscovery.test.ts b/tests/boundary/cli/doctorHopperDiscovery.test.ts index 0e7fdeacb..1cddbbbb0 100644 --- a/tests/boundary/cli/doctorHopperDiscovery.test.ts +++ b/tests/boundary/cli/doctorHopperDiscovery.test.ts @@ -56,26 +56,34 @@ cliTest.skipIf(!["darwin", "linux"].includes(process.platform))( timeoutMs: 20_000, }); - const executableOnHost = process.platform === "darwin"; + const hopperReady = process.platform === "darwin"; expect(result.signal).toBeNull(); - expect(result.exitCode).toBe(executableOnHost ? 0 : 1); + expect(result.exitCode).toBe(hopperReady ? 0 : 1); expect(result.json).toMatchObject({ - healthy: executableOnHost, + healthy: hopperReady, environment_healthy: false, - ...(executableOnHost ? { hopperPath: launcher } : {}), + ...(hopperReady ? { hopperPath: launcher } : {}), checks: expect.arrayContaining([ expect.objectContaining({ name: "hopper", - ok: executableOnHost, + ok: true, detail: launcher, - classification: executableOnHost ? "healthy" : "config_drift", - ...(executableOnHost - ? {} - : { remediation: expect.stringContaining("HOPPER_LAUNCHER_PATH") }), + classification: "healthy", }), + ...(process.platform === "linux" + ? [ + expect.objectContaining({ + name: "hopper-version", + ok: false, + detail: launcher, + classification: "config_drift", + remediation: expect.stringContaining("HOPPER_LAUNCHER_PATH"), + }), + ] + : []), ]), }); - if (!executableOnHost) expect(result.json).not.toHaveProperty("hopperPath"); + if (!hopperReady) expect(result.json).not.toHaveProperty("hopperPath"); await expect(access(launchMarker)).rejects.toMatchObject({ code: "ENOENT", }); From 0686b07546d0e2bdc01ff936c5ba7678b045bece Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Wed, 14 Oct 2026 19:23:11 +0800 Subject: [PATCH 2/3] test(doctor): retain discovered Hopper paths despite version failures --- tests/boundary/cli/doctorHopperDiscovery.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/boundary/cli/doctorHopperDiscovery.test.ts b/tests/boundary/cli/doctorHopperDiscovery.test.ts index 1cddbbbb0..12df465d7 100644 --- a/tests/boundary/cli/doctorHopperDiscovery.test.ts +++ b/tests/boundary/cli/doctorHopperDiscovery.test.ts @@ -62,7 +62,7 @@ cliTest.skipIf(!["darwin", "linux"].includes(process.platform))( expect(result.json).toMatchObject({ healthy: hopperReady, environment_healthy: false, - ...(hopperReady ? { hopperPath: launcher } : {}), + hopperPath: launcher, checks: expect.arrayContaining([ expect.objectContaining({ name: "hopper", @@ -83,7 +83,6 @@ cliTest.skipIf(!["darwin", "linux"].includes(process.platform))( : []), ]), }); - if (!hopperReady) expect(result.json).not.toHaveProperty("hopperPath"); await expect(access(launchMarker)).rejects.toMatchObject({ code: "ENOENT", }); From f75b1a416ba67b8421e7dbd248160afedb79581f Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Wed, 14 Oct 2026 19:24:14 +0800 Subject: [PATCH 3/3] test(browser): retain bounded diagnostics for scenario policy failures --- .../browser-scenario-service-workers-e2e.mjs | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/scripts/lib/browser-scenario-service-workers-e2e.mjs b/scripts/lib/browser-scenario-service-workers-e2e.mjs index 3af2f4f8d..bf88717ac 100644 --- a/scripts/lib/browser-scenario-service-workers-e2e.mjs +++ b/scripts/lib/browser-scenario-service-workers-e2e.mjs @@ -12,6 +12,20 @@ import { browserScenarioSchema } from "../../dist/domain/browserScenario.js"; import { runScenarioCli } from "./browser-scenario-verifier.mjs"; import { verifyScenarioPolicySetup } from "./browser-scenario-service-worker-setup-e2e.mjs"; +const diagnosticError = (error) => + error instanceof Error + ? { + name: error.name, + message: error.message.slice(0, 1_000), + reason: error.reason, + stack: error.stack?.slice(0, 2_000), + cause: + error.cause instanceof Error + ? error.cause.stack?.slice(0, 3_000) + : typeof error.cause, + } + : { kind: typeof error }; + const startSite = async () => { let otherOrigin; const server = createServer((request, response) => { @@ -83,6 +97,7 @@ export async function verifyScenarioServiceWorkers(endpoint, targetId) { first.setOtherOrigin(second.origin); second.setOtherOrigin(first.origin); const observer = await chromium.connectOverCDP(endpoint); + console.log("Service-worker fixture browser:", observer.version()); let provider = createBrowserScenarioProvider(process.env); let session; let failure; @@ -154,7 +169,42 @@ export async function verifyScenarioServiceWorkers(endpoint, targetId) { state: "visible", }, ]); - const cleanCapture = (await runScenarioCli(clean)).normalized_result; + let cleanCapture; + try { + cleanCapture = (await runScenarioCli(clean)).normalized_result; + } catch (cause) { + // Retain the original failure. One bounded replay navigates only this + // verifier-owned target and exposes causes the CLI does not serialize. + const state = await page + .locator("#state") + .evaluate( + (element) => ({ + text: element.textContent?.slice(0, 100), + blocked: element.getAttribute("data-blocked"), + nativeRegister: navigator.serviceWorker.register + .toString() + .includes("[native code]"), + }), + undefined, + { timeout: 1_000 }, + ) + .catch((error) => ({ unavailable: String(error).slice(0, 500) })); + console.error("Service-worker CLI failure state:", { + url: page.url(), + state, + }); + const replay = await provider + .captureScenario(clean, { signal: AbortSignal.timeout(5_000) }) + .then((result) => + result.ok ? { ok: true } : diagnosticError(result.error), + ) + .catch(diagnosticError); + console.error( + "Service-worker diagnostic replay (original CLI failure retained):", + replay, + ); + throw cause; + } assert.ok(cleanCapture.steps.every(({ status }) => status === "completed")); assert.equal(cleanCapture.browser.cleanup, "disconnected-external"); await assertRestored();