Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions tests/e2e/src/cases/aliyun-sls-content-capture-e2e.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { createHash } from "node:crypto";
import { afterAll, beforeAll, describe, expect, test } from "vitest";
import {
AdminClient,
decodedTextFor,
EtcdClient,
SeedClient,
spawnApp,
startMockSls,
startOpenAiUpstream,
Expand Down Expand Up @@ -49,45 +49,45 @@ const ANTHROPIC_PROMPT_TOKEN = "anthropic-prompt-tok-5d1f9c";
const ANTHROPIC_RESPONSE_TOKEN = "anthropic-response-tok-a4e823";

async function seedRouting(
admin: AdminClient,
seed: SeedClient,
upstream: OpenAiUpstream,
streamUpstream: OpenAiUpstream,
messagesUpstream: OpenAiUpstream,
) {
const pk = await admin.createProviderKey({
const pk = await seed.createProviderKey({
display_name: "content-capture-pk",
secret: PROVIDER_SECRET,
api_base: `${upstream.baseUrl}/v1`,
});
await admin.createModel({
await seed.createModel({
display_name: "content-capture-model",
provider: "openai",
model_name: "gpt-4o-mini",
provider_key_id: pk.id,
});
const streamPk = await admin.createProviderKey({
const streamPk = await seed.createProviderKey({
display_name: "content-capture-stream-pk",
secret: PROVIDER_SECRET,
api_base: `${streamUpstream.baseUrl}/v1`,
});
await admin.createModel({
await seed.createModel({
display_name: "content-capture-stream-model",
provider: "openai",
model_name: "gpt-4o-mini",
provider_key_id: streamPk.id,
});
const msgPk = await admin.createProviderKey({
const msgPk = await seed.createProviderKey({
display_name: "content-capture-messages-pk",
secret: PROVIDER_SECRET,
api_base: `${messagesUpstream.baseUrl}/v1`,
});
await admin.createModel({
await seed.createModel({
display_name: "content-capture-messages-model",
provider: "openai",
model_name: "gpt-4o-mini",
provider_key_id: msgPk.id,
});
await admin.createApiKey({
await seed.createApiKey({
key_hash: CALLER_KEY_HASH,
allowed_models: [
"content-capture-model",
Expand Down Expand Up @@ -251,8 +251,8 @@ describe("aliyun_sls content capture e2e (#687): full vs metadata_only", () => {
},
});
apps.push(app);
const admin = new AdminClient(app.adminUrl, app.adminKey);
await admin.createObservabilityExporter({
const seed = new SeedClient(new EtcdClient(), app.etcdPrefix);
await seed.createObservabilityExporter({
name: "sls-full",
enabled: true,
kind: "aliyun_sls",
Expand All @@ -262,7 +262,7 @@ describe("aliyun_sls content capture e2e (#687): full vs metadata_only", () => {
credential_ref: CREDENTIAL_REF,
content_mode: "full",
});
await admin.createObservabilityExporter({
await seed.createObservabilityExporter({
name: "sls-meta",
enabled: true,
kind: "aliyun_sls",
Expand All @@ -272,7 +272,7 @@ describe("aliyun_sls content capture e2e (#687): full vs metadata_only", () => {
credential_ref: CREDENTIAL_REF,
content_mode: "metadata_only",
});
await seedRouting(admin, upstream, streamUpstream, messagesUpstream);
await seedRouting(seed, upstream, streamUpstream, messagesUpstream);

await waitConfigPropagation(async () => {
try {
Expand Down
16 changes: 8 additions & 8 deletions tests/e2e/src/cases/aliyun-sls-exporter-e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { createHash } from "node:crypto";
import { createServer, type IncomingMessage, type Server } from "node:http";
import { afterAll, beforeAll, describe, expect, test } from "vitest";
import {
AdminClient,
EtcdClient,
SeedClient,
pickFreePort,
spawnApp,
startOpenAiUpstream,
Expand Down Expand Up @@ -89,19 +89,19 @@ async function startMockSls(): Promise<MockSls> {
};
}

async function seedRouting(admin: AdminClient, upstream: OpenAiUpstream) {
const pk = await admin.createProviderKey({
async function seedRouting(seed: SeedClient, upstream: OpenAiUpstream) {
const pk = await seed.createProviderKey({
display_name: "sls-exporter-pk",
secret: PROVIDER_SECRET,
api_base: `${upstream.baseUrl}/v1`,
});
await admin.createModel({
await seed.createModel({
display_name: "sls-exporter-model",
provider: "openai",
model_name: "gpt-4o-mini",
provider_key_id: pk.id,
});
await admin.createApiKey({
await seed.createApiKey({
key_hash: CALLER_KEY_HASH,
allowed_models: ["sls-exporter-model"],
});
Expand Down Expand Up @@ -176,8 +176,8 @@ describe("aliyun_sls exporter e2e (#687): DP delivers a signed PutLogs to SLS",
},
});
apps.push(app);
const admin = new AdminClient(app.adminUrl, app.adminKey);
await admin.createObservabilityExporter({
const seed = new SeedClient(new EtcdClient(), app.etcdPrefix);
await seed.createObservabilityExporter({
name: "mock-sls",
enabled: true,
kind: "aliyun_sls",
Expand All @@ -186,7 +186,7 @@ describe("aliyun_sls exporter e2e (#687): DP delivers a signed PutLogs to SLS",
logstore: SLS_LOGSTORE,
credential_ref: CREDENTIAL_REF,
});
await seedRouting(admin, upstream);
await seedRouting(seed, upstream);

await waitConfigPropagation(async () => {
try {
Expand Down
17 changes: 9 additions & 8 deletions tests/e2e/src/cases/allowed-models-e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { createHash } from "node:crypto";
import OpenAI, { APIError } from "openai";
import { afterAll, beforeAll, describe, expect, test } from "vitest";
import {
AdminClient,
EtcdClient,
SeedClient,
spawnApp,
startOpenAiUpstream,
waitConfigPropagation,
Expand All @@ -30,32 +30,33 @@ const CALLER_KEY_HASH = createHash("sha256")
describe("allowed_models e2e: 403 on disallowed model, upstream untouched", () => {
let app: SpawnedApp | undefined;
let upstream: OpenAiUpstream | undefined;
let admin: AdminClient | undefined;
let seed: SeedClient | undefined;
let etcdReachable = false;

beforeAll(async () => {
etcdReachable = await new EtcdClient().ping();
const etcd = new EtcdClient();
etcdReachable = await etcd.ping();
if (!etcdReachable) return;

upstream = await startOpenAiUpstream();
app = await spawnApp();
admin = new AdminClient(app.adminUrl, app.adminKey);
seed = new SeedClient(etcd, app.etcdPrefix);

// Single ProviderKey + two Models. They share the upstream so
// upstream.receivedRequests is a single source of truth — if the
// forbidden call leaks through, it will register here.
const pk = await admin.createProviderKey({
const pk = await seed.createProviderKey({
display_name: "am-e2e-pk",
secret: "sk-mock",
api_base: `${upstream.baseUrl}/v1`,
});
await admin.createModel({
await seed.createModel({
display_name: "am-allowed",
provider: "openai",
model_name: "gpt-4o-mini",
provider_key_id: pk.id,
});
await admin.createModel({
await seed.createModel({
display_name: "am-forbidden",
provider: "openai",
model_name: "gpt-4o-mini",
Expand All @@ -64,7 +65,7 @@ describe("allowed_models e2e: 403 on disallowed model, upstream untouched", () =
// Caller permitted ONLY for am-allowed. am-forbidden exists in
// the snapshot — the 403 must come from authz, not from "model
// not found".
await admin.createApiKey({
await seed.createApiKey({
key_hash: CALLER_KEY_HASH,
allowed_models: ["am-allowed"],
});
Expand Down
15 changes: 8 additions & 7 deletions tests/e2e/src/cases/anthropic-cache-tpm-commit-e2e.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createHash } from "node:crypto";
import { afterAll, beforeAll, describe, expect, test } from "vitest";
import {
AdminClient,
EtcdClient,
SeedClient,
ProxyClient,
spawnApp,
startOpenAiUpstream,
Expand Down Expand Up @@ -109,11 +109,12 @@ describe("anthropic cache tokens count toward TPM (AISIX-Cloud#995)", () => {
let upstreamMsg: OpenAiUpstream | undefined;
let upstreamMsgStream: OpenAiUpstream | undefined;
let upstreamResp: OpenAiUpstream | undefined;
let admin: AdminClient | undefined;
let seed: SeedClient | undefined;
let etcdReachable = false;

beforeAll(async () => {
etcdReachable = await new EtcdClient().ping();
const etcd = new EtcdClient();
etcdReachable = await etcd.ping();
if (!etcdReachable) return;

upstreamMsg = await startOpenAiUpstream({
Expand All @@ -127,7 +128,7 @@ describe("anthropic cache tokens count toward TPM (AISIX-Cloud#995)", () => {
nonStreamBody: anthropicMessageBody(NONSTREAM_USAGE),
});
app = await spawnApp();
admin = new AdminClient(app.adminUrl, app.adminKey);
seed = new SeedClient(etcd, app.etcdPrefix);

// The Anthropic adapter appends `/v1/messages` to the api_base, so the
// provider key points at the bare mock host.
Expand All @@ -136,21 +137,21 @@ describe("anthropic cache tokens count toward TPM (AISIX-Cloud#995)", () => {
upstream: OpenAiUpstream,
caller: string,
) => {
const pk = await admin!.createProviderKey({
const pk = await seed!.createProviderKey({
display_name: `${name}-pk`,
provider: "anthropic",
adapter: "anthropic",
secret: "sk-ant-mock",
api_base: upstream.baseUrl,
});
await admin!.createModel({
await seed!.createModel({
display_name: name,
provider: "anthropic",
model_name: "claude-3-5-haiku-20241022",
provider_key_id: pk.id,
});
// Separate caller keys so each scenario's TPM counter is independent.
await admin!.createApiKey({
await seed!.createApiKey({
key_hash: hash(caller),
allowed_models: [name],
rate_limit: { tpm: TPM },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { createHash } from "node:crypto";
import { afterAll, beforeAll, describe, expect, test } from "vitest";
import {
AdminClient,
EtcdClient,
SeedClient,
spawnApp,
startOpenAiUpstream,
waitConfigPropagation,
type OpenAiUpstream,
type SpawnedApp,
} from "../harness/index.js";
Expand Down Expand Up @@ -33,17 +34,18 @@ const CALLER_KEY_HASH = createHash("sha256")

describe("anthropic content blocks → OpenAI upstream (#722)", () => {
let app: SpawnedApp | undefined;
let admin: AdminClient | undefined;
let seed: SeedClient | undefined;
let etcdReachable = false;
const upstreams: OpenAiUpstream[] = [];

beforeAll(async () => {
etcdReachable = await new EtcdClient().ping();
const etcd = new EtcdClient();
etcdReachable = await etcd.ping();
if (!etcdReachable) return;

app = await spawnApp();
admin = new AdminClient(app.adminUrl, app.adminKey);
await admin.createApiKey({
seed = new SeedClient(etcd, app.etcdPrefix);
await seed.createApiKey({
key_hash: CALLER_KEY_HASH,
allowed_models: ["*"],
});
Expand Down Expand Up @@ -71,22 +73,33 @@ describe("anthropic content blocks → OpenAI upstream (#722)", () => {
},
});
upstreams.push(upstream);
const pk = await admin!.createProviderKey({
const pk = await seed!.createProviderKey({
display_name: `${name}-pk`,
secret: "sk-mock",
api_base: `${upstream.baseUrl}/v1`,
});
await admin!.createModel({
await seed!.createModel({
display_name: name,
provider: "openai",
model_name: "gpt-4o",
provider_key_id: pk.id,
});
// Gate on the DP snapshot, not the store: /v1/models only lists the
// model once the snapshot has it, and only authenticates once the
// caller key has propagated too. Touches no upstream.
await waitConfigPropagation(async () => {
const res = await fetch(`${app!.proxyUrl}/v1/models`, {
headers: { authorization: `Bearer ${CALLER_PLAINTEXT}` },
});
if (res.status !== 200) return false;
const body = (await res.json()) as { data?: Array<{ id?: string }> };
return (body.data ?? []).some((m) => m.id === name);
});
return upstream;
}

test("multi-turn tool loop history reaches the OpenAI upstream intact", async (ctx) => {
if (!etcdReachable || !app || !admin) {
if (!etcdReachable || !app || !seed) {
ctx.skip();
return;
}
Expand Down Expand Up @@ -182,7 +195,7 @@ describe("anthropic content blocks → OpenAI upstream (#722)", () => {
});

test("vision: base64 image block reaches the upstream as an image_url data URL", async (ctx) => {
if (!etcdReachable || !app || !admin) {
if (!etcdReachable || !app || !seed) {
ctx.skip();
return;
}
Expand Down
15 changes: 8 additions & 7 deletions tests/e2e/src/cases/anthropic-count-tokens-e2e.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createHash } from "node:crypto";
import { afterAll, beforeAll, describe, expect, test } from "vitest";
import {
AdminClient,
EtcdClient,
SeedClient,
spawnApp,
startOpenAiUpstream,
waitConfigPropagation,
Expand Down Expand Up @@ -49,33 +49,34 @@ const MODEL_ALIAS = "ct-e2e";
describe("anthropic count_tokens e2e: /v1/messages/count_tokens through the DP (#418)", () => {
let app: SpawnedApp | undefined;
let upstream: OpenAiUpstream | undefined;
let admin: AdminClient | undefined;
let seed: SeedClient | undefined;
let etcdReachable = false;

beforeAll(async () => {
etcdReachable = await new EtcdClient().ping();
const etcd = new EtcdClient();
etcdReachable = await etcd.ping();
if (!etcdReachable) return;

upstream = await startOpenAiUpstream({
// Anthropic's documented count_tokens response shape.
nonStreamBody: { input_tokens: 42 },
});
app = await spawnApp();
admin = new AdminClient(app.adminUrl, app.adminKey);
seed = new SeedClient(etcd, app.etcdPrefix);

// Anthropic bridge appends the path to the bare host (no `/v1`).
const pk = await admin.createProviderKey({
const pk = await seed.createProviderKey({
display_name: "ct-e2e-pk",
secret: "sk-ant-mock",
api_base: upstream.baseUrl,
});
await admin.createModel({
await seed.createModel({
display_name: MODEL_ALIAS,
provider: "anthropic",
model_name: UPSTREAM_MODEL_ID,
provider_key_id: pk.id,
});
await admin.createApiKey({
await seed.createApiKey({
key_hash: CALLER_KEY_HASH,
allowed_models: [MODEL_ALIAS],
});
Expand Down
Loading
Loading