1import { expect, test } from "bun:test";2import { createApp } from "../src/server/app.ts";3import type { Config } from "../src/server/config.ts";45const config: Config = {6 domain: "kepr.example",7 dataDir: "data",8 adminPasswordHash: "$argon2id$x",9 bucket: {10 kind: "s3",11 endpoint: "https://s3.fr-par.scw.cloud",12 name: "koh",13 region: "fr-par",14 accessKeyId: "read",15 secretAccessKey: "only",16 },17};1819test("every route the app registers is listed in docs/routes.txt", async () => {20 const listed = new Set(21 (await Bun.file("docs/routes.txt").text())22 .split("\n")23 .filter((line) => line.trim() !== "" && !line.startsWith("#")),24 );25 const registered = createApp(config)26 .routes.filter((route) => !(route.method === "ALL" && route.path === "/*"))27 .map((route) => `${route.method} ${route.path}`);28 expect(registered.filter((route) => !listed.has(route))).toEqual([]);29});3031test("responses carry the §25 content security policy", async () => {32 const response = await createApp(config).request("/");33 const csp = response.headers.get("content-security-policy") ?? "";34 expect(csp).toContain("script-src 'self'");35 expect(csp).not.toContain("unsafe-inline");36 expect(csp).toContain("img-src 'self' https://s3.fr-par.scw.cloud");37 expect(csp).toContain("frame-ancestors 'none'");38 expect(response.headers.get("x-content-type-options")).toBe("nosniff");39});4041test("request bodies over 64 KB are refused", async () => {42 const app = createApp(config);43 app.post("/echo", async (c) => c.text(String((await c.req.text()).length)));44 const post = (bytes: number) => app.request("/echo", { method: "POST", body: "x".repeat(bytes) });45 expect((await post(64 * 1024)).status).toBe(200);46 expect((await post(64 * 1024 + 1)).status).toBe(413);47});