server.test.ts

2.3 KB · raw

1import { Database } from "bun:sqlite";2import { expect, test } from "bun:test";3import { createApp } from "../src/server/app.ts";4import type { Config } from "../src/server/config.ts";5import { RESERVED_NAMES } from "../src/server/reserved.ts";67const config: Config = {8	domain: "kepr.example",9	dataDir: "data",10	adminPasswordHash: "$argon2id$x",11	bucket: {12		kind: "s3",13		endpoint: "https://s3.fr-par.scw.cloud",14		name: "koh",15		region: "fr-par",16		accessKeyId: "read",17		secretAccessKey: "only",18	},19};2021const registered = () =>22	createApp(config, new Database(":memory:"))23		.routes.filter((route) => !(route.method === "ALL" && route.path === "/*"))24		.map((route) => `${route.method} ${route.path}`);2526test("the app registers exactly the routes in docs/routes.txt, both ways (§26)", async () => {27	const listed = (await Bun.file("docs/routes.txt").text())28		.split("\n")29		.filter((line) => line.trim() !== "" && !line.startsWith("#"));30	const routes = new Set(registered());31	expect(registered().filter((route) => !listed.includes(route))).toEqual([]);32	expect(listed.filter((route) => !routes.has(route))).toEqual([]);33});3435test("every literal first segment of a route is a reserved repo name", () => {36	const firsts = registered()37		.map((route) => route.split(" ")[1]?.split("/")[1] ?? "")38		.filter((segment) => segment !== "" && !segment.startsWith(":"));39	expect([...new Set(firsts)].sort()).toEqual([...RESERVED_NAMES].sort());40});4142test("responses carry the §25 content security policy", async () => {43	const response = await createApp(config, new Database(":memory:")).request("/");44	const csp = response.headers.get("content-security-policy") ?? "";45	expect(csp).toContain("script-src 'self'");46	expect(csp).not.toContain("unsafe-inline");47	expect(csp).toContain("img-src 'self' https://s3.fr-par.scw.cloud");48	expect(csp).toContain("frame-ancestors 'none'");49	expect(response.headers.get("x-content-type-options")).toBe("nosniff");50});5152test("request bodies over 64 KB are refused", async () => {53	const app = createApp(config, new Database(":memory:"));54	app.post("/echo", async (c) => c.text(String((await c.req.text()).length)));55	const post = (bytes: number) => app.request("/echo", { method: "POST", body: "x".repeat(bytes) });56	expect((await post(64 * 1024)).status).toBe(200);57	expect((await post(64 * 1024 + 1)).status).toBe(413);58});