Skip to content

Commit 0e02a3a

Browse files
authored
ENG-1233 Group functionality testing (#678)
1 parent 41b12f4 commit 0e02a3a

2 files changed

Lines changed: 122 additions & 20 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
Feature: Group content access
2+
User story:
3+
* As a user of the Obsidian plugin
4+
* Logged in through a given space's anonymous account
5+
* I want to be able to create a group including another user outside my space
6+
* giving that user access to my private content
7+
8+
Acceptance criteria:
9+
* The second user should not have access to the content before I publish my content to the group
10+
* The second user should have access after I publish my content to the group
11+
12+
Background:
13+
Given the database is blank
14+
And the user user1 opens the Roam plugin in space s1
15+
And the user user2 opens the Roam plugin in space s2
16+
17+
Scenario: Creating content
18+
When Document are added to the database:
19+
| $id | source_local_id | created | last_modified | _author_id | _space_id |
20+
| d1 | ld1 | 2025/01/01 | 2025/01/01 | user1 | s1 |
21+
| d2 | ld2 | 2025/01/01 | 2025/01/01 | user1 | s1 |
22+
And Content are added to the database:
23+
| $id | source_local_id | _document_id | text | created | last_modified | scale | _author_id | _space_id |
24+
| ct1 | lct1 | d1 | Claim 1 | 2025/01/01 | 2025/01/01 | document | user1 | s1 |
25+
| ct2 | lct2 | d2 | Claim 2 | 2025/01/01 | 2025/01/01 | document | user1 | s1 |
26+
Then a user logged in space s1 should see 2 PlatformAccount in the database
27+
And a user logged in space s1 should see 2 Content in the database
28+
And a user logged in space s2 should see 2 PlatformAccount in the database
29+
But a user logged in space s2 should see 0 Content in the database
30+
When user of space s1 creates group my_group
31+
And user of space s1 adds space s2 to group my_group
32+
Then a user logged in space s1 should see 2 Content in the database
33+
But a user logged in space s2 should see 0 Content in the database
34+
And a user logged in space s2 should see 1 Space in the database
35+
And ResourceAccess are added to the database:
36+
| _account_uid | _space_id | source_local_id |
37+
| my_group | s1 | lct1 |
38+
And SpaceAccess are added to the database:
39+
| _account_uid | _space_id | permissions |
40+
| my_group | s1 | partial |
41+
Then a user logged in space s1 should see 2 Content in the database
42+
Then a user logged in space s2 should see 1 Content in the database
43+
And a user logged in space s2 should see 2 Space in the database

packages/database/features/step-definitions/stepdefs.ts

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919

2020
type Platform = Enums<"Platform">;
2121
type TableName = keyof Database["public"]["Tables"];
22+
type LocalRefsType = Record<string, number | string>;
2223
const PLATFORMS: readonly Platform[] = Constants.public.Enums.Platform;
2324

2425
if (getVariant() === "production") {
@@ -64,6 +65,19 @@ Given("the database is blank", async () => {
6465
assert.equal(r.error, null);
6566
r = await client.from("AgentIdentifier").delete().neq("account_id", -1);
6667
assert.equal(r.error, null);
68+
const r3 = await client.from("group_membership").select("group_id");
69+
assert.equal(r3.error, null);
70+
const groupIds = new Set((r3.data || []).map(({group_id})=>group_id));
71+
for (const id of groupIds) {
72+
const ur = await client.auth.admin.deleteUser(id);
73+
assert.equal(ur.error, null);
74+
}
75+
const r2 = await client.from("PlatformAccount").select("dg_account").not('dg_account', 'is', null);
76+
assert.equal(r2.error, null);
77+
for (const {dg_account} of r2.data || []) {
78+
const r = await client.auth.admin.deleteUser(dg_account!);
79+
assert.equal(r.error, null);
80+
}
6781
r = await client.from("PlatformAccount").delete().neq("id", -1);
6882
assert.equal(r.error, null);
6983
r = await client.from("Space").delete().neq("id", -1);
@@ -75,7 +89,7 @@ Given("the database is blank", async () => {
7589

7690
const substituteLocalReferences = (
7791
obj: any,
78-
localRefs: Record<string, number>,
92+
localRefs: LocalRefsType,
7993
prefixValue: boolean = false,
8094
): any => {
8195
const substituteLocalReferencesRec = (v: any): any => {
@@ -102,7 +116,7 @@ const substituteLocalReferences = (
102116

103117
const substituteLocalReferencesRow = (
104118
row: Record<string, string>,
105-
localRefs: Record<string, number>,
119+
localRefs: LocalRefsType,
106120
): Record<string, any> => {
107121
const processKV = ([k, v]: [string, any]): [string, any] => {
108122
const isJson = k.charAt(0) === "@";
@@ -134,7 +148,7 @@ Given(
134148
// Columns prefixed with _ are translated back from aliases to db ids.
135149
// Columns prefixed with @ are parsed as json values. (Use @ before _)
136150
const client = getServiceClient();
137-
const localRefs = (world.localRefs || {}) as Record<string, number>;
151+
const localRefs = (world.localRefs || {}) as LocalRefsType;
138152
const rows = table.hashes();
139153
const values: Record<string, any>[] = rows.map((r) =>
140154
substituteLocalReferencesRow(r, localRefs),
@@ -186,7 +200,7 @@ When(
186200
// assumption: turbo dev is running. TODO: Make into hooks
187201
if (PLATFORMS.indexOf(platform) < 0)
188202
throw new Error(`Platform must be one of ${PLATFORMS.join(", ")}`);
189-
const localRefs = (world.localRefs || {}) as Record<string, number>;
203+
const localRefs = (world.localRefs || {}) as LocalRefsType;
190204
const spaceResponse = await fetchOrCreateSpaceDirect({
191205
password: SPACE_ANONYMOUS_PASSWORD,
192206
url: `https://roamresearch.com/#/app/${spaceName}`,
@@ -246,9 +260,9 @@ const getLoggedinDatabase = async (spaceId: number) => {
246260
Then(
247261
"a user logged in space {word} should see a {word} in the database",
248262
async (spaceName: string, tableName: TableName) => {
249-
const localRefs = (world.localRefs || {}) as Record<string, number>;
263+
const localRefs = (world.localRefs || {}) as LocalRefsType;
250264
const spaceId = localRefs[spaceName];
251-
if (spaceId === undefined) assert.fail("spaceId");
265+
if (typeof spaceId !== "number") assert.fail("spaceId not a number");
252266
const client = await getLoggedinDatabase(spaceId);
253267
const response = await client
254268
.from(tableName)
@@ -261,9 +275,9 @@ Then(
261275
Then(
262276
"a user logged in space {word} should see {int} {word} in the database",
263277
async (spaceName: string, expectedCount: number, tableName: TableName) => {
264-
const localRefs = (world.localRefs || {}) as Record<string, number>;
278+
const localRefs = (world.localRefs || {}) as LocalRefsType;
265279
const spaceId = localRefs[spaceName];
266-
if (spaceId === undefined) assert.fail("spaceId");
280+
if (typeof spaceId !== "number") assert.fail("spaceId not a number");
267281
const client = await getLoggedinDatabase(spaceId);
268282
const response = await client
269283
.from(tableName)
@@ -277,9 +291,9 @@ Given(
277291
"user {word} upserts these accounts to space {word}:",
278292
async (userName: string, spaceName: string, accountsString: string) => {
279293
const accounts = JSON.parse(accountsString) as Json;
280-
const localRefs = (world.localRefs || {}) as Record<string, number>;
294+
const localRefs = (world.localRefs || {}) as LocalRefsType;
281295
const spaceId = localRefs[spaceName];
282-
if (spaceId === undefined) assert.fail("spaceId");
296+
if (typeof spaceId !== "number") assert.fail("spaceId not a number");
283297
const client = await getLoggedinDatabase(spaceId);
284298
const response = await client.rpc("upsert_accounts_in_space", {
285299
space_id_: spaceId, // eslint-disable-line @typescript-eslint/naming-convention
@@ -294,9 +308,9 @@ Given(
294308
"user {word} upserts these documents to space {word}:",
295309
async (userName: string, spaceName: string, docString: string) => {
296310
const data = JSON.parse(docString) as Json;
297-
const localRefs = (world.localRefs || {}) as Record<string, number>;
311+
const localRefs = (world.localRefs || {}) as LocalRefsType;
298312
const spaceId = localRefs[spaceName];
299-
if (spaceId === undefined) assert.fail("spaceId");
313+
if (typeof spaceId !== "number") assert.fail("spaceId not a number");
300314
const client = await getLoggedinDatabase(spaceId);
301315
const response = await client.rpc("upsert_documents", {
302316
v_space_id: spaceId, // eslint-disable-line @typescript-eslint/naming-convention
@@ -311,11 +325,11 @@ Given(
311325
"user {word} upserts this content to space {word}:",
312326
async (userName: string, spaceName: string, docString: string) => {
313327
const data = JSON.parse(docString) as Json;
314-
const localRefs = (world.localRefs || {}) as Record<string, number>;
328+
const localRefs = (world.localRefs || {}) as LocalRefsType;
315329
const spaceId = localRefs[spaceName];
316-
if (spaceId === undefined) assert.fail("spaceId");
330+
if (typeof spaceId !== "number") assert.fail("spaceId not a number");
317331
const userId = localRefs[userName];
318-
if (userId === undefined) assert.fail("userId");
332+
if (typeof userId !== "number") assert.fail("userId not a number");
319333
const client = await getLoggedinDatabase(spaceId);
320334
const response = await client.rpc("upsert_content", {
321335
v_space_id: spaceId, // eslint-disable-line @typescript-eslint/naming-convention
@@ -332,9 +346,9 @@ Given(
332346
"user {word} upserts these concepts to space {word}:",
333347
async (userName: string, spaceName: string, docString: string) => {
334348
const data = JSON.parse(docString) as Json;
335-
const localRefs = (world.localRefs || {}) as Record<string, number>;
349+
const localRefs = (world.localRefs || {}) as LocalRefsType;
336350
const spaceId = localRefs[spaceName];
337-
if (spaceId === undefined) assert.fail("spaceId");
351+
if (typeof spaceId !== "number") assert.fail("spaceId not a number");
338352
const client = await getLoggedinDatabase(spaceId);
339353
const response = await client.rpc("upsert_concepts", {
340354
v_space_id: spaceId, // eslint-disable-line @typescript-eslint/naming-convention
@@ -348,14 +362,14 @@ Given(
348362
"a user logged in space {word} and calling getConcepts with these parameters: {string}",
349363
async (spaceName: string, paramsJ: string) => {
350364
// params are assumed to be Json. Values prefixed with '@' are interpreted as aliases.
351-
const localRefs = (world.localRefs || {}) as Record<string, number>;
365+
const localRefs = (world.localRefs || {}) as LocalRefsType;
352366
const params = substituteLocalReferences(
353367
JSON.parse(paramsJ),
354368
localRefs,
355369
true,
356370
) as object;
357371
const spaceId = localRefs[spaceName];
358-
if (spaceId === undefined) assert.fail("spaceId");
372+
if (typeof spaceId !== "number") assert.fail("spaceId not a number");
359373
const supabase = await getLoggedinDatabase(spaceId);
360374
// note that we supply spaceId and supabase, they do not need to be part of the incoming Json
361375
const nodes = await getConcepts({ ...params, supabase, spaceId });
@@ -367,7 +381,7 @@ Given(
367381
type ObjectWithId = object & { id: number };
368382

369383
Then("query results should look like this", (table: DataTable) => {
370-
const localRefs = (world.localRefs || {}) as Record<string, number>;
384+
const localRefs = (world.localRefs || {}) as LocalRefsType;
371385
const rows = table.hashes();
372386
const values = rows.map((r) =>
373387
substituteLocalReferencesRow(r, localRefs),
@@ -389,3 +403,48 @@ Then("query results should look like this", (table: DataTable) => {
389403
assert.deepEqual(truncatedResults, values);
390404
}
391405
});
406+
407+
When("user of space {word} creates group {word}", async (spaceName: string, name: string) => {
408+
const localRefs = (world.localRefs || {}) as LocalRefsType;
409+
const spaceId = localRefs[spaceName];
410+
if (typeof spaceId !== "number") assert.fail("spaceId not a number");
411+
const client = await getLoggedinDatabase(spaceId);
412+
try{
413+
// eslint-disable-next-line @typescript-eslint/naming-convention
414+
const response = await client.functions.invoke<{group_id: string}>("create-group", {body:{name}});
415+
assert.equal(response.error, null);
416+
assert.ok(response.data?.group_id, "create-group response missing group_id");
417+
localRefs[name] = response.data.group_id;
418+
world.localRefs = localRefs;
419+
} catch (error) {
420+
console.error((error as Record<string, any>).actual);
421+
throw error;
422+
}
423+
})
424+
425+
When("user of space {word} adds space {word} to group {word}",
426+
async (space1Name: string, space2Name:string, groupName: string): Promise<void> =>{
427+
const localRefs = (world.localRefs || {}) as LocalRefsType;
428+
const space1Id = localRefs[space1Name];
429+
const space2Id = localRefs[space2Name];
430+
const groupId = localRefs[groupName];
431+
if (typeof space1Id !== 'number') assert.fail("space1Id not a number");
432+
if (typeof space2Id !== 'number') assert.fail("space2Id not a number");
433+
if (typeof groupId !== 'string') assert.fail("groupId not a string");
434+
const client2 = await getLoggedinDatabase(space2Id);
435+
const r1 = await client2.from("PlatformAccount")
436+
.select("dg_account")
437+
.eq("account_local_id", spaceAnonUserEmail("Roam", space2Id))
438+
.maybeSingle();
439+
assert.equal(r1.error, null);
440+
const memberId = r1.data?.dg_account;
441+
assert.ok(memberId, "memberId not found for space2");
442+
const client1 = await getLoggedinDatabase(space1Id);
443+
const r2 = await client1.from("group_membership").insert({
444+
/* eslint-disable @typescript-eslint/naming-convention */
445+
group_id: groupId,
446+
member_id: memberId
447+
/* eslint-enable @typescript-eslint/naming-convention */
448+
});
449+
assert.equal(r2.error, null);
450+
})

0 commit comments

Comments
 (0)