cojson 0.13.5 → 0.13.10
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 +1 -1
- package/CHANGELOG.md +16 -0
- package/LICENSE.txt +1 -1
- package/dist/PeerState.d.ts +6 -0
- package/dist/PeerState.d.ts.map +1 -1
- package/dist/PeerState.js +43 -0
- package/dist/PeerState.js.map +1 -1
- package/dist/coValueCore.d.ts +3 -1
- package/dist/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore.js +26 -8
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValueState.d.ts +1 -0
- package/dist/coValueState.d.ts.map +1 -1
- package/dist/coValueState.js +27 -2
- package/dist/coValueState.js.map +1 -1
- package/dist/coValues/group.d.ts +1 -0
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +45 -21
- package/dist/coValues/group.js.map +1 -1
- package/dist/crypto/crypto.d.ts +2 -2
- package/dist/crypto/crypto.d.ts.map +1 -1
- package/dist/permissions.d.ts +1 -0
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +19 -3
- package/dist/permissions.js.map +1 -1
- package/dist/storage/FileSystem.d.ts +2 -2
- package/dist/storage/FileSystem.d.ts.map +1 -1
- package/dist/sync.d.ts +14 -4
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +146 -146
- package/dist/sync.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +51 -46
- package/dist/tests/SyncStateManager.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +66 -4
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/coValueState.test.js +31 -4
- package/dist/tests/coValueState.test.js.map +1 -1
- package/dist/tests/group.test.js +135 -2
- package/dist/tests/group.test.js.map +1 -1
- package/dist/tests/messagesTestUtils.d.ts +13 -0
- package/dist/tests/messagesTestUtils.d.ts.map +1 -0
- package/dist/tests/messagesTestUtils.js +42 -0
- package/dist/tests/messagesTestUtils.js.map +1 -0
- package/dist/tests/sync.load.test.d.ts +2 -0
- package/dist/tests/sync.load.test.d.ts.map +1 -0
- package/dist/tests/sync.load.test.js +249 -0
- package/dist/tests/sync.load.test.js.map +1 -0
- package/dist/tests/sync.mesh.test.d.ts +2 -0
- package/dist/tests/sync.mesh.test.d.ts.map +1 -0
- package/dist/tests/sync.mesh.test.js +157 -0
- package/dist/tests/sync.mesh.test.js.map +1 -0
- package/dist/tests/sync.peerReconciliation.test.d.ts +2 -0
- package/dist/tests/sync.peerReconciliation.test.d.ts.map +1 -0
- package/dist/tests/sync.peerReconciliation.test.js +166 -0
- package/dist/tests/sync.peerReconciliation.test.js.map +1 -0
- package/dist/tests/sync.storage.test.d.ts +2 -0
- package/dist/tests/sync.storage.test.d.ts.map +1 -0
- package/dist/tests/sync.storage.test.js +201 -0
- package/dist/tests/sync.storage.test.js.map +1 -0
- package/dist/tests/sync.test.js +139 -1048
- package/dist/tests/sync.test.js.map +1 -1
- package/dist/tests/sync.upload.test.d.ts +2 -0
- package/dist/tests/sync.upload.test.d.ts.map +1 -0
- package/dist/tests/sync.upload.test.js +156 -0
- package/dist/tests/sync.upload.test.js.map +1 -0
- package/dist/tests/testUtils.d.ts +76 -33
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +154 -47
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +2 -2
- package/src/PeerState.ts +59 -1
- package/src/coValueCore.ts +37 -7
- package/src/coValueState.ts +34 -3
- package/src/coValues/group.ts +83 -45
- package/src/permissions.ts +31 -3
- package/src/sync.ts +169 -185
- package/src/tests/SyncStateManager.test.ts +58 -70
- package/src/tests/coValueCore.test.ts +90 -3
- package/src/tests/coValueState.test.ts +59 -5
- package/src/tests/group.test.ts +250 -2
- package/src/tests/messagesTestUtils.ts +75 -0
- package/src/tests/sync.load.test.ts +327 -0
- package/src/tests/sync.mesh.test.ts +219 -0
- package/src/tests/sync.peerReconciliation.test.ts +251 -0
- package/src/tests/sync.storage.test.ts +259 -0
- package/src/tests/sync.test.ts +170 -1245
- package/src/tests/sync.upload.test.ts +202 -0
- package/src/tests/testUtils.ts +215 -61
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
SyncMessagesLog,
|
|
5
|
+
loadCoValueOrFail,
|
|
6
|
+
setupTestNode,
|
|
7
|
+
waitFor,
|
|
8
|
+
} from "./testUtils";
|
|
9
|
+
|
|
10
|
+
function setupMesh() {
|
|
11
|
+
const coreServer = setupTestNode({
|
|
12
|
+
isSyncServer: true,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
coreServer.addStoragePeer({
|
|
16
|
+
ourName: "core",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const edgeItaly = setupTestNode();
|
|
20
|
+
edgeItaly.connectToSyncServer({
|
|
21
|
+
ourName: "edge-italy",
|
|
22
|
+
syncServerName: "core",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const edgeFrance = setupTestNode();
|
|
26
|
+
edgeFrance.connectToSyncServer({
|
|
27
|
+
ourName: "edge-france",
|
|
28
|
+
syncServerName: "core",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return { coreServer, edgeItaly, edgeFrance };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe("multiple clients syncing with the a cloud-like server mesh", () => {
|
|
35
|
+
let mesh = setupMesh();
|
|
36
|
+
|
|
37
|
+
beforeEach(async () => {
|
|
38
|
+
SyncMessagesLog.clear();
|
|
39
|
+
mesh = setupMesh();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("loading a coValue created on a different edge", async () => {
|
|
43
|
+
const client = setupTestNode();
|
|
44
|
+
|
|
45
|
+
client.connectToSyncServer({
|
|
46
|
+
syncServerName: "edge-italy",
|
|
47
|
+
syncServer: mesh.edgeItaly.node,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const group = mesh.edgeFrance.node.createGroup();
|
|
51
|
+
const map = group.createMap();
|
|
52
|
+
map.set("hello", "world", "trusting");
|
|
53
|
+
|
|
54
|
+
await map.core.waitForSync();
|
|
55
|
+
|
|
56
|
+
const mapOnClient = await loadCoValueOrFail(client.node, map.id);
|
|
57
|
+
expect(mapOnClient.get("hello")).toEqual("world");
|
|
58
|
+
|
|
59
|
+
expect(
|
|
60
|
+
SyncMessagesLog.getMessages({
|
|
61
|
+
Group: group.core,
|
|
62
|
+
Map: map.core,
|
|
63
|
+
}),
|
|
64
|
+
).toMatchInlineSnapshot(`
|
|
65
|
+
[
|
|
66
|
+
"edge-france -> core | CONTENT Group header: true new: After: 0 New: 3",
|
|
67
|
+
"core -> edge-france | KNOWN Group sessions: header/3",
|
|
68
|
+
"core -> storage | CONTENT Group header: true new: After: 0 New: 3",
|
|
69
|
+
"edge-france -> core | CONTENT Map header: true new: After: 0 New: 1",
|
|
70
|
+
"storage -> core | KNOWN Group sessions: header/3",
|
|
71
|
+
"core -> edge-france | KNOWN Map sessions: header/1",
|
|
72
|
+
"core -> storage | CONTENT Map header: true new: After: 0 New: 1",
|
|
73
|
+
"storage -> core | KNOWN Map sessions: header/1",
|
|
74
|
+
"client -> edge-italy | LOAD Map sessions: empty",
|
|
75
|
+
"edge-italy -> core | LOAD Map sessions: empty",
|
|
76
|
+
"core -> edge-italy | CONTENT Group header: true new: After: 0 New: 3",
|
|
77
|
+
"edge-italy -> core | KNOWN Group sessions: header/3",
|
|
78
|
+
"core -> edge-italy | CONTENT Map header: true new: After: 0 New: 1",
|
|
79
|
+
"edge-italy -> client | CONTENT Group header: true new: After: 0 New: 3",
|
|
80
|
+
"client -> edge-italy | KNOWN Group sessions: header/3",
|
|
81
|
+
"edge-italy -> core | KNOWN Map sessions: header/1",
|
|
82
|
+
"edge-italy -> client | CONTENT Map header: true new: After: 0 New: 1",
|
|
83
|
+
"client -> edge-italy | KNOWN Map sessions: header/1",
|
|
84
|
+
]
|
|
85
|
+
`);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// FIXME: Expected parent group to be loaded: CoValue co_zEKiodKQprnfsi2qfDtsHGCGDSo not yet loaded
|
|
89
|
+
test.skip("coValue created on a different edge with parent groups loading", async () => {
|
|
90
|
+
const client = setupTestNode();
|
|
91
|
+
|
|
92
|
+
client.connectToSyncServer({
|
|
93
|
+
syncServerName: "edge-italy",
|
|
94
|
+
syncServer: mesh.edgeItaly.node,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const group = mesh.edgeFrance.node.createGroup();
|
|
98
|
+
const parentGroup = mesh.edgeItaly.node.createGroup();
|
|
99
|
+
parentGroup.addMember("everyone", "reader");
|
|
100
|
+
|
|
101
|
+
group.extend(parentGroup);
|
|
102
|
+
|
|
103
|
+
const map = group.createMap();
|
|
104
|
+
map.set("hello", "world");
|
|
105
|
+
|
|
106
|
+
await map.core.waitForSync();
|
|
107
|
+
|
|
108
|
+
const mapOnClient = await loadCoValueOrFail(client.node, map.id);
|
|
109
|
+
expect(mapOnClient.get("hello")).toEqual("world");
|
|
110
|
+
|
|
111
|
+
expect(
|
|
112
|
+
SyncMessagesLog.getMessages({
|
|
113
|
+
ParentGroup: parentGroup.core,
|
|
114
|
+
Group: group.core,
|
|
115
|
+
Map: map.core,
|
|
116
|
+
}),
|
|
117
|
+
).toMatchInlineSnapshot();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("updating a coValue coming from a different edge", async () => {
|
|
121
|
+
const client = setupTestNode();
|
|
122
|
+
|
|
123
|
+
client.connectToSyncServer({
|
|
124
|
+
syncServerName: "edge-italy",
|
|
125
|
+
syncServer: mesh.edgeItaly.node,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const group = mesh.edgeFrance.node.createGroup();
|
|
129
|
+
group.addMember("everyone", "writer");
|
|
130
|
+
const map = group.createMap();
|
|
131
|
+
map.set("hello", "world", "trusting");
|
|
132
|
+
|
|
133
|
+
await map.core.waitForSync();
|
|
134
|
+
|
|
135
|
+
const mapOnClient = await loadCoValueOrFail(client.node, map.id);
|
|
136
|
+
expect(mapOnClient.get("hello")).toEqual("world");
|
|
137
|
+
|
|
138
|
+
SyncMessagesLog.clear(); // We want to focus on the sync messages happening from now
|
|
139
|
+
mapOnClient.set("hello", "updated", "trusting");
|
|
140
|
+
|
|
141
|
+
await waitFor(() => {
|
|
142
|
+
expect(map.get("hello")).toEqual("updated");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expect(
|
|
146
|
+
SyncMessagesLog.getMessages({
|
|
147
|
+
Group: group.core,
|
|
148
|
+
Map: map.core,
|
|
149
|
+
}),
|
|
150
|
+
).toMatchInlineSnapshot(`
|
|
151
|
+
[
|
|
152
|
+
"client -> edge-italy | CONTENT Map header: false new: After: 0 New: 1",
|
|
153
|
+
"edge-italy -> client | KNOWN Map sessions: header/2",
|
|
154
|
+
"edge-italy -> core | CONTENT Map header: false new: After: 0 New: 1",
|
|
155
|
+
"core -> edge-italy | KNOWN Map sessions: header/2",
|
|
156
|
+
"core -> storage | CONTENT Map header: false new: After: 0 New: 1",
|
|
157
|
+
"storage -> core | KNOWN Map sessions: header/2",
|
|
158
|
+
"core -> edge-france | CONTENT Map header: false new: After: 0 New: 1",
|
|
159
|
+
"edge-france -> core | KNOWN Map sessions: header/2",
|
|
160
|
+
]
|
|
161
|
+
`);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("sync of changes of a coValue with bad signatures should be blocked", async () => {
|
|
165
|
+
const italianClient = setupTestNode();
|
|
166
|
+
const frenchClient = setupTestNode();
|
|
167
|
+
|
|
168
|
+
italianClient.connectToSyncServer({
|
|
169
|
+
syncServerName: "edge-italy",
|
|
170
|
+
syncServer: mesh.edgeItaly.node,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
frenchClient.connectToSyncServer({
|
|
174
|
+
syncServerName: "edge-france",
|
|
175
|
+
syncServer: mesh.edgeFrance.node,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const group = mesh.edgeFrance.node.createGroup();
|
|
179
|
+
const map = group.createMap();
|
|
180
|
+
|
|
181
|
+
map.set("hello", "world", "trusting");
|
|
182
|
+
|
|
183
|
+
const mapOnFrenchClient = await loadCoValueOrFail(
|
|
184
|
+
frenchClient.node,
|
|
185
|
+
map.id,
|
|
186
|
+
);
|
|
187
|
+
const mapOnItalianClient = await loadCoValueOrFail(
|
|
188
|
+
italianClient.node,
|
|
189
|
+
map.id,
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
expect(mapOnItalianClient.get("hello")).toEqual("world");
|
|
193
|
+
expect(mapOnFrenchClient.get("hello")).toEqual("world");
|
|
194
|
+
|
|
195
|
+
// Return an invalid signature on the next transaction
|
|
196
|
+
vi.spyOn(italianClient.node.crypto, "sign").mockReturnValueOnce(
|
|
197
|
+
"signature_z2jYFqH6hey3Yy8EdgjFxDtD7MJWnNMkhBx5snKsBdFRNJgtPSNK73LrAyCjzMjH5f2nsssT5MbYm8r6tKJJGWDEB",
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
SyncMessagesLog.clear(); // We want to focus on the sync messages happening from now
|
|
201
|
+
mapOnItalianClient.set("hello", "updated", "trusting");
|
|
202
|
+
|
|
203
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
204
|
+
|
|
205
|
+
expect(mapOnFrenchClient.get("hello")).toEqual("world");
|
|
206
|
+
|
|
207
|
+
expect(
|
|
208
|
+
SyncMessagesLog.getMessages({
|
|
209
|
+
Group: group.core,
|
|
210
|
+
Map: map.core,
|
|
211
|
+
}),
|
|
212
|
+
).toMatchInlineSnapshot(`
|
|
213
|
+
[
|
|
214
|
+
"client -> edge-italy | CONTENT Map header: false new: After: 0 New: 1",
|
|
215
|
+
"edge-italy -> client | KNOWN Map sessions: header/1",
|
|
216
|
+
]
|
|
217
|
+
`);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { assert, beforeEach, describe, expect, test } from "vitest";
|
|
2
|
+
import { expectMap } from "../coValue";
|
|
3
|
+
import { WasmCrypto } from "../crypto/WasmCrypto";
|
|
4
|
+
import { CoValueCore, RawCoMap } from "../exports";
|
|
5
|
+
import { LocalNode } from "../localNode";
|
|
6
|
+
import { toSimplifiedMessages } from "./messagesTestUtils";
|
|
7
|
+
import {
|
|
8
|
+
SyncMessagesLog,
|
|
9
|
+
createTestNode,
|
|
10
|
+
randomAnonymousAccountAndSessionID,
|
|
11
|
+
setupTestNode,
|
|
12
|
+
waitFor,
|
|
13
|
+
} from "./testUtils";
|
|
14
|
+
|
|
15
|
+
const Crypto = await WasmCrypto.create();
|
|
16
|
+
|
|
17
|
+
let jazzCloud = setupTestNode({ isSyncServer: true });
|
|
18
|
+
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
SyncMessagesLog.clear();
|
|
21
|
+
jazzCloud = setupTestNode({ isSyncServer: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("peer reconciliation", () => {
|
|
25
|
+
test("handle new peer connections", async () => {
|
|
26
|
+
const client = setupTestNode();
|
|
27
|
+
|
|
28
|
+
const group = client.node.createGroup();
|
|
29
|
+
const map = group.createMap();
|
|
30
|
+
|
|
31
|
+
map.set("hello", "world", "trusting");
|
|
32
|
+
|
|
33
|
+
client.connectToSyncServer();
|
|
34
|
+
|
|
35
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
36
|
+
|
|
37
|
+
await map.core.waitForSync();
|
|
38
|
+
|
|
39
|
+
expect(
|
|
40
|
+
SyncMessagesLog.getMessages({
|
|
41
|
+
Group: group.core,
|
|
42
|
+
Map: map.core,
|
|
43
|
+
}),
|
|
44
|
+
).toMatchInlineSnapshot(`
|
|
45
|
+
[
|
|
46
|
+
"client -> server | LOAD Group sessions: header/3",
|
|
47
|
+
"server -> client | KNOWN Group sessions: empty",
|
|
48
|
+
"client -> server | LOAD Map sessions: header/1",
|
|
49
|
+
"server -> client | KNOWN Map sessions: empty",
|
|
50
|
+
"client -> server | CONTENT Group header: true new: After: 0 New: 3",
|
|
51
|
+
"server -> client | KNOWN Group sessions: header/3",
|
|
52
|
+
"client -> server | CONTENT Map header: true new: After: 0 New: 1",
|
|
53
|
+
"server -> client | KNOWN Map sessions: header/1",
|
|
54
|
+
]
|
|
55
|
+
`);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("handle peer reconnections", async () => {
|
|
59
|
+
const client = setupTestNode();
|
|
60
|
+
|
|
61
|
+
const group = client.node.createGroup();
|
|
62
|
+
|
|
63
|
+
const map = group.createMap();
|
|
64
|
+
|
|
65
|
+
map.set("hello", "world", "trusting");
|
|
66
|
+
|
|
67
|
+
const { peerState } = client.connectToSyncServer();
|
|
68
|
+
|
|
69
|
+
await map.core.waitForSync();
|
|
70
|
+
|
|
71
|
+
peerState.gracefulShutdown();
|
|
72
|
+
|
|
73
|
+
map.set("hello", "updated", "trusting");
|
|
74
|
+
|
|
75
|
+
SyncMessagesLog.clear();
|
|
76
|
+
client.connectToSyncServer();
|
|
77
|
+
|
|
78
|
+
await map.core.waitForSync();
|
|
79
|
+
|
|
80
|
+
const mapOnSyncServer = jazzCloud.node.coValuesStore.get(map.id);
|
|
81
|
+
|
|
82
|
+
assert(mapOnSyncServer.state.type === "available");
|
|
83
|
+
|
|
84
|
+
expect(
|
|
85
|
+
expectMap(mapOnSyncServer.state.coValue.getCurrentContent()).get("hello"),
|
|
86
|
+
).toEqual("updated");
|
|
87
|
+
|
|
88
|
+
expect(
|
|
89
|
+
SyncMessagesLog.getMessages({
|
|
90
|
+
Group: group.core,
|
|
91
|
+
Map: map.core,
|
|
92
|
+
}),
|
|
93
|
+
).toMatchInlineSnapshot(`
|
|
94
|
+
[
|
|
95
|
+
"client -> server | LOAD Group sessions: header/3",
|
|
96
|
+
"server -> client | KNOWN Group sessions: header/3",
|
|
97
|
+
"client -> server | LOAD Map sessions: header/2",
|
|
98
|
+
"server -> client | KNOWN Map sessions: header/1",
|
|
99
|
+
"client -> server | CONTENT Map header: false new: After: 1 New: 1",
|
|
100
|
+
"server -> client | KNOWN Map sessions: header/2",
|
|
101
|
+
]
|
|
102
|
+
`);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("correctly handle concurrent peer reconnections", async () => {
|
|
106
|
+
const client = setupTestNode();
|
|
107
|
+
|
|
108
|
+
const group = client.node.createGroup();
|
|
109
|
+
const map = group.createMap();
|
|
110
|
+
|
|
111
|
+
map.set("hello", "world", "trusting");
|
|
112
|
+
|
|
113
|
+
const { peerState } = client.connectToSyncServer();
|
|
114
|
+
|
|
115
|
+
await map.core.waitForSync();
|
|
116
|
+
|
|
117
|
+
peerState.gracefulShutdown();
|
|
118
|
+
|
|
119
|
+
map.set("hello", "updated", "trusting");
|
|
120
|
+
|
|
121
|
+
SyncMessagesLog.clear();
|
|
122
|
+
const { peer } = client.connectToSyncServer();
|
|
123
|
+
const { peer: latestPeer } = client.connectToSyncServer();
|
|
124
|
+
|
|
125
|
+
await map.core.waitForSync();
|
|
126
|
+
|
|
127
|
+
const mapOnSyncServer = jazzCloud.node.coValuesStore.get(map.id);
|
|
128
|
+
|
|
129
|
+
assert(mapOnSyncServer.state.type === "available");
|
|
130
|
+
|
|
131
|
+
expect(
|
|
132
|
+
expectMap(mapOnSyncServer.state.coValue.getCurrentContent()).get("hello"),
|
|
133
|
+
).toEqual("updated");
|
|
134
|
+
|
|
135
|
+
expect(peer.outgoing).toMatchObject({
|
|
136
|
+
closed: true,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(latestPeer.outgoing).toMatchObject({
|
|
140
|
+
closed: false,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
expect(
|
|
144
|
+
SyncMessagesLog.getMessages({
|
|
145
|
+
Group: group.core,
|
|
146
|
+
Map: map.core,
|
|
147
|
+
}),
|
|
148
|
+
).toMatchInlineSnapshot(`
|
|
149
|
+
[
|
|
150
|
+
"client -> server | LOAD Group sessions: header/3",
|
|
151
|
+
"server -> client | KNOWN Group sessions: header/3",
|
|
152
|
+
"client -> server | LOAD Map sessions: header/2",
|
|
153
|
+
"server -> client | KNOWN Map sessions: header/1",
|
|
154
|
+
"client -> server | CONTENT Map header: false new: After: 1 New: 1",
|
|
155
|
+
"server -> client | KNOWN Map sessions: header/2",
|
|
156
|
+
]
|
|
157
|
+
`);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("correctly handle server restarts in the middle of a sync", async () => {
|
|
161
|
+
const client = setupTestNode();
|
|
162
|
+
|
|
163
|
+
const group = client.node.createGroup();
|
|
164
|
+
const map = group.createMap();
|
|
165
|
+
|
|
166
|
+
map.set("hello", "world", "trusting");
|
|
167
|
+
|
|
168
|
+
await map.core.waitForSync();
|
|
169
|
+
|
|
170
|
+
jazzCloud.restart();
|
|
171
|
+
SyncMessagesLog.clear();
|
|
172
|
+
client.connectToSyncServer();
|
|
173
|
+
|
|
174
|
+
map.set("hello", "updated", "trusting");
|
|
175
|
+
|
|
176
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
177
|
+
|
|
178
|
+
client.connectToSyncServer();
|
|
179
|
+
|
|
180
|
+
await waitFor(() => {
|
|
181
|
+
const mapOnSyncServer = jazzCloud.node.coValuesStore.get(map.id);
|
|
182
|
+
|
|
183
|
+
expect(mapOnSyncServer.state.type).toBe("available");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
expect(
|
|
187
|
+
SyncMessagesLog.getMessages({
|
|
188
|
+
Group: group.core,
|
|
189
|
+
Map: map.core,
|
|
190
|
+
}),
|
|
191
|
+
).toMatchInlineSnapshot(`
|
|
192
|
+
[
|
|
193
|
+
"client -> server | LOAD Group sessions: header/3",
|
|
194
|
+
"server -> client | KNOWN Group sessions: empty",
|
|
195
|
+
"client -> server | LOAD Map sessions: header/2",
|
|
196
|
+
"server -> client | KNOWN Map sessions: empty",
|
|
197
|
+
"client -> server | CONTENT Group header: true new: After: 0 New: 3",
|
|
198
|
+
"server -> client | KNOWN Group sessions: header/3",
|
|
199
|
+
"client -> server | CONTENT Map header: true new: After: 0 New: 2",
|
|
200
|
+
"server -> client | KNOWN Map sessions: header/2",
|
|
201
|
+
"client -> server | LOAD Group sessions: header/3",
|
|
202
|
+
"server -> client | KNOWN Group sessions: header/3",
|
|
203
|
+
"client -> server | LOAD Map sessions: header/2",
|
|
204
|
+
"server -> client | KNOWN Map sessions: header/2",
|
|
205
|
+
]
|
|
206
|
+
`);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test.skip("handle peer reconnections with data loss", async () => {
|
|
210
|
+
const client = setupTestNode();
|
|
211
|
+
|
|
212
|
+
const group = client.node.createGroup();
|
|
213
|
+
const map = group.createMap();
|
|
214
|
+
|
|
215
|
+
map.set("hello", "world", "trusting");
|
|
216
|
+
|
|
217
|
+
client.connectToSyncServer();
|
|
218
|
+
|
|
219
|
+
await map.core.waitForSync();
|
|
220
|
+
|
|
221
|
+
jazzCloud.restart();
|
|
222
|
+
|
|
223
|
+
SyncMessagesLog.clear();
|
|
224
|
+
client.connectToSyncServer();
|
|
225
|
+
const mapOnSyncServer = jazzCloud.node.coValuesStore.get(map.id);
|
|
226
|
+
|
|
227
|
+
await waitFor(() => {
|
|
228
|
+
expect(mapOnSyncServer.state.type).toBe("available");
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
assert(mapOnSyncServer.state.type === "available");
|
|
232
|
+
|
|
233
|
+
expect(
|
|
234
|
+
SyncMessagesLog.getMessages({
|
|
235
|
+
Group: group.core,
|
|
236
|
+
Map: map.core,
|
|
237
|
+
}),
|
|
238
|
+
).toMatchInlineSnapshot(`
|
|
239
|
+
[
|
|
240
|
+
"client -> LOAD Group sessions: header/3",
|
|
241
|
+
"server -> KNOWN Group sessions: empty",
|
|
242
|
+
"client -> LOAD Map sessions: header/1",
|
|
243
|
+
"server -> KNOWN Map sessions: empty",
|
|
244
|
+
]
|
|
245
|
+
`);
|
|
246
|
+
|
|
247
|
+
expect(
|
|
248
|
+
expectMap(mapOnSyncServer.state.coValue.getCurrentContent()).get("hello"),
|
|
249
|
+
).toEqual("updated");
|
|
250
|
+
});
|
|
251
|
+
});
|