wren/val Public Public: anyone can read this; nobody can change it.
forked from benjivers/val
workbenchthis machine
Files 73f2147e185b17ec35d542e249855f7a4d951750
| 1 | import { expect, test } from "bun:test"; |
| 2 | import { mkdtempSync } from "node:fs"; |
| 3 | import { tmpdir } from "node:os"; |
| 4 | import { join } from "node:path"; |
| 5 | |
| 6 | process.env.FORGE_APP_DATA = mkdtempSync(join(tmpdir(), "app-")); |
| 7 | process.env.PORT = "0"; |
| 8 | const { server } = await import("./server.ts"); |
| 9 | const base = `http://${server.hostname}:${server.port}`; |
| 10 | |
| 11 | test("serves the page", async () => { |
| 12 | const response = await fetch(base + "/"); |
| 13 | expect(response.status).toBe(200); |
| 14 | expect(await response.text()).toContain("<h1>"); |
| 15 | }); |
| 16 | |
| 17 | test("keeps a note", async () => { |
| 18 | const form = new FormData(); |
| 19 | form.set("body", "hello from a test"); |
| 20 | const posted = await fetch(base + "/notes", { method: "POST", body: form, redirect: "manual" }); |
| 21 | expect(posted.status).toBe(303); |
| 22 | expect(await (await fetch(base + "/")).text()).toContain("hello from a test"); |
| 23 | }); |
| 24 | |
| 25 | test("stands on the security floor", async () => { |
| 26 | const response = await fetch(base + "/"); |
| 27 | expect(response.headers.get("content-security-policy")).toContain("default-src 'self'"); |
| 28 | expect(response.headers.get("x-content-type-options")).toBe("nosniff"); |
| 29 | }); |
| 30 | |
| 31 | test("a bug answers with the floor, not the runtime's page", async () => { |
| 32 | // A percent-escape that is not one makes the search params throw. |
| 33 | const response = await fetch(base + "/notes", { |
| 34 | method: "POST", |
| 35 | headers: { "content-type": "application/x-www-form-urlencoded" }, |
| 36 | body: "body=%E0%A4%A", |
| 37 | }); |
| 38 | expect(response.status).toBe(200); |
| 39 | expect(response.headers.get("content-security-policy")).toContain("default-src 'self'"); |
| 40 | }); |
| 41 | |
| 42 | test("a body is capped by the bytes read, whatever it claims", async () => { |
| 43 | // The cap counts bytes as they arrive and never reads Content-Length, so |
| 44 | // a chunked body with no length is refused the same way this one is. |
| 45 | const response = await fetch(base + "/notes", { |
| 46 | method: "POST", |
| 47 | headers: { "content-type": "application/x-www-form-urlencoded" }, |
| 48 | body: "body=" + "x".repeat(1024 * 1024), |
| 49 | }); |
| 50 | expect(response.status).toBe(413); |
| 51 | }); |
| 52 | |
| 53 | test("HEAD sees the app up", async () => { |
| 54 | const response = await server.fetch(new Request(base + "/", { method: "HEAD" })); |
| 55 | expect(response.status).toBe(200); |
| 56 | expect(await response.text()).toBe(""); |
| 57 | }); |
| 58 | |
| 59 | test("refuses a cross-site post", async () => { |
| 60 | const form = new FormData(); |
| 61 | form.set("body", "from another site"); |
| 62 | const posted = await fetch(base + "/notes", { |
| 63 | method: "POST", |
| 64 | body: form, |
| 65 | redirect: "manual", |
| 66 | headers: { origin: "https://evil.example", "sec-fetch-site": "cross-site" }, |
| 67 | }); |
| 68 | expect(posted.status).toBe(403); |
| 69 | expect(await (await fetch(base + "/")).text()).not.toContain("from another site"); |
| 70 | }); |