import http from "node:http"; import net from "node:net"; export interface PlannedResponse { status: number; body: Record; headers?: Record; delayMs?: number; } export interface TokenState { defaultResponse: PlannedResponse; queue: PlannedResponse[]; hits: number; startedAt: number[]; bodies: Array>; } function getFreePort(): Promise { return new Promise((resolve, reject) => { const server = net.createServer(); server.once("error", reject); server.listen(0, "127.0.0.1", () => { const address = server.address(); if (!address || typeof address === "string") { server.close(); reject(new Error("Failed to allocate a free port")); return; } const { port } = address; server.close((err) => { if (err) reject(err); else resolve(port); }); }); }); } export function buildCompletion( text: string, overrides: Partial & { model?: string } = {} ) { return { status: overrides.status ?? 200, delayMs: overrides.delayMs, headers: overrides.headers, body: overrides.body ?? { id: `chatcmpl_${Math.random().toString(16).slice(2, 8)}`, object: "chat.completion", model: overrides.model ?? "test-model", choices: [{ index: 0, message: { role: "assistant", content: text }, finish_reason: "stop" }], usage: { prompt_tokens: 4, completion_tokens: 2, total_tokens: 6 }, }, }; } export function buildError(status: number, message: string, headers: Record = {}) { return { status, headers, body: { error: { message } }, }; } export class MockUpstreamServer { private behaviors = new Map(); private server: http.Server | null = null; private _baseUrl = ""; get baseUrl(): string { if (!this._baseUrl) throw new Error("Server not started yet"); return this._baseUrl; } get isRunning(): boolean { return this.server !== null; } async start(): Promise { const port = await getFreePort(); this.server = http.createServer((req, res) => { void this.handleRequest(req, res); }); await new Promise((resolve, reject) => { this.server!.once("error", reject); this.server!.listen(port, "127.0.0.1", () => resolve()); }); this._baseUrl = `http://127.0.0.1:${port}/v1`; return this._baseUrl; } configureToken( token: string, config: { defaultResponse: PlannedResponse; queue?: PlannedResponse[] } ): void { this.behaviors.set(token, { defaultResponse: config.defaultResponse, queue: [...(config.queue || [])], hits: 0, startedAt: [], bodies: [], }); } getState(token: string): TokenState { const state = this.behaviors.get(token); if (!state) throw new Error(`Unknown token: ${token}`); return state; } resetState(token: string, queue?: PlannedResponse[]): void { const state = this.behaviors.get(token); if (!state) throw new Error(`Unknown token: ${token}`); state.hits = 0; state.startedAt = []; state.bodies = []; state.queue = [...(queue || [])]; } async stop(): Promise { if (!this.server) return; await new Promise((resolve) => this.server?.close(() => resolve())); this.server = null; } private async handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise { const chunks: Buffer[] = []; for await (const chunk of req) { chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); } const authHeader = String(req.headers.authorization || ""); const token = authHeader.replace(/^Bearer\s+/i, "").trim(); const rawBody = Buffer.concat(chunks).toString("utf8"); const parsedBody = rawBody ? JSON.parse(rawBody) : {}; if (req.method === "GET" && req.url?.startsWith("/v1/models")) { res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ data: [{ id: "test-model", object: "model" }] })); return; } if (req.method !== "POST" || !req.url?.startsWith("/v1/chat/completions")) { res.writeHead(404, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: { message: `Unhandled: ${req.method} ${req.url}` } })); return; } const behavior = this.behaviors.get(token); if (!behavior) { res.writeHead(401, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: { message: `Unknown token: ${token || "missing"}` } })); return; } behavior.hits += 1; behavior.startedAt.push(Date.now()); behavior.bodies.push(parsedBody as Record); const planned = behavior.queue.shift() || behavior.defaultResponse; process.stderr.write( `[MOCK] ${token.slice(0, 8)} hit=${behavior.hits} status=${planned.status}\n` ); if (planned.delayMs && planned.delayMs > 0) { await new Promise((r) => setTimeout(r, planned.delayMs)); } const headers: Record = { "Content-Type": "application/json", ...(planned.headers || {}), }; res.writeHead(planned.status, headers); res.end(JSON.stringify(planned.body)); } }