cojson 0.18.32 → 0.18.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 (117) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +9 -0
  3. package/dist/SyncStateManager.d.ts.map +1 -1
  4. package/dist/SyncStateManager.js +2 -2
  5. package/dist/SyncStateManager.js.map +1 -1
  6. package/dist/coValueCore/SessionMap.d.ts +1 -0
  7. package/dist/coValueCore/SessionMap.d.ts.map +1 -1
  8. package/dist/coValueCore/SessionMap.js +17 -1
  9. package/dist/coValueCore/SessionMap.js.map +1 -1
  10. package/dist/coValueCore/coValueCore.d.ts +14 -9
  11. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  12. package/dist/coValueCore/coValueCore.js +62 -47
  13. package/dist/coValueCore/coValueCore.js.map +1 -1
  14. package/dist/coValueCore/verifiedState.d.ts +2 -2
  15. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  16. package/dist/coValueCore/verifiedState.js +86 -75
  17. package/dist/coValueCore/verifiedState.js.map +1 -1
  18. package/dist/coValues/group.d.ts +1 -0
  19. package/dist/coValues/group.d.ts.map +1 -1
  20. package/dist/coValues/group.js +24 -4
  21. package/dist/coValues/group.js.map +1 -1
  22. package/dist/knownState.d.ts +1 -1
  23. package/dist/knownState.d.ts.map +1 -1
  24. package/dist/knownState.js +1 -1
  25. package/dist/knownState.js.map +1 -1
  26. package/dist/localNode.d.ts.map +1 -1
  27. package/dist/localNode.js +1 -2
  28. package/dist/localNode.js.map +1 -1
  29. package/dist/queue/LocalTransactionsSyncQueue.d.ts.map +1 -1
  30. package/dist/queue/LocalTransactionsSyncQueue.js +16 -1
  31. package/dist/queue/LocalTransactionsSyncQueue.js.map +1 -1
  32. package/dist/storage/knownState.js +2 -2
  33. package/dist/storage/knownState.js.map +1 -1
  34. package/dist/sync.d.ts.map +1 -1
  35. package/dist/sync.js +8 -2
  36. package/dist/sync.js.map +1 -1
  37. package/dist/tests/PureJSCrypto.test.js +1 -1
  38. package/dist/tests/PureJSCrypto.test.js.map +1 -1
  39. package/dist/tests/StorageApiAsync.test.js +11 -11
  40. package/dist/tests/StorageApiAsync.test.js.map +1 -1
  41. package/dist/tests/StorageApiSync.test.js +3 -3
  42. package/dist/tests/StorageApiSync.test.js.map +1 -1
  43. package/dist/tests/WasmCrypto.test.js +1 -1
  44. package/dist/tests/WasmCrypto.test.js.map +1 -1
  45. package/dist/tests/coPlainText.test.js +1 -1
  46. package/dist/tests/coStream.test.js +12 -12
  47. package/dist/tests/coStream.test.js.map +1 -1
  48. package/dist/tests/coValueCore.isCompletelyDownloaded.test.d.ts +2 -0
  49. package/dist/tests/coValueCore.isCompletelyDownloaded.test.d.ts.map +1 -0
  50. package/dist/tests/coValueCore.isCompletelyDownloaded.test.js +421 -0
  51. package/dist/tests/coValueCore.isCompletelyDownloaded.test.js.map +1 -0
  52. package/dist/tests/coValueCore.isStreaming.test.d.ts +2 -0
  53. package/dist/tests/coValueCore.isStreaming.test.d.ts.map +1 -0
  54. package/dist/tests/coValueCore.isStreaming.test.js +181 -0
  55. package/dist/tests/coValueCore.isStreaming.test.js.map +1 -0
  56. package/dist/tests/coValueCore.newContentSince.test.d.ts +2 -0
  57. package/dist/tests/coValueCore.newContentSince.test.d.ts.map +1 -0
  58. package/dist/tests/coValueCore.newContentSince.test.js +808 -0
  59. package/dist/tests/coValueCore.newContentSince.test.js.map +1 -0
  60. package/dist/tests/coreWasm.test.js +2 -2
  61. package/dist/tests/coreWasm.test.js.map +1 -1
  62. package/dist/tests/group.childKeyRotation.test.d.ts +2 -0
  63. package/dist/tests/group.childKeyRotation.test.d.ts.map +1 -0
  64. package/dist/tests/group.childKeyRotation.test.js +261 -0
  65. package/dist/tests/group.childKeyRotation.test.js.map +1 -0
  66. package/dist/tests/group.removeMember.test.js +1 -114
  67. package/dist/tests/group.removeMember.test.js.map +1 -1
  68. package/dist/tests/knownState.test.js +11 -11
  69. package/dist/tests/knownState.test.js.map +1 -1
  70. package/dist/tests/sync.auth.test.js +6 -6
  71. package/dist/tests/sync.load.test.js +68 -5
  72. package/dist/tests/sync.load.test.js.map +1 -1
  73. package/dist/tests/sync.mesh.test.js +11 -17
  74. package/dist/tests/sync.mesh.test.js.map +1 -1
  75. package/dist/tests/sync.peerReconciliation.test.js +1 -1
  76. package/dist/tests/sync.storage.test.js +7 -7
  77. package/dist/tests/sync.storage.test.js.map +1 -1
  78. package/dist/tests/sync.storageAsync.test.js +4 -4
  79. package/dist/tests/sync.storageAsync.test.js.map +1 -1
  80. package/dist/tests/sync.upload.test.js +96 -40
  81. package/dist/tests/sync.upload.test.js.map +1 -1
  82. package/dist/tests/testUtils.d.ts +2 -0
  83. package/dist/tests/testUtils.d.ts.map +1 -1
  84. package/dist/tests/testUtils.js +22 -1
  85. package/dist/tests/testUtils.js.map +1 -1
  86. package/package.json +3 -3
  87. package/src/SyncStateManager.ts +2 -5
  88. package/src/coValueCore/SessionMap.ts +24 -0
  89. package/src/coValueCore/coValueCore.ts +77 -55
  90. package/src/coValueCore/verifiedState.ts +123 -108
  91. package/src/coValues/group.ts +27 -4
  92. package/src/knownState.ts +1 -1
  93. package/src/localNode.ts +1 -2
  94. package/src/queue/LocalTransactionsSyncQueue.ts +25 -0
  95. package/src/storage/knownState.ts +2 -2
  96. package/src/sync.ts +7 -2
  97. package/src/tests/PureJSCrypto.test.ts +1 -2
  98. package/src/tests/StorageApiAsync.test.ts +11 -11
  99. package/src/tests/StorageApiSync.test.ts +3 -3
  100. package/src/tests/WasmCrypto.test.ts +1 -2
  101. package/src/tests/coPlainText.test.ts +1 -1
  102. package/src/tests/coStream.test.ts +12 -12
  103. package/src/tests/coValueCore.isCompletelyDownloaded.test.ts +589 -0
  104. package/src/tests/coValueCore.isStreaming.test.ts +271 -0
  105. package/src/tests/coValueCore.newContentSince.test.ts +966 -0
  106. package/src/tests/coreWasm.test.ts +2 -2
  107. package/src/tests/group.childKeyRotation.test.ts +431 -0
  108. package/src/tests/group.removeMember.test.ts +1 -184
  109. package/src/tests/knownState.test.ts +11 -11
  110. package/src/tests/sync.auth.test.ts +6 -6
  111. package/src/tests/sync.load.test.ts +80 -5
  112. package/src/tests/sync.mesh.test.ts +11 -17
  113. package/src/tests/sync.peerReconciliation.test.ts +1 -1
  114. package/src/tests/sync.storage.test.ts +7 -7
  115. package/src/tests/sync.storageAsync.test.ts +4 -4
  116. package/src/tests/sync.upload.test.ts +106 -40
  117. package/src/tests/testUtils.ts +24 -2
@@ -42,7 +42,7 @@ describe("SessionLog WASM", () => {
42
42
 
43
43
  const group = node.createGroup();
44
44
  const sessionContent =
45
- group.core.verified.newContentSince(undefined)?.[0]?.new[session];
45
+ group.core.newContentSince(undefined)?.[0]?.new[session];
46
46
  assert(sessionContent);
47
47
 
48
48
  let log = wasmCrypto.createSessionLog(
@@ -63,7 +63,7 @@ describe("SessionLog WASM", () => {
63
63
 
64
64
  const group = node.createGroup();
65
65
  const sessionContent =
66
- group.core.verified.newContentSince(undefined)?.[0]?.new[session];
66
+ group.core.newContentSince(undefined)?.[0]?.new[session];
67
67
  assert(sessionContent);
68
68
 
69
69
  let log = wasmCrypto.createSessionLog(
@@ -0,0 +1,431 @@
1
+ import { assert, beforeEach, describe, expect, test } from "vitest";
2
+ import { setCoValueLoadingRetryDelay } from "../config.js";
3
+ import {
4
+ SyncMessagesLog,
5
+ TEST_NODE_CONFIG,
6
+ loadCoValueOrFail,
7
+ setupTestAccount,
8
+ setupTestNode,
9
+ waitFor,
10
+ } from "./testUtils.js";
11
+ import { expectGroup } from "../typeUtils/expectGroup.js";
12
+ import { RawAccount } from "../exports.js";
13
+
14
+ // We want to simulate a real world communication that happens asynchronously
15
+ TEST_NODE_CONFIG.withAsyncPeers = true;
16
+
17
+ setCoValueLoadingRetryDelay(10);
18
+
19
+ let jazzCloud: ReturnType<typeof setupTestNode>;
20
+
21
+ beforeEach(async () => {
22
+ SyncMessagesLog.clear();
23
+ jazzCloud = setupTestNode({ isSyncServer: true });
24
+ });
25
+
26
+ describe("Group.childKeyRotation", () => {
27
+ let admin: Awaited<ReturnType<typeof setupTestAccount>>;
28
+ let bob: Awaited<ReturnType<typeof setupTestAccount>>;
29
+ let alice: Awaited<ReturnType<typeof setupTestAccount>>;
30
+ let charlie: Awaited<ReturnType<typeof setupTestAccount>>;
31
+ let aliceOnAdminNode: RawAccount;
32
+ let bobOnAdminNode: RawAccount;
33
+ let charlieOnAdminNode: RawAccount;
34
+
35
+ beforeEach(async () => {
36
+ admin = await setupTestAccount({
37
+ connected: true,
38
+ });
39
+ alice = await setupTestAccount({
40
+ connected: true,
41
+ });
42
+ bob = await setupTestAccount({
43
+ connected: true,
44
+ });
45
+ charlie = await setupTestAccount({
46
+ connected: true,
47
+ });
48
+ aliceOnAdminNode = await loadCoValueOrFail(admin.node, alice.accountID);
49
+ bobOnAdminNode = await loadCoValueOrFail(admin.node, bob.accountID);
50
+ charlieOnAdminNode = await loadCoValueOrFail(admin.node, charlie.accountID);
51
+ });
52
+
53
+ test("removing a member should rotate the readKey on available child groups", async () => {
54
+ const group = admin.node.createGroup();
55
+ const childGroup = admin.node.createGroup();
56
+ group.addMember(aliceOnAdminNode, "reader");
57
+
58
+ childGroup.extend(group);
59
+
60
+ group.removeMember(aliceOnAdminNode);
61
+
62
+ // ReadKey rotated, now alice can't read the map
63
+ const map = childGroup.createMap();
64
+ map.set("test", "Not readable by alice");
65
+
66
+ await map.core.waitForSync();
67
+
68
+ const mapOnAliceNode = await loadCoValueOrFail(alice.node, map.id);
69
+ expect(mapOnAliceNode.get("test")).toBeUndefined();
70
+ });
71
+
72
+ test("removing a member should rotate the readKey on unloaded child groups", async () => {
73
+ const group = admin.node.createGroup();
74
+
75
+ let childGroup = bob.node.createGroup();
76
+ group.addMember(bobOnAdminNode, "reader");
77
+ group.addMember(aliceOnAdminNode, "reader");
78
+
79
+ const groupOnBobNode = await loadCoValueOrFail(bob.node, group.id);
80
+
81
+ childGroup.extend(groupOnBobNode);
82
+
83
+ await childGroup.core.waitForSync();
84
+
85
+ group.removeMember(aliceOnAdminNode);
86
+
87
+ // Spinning a new session for bob, to be sure to trigger the group migration
88
+ // that handles the key rotation
89
+ const newBobSession = await bob.spawnNewSession();
90
+
91
+ const childGroupOnNewBobNode = await loadCoValueOrFail(
92
+ newBobSession,
93
+ childGroup.id,
94
+ );
95
+
96
+ // The key should be rotated at this point, so alice can't read the map
97
+ const map = childGroupOnNewBobNode.createMap();
98
+ map.set("test", "Not readable by alice");
99
+
100
+ await map.core.waitForSync();
101
+
102
+ const mapOnAliceNode = await loadCoValueOrFail(alice.node, map.id);
103
+ expect(mapOnAliceNode.get("test")).toBeUndefined();
104
+ });
105
+
106
+ test("removing a member on a large group should rotate the readKey on unloaded child group", async () => {
107
+ const group = admin.node.createGroup();
108
+
109
+ const childGroup = bob.node.createGroup();
110
+ group.addMember(bobOnAdminNode, "reader");
111
+ group.addMember(aliceOnAdminNode, "reader");
112
+
113
+ const groupOnBobNode = await loadCoValueOrFail(bob.node, group.id);
114
+
115
+ childGroup.extend(groupOnBobNode);
116
+
117
+ await childGroup.core.waitForSync();
118
+
119
+ // Disconnect the admin node to sync the content manually and simulate the delay of the last chunk
120
+ admin.disconnect();
121
+
122
+ // Make the group to become large enough to require multiple messages to be synced
123
+ for (let i = 0; i < 100; i++) {
124
+ // @ts-expect-error - test property is not part of the group shape
125
+ group.set("test", "1".repeat(1024));
126
+ }
127
+
128
+ expect(group.core.verified.newContentSince(undefined)?.length).toBe(2);
129
+
130
+ group.removeMember(aliceOnAdminNode);
131
+
132
+ const content = group.core.verified.newContentSince(undefined);
133
+ assert(content);
134
+ const lastChunk = content.pop();
135
+ assert(lastChunk);
136
+
137
+ // Spinning a new session for bob, to be sure to trigger the group migration
138
+ const newBobSession = await bob.spawnNewSession();
139
+
140
+ for (const chunk of content) {
141
+ newBobSession.syncManager.handleNewContent(chunk, "import");
142
+ }
143
+
144
+ const childGroupOnNewBobNode = await loadCoValueOrFail(
145
+ newBobSession,
146
+ childGroup.id,
147
+ );
148
+
149
+ newBobSession.syncManager.handleNewContent(lastChunk, "import");
150
+
151
+ // The migration waits for the group to be completely downloaded
152
+ await childGroupOnNewBobNode.core.waitForAsync((core) =>
153
+ core.isCompletelyDownloaded(),
154
+ );
155
+
156
+ // The key should be rotated at this point, so alice can't read the map
157
+ const map = childGroupOnNewBobNode.createMap();
158
+ map.set("test", "Not readable by alice");
159
+
160
+ await map.core.waitForSync();
161
+
162
+ const mapOnAliceNode = await loadCoValueOrFail(alice.node, map.id);
163
+ expect(mapOnAliceNode.get("test")).toBeUndefined();
164
+ });
165
+
166
+ test("removing a member on a large parent group should rotate the readKey on unloaded grandChild group", async () => {
167
+ const parentGroup = admin.node.createGroup();
168
+
169
+ const group = bob.node.createGroup();
170
+ const childGroup = bob.node.createGroup();
171
+ parentGroup.addMember(bobOnAdminNode, "reader");
172
+ parentGroup.addMember(aliceOnAdminNode, "reader");
173
+
174
+ const parentGroupOnBobNode = await loadCoValueOrFail(
175
+ bob.node,
176
+ parentGroup.id,
177
+ );
178
+
179
+ group.extend(parentGroupOnBobNode);
180
+ childGroup.extend(group);
181
+
182
+ await childGroup.core.waitForSync();
183
+
184
+ // Disconnect the admin node to sync the content manually and simulate the delay of the last chunk
185
+ admin.disconnect();
186
+
187
+ // Make the parent group to become large enough to require multiple messages to be synced
188
+ for (let i = 0; i < 200; i++) {
189
+ // @ts-expect-error - test property is not part of the group shape
190
+ parentGroup.set("test", "1".repeat(1024));
191
+ }
192
+
193
+ expect(parentGroup.core.verified.newContentSince(undefined)?.length).toBe(
194
+ 3,
195
+ );
196
+
197
+ parentGroup.removeMember(aliceOnAdminNode);
198
+
199
+ const content = parentGroup.core.verified.newContentSince(undefined);
200
+ assert(content);
201
+ const lastChunk = content.pop();
202
+ assert(lastChunk);
203
+
204
+ // Spinning a new session for bob, to be sure to trigger the group migration
205
+ const newBobSession = await bob.spawnNewSession();
206
+
207
+ for (const chunk of content) {
208
+ newBobSession.syncManager.handleNewContent(chunk, "import");
209
+ }
210
+
211
+ const childGroupOnNewBobNode = await loadCoValueOrFail(
212
+ newBobSession,
213
+ childGroup.id,
214
+ );
215
+
216
+ newBobSession.syncManager.handleNewContent(lastChunk, "import");
217
+
218
+ // The migration waits for the group to be completely downloaded, this includes full streaming of the parent group
219
+ await childGroupOnNewBobNode.core.waitForAsync((core) =>
220
+ core.isCompletelyDownloaded(),
221
+ );
222
+
223
+ // The key should be rotated at this point, so alice can't read the map
224
+ const map = childGroupOnNewBobNode.createMap();
225
+ map.set("test", "Not readable by alice");
226
+
227
+ await map.core.waitForSync();
228
+
229
+ const mapOnAliceNode = await loadCoValueOrFail(alice.node, map.id);
230
+ expect(mapOnAliceNode.get("test")).toBeUndefined();
231
+ });
232
+
233
+ test("non-admin accounts can't trigger the unloaded child group key rotation", async () => {
234
+ const group = admin.node.createGroup();
235
+ const childGroup = bob.node.createGroup();
236
+
237
+ group.addMember(bobOnAdminNode, "writer");
238
+ group.addMember(aliceOnAdminNode, "writer");
239
+ group.addMember(charlieOnAdminNode, "writer");
240
+
241
+ await group.core.waitForSync();
242
+
243
+ const groupOnBobNode = await loadCoValueOrFail(bob.node, group.id);
244
+
245
+ childGroup.extend(groupOnBobNode);
246
+
247
+ await childGroup.core.waitForSync();
248
+ await groupOnBobNode.core.waitForSync();
249
+
250
+ group.removeMember(charlieOnAdminNode);
251
+
252
+ await group.core.waitForSync();
253
+
254
+ // Alice only have writer access to the child group, so she can't rotate the readKey
255
+ const childGroupOnAliceNode = await loadCoValueOrFail(
256
+ alice.node,
257
+ childGroup.id,
258
+ );
259
+
260
+ const map = childGroupOnAliceNode.createMap();
261
+ map.set("test", "Readable by charlie");
262
+
263
+ await map.core.waitForSync();
264
+
265
+ // This means that Charlie can read what Alice wrote
266
+ const mapOnCharlieNode = await loadCoValueOrFail(charlie.node, map.id);
267
+ expect(mapOnCharlieNode.get("test")).toBe("Readable by charlie");
268
+
269
+ // Instead Bob is an admin, so when loading the child group he can rotate the readKey
270
+ const newBobSession = await bob.spawnNewSession();
271
+ const mapOnNewBobNode = await loadCoValueOrFail(newBobSession, map.id);
272
+
273
+ mapOnNewBobNode.set("test", "Not readable by charlie");
274
+
275
+ await mapOnNewBobNode.core.waitForSync();
276
+
277
+ const updatedMapOnCharlieNode = await loadCoValueOrFail(
278
+ charlie.node,
279
+ map.id,
280
+ );
281
+
282
+ // Ensure that the map is fully synced
283
+ await waitFor(async () => {
284
+ expect(updatedMapOnCharlieNode.core.knownState()).toEqual(
285
+ mapOnNewBobNode.core.knownState(),
286
+ );
287
+ });
288
+
289
+ // Charlie should not be able to read what Bob wrote
290
+ expect(updatedMapOnCharlieNode.get("test")).toBe("Readable by charlie");
291
+ });
292
+
293
+ test("direct manager account can trigger the unloaded child group key rotation", async () => {
294
+ const group = admin.node.createGroup();
295
+ const childGroup = bob.node.createGroup();
296
+
297
+ group.addMember(bobOnAdminNode, "writer");
298
+ group.addMember(aliceOnAdminNode, "reader");
299
+
300
+ await group.core.waitForSync();
301
+
302
+ const groupOnBobNode = await loadCoValueOrFail(bob.node, group.id);
303
+
304
+ childGroup.extend(groupOnBobNode);
305
+
306
+ const charlieOnBobNode = await loadCoValueOrFail(
307
+ bob.node,
308
+ charlie.accountID,
309
+ );
310
+
311
+ childGroup.addMember(charlieOnBobNode, "manager");
312
+
313
+ await childGroup.core.waitForSync();
314
+ await groupOnBobNode.core.waitForSync();
315
+
316
+ group.removeMember(aliceOnAdminNode);
317
+
318
+ await group.core.waitForSync();
319
+
320
+ const childGroupOnCharlieNode = await loadCoValueOrFail(
321
+ charlie.node,
322
+ childGroup.id,
323
+ );
324
+
325
+ const map = childGroupOnCharlieNode.createMap();
326
+ map.set("test", "Not readable by alice");
327
+
328
+ await map.core.waitForSync();
329
+
330
+ const mapOnAliceNode = await loadCoValueOrFail(alice.node, map.id);
331
+
332
+ // Ensure that the map is fully synced
333
+ await waitFor(async () => {
334
+ expect(mapOnAliceNode.core.knownState()).toEqual(map.core.knownState());
335
+ });
336
+
337
+ // Charlie should not be able to read what Bob wrote because key was rotated
338
+ expect(mapOnAliceNode.get("test")).toBeUndefined();
339
+ });
340
+
341
+ test("inherited admin account triggers the unloaded child group key rotation", async () => {
342
+ const group = admin.node.createGroup();
343
+ const childGroup = bob.node.createGroup();
344
+
345
+ // Alice is admin on parent group (will inherit to child), but not directly on child
346
+ group.addMember(aliceOnAdminNode, "admin");
347
+ group.addMember(bobOnAdminNode, "writer");
348
+ group.addMember(charlieOnAdminNode, "reader");
349
+
350
+ await group.core.waitForSync();
351
+
352
+ const groupOnBobNode = await loadCoValueOrFail(bob.node, group.id);
353
+
354
+ childGroup.extend(groupOnBobNode);
355
+
356
+ await childGroup.core.waitForSync();
357
+ await groupOnBobNode.core.waitForSync();
358
+
359
+ group.removeMember(charlieOnAdminNode);
360
+
361
+ await group.core.waitForSync();
362
+
363
+ // Alice has inherited admin access but is not a direct admin on the child group
364
+ // So she can't trigger key rotation
365
+ const childGroupOnNewAliceNode = await loadCoValueOrFail(
366
+ alice.node,
367
+ childGroup.id,
368
+ );
369
+
370
+ const map = childGroupOnNewAliceNode.createMap();
371
+ map.set("test", "Readable by charlie");
372
+
373
+ await map.core.waitForSync();
374
+
375
+ // Charlie should be able to read what Alice wrote because key wasn't rotated
376
+ const mapOnCharlieNode = await loadCoValueOrFail(charlie.node, map.id);
377
+ expect(mapOnCharlieNode.get("test")).toBe(undefined);
378
+ });
379
+
380
+ // TODO: In this case the child can't detect the parent group rotation, because it doesn't have access to the parent group readKey
381
+ // We need to replace the writeOnlyKey with an asymmetric key sealing mechanism to cover this case
382
+ test.skip("removing a member should rotate the writeOnlyKey on child group", async () => {
383
+ const admin = await setupTestAccount({
384
+ connected: true,
385
+ });
386
+
387
+ const bob = await setupTestAccount({
388
+ connected: true,
389
+ });
390
+
391
+ const alice = await setupTestAccount({
392
+ connected: true,
393
+ });
394
+
395
+ const aliceOnAdminNode = await loadCoValueOrFail(
396
+ admin.node,
397
+ alice.accountID,
398
+ );
399
+
400
+ const group = admin.node.createGroup();
401
+ const childGroup = bob.node.createGroup();
402
+
403
+ group.addMember(aliceOnAdminNode, "reader");
404
+
405
+ await group.core.waitForSync();
406
+
407
+ const groupOnBobNode = await loadCoValueOrFail(bob.node, group.id);
408
+
409
+ childGroup.extend(groupOnBobNode);
410
+
411
+ await childGroup.core.waitForSync();
412
+
413
+ group.removeMember(aliceOnAdminNode);
414
+
415
+ await group.core.waitForSync();
416
+
417
+ const newBobSession = await bob.spawnNewSession();
418
+ const childGroupOnNewBobNode = await loadCoValueOrFail(
419
+ newBobSession,
420
+ childGroup.id,
421
+ );
422
+
423
+ const map = childGroupOnNewBobNode.createMap();
424
+ map.set("test", "Not readable by alice");
425
+
426
+ await map.core.waitForSync();
427
+
428
+ const mapOnAliceNode = await loadCoValueOrFail(alice.node, map.id);
429
+ expect(mapOnAliceNode.get("test")).toBeUndefined();
430
+ });
431
+ });
@@ -2,6 +2,7 @@ import { beforeEach, describe, expect, test } from "vitest";
2
2
  import { setCoValueLoadingRetryDelay } from "../config.js";
3
3
  import {
4
4
  SyncMessagesLog,
5
+ TEST_NODE_CONFIG,
5
6
  blockMessageTypeOnOutgoingPeer,
6
7
  loadCoValueOrFail,
7
8
  setupTestAccount,
@@ -347,188 +348,4 @@ describe("Group.removeMember", () => {
347
348
  undefined,
348
349
  );
349
350
  });
350
-
351
- test("removing a member should rotate the readKey on available child groups", async () => {
352
- const admin = await setupTestAccount({
353
- connected: true,
354
- });
355
-
356
- const alice = await setupTestAccount({
357
- connected: true,
358
- });
359
-
360
- const aliceOnAdminNode = await loadCoValueOrFail(
361
- admin.node,
362
- alice.accountID,
363
- );
364
-
365
- const group = admin.node.createGroup();
366
- const childGroup = admin.node.createGroup();
367
- group.addMember(aliceOnAdminNode, "reader");
368
-
369
- childGroup.extend(group);
370
-
371
- group.removeMember(aliceOnAdminNode);
372
-
373
- const map = childGroup.createMap();
374
- map.set("test", "Not readable by alice");
375
-
376
- await map.core.waitForSync();
377
-
378
- const mapOnAliceNode = await loadCoValueOrFail(alice.node, map.id);
379
- expect(mapOnAliceNode.get("test")).toBeUndefined();
380
- });
381
-
382
- test.skip("removing a member should rotate the readKey on unloaded child groups", async () => {
383
- const admin = await setupTestAccount({
384
- connected: true,
385
- });
386
-
387
- const bob = await setupTestAccount({
388
- connected: true,
389
- });
390
-
391
- const alice = await setupTestAccount({
392
- connected: true,
393
- });
394
-
395
- const bobOnAdminNode = await loadCoValueOrFail(admin.node, bob.accountID);
396
-
397
- const aliceOnAdminNode = await loadCoValueOrFail(
398
- admin.node,
399
- alice.accountID,
400
- );
401
-
402
- const group = admin.node.createGroup();
403
-
404
- let childGroup = bob.node.createGroup();
405
- group.addMember(bobOnAdminNode, "reader");
406
- group.addMember(aliceOnAdminNode, "reader");
407
-
408
- const groupOnBobNode = await loadCoValueOrFail(bob.node, group.id);
409
-
410
- childGroup.extend(groupOnBobNode);
411
-
412
- await childGroup.core.waitForSync();
413
-
414
- group.removeMember(aliceOnAdminNode);
415
-
416
- // Rotating the child group keys is async when the child group is not loaded
417
- await admin.node.syncManager.waitForAllCoValuesSync();
418
-
419
- // Reload the group the trigger the loading checks
420
- childGroup.core.resetParsedTransactions();
421
- childGroup = expectGroup(childGroup.core.getCurrentContent());
422
-
423
- const map = childGroup.createMap();
424
- map.set("test", "Not readable by alice");
425
-
426
- await map.core.waitForSync();
427
-
428
- const mapOnAliceNode = await loadCoValueOrFail(alice.node, map.id);
429
- expect(mapOnAliceNode.get("test")).toBeUndefined();
430
- });
431
-
432
- test("removing a member should work even if there are partially available child groups", async () => {
433
- const admin = await setupTestAccount({
434
- connected: true,
435
- });
436
-
437
- const bob = await setupTestAccount();
438
- const { peer } = bob.connectToSyncServer();
439
-
440
- const alice = await setupTestAccount({
441
- connected: true,
442
- });
443
-
444
- const bobOnAdminNode = await loadCoValueOrFail(admin.node, bob.accountID);
445
-
446
- const aliceOnAdminNode = await loadCoValueOrFail(
447
- admin.node,
448
- alice.accountID,
449
- );
450
-
451
- const group = admin.node.createGroup();
452
- const childGroup = bob.node.createGroup();
453
-
454
- group.addMember(bobOnAdminNode, "reader");
455
- group.addMember(aliceOnAdminNode, "reader");
456
-
457
- await group.core.waitForSync();
458
-
459
- blockMessageTypeOnOutgoingPeer(peer, "content", {
460
- id: childGroup.id,
461
- });
462
-
463
- const groupOnBobNode = await loadCoValueOrFail(bob.node, group.id);
464
-
465
- childGroup.extend(groupOnBobNode);
466
-
467
- await groupOnBobNode.core.waitForSync();
468
-
469
- group.removeMember(aliceOnAdminNode);
470
-
471
- await admin.node.syncManager.waitForAllCoValuesSync();
472
-
473
- const map = group.createMap();
474
- map.set("test", "Not readable by alice");
475
-
476
- await map.core.waitForSync();
477
-
478
- const mapOnAliceNode = await loadCoValueOrFail(alice.node, map.id);
479
- expect(mapOnAliceNode.get("test")).toBeUndefined();
480
- });
481
-
482
- test("removing a member should work even if there are unavailable child groups", async () => {
483
- const admin = await setupTestAccount({
484
- connected: true,
485
- });
486
-
487
- const { peerOnServer } = admin.connectToSyncServer();
488
-
489
- const bob = await setupTestAccount({
490
- connected: true,
491
- });
492
-
493
- const alice = await setupTestAccount({
494
- connected: true,
495
- });
496
-
497
- const bobOnAdminNode = await loadCoValueOrFail(admin.node, bob.accountID);
498
-
499
- const aliceOnAdminNode = await loadCoValueOrFail(
500
- admin.node,
501
- alice.accountID,
502
- );
503
-
504
- const group = admin.node.createGroup();
505
- const childGroup = bob.node.createGroup();
506
-
507
- blockMessageTypeOnOutgoingPeer(peerOnServer, "content", {
508
- id: childGroup.id,
509
- });
510
-
511
- group.addMember(bobOnAdminNode, "reader");
512
- group.addMember(aliceOnAdminNode, "reader");
513
-
514
- await group.core.waitForSync();
515
-
516
- const groupOnBobNode = await loadCoValueOrFail(bob.node, group.id);
517
-
518
- childGroup.extend(groupOnBobNode);
519
-
520
- await groupOnBobNode.core.waitForSync();
521
-
522
- group.removeMember(aliceOnAdminNode);
523
-
524
- await group.core.waitForSync();
525
-
526
- const map = group.createMap();
527
- map.set("test", "Not readable by alice");
528
-
529
- await map.core.waitForSync();
530
-
531
- const mapOnAliceNode = await loadCoValueOrFail(alice.node, map.id);
532
- expect(mapOnAliceNode.get("test")).toBeUndefined();
533
- });
534
351
  });