import assert from "node:assert/strict"; import { spawn } from "node:child_process"; import { createServer } from "node:http"; import test from "node:test"; const bunCommand = process.env.HWLAB_TEST_BUN_COMMAND || process.env.HWLAB_BUN_COMMAND || (process.versions.bun ? process.execPath : "bun"); test("Codex API Responses forwarder maps loopback Codex paths to configurable upstream and preserves stream body", async () => { const upstream = await startUpstream(); const forwarder = await startForwarder(upstream.port, { upstreamPath: "/" }); try { const response = await fetch(`http://127.0.0.1:${forwarder.port}/v1/responses`, { method: "POST", headers: { "content-type": "application/json", authorization: "Bearer test-secret" }, body: JSON.stringify({ model: "gpt-5.5", input: "OK" }) }); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.ok, true); assert.equal(upstream.captured.url, "/responses"); assert.equal(upstream.captured.authorization, "Bearer test-secret"); assert.deepEqual(JSON.parse(upstream.captured.body), { model: "gpt-5.5", input: "OK" }); const health = await fetch(`http://127.0.0.1:${forwarder.port}/health/live`).then((item) => item.json()); assert.equal(health.upstream.host, "127.0.0.1"); assert.equal(health.upstream.directNoProxyRequired, true); } finally { await forwarder.stop(); await upstream.stop(); } }); test("Codex API Responses forwarder keeps /v1 prefix when upstream base is /v1", async () => { const upstream = await startUpstream(); const forwarder = await startForwarder(upstream.port, { upstreamPath: "/v1" }); try { const response = await fetch(`http://127.0.0.1:${forwarder.port}/responses`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ input: "OK" }) }); assert.equal(response.status, 200); assert.equal(upstream.captured.url, "/v1/responses"); } finally { await forwarder.stop(); await upstream.stop(); } }); async function startUpstream() { let captured = null; const server = createServer(async (request, response) => { const chunks = []; for await (const chunk of request) chunks.push(chunk); captured = { url: request.url, method: request.method, authorization: request.headers.authorization || null, body: Buffer.concat(chunks).toString("utf8") }; response.writeHead(200, { "content-type": "application/json" }); response.end(JSON.stringify({ ok: true })); }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); return { port: server.address().port, get captured() { return captured; }, stop: () => new Promise((resolve) => server.close(resolve)) }; } async function startForwarder(upstreamPort, { upstreamPath }) { const port = await freePort(); const child = spawn(bunCommand, ["run", "cmd/hwlab-codex-api-responses-forwarder/main.ts"], { cwd: process.cwd(), env: { ...process.env, HWLAB_CODE_AGENT_CODEX_API_FORWARDER_PORT: String(port), HWLAB_CODE_AGENT_CODEX_API_UPSTREAM_BASE_URL: `http://127.0.0.1:${upstreamPort}${upstreamPath}` }, stdio: ["ignore", "pipe", "pipe"] }); let stderr = ""; child.stderr.on("data", (chunk) => { stderr += chunk; }); await new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error(`forwarder did not start: ${stderr}`)), 5000); child.stdout.on("data", (chunk) => { if (chunk.toString().includes("listening")) { clearTimeout(timer); resolve(); } }); child.on("exit", (code) => { clearTimeout(timer); reject(new Error(`forwarder exited early code=${code}: ${stderr}`)); }); }); return { port, stop: async () => { child.kill("SIGTERM"); await new Promise((resolve) => child.once("exit", resolve)); } }; } async function freePort() { const server = createServer(); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); const port = server.address().port; await new Promise((resolve) => server.close(resolve)); return port; }