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}`;
test("serves the page", async () => {
const response = await fetch(base + "/");
expect(response.status).toBe(200);
expect(await response.text()).toContain("
");
});
test("keeps a note", async () => {
const form = new FormData();
form.set("body", "hello from a test");
const posted = await fetch(base + "/notes", { method: "POST", body: form, redirect: "manual" });
expect(posted.status).toBe(303);
expect(await (await fetch(base + "/")).text()).toContain("hello from a test");
});
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 + "/notes", {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: "body=%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 + "/notes", {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: "body=" + "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("refuses a cross-site post", async () => {
const form = new FormData();
form.set("body", "from another site");
const posted = await fetch(base + "/notes", {
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");
});