import { beforeEach, describe, expect, it, vi } from "vitest"; import { outboxEvents } from "@/db/schema"; const mocks = vi.hoisted(() => ({ previous: { isPublic: false, publishedAt: null as Date | null }, updated: [] as unknown[], current: [] as unknown[], inserts: [] as { table: unknown; values: unknown }[] })); vi.mock("server-only", () => ({})); vi.mock("@/lib/audit-log", () => ({ writeAuditLog: vi.fn() })); vi.mock("@/db", () => ({ getDb: () => ({ transaction: async (callback: (tx: unknown) => unknown) => { const tx = { select: () => { const query = { from: () => query, where: () => query, for: async () => [mocks.previous], limit: async () => mocks.current }; return query; }, update: () => { const query = { set: () => query, where: () => query, returning: async () => mocks.updated }; return query; }, insert: (table: unknown) => ({ values: async (values: unknown) => { mocks.inserts.push({ table, values }); } }), }; return callback(tx); } }) })); import { GuideVersionConflictError, setGuideVisibility } from "./mutations"; const guide = { id: "guide-id", version: 2, name: "Amber", characterKey: "amber", publishedAt: new Date("2026-10-08T10:00:00Z") }; const context = { actor: { id: "admin", label: "Admin" } }; beforeEach(() => { mocks.previous = { isPublic: false, publishedAt: null }; mocks.updated = [guide]; mocks.current = []; mocks.inserts.length = 0; }); const publicationEvents = () => mocks.inserts.filter(row => row.table === outboxEvents && !Array.isArray(row.values)); describe("guide publication trigger", () => { it("queues delivery in the publishing transaction", async () => { await setGuideVisibility(guide.id, { isPublic: true, expectedVersion: 1 }, context); expect(publicationEvents().map(row => row.values)).toEqual([{ topic: "notifications:guide", aggregateId: guide.id, eventType: "guide.published", payload: { publishedAt: guide.publishedAt.toISOString() }, }]); }); it.each(["unpublish", "republish", "already public"])("does not notify on %s", async (scenario) => { if (scenario === "republish") mocks.previous.publishedAt = guide.publishedAt; if (scenario === "already public") mocks.previous.isPublic = true; await setGuideVisibility(guide.id, { isPublic: scenario !== "unpublish", expectedVersion: 1 }, context); expect(publicationEvents()).toHaveLength(0); }); it("preserves version conflicts without queuing notifications", async () => { mocks.updated = []; mocks.current = [{ version: 5 }]; await expect(setGuideVisibility(guide.id, { isPublic: true, expectedVersion: 1 }, context)).rejects.toBeInstanceOf(GuideVersionConflictError); expect(mocks.inserts).toHaveLength(0); }); });