/** * Test-only helpers that stand up a real `node:http` server on a random * port and route a `FetchLike` through it. Used by every provider's * `*.test.ts` to exercise the full HTTP path without mocks. * * Lives under `tests/setup/` (not `src/`) so the universal dep-graph * scan never sees it. */ import { createServer, type IncomingMessage, type Server, type ServerResponse, } from "node:http"; import type { FetchLike } from "../../src/types/provider.js"; export interface RecordedRequest { readonly method: string; readonly path: string; readonly headers: Record; readonly body: string; } export interface RecordingServer { readonly origin: string; readonly requests: RecordedRequest[]; readonly fetch: FetchLike; close: () => Promise; } export type Responder = ( req: IncomingMessage, res: ServerResponse, body: string ) => void | Promise; /** * Stand up an HTTP server on a random port, capture every request, and * return a `FetchLike` that proxies any URL (regardless of original * scheme/host) to the local server preserving the path and method. */ export async function startRecordingServer( respond: Responder ): Promise { const requests: RecordedRequest[] = []; const server: Server = createServer((req, res) => { const chunks: Buffer[] = []; req.on("data", (c) => chunks.push(c)); req.on("end", () => { const body = Buffer.concat(chunks).toString("utf8"); requests.push({ method: req.method ?? "GET", path: req.url ?? "/", headers: { ...req.headers }, body, }); respond(req, res, body); }); }); await new Promise((r) => server.listen(0, "127.0.0.1", r)); const addr = server.address(); if (addr === null || typeof addr === "string") { throw new Error("server.address() returned an unexpected value"); } const origin = `http://127.0.0.1:${addr.port}`; const fetchImpl: FetchLike = async (url, init) => { const u = new URL(url); const rerouted = `${origin}${u.pathname}${u.search}`; return globalThis.fetch(rerouted, init); }; const close = async (): Promise => { await new Promise((resolve, reject) => { server.close((err) => (err ? reject(err) : resolve())); }); }; return { origin, requests, fetch: fetchImpl, close }; } /** Convenience: respond with a JSON body + status. */ export function jsonResponder(body: unknown, status = 200): Responder { return (_req, res) => { res.writeHead(status, { "content-type": "application/json" }); res.end(typeof body === "string" ? body : JSON.stringify(body)); }; }