From be7807f65eb06451ff14f778a01b8a4a3c724ad2 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 20 Jun 2026 06:55:40 +0800 Subject: [PATCH] fix(test): stabilize tooling guard probes (#95114) * fix(test): release kitchen sink probe readers * test(github): follow shared guard membership helper --- scripts/e2e/kitchen-sink-rpc-walk.mjs | 42 +++++++++++-------- test/scripts/kitchen-sink-rpc-walk.test.ts | 24 +++++++++++ .../security-sensitive-guard-workflow.test.ts | 4 +- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/scripts/e2e/kitchen-sink-rpc-walk.mjs b/scripts/e2e/kitchen-sink-rpc-walk.mjs index 8ad3ea41c02e..53c82df2ddd3 100644 --- a/scripts/e2e/kitchen-sink-rpc-walk.mjs +++ b/scripts/e2e/kitchen-sink-rpc-walk.mjs @@ -996,25 +996,31 @@ export async function readBoundedResponseText(response, byteLimit, timeoutPromis } const chunks = []; let totalBytes = 0; - for (;;) { - const read = reader.read(); - const { done, value } = await withOptionalTimeout( - read, - timeoutPromise?.catch((error) => { - cancelReaderSoon(reader); - throw error; - }), - ); - if (done) { - break; + try { + for (;;) { + const read = reader.read(); + const { done, value } = await withOptionalTimeout( + read, + timeoutPromise?.catch((error) => { + cancelReaderSoon(reader); + throw error; + }), + ); + if (done) { + break; + } + const chunk = Buffer.from(value); + totalBytes += chunk.byteLength; + if (totalBytes > resolvedByteLimit) { + await reader.cancel().catch(() => undefined); + throw createFetchBodyTooLargeError(resolvedByteLimit); + } + chunks.push(chunk); } - const chunk = Buffer.from(value); - totalBytes += chunk.byteLength; - if (totalBytes > resolvedByteLimit) { - await reader.cancel().catch(() => undefined); - throw createFetchBodyTooLargeError(resolvedByteLimit); - } - chunks.push(chunk); + } finally { + try { + reader.releaseLock?.(); + } catch {} } return Buffer.concat(chunks, totalBytes).toString("utf8"); } diff --git a/test/scripts/kitchen-sink-rpc-walk.test.ts b/test/scripts/kitchen-sink-rpc-walk.test.ts index 8b763874d374..c0629833f2e9 100644 --- a/test/scripts/kitchen-sink-rpc-walk.test.ts +++ b/test/scripts/kitchen-sink-rpc-walk.test.ts @@ -1976,6 +1976,30 @@ describe("kitchen-sink RPC process sampling", () => { ); }); + it("releases HTTP probe response stream readers after bounded reads", async () => { + const releaseLock = vi.fn(); + const response = { + headers: new Headers(), + body: { + getReader() { + return { + read: vi + .fn() + .mockResolvedValueOnce({ done: false, value: new TextEncoder().encode("ok") }) + .mockResolvedValueOnce({ done: true }), + releaseLock, + }; + }, + }, + text: vi.fn(async () => "not read"), + }; + + await expect(readBoundedResponseText(response, 1024)).resolves.toBe("ok"); + + expect(releaseLock).toHaveBeenCalledOnce(); + expect(response.text).not.toHaveBeenCalled(); + }); + it("cancels stalled HTTP probe response streams when the timeout wins", async () => { let canceled = false; const timeoutError = Object.assign(new Error("fetch probe timed out"), { diff --git a/test/scripts/security-sensitive-guard-workflow.test.ts b/test/scripts/security-sensitive-guard-workflow.test.ts index 7d8a513bb1b4..1f8e87415ae5 100644 --- a/test/scripts/security-sensitive-guard-workflow.test.ts +++ b/test/scripts/security-sensitive-guard-workflow.test.ts @@ -136,6 +136,8 @@ describe("security-sensitive guard workflow", () => { it("uses a dedicated checked-in script and detects the intended file surfaces", () => { const workflow = readFileSync(WORKFLOW, "utf8"); const script = readFileSync("scripts/github/security-sensitive-guard.mjs", "utf8"); + const sharedScript = readFileSync("scripts/github/guard-shared.mjs", "utf8"); + const guardSources = `${script}\n${sharedScript}`; expect(workflow).toContain("scripts/github/security-sensitive-guard.mjs"); expect(script).toContain('"security-sensitive-changed"'); @@ -143,7 +145,7 @@ describe("security-sensitive guard workflow", () => { expect(script).toContain(".env"); expect(script).toContain("/allow-security-sensitive-change"); expect(script).toContain("openclaw-secops"); - expect(script).toContain("/memberships/"); + expect(guardSources).toContain("/memberships/"); expect(script).toContain("A later push requires a fresh approval."); expect(script).toContain("process.exitCode = 1"); });