cojson 0.8.37 → 0.8.39
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/CHANGELOG.md +14 -0
- package/dist/native/PeerState.js +11 -2
- package/dist/native/PeerState.js.map +1 -1
- package/dist/native/PriorityBasedMessageQueue.js +20 -11
- package/dist/native/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/native/{SyncStateSubscriptionManager.js → SyncStateManager.js} +35 -24
- package/dist/native/SyncStateManager.js.map +1 -0
- package/dist/native/coValueCore.js +3 -0
- package/dist/native/coValueCore.js.map +1 -1
- package/dist/native/coValues/coMap.js +2 -3
- package/dist/native/coValues/coMap.js.map +1 -1
- package/dist/native/coValues/group.js +9 -3
- package/dist/native/coValues/group.js.map +1 -1
- package/dist/native/exports.js.map +1 -1
- package/dist/native/sync.js +34 -10
- package/dist/native/sync.js.map +1 -1
- package/dist/web/PeerState.js +11 -2
- package/dist/web/PeerState.js.map +1 -1
- package/dist/web/PriorityBasedMessageQueue.js +20 -11
- package/dist/web/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/web/{SyncStateSubscriptionManager.js → SyncStateManager.js} +35 -24
- package/dist/web/SyncStateManager.js.map +1 -0
- package/dist/web/coValueCore.js +3 -0
- package/dist/web/coValueCore.js.map +1 -1
- package/dist/web/coValues/coMap.js +2 -3
- package/dist/web/coValues/coMap.js.map +1 -1
- package/dist/web/coValues/group.js +9 -3
- package/dist/web/coValues/group.js.map +1 -1
- package/dist/web/exports.js.map +1 -1
- package/dist/web/sync.js +34 -10
- package/dist/web/sync.js.map +1 -1
- package/package.json +5 -1
- package/src/PeerState.ts +12 -2
- package/src/PriorityBasedMessageQueue.ts +24 -12
- package/src/{SyncStateSubscriptionManager.ts → SyncStateManager.ts} +48 -35
- package/src/coValueCore.ts +6 -0
- package/src/coValues/coMap.ts +2 -3
- package/src/coValues/group.ts +11 -3
- package/src/exports.ts +2 -1
- package/src/sync.ts +57 -23
- package/src/tests/PeerState.test.ts +49 -0
- package/src/tests/PriorityBasedMessageQueue.test.ts +43 -4
- package/src/tests/{SyncStateSubscriptionManager.test.ts → SyncStateManager.test.ts} +109 -25
- package/src/tests/coMap.test.ts +4 -13
- package/src/tests/group.test.ts +6 -11
- package/src/tests/permissions.test.ts +45 -15
- package/src/tests/sync.test.ts +112 -71
- package/src/tests/testUtils.ts +128 -11
- package/dist/native/SyncStateSubscriptionManager.js.map +0 -1
- package/dist/web/SyncStateSubscriptionManager.js.map +0 -1
|
@@ -188,4 +188,53 @@ describe("PeerState", () => {
|
|
|
188
188
|
expect(knownStatesSpy).toHaveBeenCalledWith(action);
|
|
189
189
|
expect(optimisticKnownStatesSpy).toHaveBeenCalledWith(action);
|
|
190
190
|
});
|
|
191
|
+
|
|
192
|
+
test("should use same reference for knownStates and optimisticKnownStates for storage peers", () => {
|
|
193
|
+
const mockStoragePeer: Peer = {
|
|
194
|
+
id: "test-storage-peer",
|
|
195
|
+
role: "storage",
|
|
196
|
+
priority: 1,
|
|
197
|
+
crashOnClose: false,
|
|
198
|
+
incoming: (async function* () {})(),
|
|
199
|
+
outgoing: {
|
|
200
|
+
push: vi.fn().mockResolvedValue(undefined),
|
|
201
|
+
close: vi.fn(),
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
const peerState = new PeerState(mockStoragePeer, undefined);
|
|
205
|
+
|
|
206
|
+
// Verify they are the same reference
|
|
207
|
+
expect(peerState.knownStates).toBe(peerState.optimisticKnownStates);
|
|
208
|
+
|
|
209
|
+
// Verify that dispatching only updates one state
|
|
210
|
+
const knownStatesSpy = vi.spyOn(peerState.knownStates, "dispatch");
|
|
211
|
+
const optimisticKnownStatesSpy = vi.spyOn(
|
|
212
|
+
peerState.optimisticKnownStates,
|
|
213
|
+
"dispatch",
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const action: PeerKnownStateActions = {
|
|
217
|
+
type: "SET",
|
|
218
|
+
id: "co_z1",
|
|
219
|
+
value: {
|
|
220
|
+
id: "co_z1",
|
|
221
|
+
header: false,
|
|
222
|
+
sessions: {},
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
peerState.dispatchToKnownStates(action);
|
|
226
|
+
|
|
227
|
+
// Only one dispatch should happen since they're the same reference
|
|
228
|
+
expect(knownStatesSpy).toHaveBeenCalledTimes(1);
|
|
229
|
+
expect(knownStatesSpy).toHaveBeenCalledWith(action);
|
|
230
|
+
expect(optimisticKnownStatesSpy).toHaveBeenCalledTimes(1);
|
|
231
|
+
expect(optimisticKnownStatesSpy).toHaveBeenCalledWith(action);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("should use separate references for knownStates and optimisticKnownStates for non-storage peers", () => {
|
|
235
|
+
const { peerState } = setup(); // Uses a regular peer
|
|
236
|
+
|
|
237
|
+
// Verify they are different references
|
|
238
|
+
expect(peerState.knownStates).not.toBe(peerState.optimisticKnownStates);
|
|
239
|
+
});
|
|
191
240
|
});
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import { describe, expect, test } from "vitest";
|
|
1
|
+
import { afterEach, describe, expect, test } from "vitest";
|
|
2
2
|
import { PriorityBasedMessageQueue } from "../PriorityBasedMessageQueue.js";
|
|
3
3
|
import { CO_VALUE_PRIORITY } from "../priority.js";
|
|
4
|
-
import { SyncMessage } from "../sync.js";
|
|
4
|
+
import type { SyncMessage } from "../sync.js";
|
|
5
|
+
import {
|
|
6
|
+
createTestMetricReader,
|
|
7
|
+
tearDownTestMetricReader,
|
|
8
|
+
} from "./testUtils.js";
|
|
5
9
|
|
|
6
10
|
function setup() {
|
|
11
|
+
const metricReader = createTestMetricReader();
|
|
7
12
|
const queue = new PriorityBasedMessageQueue(CO_VALUE_PRIORITY.MEDIUM);
|
|
8
|
-
return { queue };
|
|
13
|
+
return { queue, metricReader };
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
describe("PriorityBasedMessageQueue", () => {
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
tearDownTestMetricReader();
|
|
19
|
+
});
|
|
20
|
+
|
|
12
21
|
test("should initialize with correct properties", () => {
|
|
13
22
|
const { queue } = setup();
|
|
14
23
|
expect(queue["defaultPriority"]).toBe(CO_VALUE_PRIORITY.MEDIUM);
|
|
@@ -43,7 +52,7 @@ describe("PriorityBasedMessageQueue", () => {
|
|
|
43
52
|
});
|
|
44
53
|
|
|
45
54
|
test("should pull messages in priority order", async () => {
|
|
46
|
-
const { queue } = setup();
|
|
55
|
+
const { queue, metricReader } = setup();
|
|
47
56
|
const lowPriorityMsg: SyncMessage = {
|
|
48
57
|
action: "content",
|
|
49
58
|
id: "co_zlow",
|
|
@@ -64,12 +73,42 @@ describe("PriorityBasedMessageQueue", () => {
|
|
|
64
73
|
};
|
|
65
74
|
|
|
66
75
|
void queue.push(lowPriorityMsg);
|
|
76
|
+
expect(
|
|
77
|
+
await metricReader.getMetricValue("jazz.messagequeue.size", {
|
|
78
|
+
priority: lowPriorityMsg.priority,
|
|
79
|
+
}),
|
|
80
|
+
).toBe(1);
|
|
67
81
|
void queue.push(mediumPriorityMsg);
|
|
82
|
+
expect(
|
|
83
|
+
await metricReader.getMetricValue("jazz.messagequeue.size", {
|
|
84
|
+
priority: mediumPriorityMsg.priority,
|
|
85
|
+
}),
|
|
86
|
+
).toBe(1);
|
|
68
87
|
void queue.push(highPriorityMsg);
|
|
88
|
+
expect(
|
|
89
|
+
await metricReader.getMetricValue("jazz.messagequeue.size", {
|
|
90
|
+
priority: highPriorityMsg.priority,
|
|
91
|
+
}),
|
|
92
|
+
).toBe(1);
|
|
69
93
|
|
|
70
94
|
expect(queue.pull()?.msg).toEqual(highPriorityMsg);
|
|
95
|
+
expect(
|
|
96
|
+
await metricReader.getMetricValue("jazz.messagequeue.size", {
|
|
97
|
+
priority: highPriorityMsg.priority,
|
|
98
|
+
}),
|
|
99
|
+
).toBe(0);
|
|
71
100
|
expect(queue.pull()?.msg).toEqual(mediumPriorityMsg);
|
|
101
|
+
expect(
|
|
102
|
+
await metricReader.getMetricValue("jazz.messagequeue.size", {
|
|
103
|
+
priority: mediumPriorityMsg.priority,
|
|
104
|
+
}),
|
|
105
|
+
).toBe(0);
|
|
72
106
|
expect(queue.pull()?.msg).toEqual(lowPriorityMsg);
|
|
107
|
+
expect(
|
|
108
|
+
await metricReader.getMetricValue("jazz.messagequeue.size", {
|
|
109
|
+
priority: lowPriorityMsg.priority,
|
|
110
|
+
}),
|
|
111
|
+
).toBe(0);
|
|
73
112
|
});
|
|
74
113
|
|
|
75
114
|
test("should return undefined when pulling from empty queue", () => {
|
|
@@ -2,12 +2,19 @@ import { describe, expect, onTestFinished, test, vi } from "vitest";
|
|
|
2
2
|
import {
|
|
3
3
|
GlobalSyncStateListenerCallback,
|
|
4
4
|
PeerSyncStateListenerCallback,
|
|
5
|
-
} from "../
|
|
5
|
+
} from "../SyncStateManager.js";
|
|
6
|
+
import { accountHeaderForInitialAgentSecret } from "../coValues/account.js";
|
|
6
7
|
import { connectedPeers } from "../streamUtils.js";
|
|
7
8
|
import { emptyKnownState } from "../sync.js";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
import {
|
|
10
|
+
blockMessageTypeOnOutgoingPeer,
|
|
11
|
+
createTestNode,
|
|
12
|
+
createTwoConnectedNodes,
|
|
13
|
+
loadCoValueOrFail,
|
|
14
|
+
waitFor,
|
|
15
|
+
} from "./testUtils.js";
|
|
16
|
+
|
|
17
|
+
describe("SyncStateManager", () => {
|
|
11
18
|
test("subscribeToUpdates receives updates when peer state changes", async () => {
|
|
12
19
|
// Setup nodes
|
|
13
20
|
const client = createTestNode();
|
|
@@ -31,7 +38,7 @@ describe("SyncStateSubscriptionManager", () => {
|
|
|
31
38
|
client.syncManager.addPeer(jazzCloudAsPeer);
|
|
32
39
|
jazzCloud.syncManager.addPeer(clientAsPeer);
|
|
33
40
|
|
|
34
|
-
const subscriptionManager = client.syncManager.
|
|
41
|
+
const subscriptionManager = client.syncManager.syncState;
|
|
35
42
|
|
|
36
43
|
const updateSpy: GlobalSyncStateListenerCallback = vi.fn();
|
|
37
44
|
const unsubscribe = subscriptionManager.subscribeToUpdates(updateSpy);
|
|
@@ -41,14 +48,14 @@ describe("SyncStateSubscriptionManager", () => {
|
|
|
41
48
|
expect(updateSpy).toHaveBeenCalledWith(
|
|
42
49
|
"jazzCloudConnection",
|
|
43
50
|
emptyKnownState(map.core.id),
|
|
44
|
-
{
|
|
51
|
+
{ uploaded: false },
|
|
45
52
|
);
|
|
46
53
|
|
|
47
54
|
await waitFor(() => {
|
|
48
|
-
return subscriptionManager.
|
|
55
|
+
return subscriptionManager.getCurrentSyncState(
|
|
49
56
|
"jazzCloudConnection",
|
|
50
57
|
map.core.id,
|
|
51
|
-
);
|
|
58
|
+
).uploaded;
|
|
52
59
|
});
|
|
53
60
|
|
|
54
61
|
expect(updateSpy).toHaveBeenCalledWith(
|
|
@@ -56,7 +63,7 @@ describe("SyncStateSubscriptionManager", () => {
|
|
|
56
63
|
client.syncManager.peers["jazzCloudConnection"]!.knownStates.get(
|
|
57
64
|
map.core.id,
|
|
58
65
|
)!,
|
|
59
|
-
{
|
|
66
|
+
{ uploaded: true },
|
|
60
67
|
);
|
|
61
68
|
|
|
62
69
|
// Cleanup
|
|
@@ -92,7 +99,7 @@ describe("SyncStateSubscriptionManager", () => {
|
|
|
92
99
|
client.syncManager.addPeer(clientStoragePeer);
|
|
93
100
|
jazzCloud.syncManager.addPeer(clientAsPeer);
|
|
94
101
|
|
|
95
|
-
const subscriptionManager = client.syncManager.
|
|
102
|
+
const subscriptionManager = client.syncManager.syncState;
|
|
96
103
|
|
|
97
104
|
const updateToJazzCloudSpy: PeerSyncStateListenerCallback = vi.fn();
|
|
98
105
|
const updateToStorageSpy: PeerSyncStateListenerCallback = vi.fn();
|
|
@@ -114,26 +121,26 @@ describe("SyncStateSubscriptionManager", () => {
|
|
|
114
121
|
|
|
115
122
|
expect(updateToJazzCloudSpy).toHaveBeenCalledWith(
|
|
116
123
|
emptyKnownState(map.core.id),
|
|
117
|
-
{
|
|
124
|
+
{ uploaded: false },
|
|
118
125
|
);
|
|
119
126
|
|
|
120
127
|
await waitFor(() => {
|
|
121
|
-
return subscriptionManager.
|
|
128
|
+
return subscriptionManager.getCurrentSyncState(
|
|
122
129
|
"jazzCloudConnection",
|
|
123
130
|
map.core.id,
|
|
124
|
-
);
|
|
131
|
+
).uploaded;
|
|
125
132
|
});
|
|
126
133
|
|
|
127
134
|
expect(updateToJazzCloudSpy).toHaveBeenLastCalledWith(
|
|
128
135
|
client.syncManager.peers["jazzCloudConnection"]!.knownStates.get(
|
|
129
136
|
map.core.id,
|
|
130
137
|
)!,
|
|
131
|
-
{
|
|
138
|
+
{ uploaded: true },
|
|
132
139
|
);
|
|
133
140
|
|
|
134
141
|
expect(updateToStorageSpy).toHaveBeenLastCalledWith(
|
|
135
142
|
emptyKnownState(map.core.id),
|
|
136
|
-
{
|
|
143
|
+
{ uploaded: false },
|
|
137
144
|
);
|
|
138
145
|
});
|
|
139
146
|
|
|
@@ -162,27 +169,27 @@ describe("SyncStateSubscriptionManager", () => {
|
|
|
162
169
|
|
|
163
170
|
await client.syncManager.actuallySyncCoValue(map.core);
|
|
164
171
|
|
|
165
|
-
const subscriptionManager = client.syncManager.
|
|
172
|
+
const subscriptionManager = client.syncManager.syncState;
|
|
166
173
|
|
|
167
174
|
expect(
|
|
168
|
-
subscriptionManager.
|
|
175
|
+
subscriptionManager.getCurrentSyncState(
|
|
169
176
|
"jazzCloudConnection",
|
|
170
177
|
map.core.id,
|
|
171
|
-
),
|
|
178
|
+
).uploaded,
|
|
172
179
|
).toBe(false);
|
|
173
180
|
|
|
174
181
|
await waitFor(() => {
|
|
175
|
-
return subscriptionManager.
|
|
182
|
+
return subscriptionManager.getCurrentSyncState(
|
|
176
183
|
"jazzCloudConnection",
|
|
177
184
|
map.core.id,
|
|
178
|
-
);
|
|
185
|
+
).uploaded;
|
|
179
186
|
});
|
|
180
187
|
|
|
181
188
|
expect(
|
|
182
|
-
subscriptionManager.
|
|
189
|
+
subscriptionManager.getCurrentSyncState(
|
|
183
190
|
"jazzCloudConnection",
|
|
184
191
|
map.core.id,
|
|
185
|
-
),
|
|
192
|
+
).uploaded,
|
|
186
193
|
).toBe(true);
|
|
187
194
|
});
|
|
188
195
|
|
|
@@ -209,7 +216,7 @@ describe("SyncStateSubscriptionManager", () => {
|
|
|
209
216
|
client.syncManager.addPeer(jazzCloudAsPeer);
|
|
210
217
|
jazzCloud.syncManager.addPeer(clientAsPeer);
|
|
211
218
|
|
|
212
|
-
const subscriptionManager = client.syncManager.
|
|
219
|
+
const subscriptionManager = client.syncManager.syncState;
|
|
213
220
|
const anyUpdateSpy = vi.fn();
|
|
214
221
|
const unsubscribe1 = subscriptionManager.subscribeToUpdates(anyUpdateSpy);
|
|
215
222
|
const unsubscribe2 = subscriptionManager.subscribeToPeerUpdates(
|
|
@@ -225,12 +232,89 @@ describe("SyncStateSubscriptionManager", () => {
|
|
|
225
232
|
anyUpdateSpy.mockClear();
|
|
226
233
|
|
|
227
234
|
await waitFor(() => {
|
|
228
|
-
return
|
|
235
|
+
return subscriptionManager.getCurrentSyncState(
|
|
229
236
|
"jazzCloudConnection",
|
|
230
237
|
map.core.id,
|
|
231
|
-
);
|
|
238
|
+
).uploaded;
|
|
232
239
|
});
|
|
233
240
|
|
|
234
241
|
expect(anyUpdateSpy).not.toHaveBeenCalled();
|
|
235
242
|
});
|
|
243
|
+
|
|
244
|
+
test("getCurrentSyncState should return the correct state", async () => {
|
|
245
|
+
// Setup nodes
|
|
246
|
+
const {
|
|
247
|
+
node1: clientNode,
|
|
248
|
+
node2: serverNode,
|
|
249
|
+
node1ToNode2Peer: clientToServerPeer,
|
|
250
|
+
node2ToNode1Peer: serverToClientPeer,
|
|
251
|
+
} = await createTwoConnectedNodes("client", "server");
|
|
252
|
+
|
|
253
|
+
// Create test data
|
|
254
|
+
const group = clientNode.node.createGroup();
|
|
255
|
+
const map = group.createMap();
|
|
256
|
+
map.set("key1", "value1", "trusting");
|
|
257
|
+
group.addMember("everyone", "writer");
|
|
258
|
+
|
|
259
|
+
// Initially should not be synced
|
|
260
|
+
expect(
|
|
261
|
+
clientNode.node.syncManager.syncState.getCurrentSyncState(
|
|
262
|
+
clientToServerPeer.id,
|
|
263
|
+
map.core.id,
|
|
264
|
+
),
|
|
265
|
+
).toEqual({ uploaded: false });
|
|
266
|
+
|
|
267
|
+
// Wait for full sync
|
|
268
|
+
await map.core.waitForSync();
|
|
269
|
+
|
|
270
|
+
expect(
|
|
271
|
+
clientNode.node.syncManager.syncState.getCurrentSyncState(
|
|
272
|
+
clientToServerPeer.id,
|
|
273
|
+
map.core.id,
|
|
274
|
+
),
|
|
275
|
+
).toEqual({ uploaded: true });
|
|
276
|
+
|
|
277
|
+
const mapOnServer = await loadCoValueOrFail(serverNode.node, map.id);
|
|
278
|
+
|
|
279
|
+
// Block the content messages so the client won't fully sync immediately
|
|
280
|
+
const outgoing = blockMessageTypeOnOutgoingPeer(
|
|
281
|
+
serverToClientPeer,
|
|
282
|
+
"content",
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
mapOnServer.set("key2", "value2", "trusting");
|
|
286
|
+
|
|
287
|
+
expect(
|
|
288
|
+
clientNode.node.syncManager.syncState.getCurrentSyncState(
|
|
289
|
+
clientToServerPeer.id,
|
|
290
|
+
map.core.id,
|
|
291
|
+
),
|
|
292
|
+
).toEqual({ uploaded: true });
|
|
293
|
+
|
|
294
|
+
expect(
|
|
295
|
+
serverNode.node.syncManager.syncState.getCurrentSyncState(
|
|
296
|
+
serverToClientPeer.id,
|
|
297
|
+
map.core.id,
|
|
298
|
+
),
|
|
299
|
+
).toEqual({ uploaded: false });
|
|
300
|
+
|
|
301
|
+
await outgoing.sendBlockedMessages();
|
|
302
|
+
outgoing.unblock();
|
|
303
|
+
|
|
304
|
+
await mapOnServer.core.waitForSync();
|
|
305
|
+
|
|
306
|
+
expect(
|
|
307
|
+
clientNode.node.syncManager.syncState.getCurrentSyncState(
|
|
308
|
+
clientToServerPeer.id,
|
|
309
|
+
map.core.id,
|
|
310
|
+
),
|
|
311
|
+
).toEqual({ uploaded: true });
|
|
312
|
+
|
|
313
|
+
expect(
|
|
314
|
+
serverNode.node.syncManager.syncState.getCurrentSyncState(
|
|
315
|
+
serverToClientPeer.id,
|
|
316
|
+
map.core.id,
|
|
317
|
+
),
|
|
318
|
+
).toEqual({ uploaded: true });
|
|
319
|
+
});
|
|
236
320
|
});
|
package/src/tests/coMap.test.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { operationToEditEntry } from "../coValues/coMap.js";
|
|
|
4
4
|
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
5
5
|
import { LocalNode } from "../localNode.js";
|
|
6
6
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
7
|
-
import { randomAnonymousAccountAndSessionID } from "./testUtils.js";
|
|
7
|
+
import { hotSleep, randomAnonymousAccountAndSessionID } from "./testUtils.js";
|
|
8
8
|
|
|
9
9
|
const Crypto = await WasmCrypto.create();
|
|
10
10
|
|
|
@@ -63,20 +63,11 @@ test("Can get CoMap entry values at different points in time", () => {
|
|
|
63
63
|
|
|
64
64
|
expect(content.type).toEqual("comap");
|
|
65
65
|
|
|
66
|
-
const beforeA =
|
|
67
|
-
while (Date.now() < beforeA + 10) {
|
|
68
|
-
/* hot sleep */
|
|
69
|
-
}
|
|
66
|
+
const beforeA = hotSleep(10);
|
|
70
67
|
content.set("hello", "A", "trusting");
|
|
71
|
-
const beforeB =
|
|
72
|
-
while (Date.now() < beforeB + 10) {
|
|
73
|
-
/* hot sleep */
|
|
74
|
-
}
|
|
68
|
+
const beforeB = hotSleep(10);
|
|
75
69
|
content.set("hello", "B", "trusting");
|
|
76
|
-
const beforeC =
|
|
77
|
-
while (Date.now() < beforeC + 10) {
|
|
78
|
-
/* hot sleep */
|
|
79
|
-
}
|
|
70
|
+
const beforeC = hotSleep(10);
|
|
80
71
|
content.set("hello", "C", "trusting");
|
|
81
72
|
expect(content.get("hello")).toEqual("C");
|
|
82
73
|
expect(content.atTime(Date.now()).get("hello")).toEqual("C");
|
package/src/tests/group.test.ts
CHANGED
|
@@ -7,10 +7,8 @@ import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
|
7
7
|
import { LocalNode } from "../localNode.js";
|
|
8
8
|
import {
|
|
9
9
|
createThreeConnectedNodes,
|
|
10
|
-
createTwoConnectedNodes,
|
|
11
10
|
loadCoValueOrFail,
|
|
12
11
|
randomAnonymousAccountAndSessionID,
|
|
13
|
-
waitFor,
|
|
14
12
|
} from "./testUtils.js";
|
|
15
13
|
|
|
16
14
|
const Crypto = await WasmCrypto.create();
|
|
@@ -89,12 +87,12 @@ test("Remove a member from a group where the admin role is inherited", async ()
|
|
|
89
87
|
// The reader should be automatically kicked out of the child group
|
|
90
88
|
await group.removeMember(node3.account);
|
|
91
89
|
|
|
92
|
-
await
|
|
90
|
+
await group.core.waitForSync();
|
|
93
91
|
|
|
94
92
|
// Update the map to check that node3 can't read updates anymore
|
|
95
93
|
map.set("test", "Hidden to node3");
|
|
96
94
|
|
|
97
|
-
await
|
|
95
|
+
await map.core.waitForSync();
|
|
98
96
|
|
|
99
97
|
// Check that the value has not been updated on node3
|
|
100
98
|
expect(mapOnNode3.get("test")).toEqual("Available to everyone");
|
|
@@ -121,16 +119,13 @@ test("An admin should be able to rotate the readKey on child groups and keep acc
|
|
|
121
119
|
const childGroup = node2.createGroup();
|
|
122
120
|
childGroup.extend(groupOnNode2);
|
|
123
121
|
|
|
124
|
-
await
|
|
125
|
-
node2ToNode1Peer.id,
|
|
126
|
-
childGroup.id,
|
|
127
|
-
);
|
|
122
|
+
await childGroup.core.waitForSync();
|
|
128
123
|
|
|
129
124
|
// The node1 account removes the reader from the group
|
|
130
125
|
// In this case we want to ensure that node1 is still able to read new coValues
|
|
131
126
|
// Even if some childs are not available when the readKey is rotated
|
|
132
127
|
await group.removeMember(node3.account);
|
|
133
|
-
await
|
|
128
|
+
await group.core.waitForSync();
|
|
134
129
|
|
|
135
130
|
const map = childGroup.createMap();
|
|
136
131
|
map.set("test", "Available to node1");
|
|
@@ -160,7 +155,7 @@ test("An admin should be able to rotate the readKey on child groups even if it w
|
|
|
160
155
|
// In this case we want to ensure that node1 is still able to read new coValues
|
|
161
156
|
// Even if some childs are not available when the readKey is rotated
|
|
162
157
|
await group.removeMember(node3.account);
|
|
163
|
-
await
|
|
158
|
+
await group.core.waitForSync();
|
|
164
159
|
|
|
165
160
|
const map = childGroup.createMap();
|
|
166
161
|
map.set("test", "Available to node1");
|
|
@@ -195,7 +190,7 @@ test("An admin should be able to rotate the readKey on child groups even if it w
|
|
|
195
190
|
// In this case we want to ensure that node1 is still able to read new coValues
|
|
196
191
|
// Even if some childs are not available when the readKey is rotated
|
|
197
192
|
await group.removeMember(node3.account);
|
|
198
|
-
await
|
|
193
|
+
await group.core.waitForSync();
|
|
199
194
|
|
|
200
195
|
const map = childGroup.createMap();
|
|
201
196
|
map.set("test", "Available to node1");
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
createTwoConnectedNodes,
|
|
8
8
|
groupWithTwoAdmins,
|
|
9
9
|
groupWithTwoAdminsHighLevel,
|
|
10
|
+
hotSleep,
|
|
11
|
+
loadCoValueOrFail,
|
|
10
12
|
newGroup,
|
|
11
13
|
newGroupHighLevel,
|
|
12
14
|
} from "./testUtils.js";
|
|
@@ -1802,29 +1804,25 @@ test("Admins can set child extensions", () => {
|
|
|
1802
1804
|
});
|
|
1803
1805
|
|
|
1804
1806
|
test("Admins can set child extensions when the admin role is inherited", async () => {
|
|
1805
|
-
const { node1, node2 } = createTwoConnectedNodes("server", "server");
|
|
1807
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
1806
1808
|
|
|
1807
|
-
const
|
|
1808
|
-
|
|
1809
|
+
const node2AccountOnNode1 = await loadCoValueOrFail(
|
|
1810
|
+
node1.node,
|
|
1811
|
+
node2.accountID,
|
|
1812
|
+
);
|
|
1809
1813
|
|
|
1810
|
-
group.
|
|
1814
|
+
const group = node1.node.createGroup();
|
|
1811
1815
|
|
|
1812
|
-
|
|
1816
|
+
group.addMember(node2AccountOnNode1, "admin");
|
|
1813
1817
|
|
|
1814
|
-
|
|
1815
|
-
throw new Error("Group not found on node2");
|
|
1816
|
-
}
|
|
1818
|
+
const groupOnNode2 = await loadCoValueOrFail(node2.node, group.id);
|
|
1817
1819
|
|
|
1818
|
-
const childGroup = node2.createGroup();
|
|
1820
|
+
const childGroup = node2.node.createGroup();
|
|
1819
1821
|
childGroup.extend(groupOnNode2);
|
|
1820
1822
|
|
|
1821
|
-
const childGroupOnNode1 = await node1.
|
|
1823
|
+
const childGroupOnNode1 = await loadCoValueOrFail(node1.node, childGroup.id);
|
|
1822
1824
|
|
|
1823
|
-
|
|
1824
|
-
throw new Error("Child group not found on node1");
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
const grandChildGroup = node2.createGroup();
|
|
1825
|
+
const grandChildGroup = node2.node.createGroup();
|
|
1828
1826
|
grandChildGroup.extend(childGroupOnNode1);
|
|
1829
1827
|
|
|
1830
1828
|
expect(childGroupOnNode1.get(`child_${grandChildGroup.id}`)).toEqual(
|
|
@@ -2315,6 +2313,38 @@ test("When rotating the key of a parent group, the keys of all child groups are
|
|
|
2315
2313
|
expect(newChildReadKeyID).not.toEqual(currentChildReadKeyID);
|
|
2316
2314
|
});
|
|
2317
2315
|
|
|
2316
|
+
test("When rotating the key of a parent group, the old transactions should still be valid", async () => {
|
|
2317
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
2318
|
+
|
|
2319
|
+
const group = node1.node.createGroup();
|
|
2320
|
+
const parentGroup = node1.node.createGroup();
|
|
2321
|
+
|
|
2322
|
+
group.extend(parentGroup);
|
|
2323
|
+
|
|
2324
|
+
const node2AccountOnNode1 = await loadCoValueOrFail(
|
|
2325
|
+
node1.node,
|
|
2326
|
+
node2.accountID,
|
|
2327
|
+
);
|
|
2328
|
+
|
|
2329
|
+
parentGroup.addMember(node2AccountOnNode1, "writer");
|
|
2330
|
+
|
|
2331
|
+
const map = group.createMap();
|
|
2332
|
+
map.set("from", "node1", "private");
|
|
2333
|
+
|
|
2334
|
+
const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
|
|
2335
|
+
mapOnNode2.set("from", "node2", "private");
|
|
2336
|
+
|
|
2337
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
2338
|
+
|
|
2339
|
+
parentGroup.removeMember(node2AccountOnNode1);
|
|
2340
|
+
|
|
2341
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
2342
|
+
|
|
2343
|
+
const mapOnNode1 = await loadCoValueOrFail(node1.node, map.id);
|
|
2344
|
+
|
|
2345
|
+
expect(mapOnNode1.get("from")).toEqual("node2");
|
|
2346
|
+
});
|
|
2347
|
+
|
|
2318
2348
|
test("When rotating the key of a grand-parent group, the keys of all child and grand-child groups are also rotated", () => {
|
|
2319
2349
|
const { group, node } = newGroupHighLevel();
|
|
2320
2350
|
const grandParentGroup = node.createGroup();
|