mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-29 23:53:36 +00:00
fix(qa): reject malformed mock OpenAI JSON
This commit is contained in:
@@ -4265,6 +4265,34 @@ describe("qa mock openai server", () => {
|
||||
expect(body.error.message).toContain("Malformed JSON body");
|
||||
});
|
||||
|
||||
it("rejects malformed OpenAI-compatible JSON without crashing the mock server", async () => {
|
||||
const server = await startQaMockOpenAiServer({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
});
|
||||
cleanups.push(async () => {
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
for (const path of ["/v1/responses", "/v1/embeddings", "/v1/images/generations"]) {
|
||||
const response = await fetch(`${server.baseUrl}${path}`, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: "{bad",
|
||||
});
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
const body = (await response.json()) as {
|
||||
error: { type: string; message: string };
|
||||
};
|
||||
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("defaults empty-string Anthropic /v1/messages model to claude-opus-4-8", async () => {
|
||||
// Regression for the loop-7 Copilot finding: a bare `typeof
|
||||
// body.model === "string"` check lets an empty-string model leak
|
||||
|
||||
@@ -228,6 +228,23 @@ function readBody(req: IncomingMessage): Promise<string> {
|
||||
});
|
||||
}
|
||||
|
||||
function parseOpenAiJsonBody(raw: string): Record<string, unknown> | null {
|
||||
try {
|
||||
return raw ? (JSON.parse(raw) as Record<string, unknown>) : {};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writeOpenAiMalformedJsonError(res: ServerResponse, label: string) {
|
||||
writeJson(res, 400, {
|
||||
error: {
|
||||
type: "invalid_request_error",
|
||||
message: `Malformed JSON body for ${label} request.`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function transcriptionTextForAudioRequest(rawBody: string) {
|
||||
if (rawBody.length >= QA_GROUP_AUDIO_MIN_MULTIPART_BODY_CHARS) {
|
||||
return QA_GROUP_AUDIO_TRANSCRIPTION_TEXT;
|
||||
@@ -3418,7 +3435,11 @@ 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 = raw ? (JSON.parse(raw) as Record<string, unknown>) : {};
|
||||
const body = parseOpenAiJsonBody(raw);
|
||||
if (!body) {
|
||||
writeOpenAiMalformedJsonError(res, "OpenAI Images");
|
||||
return;
|
||||
}
|
||||
imageGenerationRequests.push(body);
|
||||
if (imageGenerationRequests.length > 20) {
|
||||
imageGenerationRequests.splice(0, imageGenerationRequests.length - 20);
|
||||
@@ -3442,7 +3463,11 @@ export async function startQaMockOpenAiServer(params?: { host?: string; port?: n
|
||||
}
|
||||
if (req.method === "POST" && url.pathname === "/v1/embeddings") {
|
||||
const raw = await readBody(req);
|
||||
const body = raw ? (JSON.parse(raw) as Record<string, unknown>) : {};
|
||||
const body = parseOpenAiJsonBody(raw);
|
||||
if (!body) {
|
||||
writeOpenAiMalformedJsonError(res, "OpenAI Embeddings");
|
||||
return;
|
||||
}
|
||||
const inputs = extractEmbeddingInputTexts(body.input);
|
||||
writeJson(res, 200, {
|
||||
object: "list",
|
||||
@@ -3464,7 +3489,11 @@ export async function startQaMockOpenAiServer(params?: { host?: string; port?: n
|
||||
}
|
||||
if (req.method === "POST" && url.pathname === "/v1/responses") {
|
||||
const raw = await readBody(req);
|
||||
const body = raw ? (JSON.parse(raw) as Record<string, unknown>) : {};
|
||||
const body = parseOpenAiJsonBody(raw);
|
||||
if (!body) {
|
||||
writeOpenAiMalformedJsonError(res, "OpenAI Responses");
|
||||
return;
|
||||
}
|
||||
const input = Array.isArray(body.input) ? (body.input as ResponsesInputItem[]) : [];
|
||||
const events = await buildResponsesPayload(body, scenarioState);
|
||||
const resolvedModel = typeof body.model === "string" ? body.model : "";
|
||||
|
||||
Reference in New Issue
Block a user