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 main 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});