From a085db6b642e589de609da3ec1b54bbf390af87e Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 20 Jun 2026 07:52:33 +0200 Subject: [PATCH] fix(qa): release docker health probe bodies --- .../qa-lab/src/docker-up.runtime.test.ts | 5 +++++ extensions/qa-lab/src/docker-up.runtime.ts | 20 ++++++++++++++----- .../src/substrate/harness.runtime.test.ts | 12 +++++++++++ .../src/substrate/harness.runtime.ts | 14 ++++++++++--- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/extensions/qa-lab/src/docker-up.runtime.test.ts b/extensions/qa-lab/src/docker-up.runtime.test.ts index e1da029fa5bf..78f765fd4efd 100644 --- a/extensions/qa-lab/src/docker-up.runtime.test.ts +++ b/extensions/qa-lab/src/docker-up.runtime.test.ts @@ -183,6 +183,7 @@ describe("runQaDockerUp", () => { it("falls back to the container IP when the host gateway port is unreachable", async () => { const calls: string[] = []; const fetchCalls: string[] = []; + const hostGatewayCancel = vi.fn(async () => {}); const outputDir = await mkdtemp(path.join(os.tmpdir(), "qa-docker-up-")); const repoRoot = path.resolve("/repo/openclaw"); const composeFile = path.join(outputDir, "docker-compose.qa.yml"); @@ -214,6 +215,9 @@ describe("runQaDockerUp", () => { }, fetchImpl: vi.fn(async (input: string) => { fetchCalls.push(input); + if (input === "http://127.0.0.1:18889/healthz") { + return { ok: false, body: { cancel: hostGatewayCancel } }; + } return { ok: input === "http://127.0.0.1:43124/healthz" || @@ -237,6 +241,7 @@ describe("runQaDockerUp", () => { "http://192.168.165.4:18789/healthz", ]); expect(result.gatewayUrl).toBe("http://192.168.165.4:18789/"); + expect(hostGatewayCancel).toHaveBeenCalledTimes(1); } finally { await rm(outputDir, { recursive: true, force: true }); } diff --git a/extensions/qa-lab/src/docker-up.runtime.ts b/extensions/qa-lab/src/docker-up.runtime.ts index 3de952654b1a..6c03a2d16e5d 100644 --- a/extensions/qa-lab/src/docker-up.runtime.ts +++ b/extensions/qa-lab/src/docker-up.runtime.ts @@ -25,6 +25,20 @@ function resolveDefaultQaDockerDir(repoRoot: string) { return path.resolve(repoRoot, ".artifacts/qa-docker"); } +async function isQaLabDockerHealthReachable(url: string, fetchImpl: FetchLike) { + let response: Awaited> | undefined; + try { + response = await fetchImpl(url); + return response.ok; + } catch { + return false; + } finally { + try { + await response?.body?.cancel?.(); + } catch {} + } +} + export async function runQaDockerUp( params: { repoRoot?: string; @@ -114,11 +128,7 @@ export async function runQaDockerUp( sleepImpl, ); let gatewayUrl = hostGatewayUrl; - if ( - !(await fetchImpl(`${hostGatewayUrl}healthz`) - .then((response) => response.ok) - .catch(() => false)) - ) { + if (!(await isQaLabDockerHealthReachable(`${hostGatewayUrl}healthz`, fetchImpl))) { const containerGatewayUrl = await resolveComposeServiceUrl( "openclaw-qa-gateway", 18789, diff --git a/extensions/qa-matrix/src/substrate/harness.runtime.test.ts b/extensions/qa-matrix/src/substrate/harness.runtime.test.ts index 509ef604567f..9ba99e41ed7c 100644 --- a/extensions/qa-matrix/src/substrate/harness.runtime.test.ts +++ b/extensions/qa-matrix/src/substrate/harness.runtime.test.ts @@ -156,6 +156,18 @@ describe("matrix harness runtime", () => { ); }); + it("cancels Matrix versions probe response bodies", async () => { + const cancel = vi.fn(async () => {}); + const fetchImpl = vi.fn(async () => ({ ok: true, body: { cancel } })); + + await expect( + testing.isMatrixVersionsReachable("http://127.0.0.1:28008/", fetchImpl), + ).resolves.toBe(true); + + expect(fetchImpl).toHaveBeenCalledWith("http://127.0.0.1:28008/_matrix/client/versions"); + expect(cancel).toHaveBeenCalledTimes(1); + }); + it("falls back to the container IP when the host port is unreachable", async () => { const calls: string[] = []; diff --git a/extensions/qa-matrix/src/substrate/harness.runtime.ts b/extensions/qa-matrix/src/substrate/harness.runtime.ts index 662951a712ac..bbc371e7eeef 100644 --- a/extensions/qa-matrix/src/substrate/harness.runtime.ts +++ b/extensions/qa-matrix/src/substrate/harness.runtime.ts @@ -51,9 +51,17 @@ function buildVersionsUrl(baseUrl: string) { } async function isMatrixVersionsReachable(baseUrl: string, fetchImpl: FetchLike) { - return await fetchImpl(buildVersionsUrl(baseUrl)) - .then((response) => response.ok) - .catch(() => false); + let response: Awaited> | undefined; + try { + response = await fetchImpl(buildVersionsUrl(baseUrl)); + return response.ok; + } catch { + return false; + } finally { + try { + await response?.body?.cancel?.(); + } catch {} + } } async function withMatrixQaHarnessTimeout(