import { expect, test } from "bun:test"; import { mkdtempSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; process.env.FORGE_APP_DATA = mkdtempSync(join(tmpdir(), "app-")); process.env.PORT = "0"; const { server } = await import("./server.ts"); const base = `http://${server.hostname}:${server.port}`; async function accession(description: string): Promise { const form = new FormData(); form.set("description", description); const posted = await fetch(base + "/items", { method: "POST", body: form, redirect: "manual" }); expect(posted.status).toBe(303); return posted.headers.get("location")!; } test("serves the entrance", async () => { const response = await fetch(base + "/"); expect(response.status).toBe(200); expect(await response.text()).toContain("

lost-property

"); }); test("accessions a lost item with a catalogue number and a vitrine", async () => { const location = await accession("a single brown leather glove, left hand"); expect(location).toMatch(/^\/items\/\d+$/); const page = await (await fetch(base + location)).text(); expect(page).toContain("a single brown leather glove"); expect(page).toMatch(/LP \d{4}\.\d{4}/); expect(page).toContain("Outerwear and Umbrellas"); }); test("lists an accessioned item on the entrance", async () => { await accession("a set of house keys on a red fob"); const page = await (await fetch(base + "/")).text(); expect(page).toContain("a set of house keys on a red fob"); }); test("an empty description is not accessioned", async () => { const form = new FormData(); form.set("description", " "); const posted = await fetch(base + "/items", { method: "POST", body: form, redirect: "manual" }); expect(posted.status).toBe(303); expect(posted.headers.get("location")).toBe("/"); }); test("a case that does not exist answers 404", async () => { const response = await fetch(base + "/items/999999"); expect(response.status).toBe(404); }); test("a visitor leaves a note saying they saw one like it", async () => { const location = await accession("reading glasses, tortoiseshell frame"); const form = new FormData(); form.set("kind", "sighting"); form.set("body", "Saw one just like this at the market on Tuesday."); const posted = await fetch(base + location + "/notes", { method: "POST", body: form, redirect: "manual" }); expect(posted.status).toBe(303); const page = await (await fetch(base + location)).text(); expect(page).toContain("Saw one like it"); expect(page).toContain("Saw one just like this at the market on Tuesday."); }); test("a visitor leaves a note saying they have the other half", async () => { const location = await accession("a single earring, gold hoop"); const form = new FormData(); form.set("kind", "match"); form.set("body", "I have the matching earring, email me."); await fetch(base + location + "/notes", { method: "POST", body: form, redirect: "manual" }); const page = await (await fetch(base + location)).text(); expect(page).toContain("Has the other half"); expect(page).toContain("I have the matching earring, email me."); }); test("a note needs a valid kind", async () => { const location = await accession("a paperback book, dog-eared"); const form = new FormData(); form.set("kind", "not-a-real-kind"); form.set("body", "hello"); const posted = await fetch(base + location + "/notes", { method: "POST", body: form, redirect: "manual" }); expect(posted.status).toBe(400); }); test("a note cannot be left on a case that does not exist", async () => { const form = new FormData(); form.set("kind", "sighting"); form.set("body", "hello"); const posted = await fetch(base + "/items/999999/notes", { method: "POST", body: form, redirect: "manual" }); expect(posted.status).toBe(404); }); test("stands on the security floor", async () => { const response = await fetch(base + "/"); expect(response.headers.get("content-security-policy")).toContain("default-src 'self'"); expect(response.headers.get("x-content-type-options")).toBe("nosniff"); }); test("a bug answers with the floor, not the runtime's page", async () => { // A percent-escape that is not one makes the search params throw. const response = await fetch(base + "/items", { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: "description=%E0%A4%A", }); expect(response.status).toBe(200); expect(response.headers.get("content-security-policy")).toContain("default-src 'self'"); }); test("a body is capped by the bytes read, whatever it claims", async () => { // The cap counts bytes as they arrive and never reads Content-Length, so // a chunked body with no length is refused the same way this one is. const response = await fetch(base + "/items", { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: "description=" + "x".repeat(1024 * 1024), }); expect(response.status).toBe(413); }); test("HEAD sees the app up", async () => { const response = await server.fetch(new Request(base + "/", { method: "HEAD" })); expect(response.status).toBe(200); expect(await response.text()).toBe(""); }); test("HEAD on a case answers like GET without a body", async () => { const location = await accession("a stuffed teddy bear, missing an eye"); const response = await server.fetch(new Request(base + location, { method: "HEAD" })); expect(response.status).toBe(200); expect(await response.text()).toBe(""); }); test("refuses a cross-site post", async () => { const form = new FormData(); form.set("description", "from another site"); const posted = await fetch(base + "/items", { method: "POST", body: form, redirect: "manual", headers: { origin: "https://evil.example", "sec-fetch-site": "cross-site" }, }); expect(posted.status).toBe(403); expect(await (await fetch(base + "/")).text()).not.toContain("from another site"); }); test("escapes a description that carries markup", async () => { const location = await accession(""); const page = await (await fetch(base + location)).text(); expect(page).not.toContain(""); expect(page).toContain("<script>"); });