From ae9ae560e943fb640edcb69ea97d06dd2d98a877 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Tue, 16 Jun 2026 20:41:12 +0200 Subject: [PATCH] fix(qa): reject loose ClickClack wait timeouts --- .../lib/release-user-journey/assertions.mjs | 8 +++-- .../release-user-journey-assertions.test.ts | 31 +++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/scripts/e2e/lib/release-user-journey/assertions.mjs b/scripts/e2e/lib/release-user-journey/assertions.mjs index e2587b24c362..f4bf122bee34 100644 --- a/scripts/e2e/lib/release-user-journey/assertions.mjs +++ b/scripts/e2e/lib/release-user-journey/assertions.mjs @@ -348,7 +348,11 @@ async function postClickClackInbound() { async function waitClickClackSocket() { const baseUrl = process.argv[3]; - const timeoutSeconds = Number(process.argv[4] ?? 30); + const timeoutSeconds = readPositiveInt( + process.argv[4], + 30, + "ClickClack websocket timeout seconds", + ); const deadline = Date.now() + timeoutSeconds * 1000; while (Date.now() < deadline) { const remainingMs = Math.max(1, deadline - Date.now()); @@ -387,7 +391,7 @@ function assertClickClackState() { async function waitClickClackReply() { const statePath = process.argv[3]; const marker = process.argv[4]; - const timeoutSeconds = Number(process.argv[5] ?? 30); + const timeoutSeconds = readPositiveInt(process.argv[5], 30, "ClickClack reply timeout seconds"); const deadline = Date.now() + timeoutSeconds * 1000; while (Date.now() < deadline) { if (fs.existsSync(statePath)) { diff --git a/test/scripts/release-user-journey-assertions.test.ts b/test/scripts/release-user-journey-assertions.test.ts index dfeda696cae1..c1085ec8e3ab 100644 --- a/test/scripts/release-user-journey-assertions.test.ts +++ b/test/scripts/release-user-journey-assertions.test.ts @@ -316,7 +316,7 @@ describe("release user journey assertions", () => { withEnv({ HOME: home, OPENCLAW_RELEASE_USER_JOURNEY_HTTP_TIMEOUT_MS: "100" }, () => runReleaseUserJourneyAssertion("wait-clickclack-socket", [ `http://127.0.0.1:${server.port}`, - "0.2", + "1", ]), ), ).rejects.toThrow("Timed out waiting for ClickClack websocket connection"); @@ -339,7 +339,7 @@ describe("release user journey assertions", () => { withEnv({ HOME: home, OPENCLAW_RELEASE_USER_JOURNEY_HTTP_TIMEOUT_MS: "100ms" }, () => runReleaseUserJourneyAssertion("wait-clickclack-socket", [ `http://127.0.0.1:${server.port}`, - "0.2", + "1", ]), ), ).rejects.toThrow( @@ -351,6 +351,33 @@ describe("release user journey assertions", () => { } }); + it("rejects loose ClickClack wait timeout args instead of parsing prefixes", async () => { + const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-user-assertions-")); + const home = path.join(root, "home"); + const statePath = path.join(root, "state.json"); + + try { + await expect( + withEnv({ HOME: home }, () => + runReleaseUserJourneyAssertion("wait-clickclack-socket", ["http://127.0.0.1:9", "1e3"]), + ), + ).rejects.toThrow( + 'ClickClack websocket timeout seconds must be a positive integer. Got: "1e3"', + ); + await expect( + withEnv({ HOME: home }, () => + runReleaseUserJourneyAssertion("wait-clickclack-reply", [ + statePath, + "OPENCLAW_E2E_OK", + "30s", + ]), + ), + ).rejects.toThrow('ClickClack reply timeout seconds must be a positive integer. Got: "30s"'); + } finally { + rmSync(root, { force: true, recursive: true }); + } + }); + it("bounds ClickClack fixture error response bodies", async () => { const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-user-assertions-")); const home = path.join(root, "home");