Files
2026-07-13 13:39:12 +08:00

86 lines
3.0 KiB
TypeScript

// tests/integration/_comboRoutingHarness.ts
// Recording-fetch helper for combo routing-decision tests.
// Wraps the chat pipeline harness so each strategy test can assert WHICH
// provider/model was dispatched, in what order, without writing a fetch mock.
import { createChatPipelineHarness } from "./_chatPipelineHarness.ts";
// Map an upstream request URL to the provider id it targets. Mirrors the URL
// shapes asserted in combo-routing-e2e.test.ts. Extend as providers are added.
export function providerFromUrl(url: string): string {
if (url.includes("?beta=true")) return "claude";
if (url.endsWith("generateContent") || url.includes(":generateContent")) return "gemini";
if (url.includes("/chat/completions")) return "openai";
return "unknown";
}
export type DispatchCall = {
index: number;
provider: string;
url: string;
authorization: string | undefined;
model: string | undefined;
};
// A scripted response decides, per call index or provider, whether the upstream
// call succeeds or returns a failure status. Default: every call succeeds (200).
export type ResponseScript = (call: DispatchCall) => Response | undefined;
export async function createComboRoutingHarness(prefix: string) {
const base = await createChatPipelineHarness(prefix);
// Records every upstream call in dispatch order.
const calls: DispatchCall[] = [];
function readModel(init: any): string | undefined {
try {
const body = typeof init?.body === "string" ? JSON.parse(init.body) : init?.body;
return body?.model;
} catch {
return undefined;
}
}
// Install a recording fetch. `script` may return a Response to override the
// default success (e.g. a 503 to force failover); returning undefined uses the
// provider's default success response.
function installRecordingFetch(script: ResponseScript = () => undefined) {
calls.length = 0;
globalThis.fetch = async (url: any, init: any = {}) => {
const u = String(url);
const provider = providerFromUrl(u);
const headers = base.toPlainHeaders(init.headers);
const call: DispatchCall = {
index: calls.length,
provider,
url: u,
authorization: headers.authorization,
model: readModel(init),
};
calls.push(call);
const override = script(call);
if (override) return override;
if (provider === "claude") return base.buildClaudeResponse("ok");
if (provider === "gemini") return base.buildGeminiResponse("ok");
return base.buildOpenAIResponse("ok");
};
}
// Convenience: a failure Response with a given status.
function failure(status: number, message = "scripted failure"): Response {
return new Response(JSON.stringify({ error: { message } }), {
status,
headers: { "Content-Type": "application/json" },
});
}
return {
...base,
calls,
installRecordingFetch,
failure,
providersSeen: () => calls.map((c) => c.provider),
authKeysSeen: () => calls.map((c) => c.authorization),
};
}