Skip to content
393 changes: 393 additions & 0 deletions src/__tests__/api-conformance.test.ts

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions src/__tests__/azure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,54 @@ describe("Azure OpenAI: journal recording", () => {
});
});

describe("Azure OpenAI: streaming", () => {
it("streaming through Azure deployment path", async () => {
const fixtures: Fixture[] = [
{
match: { userMessage: "stream-test" },
response: { content: "Azure streamed!" },
},
];
instance = await createServer(fixtures);

const { status, body } = await httpPost(
`${instance.url}/openai/deployments/my-gpt4/chat/completions?api-version=2024-02-01`,
{
model: "gpt-4",
stream: true,
messages: [{ role: "user", content: "stream-test" }],
},
);

expect(status).toBe(200);

// Parse SSE events
const events: unknown[] = [];
for (const line of body.split("\n")) {
if (line.startsWith("data: ") && line !== "data: [DONE]") {
events.push(JSON.parse(line.slice(6)));
}
}

expect(events.length).toBeGreaterThanOrEqual(3);

// All chunks should have chat.completion.chunk object type
for (const event of events) {
const ev = event as { object: string };
expect(ev.object).toBe("chat.completion.chunk");
}

// Content should be present across the chunks
const contentParts = events
.map((e) => (e as { choices: [{ delta: { content?: string } }] }).choices[0].delta.content)
.filter(Boolean);
expect(contentParts.join("")).toBe("Azure streamed!");

// Body ends with [DONE]
expect(body).toContain("data: [DONE]");
});
});

describe("Azure OpenAI: 404 when no fixture matches", () => {
it("returns 404 when no fixture matches the request", async () => {
const fixtures: Fixture[] = [
Expand Down
23 changes: 23 additions & 0 deletions src/__tests__/bedrock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,29 @@ describe("POST /model/{modelId}/invoke (error handling)", () => {
expect(body.error.message).toBe("Rate limited");
});

it("returns error in Anthropic format: { type: 'error', error: { type, message } }", async () => {
instance = await createServer(allFixtures);
const res = await post(
`${instance.url}/model/anthropic.claude-3-5-sonnet-20241022-v2:0/invoke`,
{
anthropic_version: "bedrock-2023-05-31",
max_tokens: 512,
messages: [{ role: "user", content: "fail" }],
},
);

expect(res.status).toBe(429);
const body = JSON.parse(res.body);
// Bedrock uses Anthropic Messages format for errors
expect(body.type).toBe("error");
expect(body.error).toBeDefined();
expect(body.error.type).toBe("rate_limit_error");
expect(body.error.message).toBe("Rate limited");
// Should NOT have OpenAI-style fields
expect(body.status).toBeUndefined();
expect(body.error.code).toBeUndefined();
});

it("returns 404 when no fixture matches", async () => {
instance = await createServer(allFixtures);
const res = await post(
Expand Down
Loading
Loading