import { describe, it, expect, vi } from "vitest"; import { createBoard } from "../lib/steps/create-board.js"; describe("createBoard", () => { const forumDid = "did:plc:testforum"; const categoryUri = `at://${forumDid}/space.atbb.forum.category/cattid`; const categoryId = BigInt(42); const categoryCid = "bafycategory"; function mockDb(options: { existingBoard?: any } = {}) { return { select: vi.fn().mockReturnValue({ from: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue({ limit: vi.fn().mockResolvedValue( options.existingBoard ? [options.existingBoard] : [] ), }), }), }), insert: vi.fn().mockReturnValue({ values: vi.fn().mockResolvedValue(undefined), }), } as any; } function mockAgent(overrides: Record = {}) { return { com: { atproto: { repo: { createRecord: vi.fn().mockResolvedValue({ data: { uri: `at://${forumDid}/space.atbb.forum.board/tid456`, cid: "bafyboard", }, }), ...overrides, }, }, }, } as any; } const baseInput = { name: "General Discussion", categoryUri, categoryId, categoryCid, }; it("creates board on PDS and inserts into DB", async () => { const db = mockDb(); const agent = mockAgent(); const result = await createBoard(db, agent, forumDid, baseInput); expect(result.created).toBe(true); expect(result.skipped).toBe(false); expect(result.uri).toContain("space.atbb.forum.board/tid456"); expect(result.cid).toBe("bafyboard"); expect(agent.com.atproto.repo.createRecord).toHaveBeenCalledWith( expect.objectContaining({ repo: forumDid, collection: "space.atbb.forum.board", record: expect.objectContaining({ $type: "space.atbb.forum.board", name: "General Discussion", // Board record includes the category ref nested under "category" category: { category: { uri: categoryUri, cid: categoryCid }, }, }), }) ); expect(db.insert).toHaveBeenCalled(); }); it("derives slug from name", async () => { const db = mockDb(); const agent = mockAgent(); await createBoard(db, agent, forumDid, { ...baseInput, name: "Off Topic Chat", }); expect(agent.com.atproto.repo.createRecord).toHaveBeenCalledWith( expect.objectContaining({ record: expect.objectContaining({ slug: "off-topic-chat" }), }) ); }); it("skips when board with same name exists in the same category", async () => { const db = mockDb({ existingBoard: { did: forumDid, rkey: "existingtid", cid: "bafyexisting", name: "General Discussion", }, }); const agent = mockAgent(); const result = await createBoard(db, agent, forumDid, baseInput); expect(result.created).toBe(false); expect(result.skipped).toBe(true); expect(result.existingName).toBe("General Discussion"); expect(agent.com.atproto.repo.createRecord).not.toHaveBeenCalled(); expect(db.insert).not.toHaveBeenCalled(); }); it("throws when PDS write fails", async () => { const db = mockDb(); const agent = mockAgent({ createRecord: vi.fn().mockRejectedValue(new Error("PDS write failed")), }); await expect(createBoard(db, agent, forumDid, baseInput)).rejects.toThrow( "PDS write failed" ); }); it("throws when DB insert fails after successful PDS write", async () => { const db = { select: vi.fn().mockReturnValue({ from: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue({ limit: vi.fn().mockResolvedValue([]), // no existing board }), }), }), insert: vi.fn().mockReturnValue({ values: vi.fn().mockRejectedValue(new Error("DB insert failed")), }), } as any; const agent = mockAgent(); await expect(createBoard(db, agent, forumDid, baseInput)).rejects.toThrow( "DB insert failed" ); // PDS write happened before the failing DB insert expect(agent.com.atproto.repo.createRecord).toHaveBeenCalled(); }); it("includes sortOrder in PDS record and DB row when provided", async () => { const db = mockDb(); const agent = mockAgent(); await createBoard(db, agent, forumDid, { ...baseInput, sortOrder: 3 }); expect(agent.com.atproto.repo.createRecord).toHaveBeenCalledWith( expect.objectContaining({ record: expect.objectContaining({ sortOrder: 3 }), }) ); expect(db.insert().values).toHaveBeenCalledWith( expect.objectContaining({ sortOrder: 3 }) ); }); it("omits sortOrder from PDS record and DB row when not provided", async () => { const db = mockDb(); const agent = mockAgent(); await createBoard(db, agent, forumDid, baseInput); const record = (agent.com.atproto.repo.createRecord as ReturnType).mock.calls[0][0].record; expect(record).not.toHaveProperty("sortOrder"); expect(db.insert().values).toHaveBeenCalledWith( expect.objectContaining({ sortOrder: null }) ); }); });