import { beforeEach, describe, expect, it, vi } from "vitest"; import { HttpError } from "@/lib/security/http"; const mocks = vi.hoisted(() => ({ viewer: vi.fn(), target: vi.fn(), stream: vi.fn(), limit: vi.fn() })); vi.mock("@/lib/comments/repository", () => ({ getCommentViewer: mocks.viewer, getCommentTarget: mocks.target, requireCommentAdmin: (viewer: { admin: boolean } | null) => { if (!viewer?.admin) throw new HttpError(403, "forbidden"); }, })); vi.mock("@/lib/events/redis-stream", () => ({ createRedisNamedEventResponse: mocks.stream })); vi.mock("@/lib/security/rate-limit", () => ({ limitRequest: mocks.limit, trustedClientAddress: () => "127.0.0.1" })); import { GET } from "./route"; describe("comment event authorization", () => { beforeEach(() => { vi.clearAllMocks(); mocks.viewer.mockResolvedValue(null); mocks.target.mockResolvedValue({ target: "stygian:123" }); mocks.stream.mockResolvedValue(new Response("event: changed\ndata: changed\n\n", { headers: { "Content-Type": "text/event-stream" } })); }); it("allows anonymous subscribers for a readable target", async () => { const request = new Request("https://guide.test/api/comments/events?target=stygian:123"); expect((await GET(request)).status).toBe(200); expect(mocks.target).toHaveBeenCalledWith("stygian:123", null); expect(mocks.stream).toHaveBeenCalledWith("comments:stygian:123", "changed", request.signal); }); it("does not subscribe to an inaccessible guide", async () => { mocks.target.mockRejectedValue(new HttpError(404, "guide-not-found")); expect((await GET(new Request("https://guide.test/api/comments/events?target=guide:private"))).status).toBe(404); expect(mocks.stream).not.toHaveBeenCalled(); }); it("rejects anonymous admin inbox subscriptions", async () => { expect((await GET(new Request("https://guide.test/api/comments/events?scope=admin"))).status).toBe(403); expect(mocks.stream).not.toHaveBeenCalled(); }); it("allows verified admin inbox subscriptions", async () => { mocks.viewer.mockResolvedValue({ id: "admin", admin: true }); const request = new Request("https://guide.test/api/comments/events?scope=admin"); expect((await GET(request)).status).toBe(200); expect(mocks.stream).toHaveBeenCalledWith("comments:admin", "changed", request.signal); }); it("requires a target for public subscriptions", async () => { expect((await GET(new Request("https://guide.test/api/comments/events"))).status).toBe(400); expect(mocks.stream).not.toHaveBeenCalled(); }); });