WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto
at atb-46-modlog-endpoint 176 lines 5.3 kB view raw
1import { describe, it, expect, vi } from "vitest"; 2import { createBoard } from "../lib/steps/create-board.js"; 3 4describe("createBoard", () => { 5 const forumDid = "did:plc:testforum"; 6 const categoryUri = `at://${forumDid}/space.atbb.forum.category/cattid`; 7 const categoryId = BigInt(42); 8 const categoryCid = "bafycategory"; 9 10 function mockDb(options: { existingBoard?: any } = {}) { 11 return { 12 select: vi.fn().mockReturnValue({ 13 from: vi.fn().mockReturnValue({ 14 where: vi.fn().mockReturnValue({ 15 limit: vi.fn().mockResolvedValue( 16 options.existingBoard ? [options.existingBoard] : [] 17 ), 18 }), 19 }), 20 }), 21 insert: vi.fn().mockReturnValue({ 22 values: vi.fn().mockResolvedValue(undefined), 23 }), 24 } as any; 25 } 26 27 function mockAgent(overrides: Record<string, any> = {}) { 28 return { 29 com: { 30 atproto: { 31 repo: { 32 createRecord: vi.fn().mockResolvedValue({ 33 data: { 34 uri: `at://${forumDid}/space.atbb.forum.board/tid456`, 35 cid: "bafyboard", 36 }, 37 }), 38 ...overrides, 39 }, 40 }, 41 }, 42 } as any; 43 } 44 45 const baseInput = { 46 name: "General Discussion", 47 categoryUri, 48 categoryId, 49 categoryCid, 50 }; 51 52 it("creates board on PDS and inserts into DB", async () => { 53 const db = mockDb(); 54 const agent = mockAgent(); 55 56 const result = await createBoard(db, agent, forumDid, baseInput); 57 58 expect(result.created).toBe(true); 59 expect(result.skipped).toBe(false); 60 expect(result.uri).toContain("space.atbb.forum.board/tid456"); 61 expect(result.cid).toBe("bafyboard"); 62 expect(agent.com.atproto.repo.createRecord).toHaveBeenCalledWith( 63 expect.objectContaining({ 64 repo: forumDid, 65 collection: "space.atbb.forum.board", 66 record: expect.objectContaining({ 67 $type: "space.atbb.forum.board", 68 name: "General Discussion", 69 // Board record includes the category ref nested under "category" 70 category: { 71 category: { uri: categoryUri, cid: categoryCid }, 72 }, 73 }), 74 }) 75 ); 76 expect(db.insert).toHaveBeenCalled(); 77 }); 78 79 it("derives slug from name", async () => { 80 const db = mockDb(); 81 const agent = mockAgent(); 82 83 await createBoard(db, agent, forumDid, { 84 ...baseInput, 85 name: "Off Topic Chat", 86 }); 87 88 expect(agent.com.atproto.repo.createRecord).toHaveBeenCalledWith( 89 expect.objectContaining({ 90 record: expect.objectContaining({ slug: "off-topic-chat" }), 91 }) 92 ); 93 }); 94 95 it("skips when board with same name exists in the same category", async () => { 96 const db = mockDb({ 97 existingBoard: { 98 did: forumDid, 99 rkey: "existingtid", 100 cid: "bafyexisting", 101 name: "General Discussion", 102 }, 103 }); 104 const agent = mockAgent(); 105 106 const result = await createBoard(db, agent, forumDid, baseInput); 107 108 expect(result.created).toBe(false); 109 expect(result.skipped).toBe(true); 110 expect(result.existingName).toBe("General Discussion"); 111 expect(agent.com.atproto.repo.createRecord).not.toHaveBeenCalled(); 112 expect(db.insert).not.toHaveBeenCalled(); 113 }); 114 115 it("throws when PDS write fails", async () => { 116 const db = mockDb(); 117 const agent = mockAgent({ 118 createRecord: vi.fn().mockRejectedValue(new Error("PDS write failed")), 119 }); 120 121 await expect(createBoard(db, agent, forumDid, baseInput)).rejects.toThrow( 122 "PDS write failed" 123 ); 124 }); 125 126 it("throws when DB insert fails after successful PDS write", async () => { 127 const db = { 128 select: vi.fn().mockReturnValue({ 129 from: vi.fn().mockReturnValue({ 130 where: vi.fn().mockReturnValue({ 131 limit: vi.fn().mockResolvedValue([]), // no existing board 132 }), 133 }), 134 }), 135 insert: vi.fn().mockReturnValue({ 136 values: vi.fn().mockRejectedValue(new Error("DB insert failed")), 137 }), 138 } as any; 139 const agent = mockAgent(); 140 141 await expect(createBoard(db, agent, forumDid, baseInput)).rejects.toThrow( 142 "DB insert failed" 143 ); 144 // PDS write happened before the failing DB insert 145 expect(agent.com.atproto.repo.createRecord).toHaveBeenCalled(); 146 }); 147 148 it("includes sortOrder in PDS record and DB row when provided", async () => { 149 const db = mockDb(); 150 const agent = mockAgent(); 151 152 await createBoard(db, agent, forumDid, { ...baseInput, sortOrder: 3 }); 153 154 expect(agent.com.atproto.repo.createRecord).toHaveBeenCalledWith( 155 expect.objectContaining({ 156 record: expect.objectContaining({ sortOrder: 3 }), 157 }) 158 ); 159 expect(db.insert().values).toHaveBeenCalledWith( 160 expect.objectContaining({ sortOrder: 3 }) 161 ); 162 }); 163 164 it("omits sortOrder from PDS record and DB row when not provided", async () => { 165 const db = mockDb(); 166 const agent = mockAgent(); 167 168 await createBoard(db, agent, forumDid, baseInput); 169 170 const record = (agent.com.atproto.repo.createRecord as ReturnType<typeof vi.fn>).mock.calls[0][0].record; 171 expect(record).not.toHaveProperty("sortOrder"); 172 expect(db.insert().values).toHaveBeenCalledWith( 173 expect.objectContaining({ sortOrder: null }) 174 ); 175 }); 176});