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
1import { describe, it, expect, vi } from "vitest";
2import { seedDefaultRoles, DEFAULT_ROLES } from "../lib/steps/seed-roles.js";
3
4describe("seedDefaultRoles", () => {
5 const forumDid = "did:plc:testforum";
6
7 function mockDb(existingRoleNames: string[] = []) {
8 const existingQueue = [...existingRoleNames];
9 let roleIdCounter = 1n;
10
11 return {
12 select: vi.fn().mockReturnValue({
13 from: vi.fn().mockReturnValue({
14 where: vi.fn().mockReturnValue({
15 limit: vi.fn().mockImplementation(() => {
16 const roleName = existingQueue.shift();
17 if (roleName) {
18 return [{
19 name: roleName,
20 did: forumDid,
21 rkey: roleName.toLowerCase(),
22 cid: `bafyexisting-${roleName.toLowerCase()}`,
23 }];
24 }
25 return [];
26 }),
27 }),
28 }),
29 }),
30 insert: vi.fn().mockImplementation(() => {
31 const id = roleIdCounter++;
32 // values() returns a Promise (for rolePermissions inserts that are awaited directly)
33 // but also has a .returning() method (for role inserts)
34 const resolvedPromise = Promise.resolve(undefined);
35 const valuesResult = Object.assign(resolvedPromise, {
36 returning: vi.fn().mockResolvedValue([{ id }]),
37 });
38 return {
39 values: vi.fn().mockReturnValue(valuesResult),
40 };
41 }),
42 } as any;
43 }
44
45 function mockAgent() {
46 let callCount = 0;
47 return {
48 com: {
49 atproto: {
50 repo: {
51 createRecord: vi.fn().mockImplementation(() => {
52 callCount++;
53 return Promise.resolve({
54 data: {
55 uri: `at://${forumDid}/space.atbb.forum.role/tid${callCount}`,
56 cid: `bafynew${callCount}`,
57 },
58 });
59 }),
60 },
61 },
62 },
63 } as any;
64 }
65
66 it("exports DEFAULT_ROLES with correct structure", () => {
67 expect(DEFAULT_ROLES).toHaveLength(4);
68 expect(DEFAULT_ROLES[0].name).toBe("Owner");
69 expect(DEFAULT_ROLES[0].priority).toBe(0);
70 expect(DEFAULT_ROLES[3].name).toBe("Member");
71 expect(DEFAULT_ROLES[3].priority).toBe(30);
72 });
73
74 it("creates all roles on PDS and DB when none exist", async () => {
75 const db = mockDb();
76 const agent = mockAgent();
77
78 const result = await seedDefaultRoles(db, agent, forumDid);
79
80 expect(result.created).toBe(4);
81 expect(result.skipped).toBe(0);
82 expect(agent.com.atproto.repo.createRecord).toHaveBeenCalledTimes(4);
83 // Verify DB insertions: 4 role inserts + 4 rolePermissions inserts (one per role)
84 expect(db.insert).toHaveBeenCalledTimes(8);
85 // Verify returned role data
86 expect(result.roles).toHaveLength(4);
87 expect(result.roles[0].name).toBe("Owner");
88 expect(result.roles[0].uri).toContain("space.atbb.forum.role/");
89 expect(result.roles[0].cid).toBeTruthy();
90 });
91
92 it("skips existing roles and returns their data", async () => {
93 // Simulate Owner and Admin already existing
94 const db = mockDb(["Owner", "Admin"]);
95 const agent = mockAgent();
96
97 const result = await seedDefaultRoles(db, agent, forumDid);
98
99 expect(result.skipped).toBe(2);
100 expect(result.created).toBe(2);
101 // All 4 roles should be in the return value
102 expect(result.roles).toHaveLength(4);
103 // Existing roles use data from DB
104 expect(result.roles[0].name).toBe("Owner");
105 expect(result.roles[0].cid).toBe("bafyexisting-owner");
106 // New roles use data from PDS response
107 expect(result.roles[2].name).toBe("Moderator");
108 expect(result.roles[2].cid).toMatch(/^bafynew/);
109 });
110});