Files
diegosouzapw--omniroute/tests/integration/proxy-context-passthrough.test.ts
2026-07-13 13:39:12 +08:00

192 lines
6.6 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import net from "node:net";
import { createChatPipelineHarness } from "./_chatPipelineHarness.ts";
// Proxy-context passthrough on execution paths:
// 1. combo targets must each run under their OWN connection's proxy
// 2. /v1/messages/count_tokens must apply the connection proxy to the
// provider-side count call (it used to run with no proxy context at all)
const harness = await createChatPipelineHarness("proxy-context-passthrough");
const { buildClaudeResponse, buildRequest, combosDb, handleChat, resetStorage, seedConnection, settingsDb, toPlainHeaders } =
harness;
const proxiesDb = await import("../../src/lib/db/proxies.ts");
const { resolveProxyForRequest } = await import("../../open-sse/utils/proxyFetch.ts");
const countTokensRoute = await import("../../src/app/api/v1/messages/count_tokens/route.ts");
type TcpStub = { port: number; close: () => Promise<void> };
// T14 fast-fail does a real TCP reachability check on the proxy host before
// entering the proxy context — back each fake proxy with a real listener.
async function startTcpStub(): Promise<TcpStub> {
const server = net.createServer((socket) => socket.destroy());
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
const address = server.address();
if (!address || typeof address === "string") throw new Error("no tcp stub address");
return {
port: address.port,
close: () =>
new Promise<void>((resolve) => {
server.close(() => resolve());
}),
};
}
function activeProxyUrl(): string | null {
const resolved = resolveProxyForRequest("https://upstream.example/v1");
return resolved?.proxyUrl ?? null;
}
test.beforeEach(async () => {
await resetStorage();
});
test.afterEach(async () => {
await resetStorage();
});
test.after(async () => {
await harness.cleanup();
});
test("combo targets each execute under their own connection's proxy", async () => {
const stubA = await startTcpStub();
const stubB = await startTcpStub();
try {
const openaiConn = (await seedConnection("openai", {
apiKey: "sk-openai-proxy-a",
})) as any;
const claudeConn = (await seedConnection("claude", {
apiKey: "sk-claude-proxy-b",
})) as any;
await settingsDb.updateSettings({ requestRetry: 0, maxRetryIntervalSec: 0 });
await proxiesDb.createProxyAndAssign(
{ name: "proxy-a", type: "http", host: "127.0.0.1", port: stubA.port },
{ scope: "account", scopeId: openaiConn.id }
);
await proxiesDb.createProxyAndAssign(
{ name: "proxy-b", type: "http", host: "127.0.0.1", port: stubB.port },
{ scope: "account", scopeId: claudeConn.id }
);
await combosDb.createCombo({
name: "proxy-per-target-combo",
strategy: "priority",
config: { maxRetries: 0, retryDelayMs: 0, fallbackDelayMs: 0 },
models: ["openai/gpt-4o-mini", "claude/claude-3-5-sonnet-20241022"],
});
const proxySeen: Record<string, string | null> = {};
globalThis.fetch = async (_url: any, init: any = {}) => {
const headers = toPlainHeaders(init.headers);
const authHeader = headers.authorization ?? headers.Authorization;
const apiKeyHeader = headers["x-api-key"] ?? headers["X-Api-Key"];
if (authHeader === "Bearer sk-openai-proxy-a") {
proxySeen.openai = activeProxyUrl();
return new Response(JSON.stringify({ error: { message: "upstream unavailable" } }), {
status: 503,
headers: { "Content-Type": "application/json" },
});
}
if (apiKeyHeader === "sk-claude-proxy-b" || authHeader === "Bearer sk-claude-proxy-b") {
proxySeen.claude = activeProxyUrl();
return buildClaudeResponse("served through proxy-b");
}
throw new Error(`unexpected upstream headers: ${JSON.stringify(headers)}`);
};
const response = await handleChat(
buildRequest({
body: {
model: "proxy-per-target-combo",
stream: false,
messages: [{ role: "user", content: "proxy per target" }],
},
})
);
const body = (await response.json()) as any;
assert.equal(response.status, 200);
assert.equal(body.choices[0].message.content, "served through proxy-b");
assert.ok(proxySeen.openai, "openai target must run inside a proxy context");
assert.ok(
proxySeen.openai!.includes(`127.0.0.1:${stubA.port}`),
`openai target must use proxy-a (saw ${proxySeen.openai})`
);
assert.ok(proxySeen.claude, "claude target must run inside a proxy context");
assert.ok(
proxySeen.claude!.includes(`127.0.0.1:${stubB.port}`),
`claude fallback target must use proxy-b (saw ${proxySeen.claude})`
);
} finally {
await stubA.close();
await stubB.close();
}
});
test("count_tokens provider call runs inside the connection's proxy context", async () => {
const stub = await startTcpStub();
try {
const claudeConn = (await seedConnection("claude", {
apiKey: "sk-claude-count-tokens",
})) as any;
await settingsDb.updateSettings({ requestRetry: 0, maxRetryIntervalSec: 0 });
await proxiesDb.createProxyAndAssign(
{ name: "proxy-count", type: "http", host: "127.0.0.1", port: stub.port },
{ scope: "account", scopeId: claudeConn.id }
);
let providerCallProxy: string | null | undefined;
globalThis.fetch = async (url: any, _init: any = {}) => {
const target = typeof url === "string" ? url : String(url);
if (target.includes("count_tokens")) {
providerCallProxy = activeProxyUrl();
return new Response(JSON.stringify({ input_tokens: 42 }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
throw new Error(`unexpected upstream url: ${target}`);
};
const response = await countTokensRoute.POST(
new Request("http://localhost/v1/messages/count_tokens", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "claude/claude-3-5-sonnet-20241022",
messages: [{ role: "user", content: "count me" }],
}),
})
);
const body = (await response.json()) as any;
assert.equal(response.status, 200);
assert.equal(body.source, "provider", "provider-side count should be used");
assert.equal(body.input_tokens, 42);
assert.ok(
providerCallProxy,
"count_tokens provider call must run inside the connection's proxy context"
);
assert.ok(
providerCallProxy!.includes(`127.0.0.1:${stub.port}`),
`count_tokens must use the account proxy (saw ${providerCallProxy})`
);
} finally {
await stub.close();
}
});