import type { AddressInfo } from "node:net"; import type { Server } from "node:http"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { parseTokens } from "../../content/src/config.ts"; import { createHttpServer } from "../../content/src/http.ts"; import { rig, type Rig } from "../../content/test/helpers.ts"; import { ContentApi } from "../src/client.ts"; import { createAdminServer } from "../src/server.ts"; const ADMIN = "d".repeat(24); const INTAKE = "i".repeat(24); let r: Rig; let content: Server; let admin: Server; let base: string; let contentUrl: string; const listen = async (s: Server) => { await new Promise((ok) => s.listen(0, "127.0.0.1", ok)); return `http://127.0.0.1:${(s.address() as AddressInfo).port}`; }; const file = (body: unknown) => fetch(`${contentUrl}/api/submissions`, { method: "POST", headers: { authorization: `Bearer ${INTAKE}`, "content-type": "application/json" }, body: JSON.stringify(body), }).then((res) => res.json() as Promise<{ id: number; receipt: string }>); let first: number; let second: number; beforeAll(async () => { r = rig(); r.service.scan(); content = createHttpServer(r.service, { tokens: parseTokens(`admin:${ADMIN},intake:${INTAKE}`) }); contentUrl = await listen(content); admin = createAdminServer(new ContentApi(contentUrl, ADMIN)); base = await listen(admin); const a = await file({ kind: "inquiry", type: "patiun", name: "Ann Bold", email: "ann@example.com", fields: [{ label: "Details", value: "Sixty proxies\nfor a Golgari deck" }], attachment: { name: "list.txt", data: Buffer.from("1 Sol Ring").toString("base64") }, }); first = a.id; await fetch(`${contentUrl}/api/submissions/${a.id}/delivery`, { method: "POST", headers: { authorization: `Bearer ${INTAKE}`, "content-type": "application/json" }, body: JSON.stringify({ receipt: a.receipt, status: "failed", error: "Connection refused" }), }); second = (await file({ kind: "order", type: "proxy-cards", name: "Bo", email: "bo@example.com", fields: [{ label: "Quantity", value: "75 cards" }] })).id; }); afterAll(() => { admin.close(); content.close(); r.done(); }); const get = async (path: string) => { const res = await fetch(base + path, { redirect: "manual" }); return { status: res.status, text: await res.text(), headers: res.headers }; }; const post = async (path: string, form: Record, headers: Record = { "sec-fetch-site": "same-origin" }) => { const res = await fetch(base + path, { method: "POST", redirect: "manual", headers: { "content-type": "application/x-www-form-urlencoded", ...headers }, body: new URLSearchParams(form).toString(), }); return { status: res.status, location: res.headers.get("location") ?? "" }; }; describe("admin inbox", () => { it("lists submissions newest first with their email status, escaped", async () => { const page = await get("/inbox"); expect(page.status).toBe(200); expect(page.text.indexOf("Bo")).toBeLessThan(page.text.indexOf("Ann <b>")); expect(page.text).not.toContain("Bold"); expect(page.text).toContain("Email failed"); expect(page.text).toContain('2'); }); it("shows one with the failure and the attachment", async () => { const page = await get(`/inbox/${first}`); expect(page.text).toContain("Connection refused"); expect(page.text).toContain("
Sixty proxies\nfor a Golgari deck
"); expect(page.text).toContain(`/inbox/${first}/attachment`); const dl = await fetch(`${base}/inbox/${first}/attachment`); expect(dl.headers.get("content-disposition")).toContain("attachment"); expect(await dl.text()).toBe("1 Sol Ring"); expect((await get(`/inbox/${second}/attachment`)).status).toBe(404); }); it("marks handled and back, from this page only", async () => { expect((await post(`/inbox/${second}/handled`, { handled: "1" }, { "sec-fetch-site": "cross-site" })).status).toBe(403); const done = await post(`/inbox/${second}/handled`, { handled: "1" }); expect(done.status).toBe(303); expect(done.location).toMatch(/^\/inbox\?msg=/); const open = await get("/inbox"); expect(open.text).not.toContain("Bo"); expect((await get("/inbox?show=handled")).text).toContain("Bo"); await post(`/inbox/${second}/handled`, { handled: "0" }); expect((await get("/inbox")).text).toContain("Bo"); }); });