import { describe, test, expect, beforeAll, afterAll, mock } from "bun:test"; import { db } from "@/db"; import { annotators } from "@/db/schema"; import { eq } from "drizzle-orm"; // Mock cookie store that captures set/delete calls const cookieJar = new Map(); const mockCookieStore = { get(name: string) { const value = cookieJar.get(name); return value ? { value } : undefined; }, set(name: string, value: string, _opts?: unknown) { cookieJar.set(name, value); }, delete(name: string) { cookieJar.delete(name); }, }; // Mock next/headers before importing route handlers mock.module("next/headers", () => ({ cookies: async () => mockCookieStore, })); // Import route handlers after mock is set up const { GET, POST, DELETE: DELETE_HANDLER } = await import("../route"); const TEST_ANNOTATOR = { id: "test-auth-user", displayName: "Test Auth User", password: "testpass", }; beforeAll(async () => { await db.delete(annotators).where(eq(annotators.id, TEST_ANNOTATOR.id)); await db.insert(annotators).values(TEST_ANNOTATOR); }); afterAll(async () => { await db.delete(annotators).where(eq(annotators.id, TEST_ANNOTATOR.id)); }); describe("GET /api/auth", () => { test("returns annotator list without passwords", async () => { const res = await GET(); const data = await res.json(); expect(Array.isArray(data)).toBe(true); const testAnnotator = data.find( (a: { id: string }) => a.id === TEST_ANNOTATOR.id, ); expect(testAnnotator).toBeDefined(); expect(testAnnotator.id).toBe(TEST_ANNOTATOR.id); expect(testAnnotator.displayName).toBe(TEST_ANNOTATOR.displayName); expect(testAnnotator.password).toBeUndefined(); }); }); describe("POST /api/auth", () => { test("returns 200 and sets session cookie with correct credentials", async () => { cookieJar.clear(); const req = new Request("http://localhost:3000/api/auth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ annotatorId: TEST_ANNOTATOR.id, password: TEST_ANNOTATOR.password, }), }); const res = await POST(req); const data = await res.json(); expect(res.status).toBe(200); expect(data.ok).toBe(true); expect(data.annotator.id).toBe(TEST_ANNOTATOR.id); expect(data.annotator.displayName).toBe(TEST_ANNOTATOR.displayName); // Verify session cookie was set in the mock store expect(cookieJar.has("session")).toBe(true); const sessionValue = cookieJar.get("session")!; expect(sessionValue).toContain("."); }); test("returns 401 with wrong password", async () => { const req = new Request("http://localhost:3000/api/auth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ annotatorId: TEST_ANNOTATOR.id, password: "wrongpassword", }), }); const res = await POST(req); const data = await res.json(); expect(res.status).toBe(401); expect(data.error).toBe("Invalid credentials"); }); test("returns 401 with nonexistent annotator", async () => { const req = new Request("http://localhost:3000/api/auth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ annotatorId: "nonexistent-user", password: "anything", }), }); const res = await POST(req); expect(res.status).toBe(401); }); test("returns 401 with missing fields", async () => { const req = new Request("http://localhost:3000/api/auth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({}), }); const res = await POST(req); expect(res.status).toBe(401); }); }); describe("DELETE /api/auth", () => { test("clears session cookie", async () => { // Pre-populate a session cookie cookieJar.set("session", "fake-session-value"); const res = await DELETE_HANDLER(); const data = await res.json(); expect(res.status).toBe(200); expect(data.ok).toBe(true); expect(cookieJar.has("session")).toBe(false); }); });