fix(qa): reject non-object mock Anthropic JSON

This commit is contained in:
Vincent Koc
2026-06-21 13:13:37 +02:00
parent 6f3af56952
commit 5e86c7eef4
2 changed files with 25 additions and 22 deletions

View File

@@ -4240,7 +4240,7 @@ describe("qa mock openai server", () => {
expect(body).not.toContain("HEARTBEAT_OK");
});
it("rejects malformed Anthropic /v1/messages JSON with an invalid_request_error", async () => {
it("rejects malformed or non-object Anthropic /v1/messages JSON", async () => {
const server = await startQaMockOpenAiServer({
host: "127.0.0.1",
port: 0,
@@ -4249,20 +4249,25 @@ describe("qa mock openai server", () => {
await server.stop();
});
const response = await fetch(`${server.baseUrl}/v1/messages`, {
method: "POST",
headers: { "content-type": "application/json" },
body: '{"model":"claude-opus-4-8","messages":[',
});
for (const rawBody of ['{"model":"claude-opus-4-8","messages":[', "null", "[]", '"text"']) {
const response = await fetch(`${server.baseUrl}/v1/messages`, {
method: "POST",
headers: { "content-type": "application/json" },
body: rawBody,
});
expect(response.status).toBe(400);
const body = (await response.json()) as {
type: string;
error: { type: string; message: string };
};
expect(body.type).toBe("error");
expect(body.error.type).toBe("invalid_request_error");
expect(body.error.message).toContain("Malformed JSON body");
expect(response.status).toBe(400);
const body = (await response.json()) as {
type: string;
error: { type: string; message: string };
};
expect(body.type).toBe("error");
expect(body.error.type).toBe("invalid_request_error");
expect(body.error.message).toContain("Malformed JSON body");
}
const health = await fetch(`${server.baseUrl}/healthz`);
expect(health.status).toBe(200);
});
it("rejects malformed OpenAI-compatible JSON without crashing the mock server", async () => {

View File

@@ -228,7 +228,7 @@ function readBody(req: IncomingMessage): Promise<string> {
});
}
function parseOpenAiJsonBody(raw: string): Record<string, unknown> | null {
function parseJsonObjectBody(raw: string): Record<string, unknown> | null {
try {
const parsed = raw ? (JSON.parse(raw) as unknown) : {};
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
@@ -3438,7 +3438,7 @@ export async function startQaMockOpenAiServer(params?: { host?: string; port?: n
}
if (req.method === "POST" && url.pathname === "/v1/images/generations") {
const raw = await readBody(req);
const body = parseOpenAiJsonBody(raw);
const body = parseJsonObjectBody(raw);
if (!body) {
writeOpenAiMalformedJsonError(res, "OpenAI Images");
return;
@@ -3466,7 +3466,7 @@ export async function startQaMockOpenAiServer(params?: { host?: string; port?: n
}
if (req.method === "POST" && url.pathname === "/v1/embeddings") {
const raw = await readBody(req);
const body = parseOpenAiJsonBody(raw);
const body = parseJsonObjectBody(raw);
if (!body) {
writeOpenAiMalformedJsonError(res, "OpenAI Embeddings");
return;
@@ -3492,7 +3492,7 @@ export async function startQaMockOpenAiServer(params?: { host?: string; port?: n
}
if (req.method === "POST" && url.pathname === "/v1/responses") {
const raw = await readBody(req);
const body = parseOpenAiJsonBody(raw);
const body = parseJsonObjectBody(raw);
if (!body) {
writeOpenAiMalformedJsonError(res, "OpenAI Responses");
return;
@@ -3534,10 +3534,8 @@ export async function startQaMockOpenAiServer(params?: { host?: string; port?: n
}
if (req.method === "POST" && url.pathname === "/v1/messages") {
const raw = await readBody(req);
let body: AnthropicMessagesRequest;
try {
body = raw ? (JSON.parse(raw) as AnthropicMessagesRequest) : {};
} catch {
const body = parseJsonObjectBody(raw) as AnthropicMessagesRequest | null;
if (!body) {
writeJson(res, 400, {
type: "error",
error: {