Skip to content

Commit b06333c

Browse files
committed
Add a separate view for Pseudo-accounts. Focus on space membership. Testing.
1 parent 5fcd009 commit b06333c

5 files changed

Lines changed: 368 additions & 60 deletions

File tree

apps/website/app/utils/supabase/account.ts

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,5 @@
1-
import type { Database } from "@repo/database/dbTypes";
21
import type { DGSupabaseClient } from "@repo/database/lib/client";
32

4-
type AgentType = Database["public"]["Enums"]["AgentType"] | "group";
5-
6-
export const getGroupMemberList = async (
7-
client: DGSupabaseClient,
8-
groupId: string,
9-
): Promise<
10-
{
11-
id: string;
12-
name: string;
13-
agentType: AgentType; // eslint-disable-line @typescript-eslint/naming-convention
14-
admin: boolean;
15-
}[]
16-
> => {
17-
// eslint-disable-next-line @typescript-eslint/naming-convention
18-
const results = await client.rpc("group_members", { p_group_id: groupId });
19-
if (results.error) throw results.error;
20-
if (!results.data) return [];
21-
return (
22-
results.data
23-
// eslint-disable-next-line @typescript-eslint/naming-convention
24-
.map(({ id, name, agent_type, admin }) => ({
25-
id,
26-
name,
27-
agentType: agent_type,
28-
admin,
29-
}))
30-
.filter(
31-
({ id, name, agentType, admin }) =>
32-
admin !== null && id !== null && name !== null && agentType !== null,
33-
) as unknown as {
34-
admin: boolean;
35-
id: string;
36-
name: string;
37-
agentType: AgentType;
38-
}[]
39-
);
40-
};
41-
423
export const createGroup = async (
434
client: DGSupabaseClient,
445
name: string,
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import assert from "assert";
2+
import { describe, it, beforeAll, afterAll } from "vitest";
3+
import { createClient } from "@supabase/supabase-js";
4+
import type { Database } from "@repo/database/dbTypes";
5+
import type { DGSupabaseClient } from "@repo/database/lib/client";
6+
import {
7+
fetchOrCreateSpaceDirect,
8+
spaceAnonUserEmail,
9+
} from "@repo/database/lib/contextFunctions";
10+
import { createGroup } from "../../app/utils/supabase/account";
11+
12+
const SUPABASE_URL = process.env.SUPABASE_URL!;
13+
const ANON_KEY = process.env.SUPABASE_PUBLISHABLE_KEY!;
14+
const SERVICE_KEY = process.env.SUPABASE_SECRET_KEY!;
15+
const PASSWORD = "abcdefgh";
16+
17+
const freshClient = (): DGSupabaseClient =>
18+
createClient<Database, "public">(SUPABASE_URL, ANON_KEY);
19+
20+
const serviceClient = () =>
21+
createClient<Database, "public">(SUPABASE_URL, SERVICE_KEY);
22+
23+
const signedInClient = async (spaceId: number): Promise<DGSupabaseClient> => {
24+
const client = freshClient();
25+
const { error } = await client.auth.signInWithPassword({
26+
email: spaceAnonUserEmail("Roam", spaceId),
27+
password: PASSWORD,
28+
});
29+
if (error) throw new Error(`Sign-in failed: ${error.message}`);
30+
return client;
31+
};
32+
33+
describe("list group members flow", { tags: ["database"] }, () => {
34+
let spaceId1: number;
35+
let spaceId2: number;
36+
let spaceAccountId1: number;
37+
let spaceAccountUuid1: string;
38+
let spaceAccountId2: number;
39+
let spaceAccountUuid2: string;
40+
let client1: DGSupabaseClient;
41+
let client2: DGSupabaseClient;
42+
let createdGroupId: string | null = null;
43+
44+
beforeAll(async () => {
45+
const s1 = await fetchOrCreateSpaceDirect({
46+
name: "vitest-s1",
47+
url: "https://roamresearch.com/#/app/vitest-s1",
48+
platform: "Roam",
49+
password: PASSWORD,
50+
});
51+
if (!s1.data)
52+
throw new Error(`Failed to create space 1: ${s1.error?.message}`);
53+
spaceId1 = s1.data.id;
54+
client1 = await signedInClient(spaceId1);
55+
assert(client1);
56+
const accountReq1 = await client1
57+
.from("PlatformAccount")
58+
.select("id,dg_account")
59+
.eq(
60+
"account_local_id",
61+
`roam-${spaceId1}-anon@database.discoursegraphs.com`,
62+
)
63+
.maybeSingle();
64+
assert(!accountReq1.error);
65+
assert(accountReq1.data);
66+
assert(accountReq1.data.dg_account);
67+
spaceAccountId1 = accountReq1.data.id;
68+
spaceAccountUuid1 = accountReq1.data.dg_account;
69+
const s2 = await fetchOrCreateSpaceDirect({
70+
name: "vitest-s2",
71+
url: "https://roamresearch.com/#/app/vitest-s2",
72+
platform: "Roam",
73+
password: PASSWORD,
74+
});
75+
if (!s2.data)
76+
throw new Error(`Failed to create space 2: ${s2.error?.message}`);
77+
spaceId2 = s2.data.id;
78+
client2 = await signedInClient(spaceId2);
79+
assert(client2);
80+
const accountReq2 = await client2
81+
.from("PlatformAccount")
82+
.select("id,dg_account")
83+
.eq(
84+
"account_local_id",
85+
`roam-${spaceId2}-anon@database.discoursegraphs.com`,
86+
)
87+
.maybeSingle();
88+
assert(!accountReq2.error);
89+
assert(accountReq2.data);
90+
assert(accountReq2.data.dg_account);
91+
spaceAccountId2 = accountReq2.data.id;
92+
spaceAccountUuid2 = accountReq2.data.dg_account;
93+
});
94+
95+
afterAll(async () => {
96+
if (createdGroupId)
97+
await serviceClient().auth.admin.deleteUser(createdGroupId);
98+
if (spaceAccountUuid1)
99+
await serviceClient().auth.admin.deleteUser(spaceAccountUuid1);
100+
if (spaceAccountUuid2)
101+
await serviceClient().auth.admin.deleteUser(spaceAccountUuid2);
102+
if (spaceId1)
103+
await serviceClient().from("Space").delete().eq("id", spaceId1);
104+
if (spaceId2)
105+
await serviceClient().from("Space").delete().eq("id", spaceId2);
106+
});
107+
108+
it("lists group members", async () => {
109+
// Step 1: user1 creates a group
110+
const groupId = await createGroup(client1, "vitest-invite-group");
111+
assert(groupId !== null, "createGroup should return a group ID");
112+
createdGroupId = groupId!;
113+
114+
// Step 2: Add another member
115+
const { error: errorAddMember } = await client1
116+
.from("group_membership")
117+
.insert({
118+
member_id: spaceAccountUuid2,
119+
group_id: groupId,
120+
admin: false,
121+
});
122+
assert(!errorAddMember);
123+
124+
// Step 3: Mutual publish setup
125+
const { error: errorPublishSpace1 } = await client1
126+
.from("SpaceAccess")
127+
.insert({
128+
space_id: spaceId1,
129+
account_uid: groupId,
130+
permissions: "partial",
131+
});
132+
assert(!errorPublishSpace1);
133+
134+
const { error: errorPublishSpace2 } = await client2
135+
.from("SpaceAccess")
136+
.insert({
137+
space_id: spaceId2,
138+
account_uid: groupId,
139+
permissions: "partial",
140+
});
141+
assert(!errorPublishSpace2);
142+
143+
const expectedSpaceIds = [spaceId1, spaceId2];
144+
// Step 4: user1 lists group members
145+
const { data: data1, error: error1 } = await client1.rpc(
146+
"spaces_in_group",
147+
{
148+
// eslint-disable-next-line @typescript-eslint/naming-convention
149+
p_group_id: createdGroupId,
150+
},
151+
);
152+
assert(error1 === null, error1 ? error1.message : "");
153+
assert(data1 !== null, "group spaces should not be empty");
154+
assert(data1.length === 2, "There should be two spaces");
155+
const spacesSeenBy1 = new Set(
156+
data1.map((gm) => gm.id).filter((id) => id !== null),
157+
);
158+
assert(
159+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
160+
expectedSpaceIds.every((id) => spacesSeenBy1.has(id)),
161+
"Wrong membership information",
162+
);
163+
// Step 5: user2 lists group members
164+
const { data: data2, error: error2 } = await client2.rpc(
165+
"spaces_in_group",
166+
{
167+
// eslint-disable-next-line @typescript-eslint/naming-convention
168+
p_group_id: createdGroupId,
169+
},
170+
);
171+
assert(error2 === null, error2 ? error2.message : "");
172+
assert(data2 !== null, "group spaces should not be empty");
173+
assert(data2.length === 2, "There should be two spaces");
174+
const spacesSeenBy2 = new Set(
175+
data2.map((gm) => gm.id).filter((id) => id !== null),
176+
);
177+
assert(
178+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
179+
expectedSpaceIds.every((id) => spacesSeenBy2.has(id)),
180+
"Wrong membership information",
181+
);
182+
});
183+
});

0 commit comments

Comments
 (0)