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.
- package/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +12 -0
- package/dist/native/coValues/group.js +4 -4
- package/dist/native/coValues/group.js.map +1 -1
- package/dist/native/exports.js +1 -1
- package/dist/native/exports.js.map +1 -1
- package/dist/web/coValues/group.js +4 -4
- package/dist/web/coValues/group.js.map +1 -1
- package/dist/web/exports.js +1 -1
- package/dist/web/exports.js.map +1 -1
- package/package.json +1 -1
- package/src/coValues/group.ts +7 -4
- package/src/exports.ts +8 -1
- package/src/tests/SyncStateManager.test.ts +41 -91
- package/src/tests/permissions.test.ts +59 -1
- package/src/tests/sync.test.ts +257 -325
- package/src/tests/testUtils.ts +112 -19
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
49
|
+
nodeToServerPeer.id,
|
|
57
50
|
map.core.id,
|
|
58
51
|
).uploaded;
|
|
59
52
|
});
|
|
60
53
|
|
|
61
54
|
expect(updateSpy).toHaveBeenCalledWith(
|
|
62
|
-
|
|
63
|
-
client.syncManager.peers[
|
|
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
|
|
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
|
-
|
|
88
|
+
nodeToServerPeer.id,
|
|
108
89
|
updateToJazzCloudSpy,
|
|
109
90
|
);
|
|
110
91
|
const unsubscribe2 = subscriptionManager.subscribeToPeerUpdates(
|
|
111
|
-
|
|
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
|
-
|
|
110
|
+
nodeToServerPeer.id,
|
|
130
111
|
map.core.id,
|
|
131
112
|
).uploaded;
|
|
132
113
|
});
|
|
133
114
|
|
|
134
115
|
expect(updateToJazzCloudSpy).toHaveBeenLastCalledWith(
|
|
135
|
-
client.syncManager.peers[
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
149
|
+
nodeToServerPeer.id,
|
|
184
150
|
map.core.id,
|
|
185
151
|
).uploaded;
|
|
186
152
|
});
|
|
187
153
|
|
|
188
154
|
expect(
|
|
189
|
-
subscriptionManager.getCurrentSyncState(
|
|
190
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
+
});
|