From fc1bdecf08297ca97aa93d975807486c14282b4b Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 19 Jun 2026 08:14:39 +0200 Subject: [PATCH] fix(e2e): cancel ClickClack fixture bodies --- .../lib/release-user-journey/assertions.mjs | 4 +- .../release-user-journey-assertions.test.ts | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/scripts/e2e/lib/release-user-journey/assertions.mjs b/scripts/e2e/lib/release-user-journey/assertions.mjs index 37d00aafa22e..adf6c9ddc926 100644 --- a/scripts/e2e/lib/release-user-journey/assertions.mjs +++ b/scripts/e2e/lib/release-user-journey/assertions.mjs @@ -55,6 +55,7 @@ async function withClickClackFixtureResponse(url, init, consume, options = {}) { const controller = new AbortController(); const timeoutError = new Error(`${url} timed out after ${timeoutMs}ms`); let timer; + let response; const timeoutPromise = new Promise((_resolve, reject) => { timer = setTimeout(() => { controller.abort(timeoutError); @@ -62,7 +63,7 @@ async function withClickClackFixtureResponse(url, init, consume, options = {}) { }, timeoutMs); }); try { - const response = await Promise.race([ + response = await Promise.race([ fetch(url, { ...init, signal: controller.signal, @@ -72,6 +73,7 @@ async function withClickClackFixtureResponse(url, init, consume, options = {}) { return await consume(response, { timeoutPromise }); } finally { clearTimeout(timer); + await response?.body?.cancel?.().catch(() => undefined); } } diff --git a/test/scripts/release-user-journey-assertions.test.ts b/test/scripts/release-user-journey-assertions.test.ts index 91a2f9669ecc..3ff8dcf75266 100644 --- a/test/scripts/release-user-journey-assertions.test.ts +++ b/test/scripts/release-user-journey-assertions.test.ts @@ -59,6 +59,17 @@ async function withEnv(env: Record, callback: () => Promise boolean, label: string): Promise { + const startedAt = Date.now(); + while (Date.now() - startedAt < 1000) { + if (matches()) { + return; + } + await new Promise((resolve) => setTimeout(resolve, 20)); + } + throw new Error(`timed out waiting for ${label}`); +} + async function startTcpFixtureServer(handler: (socket: Socket) => void): Promise<{ port: number; stop: () => Promise; @@ -303,6 +314,33 @@ describe("release user journey assertions", () => { } }); + it("cancels successful ClickClack inbound response bodies", async () => { + const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-user-assertions-")); + const home = path.join(root, "home"); + let socketClosed = false; + const server = await startTcpFixtureServer((socket) => { + socket.on("close", () => { + socketClosed = true; + }); + socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nleft-open"); + }); + + try { + await expect( + withEnv({ HOME: home, OPENCLAW_RELEASE_USER_JOURNEY_HTTP_TIMEOUT_MS: "1000" }, () => + runReleaseUserJourneyAssertion("post-clickclack-inbound", [ + `http://127.0.0.1:${server.port}`, + "hello", + ]), + ), + ).resolves.toBeUndefined(); + await waitUntil(() => socketClosed, "ClickClack inbound socket close"); + } finally { + await server.stop(); + rmSync(root, { force: true, recursive: true }); + } + }); + it("bounds stalled ClickClack fixture HTTP probes", async () => { const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-user-assertions-")); const home = path.join(root, "home");