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 252 lines 7.3 kB view raw
1import { describe, it, expect, beforeEach, afterEach } from "vitest"; 2import { Indexer } from "../indexer.js"; 3import { createTestContext, type TestContext } from "./test-context.js"; 4import { boards, categories, forums } from "@atbb/db"; 5import { eq } from "drizzle-orm"; 6import type { 7 CommitCreateEvent, 8 CommitUpdateEvent, 9 CommitDeleteEvent, 10} from "@skyware/jetstream"; 11 12describe("Indexer - Board Handlers", () => { 13 let ctx: TestContext; 14 let indexer: Indexer; 15 16 let testCategoryId: bigint; 17 18 beforeEach(async () => { 19 ctx = await createTestContext(); 20 indexer = new Indexer(ctx.db, ctx.logger); 21 22 // Get the forum ID created by createTestContext 23 const [forum] = await ctx.db 24 .select({ id: forums.id }) 25 .from(forums) 26 .where(eq(forums.did, "did:plc:test-forum")) 27 .limit(1); 28 29 // Insert test category and save the ID 30 const [category] = await ctx.db.insert(categories).values({ 31 did: "did:plc:test-forum", 32 rkey: "cat1", 33 cid: "bafycat", 34 name: "Test Category", 35 forumId: forum.id, 36 createdAt: new Date(), 37 indexedAt: new Date(), 38 }).returning(); 39 40 testCategoryId = category.id; 41 }); 42 43 afterEach(async () => { 44 // Clean up in correct FK order: boards -> categories -> forums 45 await ctx.db.delete(boards).where(eq(boards.did, "did:plc:test-forum")); 46 await ctx.cleanup(); 47 }); 48 49 it("handleBoardCreate indexes board record", async () => { 50 const event: CommitCreateEvent<"space.atbb.forum.board"> = { 51 kind: "commit", 52 commit: { 53 rev: "abc123", 54 operation: "create", 55 collection: "space.atbb.forum.board", 56 rkey: "board1", 57 record: { 58 $type: "space.atbb.forum.board", 59 name: "General Discussion", 60 description: "Talk about anything", 61 slug: "general", 62 sortOrder: 1, 63 category: { 64 category: { 65 uri: "at://did:plc:test-forum/space.atbb.forum.category/cat1", 66 cid: "bafycat", 67 }, 68 }, 69 createdAt: "2026-02-13T00:00:00Z", 70 } as any, 71 cid: "bafyboard", 72 }, 73 did: "did:plc:test-forum", 74 time_us: 1000000, 75 }; 76 77 await indexer.handleBoardCreate(event); 78 79 const [board] = await ctx.db 80 .select() 81 .from(boards) 82 .where(eq(boards.rkey, "board1")); 83 84 expect(board).toBeDefined(); 85 expect(board.name).toBe("General Discussion"); 86 expect(board.slug).toBe("general"); 87 expect(board.sortOrder).toBe(1); 88 expect(board.categoryId).toBe(testCategoryId); 89 expect(board.categoryUri).toBe( 90 "at://did:plc:test-forum/space.atbb.forum.category/cat1" 91 ); 92 }); 93 94 it("handleBoardCreate throws when category not found", async () => { 95 const event: CommitCreateEvent<"space.atbb.forum.board"> = { 96 kind: "commit", 97 commit: { 98 rev: "abc123", 99 operation: "create", 100 collection: "space.atbb.forum.board", 101 rkey: "board2", 102 record: { 103 $type: "space.atbb.forum.board", 104 name: "Orphan Board", 105 category: { 106 category: { 107 uri: "at://did:plc:test-forum/space.atbb.forum.category/nonexistent", 108 cid: "bafynonexistent", 109 }, 110 }, 111 createdAt: "2026-02-13T00:00:00Z", 112 } as any, 113 cid: "bafyboard2", 114 }, 115 did: "did:plc:test-forum", 116 time_us: 1000000, 117 }; 118 119 await expect(indexer.handleBoardCreate(event)).rejects.toThrow( 120 "Category not found: at://did:plc:test-forum/space.atbb.forum.category/nonexistent" 121 ); 122 123 const results = await ctx.db.select().from(boards); 124 expect(results).toHaveLength(0); 125 }); 126 127 it("handleBoardUpdate updates existing board", async () => { 128 // First create a board 129 const createEvent: CommitCreateEvent<"space.atbb.forum.board"> = { 130 kind: "commit", 131 commit: { 132 rev: "abc123", 133 operation: "create", 134 collection: "space.atbb.forum.board", 135 rkey: "board1", 136 record: { 137 $type: "space.atbb.forum.board", 138 name: "General Discussion", 139 description: "Talk about anything", 140 slug: "general", 141 sortOrder: 1, 142 category: { 143 category: { 144 uri: "at://did:plc:test-forum/space.atbb.forum.category/cat1", 145 cid: "bafycat", 146 }, 147 }, 148 createdAt: "2026-02-13T00:00:00Z", 149 } as any, 150 cid: "bafyboard", 151 }, 152 did: "did:plc:test-forum", 153 time_us: 1000000, 154 }; 155 156 await indexer.handleBoardCreate(createEvent); 157 158 // Now update it 159 const updateEvent: CommitUpdateEvent<"space.atbb.forum.board"> = { 160 kind: "commit", 161 commit: { 162 rev: "def456", 163 operation: "update", 164 collection: "space.atbb.forum.board", 165 rkey: "board1", 166 record: { 167 $type: "space.atbb.forum.board", 168 name: "Updated Board Name", 169 description: "Updated description", 170 slug: "updated-general", 171 sortOrder: 2, 172 category: { 173 category: { 174 uri: "at://did:plc:test-forum/space.atbb.forum.category/cat1", 175 cid: "bafycat", 176 }, 177 }, 178 createdAt: "2026-02-13T00:00:00Z", 179 } as any, 180 cid: "bafyboard-updated", 181 }, 182 did: "did:plc:test-forum", 183 time_us: 2000000, 184 }; 185 186 await indexer.handleBoardUpdate(updateEvent); 187 188 const [board] = await ctx.db 189 .select() 190 .from(boards) 191 .where(eq(boards.rkey, "board1")); 192 193 expect(board).toBeDefined(); 194 expect(board.name).toBe("Updated Board Name"); 195 expect(board.description).toBe("Updated description"); 196 expect(board.slug).toBe("updated-general"); 197 expect(board.sortOrder).toBe(2); 198 expect(board.cid).toBe("bafyboard-updated"); 199 }); 200 201 it("handleBoardDelete removes board", async () => { 202 // First create a board 203 const createEvent: CommitCreateEvent<"space.atbb.forum.board"> = { 204 kind: "commit", 205 commit: { 206 rev: "abc123", 207 operation: "create", 208 collection: "space.atbb.forum.board", 209 rkey: "board1", 210 record: { 211 $type: "space.atbb.forum.board", 212 name: "General Discussion", 213 category: { 214 category: { 215 uri: "at://did:plc:test-forum/space.atbb.forum.category/cat1", 216 cid: "bafycat", 217 }, 218 }, 219 createdAt: "2026-02-13T00:00:00Z", 220 } as any, 221 cid: "bafyboard", 222 }, 223 did: "did:plc:test-forum", 224 time_us: 1000000, 225 }; 226 227 await indexer.handleBoardCreate(createEvent); 228 229 // Verify it exists 230 let results = await ctx.db.select().from(boards); 231 expect(results).toHaveLength(1); 232 233 // Now delete it 234 const deleteEvent: CommitDeleteEvent<"space.atbb.forum.board"> = { 235 kind: "commit", 236 commit: { 237 rev: "ghi789", 238 operation: "delete", 239 collection: "space.atbb.forum.board", 240 rkey: "board1", 241 }, 242 did: "did:plc:test-forum", 243 time_us: 3000000, 244 }; 245 246 await indexer.handleBoardDelete(deleteEvent); 247 248 // Verify it's gone 249 results = await ctx.db.select().from(boards); 250 expect(results).toHaveLength(0); 251 }); 252});