cojson 0.13.30 → 0.13.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.turbo/turbo-build.log +5 -4
  2. package/.turbo/turbo-lint.log +4 -0
  3. package/.turbo/turbo-test.log +1001 -0
  4. package/CHANGELOG.md +13 -0
  5. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  6. package/dist/coValueCore/coValueCore.js +1 -4
  7. package/dist/coValueCore/coValueCore.js.map +1 -1
  8. package/dist/coValues/group.d.ts.map +1 -1
  9. package/dist/coValues/group.js +4 -1
  10. package/dist/coValues/group.js.map +1 -1
  11. package/dist/localNode.d.ts.map +1 -1
  12. package/dist/localNode.js +1 -1
  13. package/dist/localNode.js.map +1 -1
  14. package/dist/permissions.d.ts.map +1 -1
  15. package/dist/permissions.js +0 -12
  16. package/dist/permissions.js.map +1 -1
  17. package/dist/tests/group.inheritance.test.d.ts +2 -0
  18. package/dist/tests/group.inheritance.test.d.ts.map +1 -0
  19. package/dist/tests/group.inheritance.test.js +235 -0
  20. package/dist/tests/group.inheritance.test.js.map +1 -0
  21. package/dist/tests/group.invite.test.d.ts +2 -0
  22. package/dist/tests/group.invite.test.d.ts.map +1 -0
  23. package/dist/tests/group.invite.test.js +214 -0
  24. package/dist/tests/group.invite.test.js.map +1 -0
  25. package/dist/tests/group.removeMember.test.js +22 -0
  26. package/dist/tests/group.removeMember.test.js.map +1 -1
  27. package/dist/tests/group.roleOf.test.d.ts +2 -0
  28. package/dist/tests/group.roleOf.test.d.ts.map +1 -0
  29. package/dist/tests/group.roleOf.test.js +275 -0
  30. package/dist/tests/group.roleOf.test.js.map +1 -0
  31. package/dist/tests/group.test.js +1 -505
  32. package/dist/tests/group.test.js.map +1 -1
  33. package/dist/tests/permissions.test.js +0 -33
  34. package/dist/tests/permissions.test.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/coValueCore/coValueCore.ts +1 -6
  37. package/src/coValues/group.ts +4 -1
  38. package/src/localNode.ts +3 -1
  39. package/src/permissions.ts +0 -18
  40. package/src/tests/group.inheritance.test.ts +418 -0
  41. package/src/tests/group.invite.test.ts +354 -0
  42. package/src/tests/group.removeMember.test.ts +48 -0
  43. package/src/tests/group.roleOf.test.ts +450 -0
  44. package/src/tests/group.test.ts +0 -852
  45. package/src/tests/permissions.test.ts +0 -52
  46. package/src/tests/sync.invite.test.ts +12 -4
@@ -0,0 +1,354 @@
1
+ import { beforeEach, describe, expect, test } from "vitest";
2
+ import { expectMap } from "../coValue.js";
3
+ import { LogLevel, logger } from "../logger.js";
4
+ import {
5
+ SyncMessagesLog,
6
+ loadCoValueOrFail,
7
+ setupTestAccount,
8
+ setupTestNode,
9
+ waitFor,
10
+ } from "./testUtils.js";
11
+
12
+ beforeEach(async () => {
13
+ SyncMessagesLog.clear();
14
+ setupTestNode({ isSyncServer: true });
15
+ });
16
+
17
+ describe("Group invites", () => {
18
+ test("should be able to accept a reader invite", async () => {
19
+ const admin = await setupTestAccount({
20
+ connected: true,
21
+ });
22
+
23
+ const newMember = await setupTestAccount({
24
+ connected: true,
25
+ });
26
+
27
+ const group = admin.node.createGroup();
28
+ const person = group.createMap({
29
+ name: "John Doe",
30
+ });
31
+
32
+ const inviteSecret = group.createInvite("reader");
33
+
34
+ const personOnNewMemberNode = await loadCoValueOrFail(
35
+ newMember.node,
36
+ person.id,
37
+ );
38
+ expect(personOnNewMemberNode.get("name")).toEqual(undefined);
39
+
40
+ await newMember.node.acceptInvite(group.id, inviteSecret);
41
+
42
+ await waitFor(() => {
43
+ expect(
44
+ expectMap(personOnNewMemberNode.core.getCurrentContent()).get("name"),
45
+ ).toEqual("John Doe");
46
+ });
47
+
48
+ const groupOnNewMemberNode = await loadCoValueOrFail(
49
+ newMember.node,
50
+ group.id,
51
+ );
52
+
53
+ expect(groupOnNewMemberNode.roleOf(newMember.accountID)).toEqual("reader");
54
+ });
55
+
56
+ test("should be able to accept a writer invite", async () => {
57
+ const admin = await setupTestAccount({
58
+ connected: true,
59
+ });
60
+
61
+ const newMember = await setupTestAccount({
62
+ connected: true,
63
+ });
64
+
65
+ const group = admin.node.createGroup();
66
+ const person = group.createMap({
67
+ name: "John Doe",
68
+ });
69
+
70
+ const inviteSecret = group.createInvite("writer");
71
+
72
+ const personOnNewMemberNode = await loadCoValueOrFail(
73
+ newMember.node,
74
+ person.id,
75
+ );
76
+ expect(personOnNewMemberNode.get("name")).toEqual(undefined);
77
+
78
+ await newMember.node.acceptInvite(group.id, inviteSecret);
79
+
80
+ await waitFor(() => {
81
+ expect(
82
+ expectMap(personOnNewMemberNode.core.getCurrentContent()).get("name"),
83
+ ).toEqual("John Doe");
84
+ });
85
+
86
+ const groupOnNewMemberNode = await loadCoValueOrFail(
87
+ newMember.node,
88
+ group.id,
89
+ );
90
+
91
+ expect(groupOnNewMemberNode.roleOf(newMember.accountID)).toEqual("writer");
92
+
93
+ // Verify write access
94
+ personOnNewMemberNode.set("name", "Jane Doe");
95
+ expect(personOnNewMemberNode.get("name")).toEqual("Jane Doe");
96
+ });
97
+
98
+ test("should be able to accept a writeOnly invite", async () => {
99
+ const admin = await setupTestAccount({
100
+ connected: true,
101
+ });
102
+
103
+ const newMember = await setupTestAccount({
104
+ connected: true,
105
+ });
106
+
107
+ const group = admin.node.createGroup();
108
+ const person = group.createMap({
109
+ name: "John Doe",
110
+ });
111
+
112
+ const inviteSecret = group.createInvite("writeOnly");
113
+
114
+ const personOnNewMemberNode = await loadCoValueOrFail(
115
+ newMember.node,
116
+ person.id,
117
+ );
118
+ expect(personOnNewMemberNode.get("name")).toEqual(undefined);
119
+
120
+ await newMember.node.acceptInvite(group.id, inviteSecret);
121
+
122
+ const groupOnNewMemberNode = await loadCoValueOrFail(
123
+ newMember.node,
124
+ group.id,
125
+ );
126
+
127
+ expect(groupOnNewMemberNode.roleOf(newMember.accountID)).toEqual(
128
+ "writeOnly",
129
+ );
130
+
131
+ // Should not be able to read
132
+ expect(personOnNewMemberNode.get("name")).toEqual(undefined);
133
+
134
+ // Should be able to write
135
+ personOnNewMemberNode.set("name", "Jane Doe");
136
+ expect(personOnNewMemberNode.get("name")).toEqual("Jane Doe");
137
+ });
138
+
139
+ test("should be able to accept an admin invite", async () => {
140
+ const admin = await setupTestAccount({
141
+ connected: true,
142
+ });
143
+
144
+ const newMember = await setupTestAccount({
145
+ connected: true,
146
+ });
147
+
148
+ const group = admin.node.createGroup();
149
+ const person = group.createMap({
150
+ name: "John Doe",
151
+ });
152
+
153
+ const inviteSecret = group.createInvite("admin");
154
+
155
+ const personOnNewMemberNode = await loadCoValueOrFail(
156
+ newMember.node,
157
+ person.id,
158
+ );
159
+ expect(personOnNewMemberNode.get("name")).toEqual(undefined);
160
+
161
+ await newMember.node.acceptInvite(group.id, inviteSecret);
162
+
163
+ await waitFor(() => {
164
+ expect(
165
+ expectMap(personOnNewMemberNode.core.getCurrentContent()).get("name"),
166
+ ).toEqual("John Doe");
167
+ });
168
+
169
+ const groupOnNewMemberNode = await loadCoValueOrFail(
170
+ newMember.node,
171
+ group.id,
172
+ );
173
+
174
+ expect(groupOnNewMemberNode.roleOf(newMember.accountID)).toEqual("admin");
175
+
176
+ // Verify admin access by adding another member
177
+ const reader = await setupTestAccount({
178
+ connected: true,
179
+ });
180
+
181
+ const readerOnNewMemberNode = await loadCoValueOrFail(
182
+ newMember.node,
183
+ reader.accountID,
184
+ );
185
+ groupOnNewMemberNode.addMember(readerOnNewMemberNode, "reader");
186
+
187
+ const personOnReaderNode = await loadCoValueOrFail(reader.node, person.id);
188
+
189
+ await waitFor(() => {
190
+ expect(
191
+ expectMap(personOnReaderNode.core.getCurrentContent()).get("name"),
192
+ ).toEqual("John Doe");
193
+ });
194
+ });
195
+
196
+ test("should not be able to accept an invite twice", async () => {
197
+ const admin = await setupTestAccount({
198
+ connected: true,
199
+ });
200
+
201
+ const newMember = await setupTestAccount({
202
+ connected: true,
203
+ });
204
+
205
+ const group = admin.node.createGroup();
206
+ const inviteSecret = group.createInvite("reader");
207
+
208
+ await newMember.node.acceptInvite(group.id, inviteSecret);
209
+
210
+ const groupOnNewMemberNode = await loadCoValueOrFail(
211
+ newMember.node,
212
+ group.id,
213
+ );
214
+
215
+ expect(groupOnNewMemberNode.roleOf(newMember.accountID)).toEqual("reader");
216
+
217
+ // Try to accept the same invite again
218
+ await newMember.node.acceptInvite(group.id, inviteSecret);
219
+ expect(groupOnNewMemberNode.roleOf(newMember.accountID)).toEqual("reader");
220
+ });
221
+
222
+ test("invites should not downgrade the role of an existing member", async () => {
223
+ const admin = await setupTestAccount({
224
+ connected: true,
225
+ });
226
+
227
+ const member = await setupTestAccount({
228
+ connected: true,
229
+ });
230
+
231
+ const group = admin.node.createGroup();
232
+ const person = group.createMap({
233
+ name: "John Doe",
234
+ });
235
+
236
+ // First add member as admin
237
+ const memberAccount = await loadCoValueOrFail(
238
+ member.node,
239
+ member.accountID,
240
+ );
241
+ group.addMember(memberAccount, "admin");
242
+
243
+ // Create a reader invite
244
+ const inviteSecret = group.createInvite("reader");
245
+
246
+ // Try to accept the lower-privilege invite
247
+ await member.node.acceptInvite(group.id, inviteSecret);
248
+
249
+ const groupOnMemberNode = await loadCoValueOrFail(member.node, group.id);
250
+ expect(groupOnMemberNode.roleOf(member.accountID)).toEqual("admin");
251
+ });
252
+
253
+ logger.setLevel(LogLevel.DEBUG);
254
+
255
+ test("invites should be able to upgrade the role of an existing member", async () => {
256
+ const admin = await setupTestAccount({
257
+ connected: true,
258
+ });
259
+
260
+ const member = await setupTestAccount({
261
+ connected: true,
262
+ });
263
+
264
+ const group = admin.node.createGroup();
265
+
266
+ // First add member as reader
267
+ const memberAccount = await loadCoValueOrFail(
268
+ member.node,
269
+ member.accountID,
270
+ );
271
+ group.addMember(memberAccount, "reader");
272
+
273
+ // Create an admin invite
274
+ const inviteSecret = group.createInvite("admin");
275
+
276
+ const groupOnMemberNode = await loadCoValueOrFail(member.node, group.id);
277
+
278
+ // Accept the higher-privilege invite
279
+ await member.node.acceptInvite(groupOnMemberNode.id, inviteSecret);
280
+
281
+ expect(groupOnMemberNode.roleOf(member.accountID)).toEqual("admin");
282
+
283
+ // Verify admin access by adding another member
284
+ const reader = await setupTestAccount({
285
+ connected: true,
286
+ });
287
+ const readerAccount = await loadCoValueOrFail(
288
+ member.node,
289
+ reader.accountID,
290
+ );
291
+ groupOnMemberNode.addMember(readerAccount, "reader");
292
+ });
293
+
294
+ test("invites should work on revoked members", async () => {
295
+ const admin = await setupTestAccount({
296
+ connected: true,
297
+ });
298
+
299
+ const member = await setupTestAccount({
300
+ connected: true,
301
+ });
302
+
303
+ const group = admin.node.createGroup();
304
+ const person = group.createMap({
305
+ name: "John Doe",
306
+ });
307
+
308
+ // First add member as reader
309
+ const memberAccount = await loadCoValueOrFail(
310
+ member.node,
311
+ member.accountID,
312
+ );
313
+ group.addMember(memberAccount, "reader");
314
+ group.removeMember(memberAccount);
315
+
316
+ // Create a new reader invite
317
+ const inviteSecret = group.createInvite("reader");
318
+
319
+ const groupOnMemberNode = await loadCoValueOrFail(member.node, group.id);
320
+
321
+ // Accept the invite after being revoked
322
+ await member.node.acceptInvite(groupOnMemberNode.id, inviteSecret);
323
+
324
+ expect(groupOnMemberNode.roleOf(member.accountID)).toEqual("reader");
325
+
326
+ await waitFor(() => {
327
+ expect(group.roleOf(member.accountID)).toEqual("reader");
328
+ });
329
+
330
+ // Verify read access is restored
331
+ const personOnMemberNode = await loadCoValueOrFail(member.node, person.id);
332
+ expect(personOnMemberNode.get("name")).toEqual("John Doe");
333
+ });
334
+
335
+ test("should not be able to accept an invalid invite", async () => {
336
+ const admin = await setupTestAccount({
337
+ connected: true,
338
+ });
339
+
340
+ const newMember = await setupTestAccount({
341
+ connected: true,
342
+ });
343
+
344
+ const group = admin.node.createGroup();
345
+ const invalidInvite = "inviteSecret_zinvalid";
346
+
347
+ try {
348
+ await newMember.node.acceptInvite(group.id, invalidInvite);
349
+ throw new Error("Should not be able to accept invalid invite");
350
+ } catch (e) {
351
+ expect(e).toBeDefined();
352
+ }
353
+ });
354
+ });
@@ -252,4 +252,52 @@ describe("Group.removeMember", () => {
252
252
 
253
253
  expect(groupOnReaderNode.roleOf(otherMember.accountID)).toEqual("writer");
254
254
  });
255
+
256
+ test("removing a member when inheriting a group where the user lacks read rights", async () => {
257
+ const admin = await setupTestAccount({
258
+ connected: true,
259
+ });
260
+
261
+ const childAdmin = await setupTestAccount({
262
+ connected: true,
263
+ });
264
+
265
+ const reader = await setupTestAccount({
266
+ connected: true,
267
+ });
268
+
269
+ const childAdminOnAdminNode = await loadCoValueOrFail(
270
+ admin.node,
271
+ childAdmin.accountID,
272
+ );
273
+
274
+ const readerOnAdminNode = await loadCoValueOrFail(
275
+ admin.node,
276
+ reader.accountID,
277
+ );
278
+
279
+ const group = admin.node.createGroup();
280
+
281
+ const childGroup = admin.node.createGroup();
282
+ childGroup.addMember(childAdminOnAdminNode, "admin");
283
+ childGroup.addMember(readerOnAdminNode, "reader");
284
+
285
+ childGroup.extend(group);
286
+
287
+ const readerOnChildAdminNode = await loadCoValueOrFail(
288
+ childAdmin.node,
289
+ reader.accountID,
290
+ );
291
+
292
+ const childGroupOnChildAdminNode = await loadCoValueOrFail(
293
+ childAdmin.node,
294
+ childGroup.id,
295
+ );
296
+
297
+ await childGroupOnChildAdminNode.removeMember(readerOnChildAdminNode);
298
+
299
+ expect(childGroupOnChildAdminNode.roleOf(reader.accountID)).toEqual(
300
+ undefined,
301
+ );
302
+ });
255
303
  });