cojson 0.9.18 → 0.9.23

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.
@@ -1,43 +1,36 @@
1
- import { describe, expect, onTestFinished, test, vi } from "vitest";
1
+ import { beforeEach, describe, expect, onTestFinished, test, vi } from "vitest";
2
2
  import {
3
3
  GlobalSyncStateListenerCallback,
4
4
  PeerSyncStateListenerCallback,
5
5
  } from "../SyncStateManager.js";
6
- import { accountHeaderForInitialAgentSecret } from "../coValues/account.js";
7
6
  import { connectedPeers } from "../streamUtils.js";
8
7
  import { emptyKnownState } from "../sync.js";
9
8
  import {
10
9
  blockMessageTypeOnOutgoingPeer,
10
+ connectNodeToSyncServer,
11
11
  createTestNode,
12
- createTwoConnectedNodes,
13
12
  loadCoValueOrFail,
13
+ setupSyncServer,
14
14
  waitFor,
15
15
  } from "./testUtils.js";
16
16
 
17
+ let jazzCloud = setupSyncServer();
18
+
19
+ beforeEach(async () => {
20
+ jazzCloud = setupSyncServer();
21
+ });
22
+
17
23
  describe("SyncStateManager", () => {
18
24
  test("subscribeToUpdates receives updates when peer state changes", async () => {
19
25
  // Setup nodes
20
26
  const client = createTestNode();
21
- const jazzCloud = createTestNode();
27
+ const { nodeToServerPeer } = connectNodeToSyncServer(client);
22
28
 
23
29
  // Create test data
24
30
  const group = client.createGroup();
25
31
  const map = group.createMap();
26
32
  map.set("key1", "value1", "trusting");
27
33
 
28
- // Connect nodes
29
- const [clientAsPeer, jazzCloudAsPeer] = connectedPeers(
30
- "clientConnection",
31
- "jazzCloudConnection",
32
- {
33
- peer1role: "client",
34
- peer2role: "server",
35
- },
36
- );
37
-
38
- client.syncManager.addPeer(jazzCloudAsPeer);
39
- jazzCloud.syncManager.addPeer(clientAsPeer);
40
-
41
34
  const subscriptionManager = client.syncManager.syncState;
42
35
 
43
36
  const updateSpy: GlobalSyncStateListenerCallback = vi.fn();
@@ -46,21 +39,21 @@ describe("SyncStateManager", () => {
46
39
  await client.syncManager.actuallySyncCoValue(map.core);
47
40
 
48
41
  expect(updateSpy).toHaveBeenCalledWith(
49
- "jazzCloudConnection",
42
+ nodeToServerPeer.id,
50
43
  emptyKnownState(map.core.id),
51
44
  { uploaded: false },
52
45
  );
53
46
 
54
47
  await waitFor(() => {
55
48
  return subscriptionManager.getCurrentSyncState(
56
- "jazzCloudConnection",
49
+ nodeToServerPeer.id,
57
50
  map.core.id,
58
51
  ).uploaded;
59
52
  });
60
53
 
61
54
  expect(updateSpy).toHaveBeenCalledWith(
62
- "jazzCloudConnection",
63
- client.syncManager.peers["jazzCloudConnection"]!.knownStates.get(
55
+ nodeToServerPeer.id,
56
+ client.syncManager.peers[nodeToServerPeer.id]!.knownStates.get(
64
57
  map.core.id,
65
58
  )!,
66
59
  { uploaded: true },
@@ -73,42 +66,30 @@ describe("SyncStateManager", () => {
73
66
  test("subscribeToPeerUpdates receives updates only for specific peer", async () => {
74
67
  // Setup nodes
75
68
  const client = createTestNode();
76
- const jazzCloud = createTestNode();
69
+ const { nodeToServerPeer } = connectNodeToSyncServer(client);
77
70
 
78
71
  // Create test data
79
72
  const group = client.createGroup();
80
73
  const map = group.createMap();
81
74
  map.set("key1", "value1", "trusting");
82
75
 
83
- // Connect nodes
84
- const [clientAsPeer, jazzCloudAsPeer] = connectedPeers(
85
- "clientConnection",
86
- "jazzCloudConnection",
87
- {
88
- peer1role: "client",
89
- peer2role: "server",
90
- },
91
- );
92
-
93
76
  const [clientStoragePeer] = connectedPeers("clientStorage", "unusedPeer", {
94
77
  peer1role: "client",
95
78
  peer2role: "server",
96
79
  });
97
80
 
98
- client.syncManager.addPeer(jazzCloudAsPeer);
99
81
  client.syncManager.addPeer(clientStoragePeer);
100
- jazzCloud.syncManager.addPeer(clientAsPeer);
101
82
 
102
83
  const subscriptionManager = client.syncManager.syncState;
103
84
 
104
85
  const updateToJazzCloudSpy: PeerSyncStateListenerCallback = vi.fn();
105
86
  const updateToStorageSpy: PeerSyncStateListenerCallback = vi.fn();
106
87
  const unsubscribe1 = subscriptionManager.subscribeToPeerUpdates(
107
- "jazzCloudConnection",
88
+ nodeToServerPeer.id,
108
89
  updateToJazzCloudSpy,
109
90
  );
110
91
  const unsubscribe2 = subscriptionManager.subscribeToPeerUpdates(
111
- "clientStorage",
92
+ clientStoragePeer.id,
112
93
  updateToStorageSpy,
113
94
  );
114
95
 
@@ -126,13 +107,13 @@ describe("SyncStateManager", () => {
126
107
 
127
108
  await waitFor(() => {
128
109
  return subscriptionManager.getCurrentSyncState(
129
- "jazzCloudConnection",
110
+ nodeToServerPeer.id,
130
111
  map.core.id,
131
112
  ).uploaded;
132
113
  });
133
114
 
134
115
  expect(updateToJazzCloudSpy).toHaveBeenLastCalledWith(
135
- client.syncManager.peers["jazzCloudConnection"]!.knownStates.get(
116
+ client.syncManager.peers[nodeToServerPeer.id]!.knownStates.get(
136
117
  map.core.id,
137
118
  )!,
138
119
  { uploaded: true },
@@ -147,80 +128,50 @@ describe("SyncStateManager", () => {
147
128
  test("getIsCoValueFullyUploadedIntoPeer returns correct status", async () => {
148
129
  // Setup nodes
149
130
  const client = createTestNode();
150
- const jazzCloud = createTestNode();
131
+ const { nodeToServerPeer } = connectNodeToSyncServer(client);
151
132
 
152
133
  // Create test data
153
134
  const group = client.createGroup();
154
135
  const map = group.createMap();
155
136
  map.set("key1", "value1", "trusting");
156
137
 
157
- // Connect nodes
158
- const [clientAsPeer, jazzCloudAsPeer] = connectedPeers(
159
- "clientConnection",
160
- "jazzCloudConnection",
161
- {
162
- peer1role: "client",
163
- peer2role: "server",
164
- },
165
- );
166
-
167
- client.syncManager.addPeer(jazzCloudAsPeer);
168
- jazzCloud.syncManager.addPeer(clientAsPeer);
169
-
170
138
  await client.syncManager.actuallySyncCoValue(map.core);
171
139
 
172
140
  const subscriptionManager = client.syncManager.syncState;
173
141
 
174
142
  expect(
175
- subscriptionManager.getCurrentSyncState(
176
- "jazzCloudConnection",
177
- map.core.id,
178
- ).uploaded,
143
+ subscriptionManager.getCurrentSyncState(nodeToServerPeer.id, map.core.id)
144
+ .uploaded,
179
145
  ).toBe(false);
180
146
 
181
147
  await waitFor(() => {
182
148
  return subscriptionManager.getCurrentSyncState(
183
- "jazzCloudConnection",
149
+ nodeToServerPeer.id,
184
150
  map.core.id,
185
151
  ).uploaded;
186
152
  });
187
153
 
188
154
  expect(
189
- subscriptionManager.getCurrentSyncState(
190
- "jazzCloudConnection",
191
- map.core.id,
192
- ).uploaded,
155
+ subscriptionManager.getCurrentSyncState(nodeToServerPeer.id, map.core.id)
156
+ .uploaded,
193
157
  ).toBe(true);
194
158
  });
195
159
 
196
160
  test("unsubscribe stops receiving updates", async () => {
197
161
  // Setup nodes
198
162
  const client = createTestNode();
199
- const jazzCloud = createTestNode();
163
+ const { nodeToServerPeer } = connectNodeToSyncServer(client);
200
164
 
201
165
  // Create test data
202
166
  const group = client.createGroup();
203
167
  const map = group.createMap();
204
168
  map.set("key1", "value1", "trusting");
205
169
 
206
- // Connect nodes
207
- const [clientAsPeer, jazzCloudAsPeer] = connectedPeers(
208
- "clientConnection",
209
- "jazzCloudConnection",
210
- {
211
- peer1role: "client",
212
- peer2role: "server",
213
- },
214
- );
215
-
216
- client.syncManager.addPeer(jazzCloudAsPeer);
217
- jazzCloud.syncManager.addPeer(clientAsPeer);
218
-
219
170
  const subscriptionManager = client.syncManager.syncState;
220
171
  const anyUpdateSpy = vi.fn();
221
172
  const unsubscribe1 = subscriptionManager.subscribeToUpdates(anyUpdateSpy);
222
173
  const unsubscribe2 = subscriptionManager.subscribeToPeerUpdates(
223
- "jazzCloudConnection",
174
+ nodeToServerPeer.id,
224
175
  anyUpdateSpy,
225
176
  );
226
177
 
@@ -233,7 +184,7 @@ describe("SyncStateManager", () => {
233
184
 
234
185
  await waitFor(() => {
235
186
  return subscriptionManager.getCurrentSyncState(
236
- "jazzCloudConnection",
187
+ nodeToServerPeer.id,
237
188
  map.core.id,
238
189
  ).uploaded;
239
190
  });
@@ -243,22 +194,21 @@ describe("SyncStateManager", () => {
243
194
 
244
195
  test("getCurrentSyncState should return the correct state", async () => {
245
196
  // Setup nodes
197
+ const clientNode = createTestNode();
198
+ const serverNode = jazzCloud;
246
199
  const {
247
- node1: clientNode,
248
- node2: serverNode,
249
- node1ToNode2Peer: clientToServerPeer,
250
- node2ToNode1Peer: serverToClientPeer,
251
- } = await createTwoConnectedNodes("client", "server");
252
-
200
+ nodeToServerPeer: clientToServerPeer,
201
+ serverToNodePeer: serverToClientPeer,
202
+ } = connectNodeToSyncServer(clientNode);
253
203
  // Create test data
254
- const group = clientNode.node.createGroup();
204
+ const group = clientNode.createGroup();
255
205
  const map = group.createMap();
256
206
  map.set("key1", "value1", "trusting");
257
207
  group.addMember("everyone", "writer");
258
208
 
259
209
  // Initially should not be synced
260
210
  expect(
261
- clientNode.node.syncManager.syncState.getCurrentSyncState(
211
+ clientNode.syncManager.syncState.getCurrentSyncState(
262
212
  clientToServerPeer.id,
263
213
  map.core.id,
264
214
  ),
@@ -268,13 +218,13 @@ describe("SyncStateManager", () => {
268
218
  await map.core.waitForSync();
269
219
 
270
220
  expect(
271
- clientNode.node.syncManager.syncState.getCurrentSyncState(
221
+ clientNode.syncManager.syncState.getCurrentSyncState(
272
222
  clientToServerPeer.id,
273
223
  map.core.id,
274
224
  ),
275
225
  ).toEqual({ uploaded: true });
276
226
 
277
- const mapOnServer = await loadCoValueOrFail(serverNode.node, map.id);
227
+ const mapOnServer = await loadCoValueOrFail(serverNode, map.id);
278
228
 
279
229
  // Block the content messages so the client won't fully sync immediately
280
230
  const outgoing = blockMessageTypeOnOutgoingPeer(
@@ -285,14 +235,14 @@ describe("SyncStateManager", () => {
285
235
  mapOnServer.set("key2", "value2", "trusting");
286
236
 
287
237
  expect(
288
- clientNode.node.syncManager.syncState.getCurrentSyncState(
238
+ clientNode.syncManager.syncState.getCurrentSyncState(
289
239
  clientToServerPeer.id,
290
240
  map.core.id,
291
241
  ),
292
242
  ).toEqual({ uploaded: true });
293
243
 
294
244
  expect(
295
- serverNode.node.syncManager.syncState.getCurrentSyncState(
245
+ serverNode.syncManager.syncState.getCurrentSyncState(
296
246
  serverToClientPeer.id,
297
247
  map.core.id,
298
248
  ),
@@ -304,14 +254,14 @@ describe("SyncStateManager", () => {
304
254
  await mapOnServer.core.waitForSync();
305
255
 
306
256
  expect(
307
- clientNode.node.syncManager.syncState.getCurrentSyncState(
257
+ clientNode.syncManager.syncState.getCurrentSyncState(
308
258
  clientToServerPeer.id,
309
259
  map.core.id,
310
260
  ),
311
261
  ).toEqual({ uploaded: true });
312
262
 
313
263
  expect(
314
- serverNode.node.syncManager.syncState.getCurrentSyncState(
264
+ serverNode.syncManager.syncState.getCurrentSyncState(
315
265
  serverToClientPeer.id,
316
266
  map.core.id,
317
267
  ),
@@ -1,4 +1,4 @@
1
- import { expect, test } from "vitest";
1
+ import { expect, test, vi } from "vitest";
2
2
  import { expectMap } from "../coValue.js";
3
3
  import { ControlledAgent } from "../coValues/account.js";
4
4
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
@@ -2908,3 +2908,61 @@ test("extend cycles should not break the keys rotation", () => {
2908
2908
 
2909
2909
  expect(map.get("test")).toEqual("Hello!");
2910
2910
  });
2911
+
2912
+ test("Admin can remove themselves from a group", async () => {
2913
+ const warnSpy = vi.spyOn(console, "warn");
2914
+ const { group, admin } = newGroupHighLevel();
2915
+
2916
+ // Admin removes themselves
2917
+ await group.removeMember(admin);
2918
+
2919
+ expect(group.myRole()).toBeUndefined();
2920
+ expect(warnSpy).not.toHaveBeenCalled();
2921
+ });
2922
+
2923
+ test("Can revoke read permission from 'everyone'", async () => {
2924
+ const { group } = newGroupHighLevel();
2925
+ const childObject = group.createMap();
2926
+
2927
+ // Give everyone read access
2928
+ group.addMember("everyone", "reader");
2929
+
2930
+ childObject.set("foo", "bar", "private");
2931
+ expect(childObject.get("foo")).toEqual("bar");
2932
+
2933
+ // Create a new account to verify access
2934
+ const newAccount = new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto);
2935
+ const childContent = expectMap(
2936
+ childObject.core
2937
+ .testWithDifferentAccount(
2938
+ newAccount,
2939
+ Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
2940
+ )
2941
+ .getCurrentContent(),
2942
+ );
2943
+
2944
+ // Verify the new account can read
2945
+ expect(childContent.get("foo")).toEqual("bar");
2946
+
2947
+ // Revoke everyone's access
2948
+ await group.removeMember("everyone");
2949
+
2950
+ childObject.set("foo", "updated after revoke", "private");
2951
+
2952
+ // Create another new account to verify access is revoked
2953
+ const newAccount2 = new ControlledAgent(
2954
+ Crypto.newRandomAgentSecret(),
2955
+ Crypto,
2956
+ );
2957
+ const childContent2 = expectMap(
2958
+ childObject.core
2959
+ .testWithDifferentAccount(
2960
+ newAccount2,
2961
+ Crypto.newRandomSessionID(newAccount2.currentAgentID()._unsafeUnwrap()),
2962
+ )
2963
+ .getCurrentContent(),
2964
+ );
2965
+
2966
+ // Verify the new account cannot read after revocation
2967
+ expect(childContent2.get("foo")).toEqual("bar");
2968
+ });