From f3f2d398f6040e36dc57a76e58d08586f99dfe76 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 17 Jun 2026 14:58:19 +0200 Subject: [PATCH] fix(testing): normalize QA compose service lookup --- src/plugin-sdk/qa-runtime.test.ts | 43 +++++++++++++++++++++++++++++++ src/plugin-sdk/qa-runtime.ts | 10 ++++--- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/plugin-sdk/qa-runtime.test.ts b/src/plugin-sdk/qa-runtime.test.ts index ca5f3a5e255a..5b6d4694b6e3 100644 --- a/src/plugin-sdk/qa-runtime.test.ts +++ b/src/plugin-sdk/qa-runtime.test.ts @@ -298,6 +298,49 @@ describe("plugin-sdk qa-runtime", () => { expect(sleepImpl).toHaveBeenCalledTimes(1); }); + it("normalizes multiline Docker compose service lookup output", async () => { + const module = await import("./qa-runtime.js"); + const runtime = module.createQaDockerRuntime({ auditContext: "qa-test" }); + const runCommand = vi.fn(async (command: string, args: string[], cwd: string) => { + expect(command).toBe("docker"); + expect(cwd).toBe("/repo"); + + if (args.includes("ps") && args.includes("-q")) { + return { + stdout: "\nqa-gateway-one\nqa-gateway-two\n", + stderr: "", + }; + } + + if (args[0] === "inspect") { + expect(args.at(-1)).toBe("qa-gateway-one"); + return { + stdout: "\n172.18.0.4\n172.19.0.4\n", + stderr: "", + }; + } + + throw new Error(`unexpected docker args: ${args.join(" ")}`); + }); + const fetchImpl = vi.fn(async (url: string) => ({ + ok: url === "http://172.18.0.4:18789/healthz", + })); + + await expect( + runtime.resolveComposeServiceUrl( + "gateway", + 18789, + "/tmp/docker-compose.yml", + "/repo", + runCommand, + fetchImpl, + ), + ).resolves.toBe("http://172.18.0.4:18789/"); + + expect(runCommand).toHaveBeenCalledTimes(2); + expect(fetchImpl).toHaveBeenCalledWith("http://172.18.0.4:18789/healthz"); + }); + it("resolves an unpinned QA Docker host port away from an occupied loopback default", async () => { const module = await import("./qa-runtime.js"); const reservation = await occupyLoopbackPort(); diff --git a/src/plugin-sdk/qa-runtime.ts b/src/plugin-sdk/qa-runtime.ts index 45a43564faad..f1195e817bdc 100644 --- a/src/plugin-sdk/qa-runtime.ts +++ b/src/plugin-sdk/qa-runtime.ts @@ -458,6 +458,10 @@ function normalizeDockerServiceStatus(row?: { Health?: string; State?: string }) return "unknown"; } +function firstDockerOutputLine(stdout: string) { + return normalizeStringEntries(stdout.split("\n"))[0] ?? ""; +} + function parseDockerComposePsRows(stdout: string) { const trimmed = stdout.trim(); if (!trimmed) { @@ -620,7 +624,7 @@ export function createQaDockerRuntime(params: { ["compose", "-f", composeFile, "ps", "-q", service], repoRoot, ); - const containerId = containerStdout.trim(); + const containerId = firstDockerOutputLine(containerStdout); if (!containerId) { return null; } @@ -629,12 +633,12 @@ export function createQaDockerRuntime(params: { [ "inspect", "--format", - "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}", + "{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}", containerId, ], repoRoot, ); - const ip = ipStdout.trim(); + const ip = firstDockerOutputLine(ipStdout); if (!ip) { return null; }