import { afterAll, beforeAll, describe, expect, setSystemTime, test } from "bun:test"; import { join } from "node:path"; import { type Bucket, openBucket } from "../src/bucket/reader.ts"; import { fileIssue } from "../src/issues/store.ts"; import { openMirror } from "../src/mirror/store.ts"; import { deleteRelease, operatorReleases } from "../src/releases/releases.ts"; import { Browser, type Instance, instance } from "./browser.ts"; import { makeRepos, writeRelease } from "./fixture.ts"; let clock = Date.parse("2026-09-26T12:00:00Z"); const tick = (seconds: number) => { clock += seconds * 1000; setSystemTime(new Date(clock)); }; describe("repos and visibility", () => { let inst: Instance; let browser: Browser; let bucketDir: string; let save: string; const publicPaths = () => [ "/app", "/app/files/README.md", "/app/raw/README.md", `/app/saves/${save}`, `/app/archive/${save}.tar.gz`, "/app/archive/HEAD.tar.gz", "/app/archives", "/app/releases", "/app/releases/latest", "/app/releases/latest.json", "/app/releases/history.json", "/app/releases/latest/version.txt", "/app/releases/latest/linux-x86_64", "/app/releases/1.0.0/checksums.txt", "/app/releases/1.0.0/linux-x86_64", "/app/install.sh", "/api/app/releases/latest", "/api/app/releases/history", "/app/update-info", "/app/meta", "/app/issues", "/app/issues/new", "/app/issues/1", "/app?panel=issues", "/app/file?path=README.md", "/app/bundle", "/app/bundle.blake3", "/app/releases/1.0.0", ]; const statuses = async () => { const out: string[] = []; for (const path of publicPaths()) out.push(`${path} ${(await inst.app.request(path)).status}`); return out; }; const text = async (path: string) => (await inst.app.request(path)).text(); const publish = (repo: string, fields: Record) => browser.post(`/admin/repos/${repo}`, fields); beforeAll(async () => { setSystemTime(new Date(clock)); const repos = await makeRepos(["app", "other"]); bucketDir = repos.bucketDir; save = repos.saves.app as string; await writeRelease(bucketDir, "app", { version: "1.0.0", save, files: [{ platform: "linux-x86_64", name: "app", bytes: new Uint8Array([1]) }], }); inst = await instance(bucketDir); inst.kepr.run("INSERT INTO settings (key, value) VALUES ('handle', 'june')"); fileIssueDirect(inst, "app"); browser = new Browser(inst.app); await browser.signIn(); }); afterAll(() => setSystemTime()); test("every repo the sync found is listed, private, with its last sync", async () => { const page = await (await browser.get("/admin/repos?tab=settings")).text(); expect(page).toContain('id="repo-app"'); expect(page).toContain('id="repo-other"'); expect(page.match(/action="\/admin\/repos\/[^/"]+"/g)?.length).toBe(1); expect(page).toContain("last sync 2026-09-26 12:00 UTC"); expect((await statuses()).every((line) => line.endsWith(" 404"))).toBe(true); }); test("repo selection focuses one settings form without a search form", async () => { const page = await (await browser.get("/admin/repos?repo=other&tab=settings")).text(); expect(page).toContain('action="/admin/repos/other"'); expect(page).toContain('id="repo-app"'); expect(page).not.toContain('type="search"'); const saved = await publish("other", {}); expect(saved.headers.get("location")).toBe("/admin/repos?repo=other&tab=settings&saved=1"); expect(await (await browser.get("/admin/repos?repo=other&saved=1")).text()).toContain( "Changes saved.", ); }); test("bad settings are refused and change nothing", async () => { for (const fields of []>[ { homepage: "javascript:alert(1)" }, { category: "x".repeat(501) }, ]) { expect((await publish("app", { public: "1", ...fields })).status).toBe(400); } expect((await inst.app.request("/app")).status).toBe(404); expect((await browser.post("/admin/repos/nowhere", { public: "1" })).status).toBe(404); }); test("publishing opens every public surface, with the settings shown", async () => { await publish("other", { public: "1" }); const response = await publish("app", { public: "1", category: "tools", homepage: "https://app.example", }); expect(response.status).toBe(303); const lines = await statuses(); expect(lines.filter((line) => line.endsWith(" 404"))).toEqual([]); expect(await text("/app")).toContain("https://app.example"); expect(await text("/")).toContain('href="/app"'); expect(await text("/browse")).toContain('href="/app"'); expect(await text("/@june")).toContain('href="/app"'); expect(await text("/sitemap.xml")).toContain("/app"); }); test("flipping a repo private 404s every surface and drops it from lists and counts", async () => { const stat = async () => [...(await text("/@june")).matchAll(/profile-stat-number">([^<]*) m[1]); // Both download routes above counted; the issue is the repo's one. expect(await stat()).toEqual(["2", "2", "1", "2"]); await publish("app", { category: "tools" }); expect((await statuses()).filter((line) => !line.endsWith(" 404"))).toEqual([]); for (const path of ["/", "/browse", "/@june"]) expect(await text(path)).not.toContain("/app"); expect(await text("/sitemap.xml")).not.toContain("/app<"); expect(await text("/")).not.toContain("tools"); expect(await stat()).toEqual(["1", "1", "0", "0"]); // Settings survive the flip. expect(await (await browser.get("/admin/repos?tab=settings")).text()).toContain( 'value="tools"', ); }); test("sync now reads the bucket at once and records the time", async () => { await writeRelease(bucketDir, "other", { version: "2.0.0", save: save, files: [{ platform: "linux-x86_64", name: "other", bytes: new Uint8Array([2]) }], }); tick(60); expect((await inst.app.request("/other/releases/latest/version.txt")).status).toBe(404); const response = await browser.post("/admin/sync", {}); expect(response.status).toBe(303); expect(await text("/other/releases/latest/version.txt")).toBe("2.0.0"); expect(await (await browser.get("/admin/repos")).text()).toContain( "last sync 2026-09-26 12:01 UTC", ); }); }); describe("issues: filed, answered and closed end to end through real routes", () => { let inst: Instance; let operator: Browser; let save: string; const text = async (path: string) => (await inst.app.request(path)).text(); const act = (fields: Record) => operator.post("/admin/issues/app/1", fields); beforeAll(async () => { setSystemTime(new Date(clock)); const repos = await makeRepos(["app", "hush"]); save = repos.saves.app as string; inst = await instance(repos.bucketDir); operator = new Browser(inst.app); await operator.signIn(); await operator.post("/admin/repos/app", { public: "1" }); // A reporter files through the public form, as a browser would. const reporter = new Browser(inst.app, "198.51.100.40"); const form = await (await reporter.request("/app/issues/new")).text(); const stamp = /name="stamp" value="([^"]+)"/.exec(form)?.[1] ?? ""; tick(4); const filed = await reporter.request("/app/issues/new", { method: "POST", body: new URLSearchParams({ stamp, website: "", title: "Crash start", body: "It crashed.", type: "bug", version: "", name: "Ada", email: "ada@private.example", }), }); expect(filed.status).toBe(200); fileIssueDirect(inst, "hush"); }); afterAll(() => setSystemTime()); test("the console lists issues across repos, private ones too, with the contact detail", async () => { const list = await (await operator.get("/admin/issues")).text(); expect(list).toContain('href="/admin/issues/app/1"'); expect(list).toContain('href="/admin/issues/hush/1"'); expect(list).toContain("Crash <on> start"); const page = await (await operator.get("/admin/issues/app/1")).text(); expect(page).toContain("contact: ada@private.example"); expect(await text("/app/issues/1")).not.toContain("ada@private.example"); }); test("an operator reply shows on the public thread, marked", async () => { expect((await act({ action: "reply", body: "Fixed ." })).status).toBe(303); expect((await act({ action: "reply", body: " " })).status).toBe(400); const thread = await text("/app/issues/1"); expect(thread).toContain('operator'); expect(thread).toContain("Fixed <soon>."); }); test("close with a fixed-in save prefix, then reopen", async () => { expect((await act({ action: "close", fixed_in: "zzzzzz" })).status).toBe(400); expect((await act({ action: "close", fixed_in: "0123456789ab" })).status).toBe(400); expect((await act({ action: "close", fixed_in: save.slice(0, 6) })).status).toBe(303); const thread = await text("/app/issues/1"); expect(thread).toContain("◈ closed"); expect(thread).toContain(`fixed in `); expect(await text("/app/issues?status=closed")).toContain('href="/app/issues/1"'); expect(await (await operator.get("/admin/issues?filter=closed")).text()).toContain( 'href="/admin/issues/app/1"', ); expect((await act({ action: "reopen" })).status).toBe(303); expect(await text("/app/issues/1")).toContain("◉ open"); expect(await text("/app/issues/1")).not.toContain("fixed in"); }); test("set type, and hide for spam: gone publicly, kept in the console", async () => { expect((await act({ action: "type", type: "nonsense" })).status).toBe(400); expect((await act({ action: "type", type: "idea" })).status).toBe(303); expect(await text("/app/issues")).toContain("#1 · idea"); expect((await act({ action: "hide" })).status).toBe(303); expect((await inst.app.request("/app/issues/1")).status).toBe(404); expect(await (await operator.get("/admin/issues")).text()).not.toContain("/admin/issues/app/1"); expect(await (await operator.get("/admin/issues?filter=hidden")).text()).toContain( 'href="/admin/issues/app/1"', ); expect((await act({ action: "unhide" })).status).toBe(303); expect((await inst.app.request("/app/issues/1")).status).toBe(200); expect((await act({ action: "delete" })).status).toBe(400); expect((await operator.post("/admin/issues/app/9", { action: "reopen" })).status).toBe(404); }); }); function fileIssueDirect(inst: Instance, repo: string): void { fileIssue(inst.kepr, { repo, title: `A ${repo} issue`, body: "b", type: "bug", version: "", reporterName: "", contactEmail: "", ipHash: "x", filedAt: 1, }); } describe("releases: counts, displayed notes and hiding", () => { let inst: Instance; let operator: Browser; const text = async (path: string) => (await inst.app.request(path)).text(); const edit = (version: string, fields: Record) => operator.post(`/admin/releases/app/${version}`, fields); beforeAll(async () => { const repos = await makeRepos(["app"]); const save = repos.saves.app as string; for (const version of ["1.0.0", "1.1.0"]) { await writeRelease(repos.bucketDir, "app", { version, save, notes: `notes for ${version}`, files: [{ platform: "linux-x86_64", name: "app", bytes: new Uint8Array([1]) }], }); } inst = await instance(repos.bucketDir); operator = new Browser(inst.app); await operator.signIn(); await operator.post("/admin/repos/app", { public: "1" }); await inst.app.request("/app/releases/1.1.0/linux-x86_64"); }); test("every version is listed with its download counts and notes", async () => { const page = await (await operator.get("/admin/repos?repo=app&tab=builds")).text(); expect(page).toContain('id="app-1.1.0"'); expect(page).toContain('id="app-1.0.0"'); expect(page.indexOf('id="app-1.1.0"')).toBeLessThan(page.indexOf('id="app-1.0.0"')); expect(page).toContain('1 downloadlinux-x86_64 1'); expect(page).toContain(">notes for 1.1.0"); }); test("edited notes replace the record's everywhere; the record's own text clears the edit", async () => { const saved = await edit("1.1.0", { notes: "Better ." }); expect(saved.headers.get("location")).toBe( "/admin/repos?repo=app&tab=builds&saved=1#app-1.1.0", ); expect(JSON.parse(await text("/api/app/releases/latest")).notes).toBe("Better ."); expect(await text("/app/releases")).toContain("Better <notes>."); expect(await (await operator.get("/admin/repos?repo=app&tab=builds")).text()).toContain( "(edited)", ); expect((await edit("1.1.0", { notes: "notes for 1.1.0" })).status).toBe(303); const row = inst.kepr.query("SELECT notes FROM release_settings WHERE version = '1.1.0'").get(); expect(row).toEqual({ notes: null }); expect((await edit("1.1.0", { notes: "x".repeat(20_001) })).status).toBe(400); }); test("a hidden version leaves pages, latest and every feed, and stays in the console", async () => { expect((await edit("1.1.0", { notes: "notes for 1.1.0", hidden: "1" })).status).toBe(303); expect(await text("/app/releases/latest/version.txt")).toBe("1.0.0"); expect(JSON.parse(await text("/api/app/releases/latest")).version).toBe("1.0.0"); expect(await text("/api/app/releases/history")).not.toContain("1.1.0"); expect(await text("/app/releases")).not.toContain("1.1.0"); expect((await inst.app.request("/app/releases/1.1.0/linux-x86_64")).status).toBe(404); const page = await (await operator.get("/admin/repos?repo=app&tab=builds")).text(); expect(page).toContain('id="app-1.1.0"'); expect(page).toContain('value="1" checked> hidden'); expect((await edit("1.1.0", { notes: "notes for 1.1.0" })).status).toBe(303); expect(await text("/app/releases/latest/version.txt")).toBe("1.1.0"); expect((await edit("9.9.9", { notes: "" })).status).toBe(404); expect((await operator.post("/admin/releases/nowhere/1.0.0", { notes: "" })).status).toBe(404); }); }); describe("deleting a build from the bucket", () => { let inst: Instance; let operator: Browser; let bucketDir: string; const text = async (path: string) => (await inst.app.request(path)).text(); const remove = (version: string, fields: Record = { confirm: "1" }) => operator.post(`/admin/releases/app/${version}/delete`, fields); beforeAll(async () => { const repos = await makeRepos(["app"]); bucketDir = repos.bucketDir; for (const version of ["1.0.0", "1.1.0"]) { await writeRelease(bucketDir, "app", { version, save: repos.saves.app as string, files: [{ platform: "linux-x86_64", name: "app", bytes: new Uint8Array([1]) }], }); } inst = await instance(bucketDir); operator = new Browser(inst.app); await operator.signIn(); await operator.post("/admin/repos/app", { public: "1" }); await inst.app.request("/app/releases/1.1.0/linux-x86_64"); }); test("needs its confirmation, and an unknown version is a 404", async () => { expect((await remove("1.1.0", {})).status).toBe(400); expect(await Bun.file(join(bucketDir, "app/releases/1.1.0/release.json")).exists()).toBe(true); expect((await remove("9.9.9")).status).toBe(404); }); test("the newest build goes, latest falls back, and a sync does not bring it back", async () => { const response = await remove("1.1.0"); expect(response.headers.get("location")).toBe("/admin/repos?repo=app&tab=builds&saved=1"); expect(await Bun.file(join(bucketDir, "app/releases/1.1.0/release.json")).exists()).toBe(false); expect(await Bun.file(join(bucketDir, "app/releases/1.1.0/linux-x86_64/app")).exists()).toBe( false, ); expect(await Bun.file(join(bucketDir, "app/releases/latest")).text()).toBe("1.0.0"); expect(JSON.parse(await text("/api/app/releases/latest")).version).toBe("1.0.0"); expect(inst.kepr.query("SELECT * FROM downloads WHERE version = '1.1.0'").all()).toEqual([]); await operator.post("/admin/sync", {}); const page = await (await operator.get("/admin/repos?repo=app&tab=builds")).text(); expect(page).not.toContain('id="app-1.1.0"'); expect(page).toContain('id="app-1.0.0"'); }); test("the last build goes, and latest with it", async () => { expect((await remove("1.0.0")).status).toBe(303); expect(await Bun.file(join(bucketDir, "app/releases/latest")).exists()).toBe(false); expect((await inst.app.request("/app/releases/latest/version.txt")).status).toBe(404); }); }); describe("deleting one file of a build", () => { let inst: Instance; let operator: Browser; let bucketDir: string; const record = async () => JSON.parse(await Bun.file(join(bucketDir, "app/releases/1.0.0/release.json")).text()); const removeFile = (platform: string, fields: Record = { confirm: "1" }) => operator.post(`/admin/releases/app/1.0.0/${platform}/delete`, fields); beforeAll(async () => { const repos = await makeRepos(["app"]); bucketDir = repos.bucketDir; await writeRelease(bucketDir, "app", { version: "1.0.0", save: repos.saves.app as string, files: [ { platform: "linux-x86_64", name: "app", bytes: new Uint8Array([1]) }, { platform: "macos-aarch64", name: "app", bytes: new Uint8Array([2]) }, ], }); inst = await instance(bucketDir); operator = new Browser(inst.app); await operator.signIn(); await operator.post("/admin/repos/app", { public: "1" }); await inst.app.request("/app/releases/1.0.0/macos-aarch64"); }); test("needs its confirmation, and an unknown platform is a 404", async () => { expect((await removeFile("macos-aarch64", {})).status).toBe(400); expect((await removeFile("windows-x86_64")).status).toBe(404); expect((await record()).files).toHaveLength(2); }); test("the file leaves the record, the bucket, the counts and the public routes", async () => { const response = await removeFile("macos-aarch64"); expect(response.headers.get("location")).toBe( "/admin/repos?repo=app&tab=builds&saved=1#app-1.0.0", ); const files = (await record()).files.map((file: { platform: string }) => file.platform); expect(files).toEqual(["linux-x86_64"]); const gone = join(bucketDir, "app/releases/1.0.0/macos-aarch64/app"); expect(await Bun.file(gone).exists()).toBe(false); expect( inst.kepr.query("SELECT * FROM downloads WHERE platform = 'macos-aarch64'").all(), ).toEqual([]); expect((await inst.app.request("/app/releases/1.0.0/macos-aarch64")).status).toBe(404); expect((await inst.app.request("/app/releases/1.0.0/linux-x86_64")).status).toBe(302); }); test("the last file can't go alone", async () => { expect((await removeFile("linux-x86_64")).status).toBe(400); expect((await record()).files).toHaveLength(1); }); }); describe("deleting builds when a step fails or two run at once", () => { let inst: Instance; let bucketDir: string; const folder = () => openBucket({ kind: "folder", path: bucketDir }); const mirror = () => openMirror(join(inst.dataDir, "mirror"), "app"); const versions = () => operatorReleases(inst.kepr, mirror(), "app").map((r) => r.version); const latest = () => Bun.file(join(bucketDir, "app/releases/latest")).text(); beforeAll(async () => { const repos = await makeRepos(["app"]); bucketDir = repos.bucketDir; for (const version of ["1.0.0", "2.0.0", "3.0.0", "4.0.0"]) { await writeRelease(bucketDir, "app", { version, save: repos.saves.app as string, files: [{ platform: "linux-x86_64", name: "app", bytes: new Uint8Array([1]) }], }); } inst = await instance(bucketDir); }); test("a failed file delete leaves the build whole, and deleting it again finishes", async () => { const failing: Bucket = { ...folder(), async remove(key) { if (key.endsWith("/app")) throw new Error("network down"); return folder().remove(key); }, }; await expect(deleteRelease(failing, inst.kepr, mirror(), "app", "4.0.0")).rejects.toThrow( "network down", ); expect(await Bun.file(join(bucketDir, "app/releases/4.0.0/release.json")).exists()).toBe(true); expect(versions()).toContain("4.0.0"); expect(await latest()).toBe("3.0.0"); await deleteRelease(folder(), inst.kepr, mirror(), "app", "4.0.0"); expect(await Bun.file(join(bucketDir, "app/releases/4.0.0/release.json")).exists()).toBe(false); expect(versions()).not.toContain("4.0.0"); }); test("two deletes at once leave latest naming a build that still exists", async () => { await Promise.all([ deleteRelease(folder(), inst.kepr, mirror(), "app", "3.0.0"), deleteRelease(folder(), inst.kepr, mirror(), "app", "2.0.0"), ]); expect(await latest()).toBe("1.0.0"); expect(versions()).toEqual(["1.0.0"]); }); }); describe("site and profile settings, and the signature upload", () => { let inst: Instance; let operator: Browser; const PNG = Uint8Array.from( atob( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==", ), (ch) => ch.charCodeAt(0), ); const settings = { site_name: "ada's library", tagline: "Small .", branding: "by ada", handle: "ada", bio: "Writes code.", location: "Lisbon", link: "https://ada.example", building_since: "june 2026", support: "1", }; const text = async (path: string) => (await inst.app.request(path)).text(); const upload = (bytes: Uint8Array | string, name: string, type: string, extra = {}) => { const form = new FormData(); for (const [key, value] of Object.entries({ ...settings, ...extra })) form.set(key, value); form.set("signature", new File([bytes], name, { type })); return operator.post("/admin/site", form); }; const signature = () => inst.kepr.query("SELECT value FROM settings WHERE key = 'signature'").get(); beforeAll(async () => { const repos = await makeRepos(["app"]); inst = await instance(repos.bucketDir); operator = new Browser(inst.app); await operator.signIn(); }); test("settings are saved and every public page reads them", async () => { expect((await operator.post("/admin/site", settings)).status).toBe(303); const front = await text("/"); expect(front).toContain("ada's library"); expect(front).toContain("Small <tools>."); expect(front).toContain('by ada'); const profile = await text("/@ada"); for (const value of [ "Writes code.", "Lisbon", 'href="https://ada.example"', "building since june 2026", ]) { expect(profile).toContain(value); } expect(await (await operator.get("/admin/site?section=profile")).text()).toContain( 'value="Lisbon"', ); // Emptying a field removes the setting, so its default comes back. expect((await operator.post("/admin/site", { ...settings, site_name: "" })).status).toBe(303); expect(await text("/")).toContain("kepr"); }); test("profile and site forms preserve each other's settings and retain invalid input", async () => { await operator.post("/admin/site", settings); const profile = await (await operator.get("/admin/site?section=profile")).text(); expect(profile).toContain('aria-label="operator workspace"'); expect(profile).toContain('href="/@ada"'); expect(profile).not.toContain('name="site_name"'); expect(profile).not.toContain('name="signature"'); expect(profile).not.toContain('name="remove_signature"'); expect( ( await operator.post("/admin/site", { section: "profile", handle: "ada", bio: "New bio", link: "https://ada.example", }) ).status, ).toBe(303); expect(await text("/")).toContain("Small <tools>."); await operator.post("/admin/site", { section: "site", site_name: "New site", support: "1" }); expect(await text("/@ada")).toContain("New bio"); const bad = await operator.post("/admin/site", { section: "profile", handle: "a/b", bio: "Keep my draft", }); expect(bad.status).toBe(400); expect(await bad.text()).toContain("Keep my draft"); expect(await text("/@ada")).toContain("New bio"); }); test("the support page is the operator's to write, and to switch off", async () => { // Until written, the shipped words show, and the console's box starts from them. const shipped = await text("/support"); expect(await (await operator.get("/admin/site")).text()).toContain("show the support page"); const own = { ...settings, support_page: "## Help\n\nMail **me**." }; expect((await operator.post("/admin/site", own)).status).toBe(303); const support = await text("/support"); expect(support).toContain('

Help

\n

Mail me.

'); expect(support).not.toBe(shipped); const { support: _, ...off } = own; expect((await operator.post("/admin/site", off)).status).toBe(303); expect((await inst.app.request("/support")).status).toBe(404); expect(await text("/")).not.toContain('href="/support"'); expect((await operator.post("/admin/site", own)).status).toBe(303); expect(await text("/")).toContain('
support'); }); test("a bad handle, link or overlong field is refused", async () => { for (const bad of [ { handle: "a/b" }, { link: "javascript:alert(1)" }, { bio: "x".repeat(2001) }, ]) { expect((await operator.post("/admin/site", { ...settings, ...bad })).status).toBe(400); } expect((await inst.app.request("/@ada")).status).toBe(200); }); test("a PNG signature is stored in data/assets and shown on the profile", async () => { expect((await upload(PNG, "sig.png", "image/png")).status).toBe(303); const name = (signature() as { value: string }).value; expect(name).toMatch(/^signature-[0-9a-f]{16}\.png$/); expect(await Bun.file(join(inst.dataDir, "assets", name)).exists()).toBe(true); expect(await text("/@ada")).toContain( ` { const before = signature(); const svg = ''; expect((await upload(svg, "sig.svg", "image/svg+xml")).status).toBe(400); expect((await upload(svg, "sig.png", "image/png")).status).toBe(400); expect((await upload("GIF89a", "sig.gif", "image/gif")).status).toBe(400); expect(signature()).toEqual(before); expect((await upload(new Uint8Array(70 * 1024), "big.png", "image/png")).status).toBe(413); // Between the 60 KB limit and the 64 KiB body cap, the limit answers. const png = (size: number) => { const bytes = new Uint8Array(size); bytes.set(PNG); return bytes; }; const over = await upload(png(60 * 1024 + 1), "big.png", "image/png"); expect(over.status).toBe(400); expect(await over.text()).toContain("The signature must be under 60 KB."); expect(signature()).toEqual(before); expect((await upload(png(60 * 1024), "edge.png", "image/png")).status).toBe(303); }); test("a new signature replaces the old file; removing it brings the handle back", async () => { const old = (signature() as { value: string }).value; const webp = new Uint8Array([0x52, 0x49, 0x46, 0x46, 0, 0, 0, 0, 0x57, 0x45, 0x42, 0x50, 1]); expect((await upload(webp, "sig.webp", "image/webp")).status).toBe(303); const name = (signature() as { value: string }).value; expect(name).toMatch(/\.webp$/); expect(await Bun.file(join(inst.dataDir, "assets", old)).exists()).toBe(false); expect( (await operator.post("/admin/site", { ...settings, remove_signature: "1" })).status, ).toBe(303); expect(signature()).toBeNull(); expect(await Bun.file(join(inst.dataDir, "assets", name)).exists()).toBe(false); expect(await text("/@ada")).toContain('

ada

'); }); });