diff --git a/apps/commandboard-api/src/contract.test.ts b/apps/commandboard-api/src/contract.test.ts index 2b9629d..1f78488 100644 --- a/apps/commandboard-api/src/contract.test.ts +++ b/apps/commandboard-api/src/contract.test.ts @@ -39,6 +39,20 @@ describe("CommandBoard API contracts", () => { expect(body).toEqual({ ok: true, service: "commandboard-api" }); }); + + + it("rejects malformed task JSON with a client error", async () => { + const response = await fetch(`${baseUrl}/api/tasks`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: "{not-json" + }); + const body = await response.json() as { error: string }; + + expect(response.status).toBe(400); + expect(body).toEqual({ error: "Invalid JSON body" }); + }); + it("exposes default plugin contract including product plugins", async () => { const response = await fetch(`${baseUrl}/api/plugins`); const body = await response.json() as { diff --git a/apps/commandboard-api/src/index.ts b/apps/commandboard-api/src/index.ts index 58b6166..7d9858f 100644 --- a/apps/commandboard-api/src/index.ts +++ b/apps/commandboard-api/src/index.ts @@ -93,7 +93,14 @@ async function route(request: IncomingMessage, response: ServerResponse) { } if (request.method === "POST" && url.pathname === "/api/tasks") { - const body = await readJson(request); + let body: unknown; + try { + body = await readJson(request); + } catch { + json(response, 400, { error: "Invalid JSON body" }); + return; + } + const result = validate("task", body); if (!result.ok) { json(response, 422, { errors: result.errors });