Files
abode/test/tools/jsonBody.test.ts
codingetandCodex 31d4636dde
CI / install-and-build (pull_request) Successful in 1m29s
CI / format (pull_request) Successful in 51s
CI / typecheck-source (pull_request) Successful in 41s
CI / typecheck-tests (pull_request) Successful in 39s
CI / test (pull_request) Successful in 51s
CI / lint (pull_request) Successful in 21s
ci: add pull request quality gates
Co-Authored-By: gpt-5.6-terra <noreply@openai.com>
2026-07-22 21:50:29 +00:00

147 lines
4.6 KiB
TypeScript

import { describe, it, before, after } from "node:test";
import assert from "node:assert/strict";
import Koa from "koa";
import KoaRouter from "@koa/router";
import { createServer } from "node:http";
import type { AddressInfo } from "node:net";
import { jsonBody } from "../../src/webapi/middleware/jsonBody.js";
async function request(
url: string,
opts: { method?: string; body?: unknown; contentType?: string } = {},
): Promise<{ status: number; body: unknown }> {
const method = opts.method ?? "POST";
const bodyStr =
opts.body !== undefined ? JSON.stringify(opts.body) : undefined;
const headers: Record<string, string> = {};
if (bodyStr !== undefined) {
headers["Content-Type"] = opts.contentType ?? "application/json";
headers["Content-Length"] = String(bodyStr.length);
}
const res = await fetch(url, { method, headers, body: bodyStr });
const text = await res.text();
let parsed: unknown;
try {
parsed = JSON.parse(text);
} catch {
parsed = text;
}
return { status: res.status, body: parsed };
}
async function makeTestServer() {
const failValidator = Object.assign((_obj: unknown): _obj is never => false, {
errors: [{ message: "required" }] as unknown[],
schema: { $id: "test-schema", title: "Test", description: "" },
});
const app = new Koa();
const router = new KoaRouter();
router.post("/echo", jsonBody(), async (ctx) => {
ctx.status = 200;
ctx.body = { ok: true, received: ctx.request.body };
});
router.post(
"/fail-validate",
jsonBody({ validate: failValidator as any }),
async (ctx) => {
ctx.status = 200;
ctx.body = { ok: true };
},
);
router.post(
"/with-params/:uid",
jsonBody({ includeParams: ["uid"] }),
async (ctx) => {
ctx.status = 200;
ctx.body = { ok: true, body: ctx.request.body };
},
);
app.use(router.routes());
app.use(router.allowedMethods());
const server = createServer(app.callback());
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
const { port } = server.address() as AddressInfo;
const url = `http://127.0.0.1:${port}`;
const close = () =>
new Promise<void>((resolve, reject) =>
server.close((err) => (err ? reject(err) : resolve())),
);
return { url, close };
}
describe("jsonBody middleware", () => {
let url: string;
let closeServer: () => Promise<void>;
before(async () => {
({ url, close: closeServer } = await makeTestServer());
});
after(() => closeServer());
it("no Content-Type → 200 with empty body (bodyparser sets body to {})", async () => {
const res = await fetch(url + "/echo", { method: "POST" });
const json = await res.json();
assert.equal(res.status, 200);
assert.deepEqual(json.received, {});
});
it("invalid JSON with Content-Type: application/json → 400 from bodyparser", async () => {
const res = await fetch(url + "/echo", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: "not-valid-json",
});
assert.equal(res.status, 400);
});
it("valid JSON body → 200 with echoed body", async () => {
const res = await request(url + "/echo", { body: { hello: "world" } });
assert.equal(res.status, 200);
assert.deepEqual((res.body as any).received, { hello: "world" });
});
it("failing validator → 400 jsonchema_validation_failed with schema and errors", async () => {
const res = await request(url + "/fail-validate", {
body: { any: "thing" },
});
assert.equal(res.status, 400);
assert.equal((res.body as any).error, "jsonchema_validation_failed");
assert.ok((res.body as any).schema, "response includes schema");
assert.ok(
Array.isArray((res.body as any).errors),
"response includes errors",
);
});
it("includeParams: param absent from body → merged in", async () => {
const res = await request(url + "/with-params/user-42", {
body: { other: "field" },
});
assert.equal(res.status, 200);
assert.equal((res.body as any).body?.uid, "user-42");
});
it("includeParams: param present with matching value → ok", async () => {
const res = await request(url + "/with-params/user-42", {
body: { uid: "user-42" },
});
assert.equal(res.status, 200);
});
it("includeParams: mismatching param value → 400 mismatch_params", async () => {
const res = await request(url + "/with-params/user-42", {
body: { uid: "different-uid" },
});
assert.equal(res.status, 400);
assert.equal((res.body as any).error, "mismatch_params");
assert.equal((res.body as any).param, "uid");
});
});