import { describe, expect, test } from "bun:test"; import { editNoteUrl, exportNoteUrl, newNoteUrl, noteUrl, redirect, settingsUrl, wikiUrl, } from "../../src/lib/urls.ts"; describe("URL helpers", () => { test("builds correct paths for all route types", () => { expect(wikiUrl("alice", "cooking")).toBe("/@alice/cooking"); expect(noteUrl("alice", "cooking", "pasta")).toBe("/@alice/cooking/pasta"); expect(editNoteUrl("alice", "cooking", "pasta")).toBe( "/@alice/cooking/pasta/edit", ); expect(newNoteUrl("alice", "cooking")).toBe("/@alice/cooking/new"); expect(settingsUrl("alice", "cooking")).toBe("/@alice/cooking/-/settings"); expect(exportNoteUrl("alice", "cooking", "pasta")).toBe( "/@alice/cooking/pasta/-/export", ); }); test("redirect returns 302 with Location header", () => { const res = redirect("/@alice/cooking"); expect(res.status).toBe(302); expect(res.headers.get("Location")).toBe("/@alice/cooking"); }); }); // Results are interpolated straight into href="…", and slugs can originate from // remote records, so the helpers must not be able to emit attribute-breaking text. describe("URL segment encoding", () => { test("percent-encodes characters that would break out of an attribute", () => { const url = noteUrl("alice", "cooking", `x" onmouseover="alert(1)`); expect(url).not.toContain('"'); expect(url).toBe("/@alice/cooking/x%22%20onmouseover%3D%22alert%281%29"); }); test("encodes quotes, angle brackets and slashes in a slug", () => { expect(wikiUrl("alice", "a/b")).toBe("/@alice/a%2Fb"); expect(wikiUrl("alice", "