import { afterAll, beforeAll, describe, expect, mock, test } from "bun:test"; const TEST_DID = "did:plc:test-user"; // Mock session resolution so cookie-based auth works in tests. Spread the real // module so other test files in the same worker still see the rest of the // exports — Bun's mock.module is process-global. const realSession = await import("../../src/atproto/session.ts"); mock.module("../../src/atproto/session.ts", () => ({ ...realSession, getSessionFromRequest: async (request: Request) => { const cookie = request.headers.get("cookie") ?? ""; const match = cookie.match(/(?:^|;\s*)did=([^;]+)/); if (match?.[1]) { const did = decodeURIComponent(match[1]); return { did, handle: did }; } return null; }, getAgent: () => null, })); const { resolveWikiContext, resolveWikiContextSoft } = await import( "../../src/lib/access-resolve.ts" ); const { ForbiddenError, NotFoundError } = await import( "../../src/lib/errors.ts" ); const { upsertMembership, upsertWiki } = await import( "../../src/server/db/queries/index.ts" ); const { cleanupWikiAndDependents } = await import("../helpers/cleanup.ts"); const PUBLIC_SLUG = "ctx-public-wiki"; const PRIVATE_SLUG = "ctx-private-wiki"; const OTHER_DID = "did:plc:ctx-other"; const PUBLIC_AT_URI = `at://${TEST_DID}/wiki.lichen.wiki/${PUBLIC_SLUG}`; const PRIVATE_AT_URI = `at://${OTHER_DID}/wiki.lichen.wiki/${PRIVATE_SLUG}`; beforeAll(() => { upsertWiki( PUBLIC_SLUG, TEST_DID, "Ctx Public", "public", PUBLIC_AT_URI, new Date().toISOString(), ); upsertWiki( PRIVATE_SLUG, OTHER_DID, "Ctx Private", "private", PRIVATE_AT_URI, new Date().toISOString(), ); }); afterAll(() => { cleanupWikiAndDependents(PUBLIC_SLUG); cleanupWikiAndDependents(PRIVATE_SLUG); }); function authedRequest(did = TEST_DID): Request { return new Request("http://localhost/test", { headers: { cookie: `did=${encodeURIComponent(did)}` }, }); } function anonRequest(): Request { return new Request("http://localhost/test"); } // Pass DIDs directly as the "handle" param — resolveHandleToDid passes them through. describe("resolveWikiContext", () => { test("throws NotFoundError for nonexistent wiki", async () => { await expect( resolveWikiContext( anonRequest(), TEST_DID, "nonexistent-slug-xyz", "read", ), ).rejects.toBeInstanceOf(NotFoundError); }); test("returns admin context for owner on public wiki", async () => { const ctx = await resolveWikiContext( authedRequest(), TEST_DID, PUBLIC_SLUG, "read", ); expect(ctx.wiki.slug).toBe(PUBLIC_SLUG); expect(ctx.access).toBe("admin"); }); test("returns read context for anonymous on public wiki", async () => { const ctx = await resolveWikiContext( anonRequest(), TEST_DID, PUBLIC_SLUG, "read", ); expect(ctx.wiki.slug).toBe(PUBLIC_SLUG); expect(ctx.access).toBe("read"); }); test("throws ForbiddenError for anonymous requesting edit", async () => { await expect( resolveWikiContext(anonRequest(), TEST_DID, PUBLIC_SLUG, "edit"), ).rejects.toBeInstanceOf(ForbiddenError); }); test("throws ForbiddenError for private wiki when user has no membership", async () => { await expect( resolveWikiContext(authedRequest(), OTHER_DID, PRIVATE_SLUG, "read"), ).rejects.toBeInstanceOf(ForbiddenError); }); test("throws ForbiddenError when requiring edit on read-only access", async () => { upsertMembership( PRIVATE_AT_URI, PRIVATE_SLUG, TEST_DID, "viewer", `at://${TEST_DID}/wiki.lichen.membership/ctx-viewer`, new Date().toISOString(), ); await expect( resolveWikiContext(authedRequest(), OTHER_DID, PRIVATE_SLUG, "edit"), ).rejects.toBeInstanceOf(ForbiddenError); const { getDb } = await import("../../src/server/db/index.ts"); getDb().run("DELETE FROM memberships WHERE wiki_at_uri = ? AND did = ?", [ PRIVATE_AT_URI, TEST_DID, ]); }); test("throws ForbiddenError when requiring admin on edit access", async () => { upsertMembership( PRIVATE_AT_URI, PRIVATE_SLUG, TEST_DID, "contributor", `at://${TEST_DID}/wiki.lichen.membership/ctx-contrib`, new Date().toISOString(), ); await expect( resolveWikiContext(authedRequest(), OTHER_DID, PRIVATE_SLUG, "admin"), ).rejects.toBeInstanceOf(ForbiddenError); const { getDb } = await import("../../src/server/db/index.ts"); getDb().run("DELETE FROM memberships WHERE wiki_at_uri = ? AND did = ?", [ PRIVATE_AT_URI, TEST_DID, ]); }); }); describe("resolveWikiContextSoft", () => { test("throws NotFoundError for nonexistent wiki", async () => { await expect( resolveWikiContextSoft(anonRequest(), TEST_DID, "nonexistent-slug-xyz"), ).rejects.toBeInstanceOf(NotFoundError); }); test("returns context with none access for private wiki (does not throw)", async () => { const ctx = await resolveWikiContextSoft( anonRequest(), OTHER_DID, PRIVATE_SLUG, ); expect(ctx.wiki.slug).toBe(PRIVATE_SLUG); expect(ctx.access).toBe("none"); }); });