cojson 0.18.31 → 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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +17 -0
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +2 -2
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValueCore/SessionMap.d.ts +1 -0
- package/dist/coValueCore/SessionMap.d.ts.map +1 -1
- package/dist/coValueCore/SessionMap.js +17 -2
- package/dist/coValueCore/SessionMap.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +14 -9
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +62 -47
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +2 -2
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +86 -75
- package/dist/coValueCore/verifiedState.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 +24 -4
- package/dist/coValues/group.js.map +1 -1
- package/dist/crypto/PureJSCrypto.d.ts.map +1 -1
- package/dist/crypto/PureJSCrypto.js +2 -10
- package/dist/crypto/PureJSCrypto.js.map +1 -1
- package/dist/knownState.d.ts +1 -1
- package/dist/knownState.d.ts.map +1 -1
- package/dist/knownState.js +1 -1
- package/dist/knownState.js.map +1 -1
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +1 -2
- package/dist/localNode.js.map +1 -1
- package/dist/queue/LocalTransactionsSyncQueue.d.ts.map +1 -1
- package/dist/queue/LocalTransactionsSyncQueue.js +16 -1
- package/dist/queue/LocalTransactionsSyncQueue.js.map +1 -1
- package/dist/storage/knownState.js +2 -2
- package/dist/storage/knownState.js.map +1 -1
- package/dist/storage/sqlite/index.d.ts.map +1 -1
- package/dist/storage/sqlite/index.js +17 -3
- package/dist/storage/sqlite/index.js.map +1 -1
- package/dist/storage/sqlite/sqliteMigrations.d.ts +6 -1
- package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -1
- package/dist/storage/sqlite/sqliteMigrations.js +1 -3
- package/dist/storage/sqlite/sqliteMigrations.js.map +1 -1
- package/dist/storage/sqlite/types.d.ts +2 -0
- package/dist/storage/sqlite/types.d.ts.map +1 -1
- package/dist/storage/sqliteAsync/index.d.ts.map +1 -1
- package/dist/storage/sqliteAsync/index.js +17 -3
- package/dist/storage/sqliteAsync/index.js.map +1 -1
- package/dist/storage/sqliteAsync/types.d.ts +2 -0
- package/dist/storage/sqliteAsync/types.d.ts.map +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +8 -2
- package/dist/sync.js.map +1 -1
- package/dist/tests/PureJSCrypto.test.js +1 -1
- package/dist/tests/PureJSCrypto.test.js.map +1 -1
- package/dist/tests/StorageApiAsync.test.js +11 -11
- package/dist/tests/StorageApiAsync.test.js.map +1 -1
- package/dist/tests/StorageApiSync.test.js +3 -3
- package/dist/tests/StorageApiSync.test.js.map +1 -1
- package/dist/tests/WasmCrypto.test.js +1 -1
- package/dist/tests/WasmCrypto.test.js.map +1 -1
- package/dist/tests/coPlainText.test.js +1 -1
- package/dist/tests/coStream.test.js +12 -12
- package/dist/tests/coStream.test.js.map +1 -1
- package/dist/tests/coValueCore.isCompletelyDownloaded.test.d.ts +2 -0
- package/dist/tests/coValueCore.isCompletelyDownloaded.test.d.ts.map +1 -0
- package/dist/tests/coValueCore.isCompletelyDownloaded.test.js +421 -0
- package/dist/tests/coValueCore.isCompletelyDownloaded.test.js.map +1 -0
- package/dist/tests/coValueCore.isStreaming.test.d.ts +2 -0
- package/dist/tests/coValueCore.isStreaming.test.d.ts.map +1 -0
- package/dist/tests/coValueCore.isStreaming.test.js +181 -0
- package/dist/tests/coValueCore.isStreaming.test.js.map +1 -0
- package/dist/tests/coValueCore.newContentSince.test.d.ts +2 -0
- package/dist/tests/coValueCore.newContentSince.test.d.ts.map +1 -0
- package/dist/tests/coValueCore.newContentSince.test.js +808 -0
- package/dist/tests/coValueCore.newContentSince.test.js.map +1 -0
- package/dist/tests/coreWasm.test.js +2 -2
- package/dist/tests/coreWasm.test.js.map +1 -1
- package/dist/tests/group.childKeyRotation.test.d.ts +2 -0
- package/dist/tests/group.childKeyRotation.test.d.ts.map +1 -0
- package/dist/tests/group.childKeyRotation.test.js +261 -0
- package/dist/tests/group.childKeyRotation.test.js.map +1 -0
- package/dist/tests/group.removeMember.test.js +1 -114
- package/dist/tests/group.removeMember.test.js.map +1 -1
- package/dist/tests/knownState.test.js +11 -11
- package/dist/tests/knownState.test.js.map +1 -1
- package/dist/tests/sync.auth.test.js +6 -6
- package/dist/tests/sync.load.test.js +68 -5
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +11 -17
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.peerReconciliation.test.js +1 -1
- package/dist/tests/sync.storage.test.js +7 -7
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.storageAsync.test.js +4 -4
- package/dist/tests/sync.storageAsync.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js +96 -40
- package/dist/tests/sync.upload.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts +2 -0
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +22 -1
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +3 -3
- package/src/SyncStateManager.ts +2 -5
- package/src/coValueCore/SessionMap.ts +26 -1
- package/src/coValueCore/coValueCore.ts +77 -55
- package/src/coValueCore/verifiedState.ts +123 -108
- package/src/coValues/group.ts +27 -4
- package/src/crypto/PureJSCrypto.ts +5 -21
- package/src/knownState.ts +1 -1
- package/src/localNode.ts +1 -2
- package/src/queue/LocalTransactionsSyncQueue.ts +25 -0
- package/src/storage/knownState.ts +2 -2
- package/src/storage/sqlite/index.ts +16 -3
- package/src/storage/sqlite/sqliteMigrations.ts +7 -4
- package/src/storage/sqlite/types.ts +2 -0
- package/src/storage/sqliteAsync/index.ts +19 -6
- package/src/storage/sqliteAsync/types.ts +2 -0
- package/src/sync.ts +7 -2
- package/src/tests/PureJSCrypto.test.ts +1 -2
- package/src/tests/StorageApiAsync.test.ts +11 -11
- package/src/tests/StorageApiSync.test.ts +3 -3
- package/src/tests/WasmCrypto.test.ts +1 -2
- package/src/tests/coPlainText.test.ts +1 -1
- package/src/tests/coStream.test.ts +12 -12
- package/src/tests/coValueCore.isCompletelyDownloaded.test.ts +589 -0
- package/src/tests/coValueCore.isStreaming.test.ts +271 -0
- package/src/tests/coValueCore.newContentSince.test.ts +966 -0
- package/src/tests/coreWasm.test.ts +2 -2
- package/src/tests/group.childKeyRotation.test.ts +431 -0
- package/src/tests/group.removeMember.test.ts +1 -184
- package/src/tests/knownState.test.ts +11 -11
- package/src/tests/sync.auth.test.ts +6 -6
- package/src/tests/sync.load.test.ts +80 -5
- package/src/tests/sync.mesh.test.ts +11 -17
- package/src/tests/sync.peerReconciliation.test.ts +1 -1
- package/src/tests/sync.storage.test.ts +7 -7
- package/src/tests/sync.storageAsync.test.ts +4 -4
- package/src/tests/sync.upload.test.ts +106 -40
- package/src/tests/testUtils.ts +24 -2
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { assert, beforeEach, describe, expect, test } from "vitest";
|
|
2
|
+
import { SyncMessagesLog, TEST_NODE_CONFIG, loadCoValueOrFail, setupTestAccount, setupTestNode, waitFor, } from "./testUtils.js";
|
|
3
|
+
import { expectMap } from "../coValue.js";
|
|
4
|
+
// We want to simulate a real world communication that happens asynchronously
|
|
5
|
+
TEST_NODE_CONFIG.withAsyncPeers = true;
|
|
6
|
+
let jazzCloud;
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
SyncMessagesLog.clear();
|
|
9
|
+
jazzCloud = setupTestNode({ isSyncServer: true });
|
|
10
|
+
});
|
|
11
|
+
describe("CoValueCore.isCompletelyDownloaded", () => {
|
|
12
|
+
let admin;
|
|
13
|
+
let bob;
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
admin = await setupTestAccount({
|
|
16
|
+
connected: true,
|
|
17
|
+
});
|
|
18
|
+
bob = await setupTestAccount({
|
|
19
|
+
connected: true,
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe("streaming from start", () => {
|
|
23
|
+
test("should return false when the value itself is streaming", async () => {
|
|
24
|
+
const group = admin.node.createGroup();
|
|
25
|
+
const map = group.createMap();
|
|
26
|
+
map.set("initial", "value");
|
|
27
|
+
await map.core.waitForSync();
|
|
28
|
+
// Disconnect the admin node to sync the content manually and simulate the delay of the last chunk
|
|
29
|
+
admin.disconnect();
|
|
30
|
+
// Make the map large enough to require multiple messages to be synced
|
|
31
|
+
for (let i = 0; i < 100; i++) {
|
|
32
|
+
map.set(`key${i}`, "1".repeat(1024));
|
|
33
|
+
}
|
|
34
|
+
const content = map.core.verified.newContentSince(undefined);
|
|
35
|
+
assert(content);
|
|
36
|
+
expect(content.length).toBeGreaterThan(1);
|
|
37
|
+
const lastChunk = content.pop();
|
|
38
|
+
assert(lastChunk);
|
|
39
|
+
// Create a new session for bob
|
|
40
|
+
const bobSession = bob.node;
|
|
41
|
+
// Feed all chunks except the last one
|
|
42
|
+
for (const chunk of content) {
|
|
43
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
44
|
+
}
|
|
45
|
+
const mapOnBob = await loadCoValueOrFail(bobSession, map.id);
|
|
46
|
+
// The map should not be completely downloaded yet
|
|
47
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
48
|
+
// Feed the last chunk
|
|
49
|
+
bobSession.syncManager.handleNewContent(lastChunk, "import");
|
|
50
|
+
// Wait for the notification to be scheduled and executed
|
|
51
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
52
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
test("should return false when the owner of the value is streaming", async () => {
|
|
55
|
+
const group = admin.node.createGroup();
|
|
56
|
+
const map = group.createMap();
|
|
57
|
+
map.set("test", "value");
|
|
58
|
+
await map.core.waitForSync();
|
|
59
|
+
// Disconnect the admin node to sync the content manually
|
|
60
|
+
admin.disconnect();
|
|
61
|
+
// Make the group large enough to require multiple messages to be synced
|
|
62
|
+
for (let i = 0; i < 100; i++) {
|
|
63
|
+
// @ts-expect-error - test property is not part of the group shape
|
|
64
|
+
group.set(`key${i}`, "1".repeat(1024));
|
|
65
|
+
}
|
|
66
|
+
const content = group.core.verified.newContentSince(undefined);
|
|
67
|
+
assert(content);
|
|
68
|
+
expect(content.length).toBeGreaterThan(1);
|
|
69
|
+
const lastChunk = content.pop();
|
|
70
|
+
assert(lastChunk);
|
|
71
|
+
// Create a new session for bob
|
|
72
|
+
const bobSession = bob.node;
|
|
73
|
+
// Feed all chunks except the last one
|
|
74
|
+
for (const chunk of content) {
|
|
75
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
76
|
+
}
|
|
77
|
+
const mapOnBob = await loadCoValueOrFail(bobSession, map.id);
|
|
78
|
+
// The map should not be completely downloaded because its owner (group) is still streaming
|
|
79
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
80
|
+
// Feed the last chunk
|
|
81
|
+
bobSession.syncManager.handleNewContent(lastChunk, "import");
|
|
82
|
+
// Wait for the notification to be scheduled and executed
|
|
83
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
84
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
test("should return false when the parent of the owner is streaming", async () => {
|
|
87
|
+
const parentGroup = admin.node.createGroup();
|
|
88
|
+
const childGroup = admin.node.createGroup();
|
|
89
|
+
childGroup.extend(parentGroup);
|
|
90
|
+
const map = childGroup.createMap();
|
|
91
|
+
map.set("test", "value");
|
|
92
|
+
await map.core.waitForSync();
|
|
93
|
+
// Disconnect the admin node to sync the content manually
|
|
94
|
+
admin.disconnect();
|
|
95
|
+
// Make the parent group large enough to require multiple messages to be synced
|
|
96
|
+
for (let i = 0; i < 100; i++) {
|
|
97
|
+
// @ts-expect-error - test property is not part of the group shape
|
|
98
|
+
parentGroup.set(`key${i}`, "1".repeat(1024));
|
|
99
|
+
}
|
|
100
|
+
const content = parentGroup.core.verified.newContentSince(undefined);
|
|
101
|
+
assert(content);
|
|
102
|
+
expect(content.length).toBeGreaterThan(1);
|
|
103
|
+
const lastChunk = content.pop();
|
|
104
|
+
assert(lastChunk);
|
|
105
|
+
// Create a new session for bob
|
|
106
|
+
const bobSession = bob.node;
|
|
107
|
+
// Feed all chunks except the last one
|
|
108
|
+
for (const chunk of content) {
|
|
109
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
110
|
+
}
|
|
111
|
+
const mapOnBob = await loadCoValueOrFail(bobSession, map.id);
|
|
112
|
+
// The map should not be completely downloaded because the parent of its owner is still streaming
|
|
113
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
114
|
+
// Feed the last chunk
|
|
115
|
+
bobSession.syncManager.handleNewContent(lastChunk, "import");
|
|
116
|
+
// Wait for the notification to be scheduled and executed
|
|
117
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
118
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
test("should return false when the grandparent of the owner is streaming", async () => {
|
|
121
|
+
const grandParentGroup = admin.node.createGroup();
|
|
122
|
+
const parentGroup = admin.node.createGroup();
|
|
123
|
+
const childGroup = admin.node.createGroup();
|
|
124
|
+
parentGroup.extend(grandParentGroup);
|
|
125
|
+
childGroup.extend(parentGroup);
|
|
126
|
+
const map = childGroup.createMap();
|
|
127
|
+
map.set("test", "value");
|
|
128
|
+
await map.core.waitForSync();
|
|
129
|
+
// Disconnect the admin node to sync the content manually
|
|
130
|
+
admin.disconnect();
|
|
131
|
+
// Make the grandparent group large enough to require multiple messages to be synced
|
|
132
|
+
for (let i = 0; i < 100; i++) {
|
|
133
|
+
// @ts-expect-error - test property is not part of the group shape
|
|
134
|
+
grandParentGroup.set(`key${i}`, "1".repeat(1024));
|
|
135
|
+
}
|
|
136
|
+
const content = grandParentGroup.core.verified.newContentSince(undefined);
|
|
137
|
+
assert(content);
|
|
138
|
+
expect(content.length).toBeGreaterThan(1);
|
|
139
|
+
const lastChunk = content.pop();
|
|
140
|
+
assert(lastChunk);
|
|
141
|
+
// Create a new session for bob
|
|
142
|
+
const bobSession = bob.node;
|
|
143
|
+
// Feed all chunks except the last one
|
|
144
|
+
for (const chunk of content) {
|
|
145
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
146
|
+
}
|
|
147
|
+
const mapOnBob = await loadCoValueOrFail(bobSession, map.id);
|
|
148
|
+
// The map should not be completely downloaded because the grandparent of its owner is still streaming
|
|
149
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
150
|
+
// Feed the last chunk
|
|
151
|
+
bobSession.syncManager.handleNewContent(lastChunk, "import");
|
|
152
|
+
// Wait for the notification to be scheduled and executed
|
|
153
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
154
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
test("should return false when multiple parents of the owner are streaming", async () => {
|
|
157
|
+
const parentGroup1 = admin.node.createGroup();
|
|
158
|
+
const parentGroup2 = admin.node.createGroup();
|
|
159
|
+
const childGroup = admin.node.createGroup();
|
|
160
|
+
childGroup.extend(parentGroup1);
|
|
161
|
+
childGroup.extend(parentGroup2);
|
|
162
|
+
const map = childGroup.createMap();
|
|
163
|
+
map.set("test", "value");
|
|
164
|
+
await map.core.waitForSync();
|
|
165
|
+
// Disconnect the admin node to sync the content manually
|
|
166
|
+
admin.disconnect();
|
|
167
|
+
// Make both parent groups large enough to require multiple messages to be synced
|
|
168
|
+
for (let i = 0; i < 100; i++) {
|
|
169
|
+
// @ts-expect-error - test property is not part of the group shape
|
|
170
|
+
parentGroup1.set(`key1_${i}`, "1".repeat(1024));
|
|
171
|
+
// @ts-expect-error - test property is not part of the group shape
|
|
172
|
+
parentGroup2.set(`key2_${i}`, "1".repeat(1024));
|
|
173
|
+
}
|
|
174
|
+
const content1 = parentGroup1.core.verified.newContentSince(undefined);
|
|
175
|
+
const content2 = parentGroup2.core.verified.newContentSince(undefined);
|
|
176
|
+
assert(content1);
|
|
177
|
+
assert(content2);
|
|
178
|
+
expect(content1.length).toBeGreaterThan(1);
|
|
179
|
+
expect(content2.length).toBeGreaterThan(1);
|
|
180
|
+
const lastChunk1 = content1.pop();
|
|
181
|
+
const lastChunk2 = content2.pop();
|
|
182
|
+
assert(lastChunk1);
|
|
183
|
+
assert(lastChunk2);
|
|
184
|
+
// Create a new session for bob
|
|
185
|
+
const bobSession = bob.node;
|
|
186
|
+
// Feed all chunks except the last ones from both parents
|
|
187
|
+
for (const chunk of content1) {
|
|
188
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
189
|
+
}
|
|
190
|
+
for (const chunk of content2) {
|
|
191
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
192
|
+
}
|
|
193
|
+
const mapOnBob = await loadCoValueOrFail(bobSession, map.id);
|
|
194
|
+
// The map should not be completely downloaded because both parents are still streaming
|
|
195
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
196
|
+
// Feed the last chunk from first parent
|
|
197
|
+
bobSession.syncManager.handleNewContent(lastChunk1, "import");
|
|
198
|
+
// Still should not be completely downloaded because second parent is still streaming
|
|
199
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
200
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
201
|
+
// Feed the last chunk from second parent
|
|
202
|
+
bobSession.syncManager.handleNewContent(lastChunk2, "import");
|
|
203
|
+
// Now it should be completely downloaded
|
|
204
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
205
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
206
|
+
});
|
|
207
|
+
test("should return false when the source branch of the value is streaming", async () => {
|
|
208
|
+
const group = admin.node.createGroup();
|
|
209
|
+
const originalMap = group.createMap();
|
|
210
|
+
// Add initial data to original map
|
|
211
|
+
originalMap.set("key1", "value1");
|
|
212
|
+
await originalMap.core.waitForSync();
|
|
213
|
+
// Disconnect the admin node to sync the content manually
|
|
214
|
+
admin.disconnect();
|
|
215
|
+
// Make the original map large enough to require multiple messages to be synced
|
|
216
|
+
for (let i = 0; i < 100; i++) {
|
|
217
|
+
originalMap.set(`key${i}`, "1".repeat(1024));
|
|
218
|
+
}
|
|
219
|
+
// Create a branch from the original map
|
|
220
|
+
const branch = expectMap(originalMap.core
|
|
221
|
+
.createBranch("feature-branch", group.id)
|
|
222
|
+
.getCurrentContent());
|
|
223
|
+
branch.set("branchKey", "branchValue");
|
|
224
|
+
const originalMapContent = originalMap.core.verified.newContentSince(undefined);
|
|
225
|
+
const branchContent = branch.core.verified.newContentSince(undefined);
|
|
226
|
+
assert(originalMapContent);
|
|
227
|
+
assert(branchContent);
|
|
228
|
+
expect(originalMapContent.length).toBeGreaterThan(1);
|
|
229
|
+
const lastOriginalMapChunk = originalMapContent.pop();
|
|
230
|
+
assert(lastOriginalMapChunk);
|
|
231
|
+
// Create a new session for bob
|
|
232
|
+
const bobSession = bob.node;
|
|
233
|
+
// Feed all chunks except the last one from the original map
|
|
234
|
+
for (const chunk of originalMapContent) {
|
|
235
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
236
|
+
}
|
|
237
|
+
// Feed all branch content
|
|
238
|
+
for (const chunk of branchContent) {
|
|
239
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
240
|
+
}
|
|
241
|
+
const branchOnBob = await loadCoValueOrFail(bobSession, branch.id);
|
|
242
|
+
// The branch should not be completely downloaded because its source (original map) is still streaming
|
|
243
|
+
expect(branchOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
244
|
+
// Feed the last chunk from the original map
|
|
245
|
+
bobSession.syncManager.handleNewContent(lastOriginalMapChunk, "import");
|
|
246
|
+
// Now the branch should be completely downloaded
|
|
247
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
248
|
+
expect(branchOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
249
|
+
});
|
|
250
|
+
test("should return true when the value and all its dependencies are fully downloaded", async () => {
|
|
251
|
+
const parentGroup = admin.node.createGroup();
|
|
252
|
+
const childGroup = admin.node.createGroup();
|
|
253
|
+
childGroup.extend(parentGroup);
|
|
254
|
+
const map = childGroup.createMap();
|
|
255
|
+
map.set("test", "value");
|
|
256
|
+
await map.core.waitForSync();
|
|
257
|
+
const mapOnBob = await loadCoValueOrFail(bob.node, map.id);
|
|
258
|
+
// Everything should be fully synced
|
|
259
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
260
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
261
|
+
});
|
|
262
|
+
test("should handle complex nested hierarchy with streaming at different levels", async () => {
|
|
263
|
+
const grandParentGroup = admin.node.createGroup();
|
|
264
|
+
const parentGroup = admin.node.createGroup();
|
|
265
|
+
const childGroup = admin.node.createGroup();
|
|
266
|
+
parentGroup.extend(grandParentGroup);
|
|
267
|
+
childGroup.extend(parentGroup);
|
|
268
|
+
const map = childGroup.createMap();
|
|
269
|
+
map.set("test", "value");
|
|
270
|
+
await map.core.waitForSync();
|
|
271
|
+
// Disconnect the admin node
|
|
272
|
+
admin.disconnect();
|
|
273
|
+
// Make both the map and grandparent group large enough to require multiple chunks
|
|
274
|
+
for (let i = 0; i < 100; i++) {
|
|
275
|
+
map.set(`mapKey${i}`, "1".repeat(1024));
|
|
276
|
+
// @ts-expect-error - test property is not part of the group shape
|
|
277
|
+
grandParentGroup.set(`groupKey${i}`, "1".repeat(1024));
|
|
278
|
+
}
|
|
279
|
+
const mapContent = map.core.verified.newContentSince(undefined);
|
|
280
|
+
const groupContent = grandParentGroup.core.verified.newContentSince(undefined);
|
|
281
|
+
assert(mapContent);
|
|
282
|
+
assert(groupContent);
|
|
283
|
+
expect(mapContent.length).toBeGreaterThan(1);
|
|
284
|
+
expect(groupContent.length).toBeGreaterThan(1);
|
|
285
|
+
const lastMapChunk = mapContent.pop();
|
|
286
|
+
const lastGroupChunk = groupContent.pop();
|
|
287
|
+
assert(lastMapChunk);
|
|
288
|
+
assert(lastGroupChunk);
|
|
289
|
+
// Create a new session for bob
|
|
290
|
+
const bobSession = bob.node;
|
|
291
|
+
// Feed all chunks except the last ones
|
|
292
|
+
for (const chunk of mapContent) {
|
|
293
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
294
|
+
}
|
|
295
|
+
for (const chunk of groupContent) {
|
|
296
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
297
|
+
}
|
|
298
|
+
const mapOnBob = await loadCoValueOrFail(bobSession, map.id);
|
|
299
|
+
// Should not be completely downloaded
|
|
300
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
301
|
+
// Feed the last map chunk
|
|
302
|
+
bobSession.syncManager.handleNewContent(lastMapChunk, "import");
|
|
303
|
+
// Still should not be completely downloaded because grandparent is still streaming
|
|
304
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
305
|
+
// Feed the last group chunk
|
|
306
|
+
bobSession.syncManager.handleNewContent(lastGroupChunk, "import");
|
|
307
|
+
// Now it should be completely downloaded
|
|
308
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
309
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
310
|
+
});
|
|
311
|
+
test("should emit an update when isCompletelyDownloaded status changes due to parent group streaming", async () => {
|
|
312
|
+
const group = admin.node.createGroup();
|
|
313
|
+
const map = group.createMap();
|
|
314
|
+
map.set("test", "value");
|
|
315
|
+
await map.core.waitForSync();
|
|
316
|
+
// Disconnect the admin node to sync the content manually
|
|
317
|
+
admin.disconnect();
|
|
318
|
+
// Make the group large enough to require multiple messages to be synced
|
|
319
|
+
for (let i = 0; i < 100; i++) {
|
|
320
|
+
// @ts-expect-error - test property is not part of the group shape
|
|
321
|
+
group.set(`key${i}`, "1".repeat(1024));
|
|
322
|
+
}
|
|
323
|
+
const content = group.core.verified.newContentSince(undefined);
|
|
324
|
+
assert(content);
|
|
325
|
+
expect(content.length).toBeGreaterThan(1);
|
|
326
|
+
const lastChunk = content.pop();
|
|
327
|
+
assert(lastChunk);
|
|
328
|
+
// Create a new session for bob
|
|
329
|
+
const bobSession = bob.node;
|
|
330
|
+
// Feed all chunks except the last one
|
|
331
|
+
for (const chunk of content) {
|
|
332
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
333
|
+
}
|
|
334
|
+
const mapOnBob = await loadCoValueOrFail(bobSession, map.id);
|
|
335
|
+
// Verify the map is not completely downloaded yet because its owner (group) is still streaming
|
|
336
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
337
|
+
// Set up a listener to track updates
|
|
338
|
+
const updates = [];
|
|
339
|
+
mapOnBob.core.subscribe((content) => {
|
|
340
|
+
updates.push(content.isCompletelyDownloaded());
|
|
341
|
+
});
|
|
342
|
+
// Feed the last chunk
|
|
343
|
+
bobSession.syncManager.handleNewContent(lastChunk, "import");
|
|
344
|
+
// Wait for the notification to be scheduled and executed
|
|
345
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
346
|
+
// Verify the CoValue is now completely downloaded
|
|
347
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
348
|
+
// Verify the listener was called with the new status
|
|
349
|
+
expect(updates).toContain(true);
|
|
350
|
+
expect(updates[updates.length - 1]).toBe(true);
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
describe("streaming from the middle", () => {
|
|
354
|
+
test("should return false when the value itself is streaming", async () => {
|
|
355
|
+
const group = admin.node.createGroup();
|
|
356
|
+
const map = group.createMap();
|
|
357
|
+
map.set("initial", "value");
|
|
358
|
+
const initialKnownState = map.core.knownState();
|
|
359
|
+
await map.core.waitForSync();
|
|
360
|
+
// Disconnect the admin node to sync the content manually and simulate the delay of the last chunk
|
|
361
|
+
admin.disconnect();
|
|
362
|
+
// Create a new session for bob
|
|
363
|
+
const bobSession = bob.node;
|
|
364
|
+
const mapOnBob = await loadCoValueOrFail(bobSession, map.id);
|
|
365
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
366
|
+
// Make the map large enough to require multiple messages to be synced
|
|
367
|
+
for (let i = 0; i < 100; i++) {
|
|
368
|
+
map.set(`key${i}`, "1".repeat(1024));
|
|
369
|
+
}
|
|
370
|
+
const content = map.core.verified.newContentSince(initialKnownState);
|
|
371
|
+
assert(content);
|
|
372
|
+
expect(content.length).toBeGreaterThan(1);
|
|
373
|
+
const lastChunk = content.pop();
|
|
374
|
+
assert(lastChunk);
|
|
375
|
+
// Feed all chunks except the last one
|
|
376
|
+
for (const chunk of content) {
|
|
377
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
378
|
+
}
|
|
379
|
+
// The map should not be completely downloaded yet
|
|
380
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
381
|
+
// Feed the last chunk
|
|
382
|
+
bobSession.syncManager.handleNewContent(lastChunk, "import");
|
|
383
|
+
// Wait for the notification to be scheduled and executed
|
|
384
|
+
await waitFor(() => mapOnBob.core.isCompletelyDownloaded());
|
|
385
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
386
|
+
});
|
|
387
|
+
test.skip("should return false when the owner of the value is streaming", async () => {
|
|
388
|
+
const group = admin.node.createGroup();
|
|
389
|
+
const map = group.createMap();
|
|
390
|
+
map.set("test", "value");
|
|
391
|
+
await map.core.waitForSync();
|
|
392
|
+
// Disconnect the admin node to sync the content manually
|
|
393
|
+
admin.disconnect();
|
|
394
|
+
const bobSession = bob.node;
|
|
395
|
+
const mapOnBob = await loadCoValueOrFail(bobSession, map.id);
|
|
396
|
+
// The map should not be completely downloaded because its owner (group) is still streaming
|
|
397
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
398
|
+
// Make the group large enough to require multiple messages to be synced
|
|
399
|
+
for (let i = 0; i < 100; i++) {
|
|
400
|
+
// @ts-expect-error - test property is not part of the group shape
|
|
401
|
+
group.set(`key${i}`, "1".repeat(1024));
|
|
402
|
+
}
|
|
403
|
+
const content = group.core.verified.newContentSince(undefined);
|
|
404
|
+
assert(content);
|
|
405
|
+
expect(content.length).toBeGreaterThan(1);
|
|
406
|
+
const lastChunk = content.pop();
|
|
407
|
+
assert(lastChunk);
|
|
408
|
+
// Feed all chunks except the last one
|
|
409
|
+
for (const chunk of content) {
|
|
410
|
+
bobSession.syncManager.handleNewContent(chunk, "import");
|
|
411
|
+
}
|
|
412
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(false);
|
|
413
|
+
// Feed the last chunk
|
|
414
|
+
bobSession.syncManager.handleNewContent(lastChunk, "import");
|
|
415
|
+
// Wait for the notification to be scheduled and executed
|
|
416
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
417
|
+
expect(mapOnBob.core.isCompletelyDownloaded()).toBe(true);
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
//# sourceMappingURL=coValueCore.isCompletelyDownloaded.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coValueCore.isCompletelyDownloaded.test.js","sourceRoot":"","sources":["../../src/tests/coValueCore.isCompletelyDownloaded.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coValueCore.isStreaming.test.d.ts","sourceRoot":"","sources":["../../src/tests/coValueCore.isStreaming.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { assert, beforeEach, describe, expect, test } from "vitest";
|
|
2
|
+
import { SyncMessagesLog, TEST_NODE_CONFIG, loadCoValueOrFail, setupTestNode, waitFor, } from "./testUtils";
|
|
3
|
+
let jazzCloud;
|
|
4
|
+
beforeEach(async () => {
|
|
5
|
+
// We want to simulate a real world communication that happens asynchronously
|
|
6
|
+
TEST_NODE_CONFIG.withAsyncPeers = true;
|
|
7
|
+
SyncMessagesLog.clear();
|
|
8
|
+
jazzCloud = setupTestNode({ isSyncServer: true });
|
|
9
|
+
});
|
|
10
|
+
describe("isStreaming", () => {
|
|
11
|
+
test("loading a small value should not be streaming", async () => {
|
|
12
|
+
const client = setupTestNode({
|
|
13
|
+
connected: true,
|
|
14
|
+
});
|
|
15
|
+
const group = jazzCloud.node.createGroup();
|
|
16
|
+
group.addMember("everyone", "writer");
|
|
17
|
+
const map = group.createMap();
|
|
18
|
+
map.set("hello", "world", "trusting");
|
|
19
|
+
const newSession = client.spawnNewSession();
|
|
20
|
+
const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
|
|
21
|
+
// For a small value, isStreaming should always be false
|
|
22
|
+
expect(mapInNewSession.core.isStreaming()).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
test("loading a large value should be streaming until all chunks are sent", async () => {
|
|
25
|
+
const client = setupTestNode({
|
|
26
|
+
connected: true,
|
|
27
|
+
});
|
|
28
|
+
const group = client.node.createGroup();
|
|
29
|
+
await group.core.waitForSync();
|
|
30
|
+
client.disconnect();
|
|
31
|
+
const map = group.createMap();
|
|
32
|
+
// Generate a large amount of data that requires multiple chunks
|
|
33
|
+
const dataSize = 1 * 1024 * 100;
|
|
34
|
+
const chunkSize = 1024; // 1KB chunks
|
|
35
|
+
const chunks = dataSize / chunkSize;
|
|
36
|
+
const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
|
|
37
|
+
for (let i = 0; i < chunks; i++) {
|
|
38
|
+
const key = `key${i}`;
|
|
39
|
+
map.set(key, value, "trusting");
|
|
40
|
+
}
|
|
41
|
+
const newSession = client.spawnNewSession();
|
|
42
|
+
await loadCoValueOrFail(client.node, group.id);
|
|
43
|
+
const content = map.core.verified.newContentSince(undefined);
|
|
44
|
+
assert(content);
|
|
45
|
+
const lastChunk = content.pop();
|
|
46
|
+
assert(lastChunk);
|
|
47
|
+
for (const chunk of content) {
|
|
48
|
+
newSession.node.syncManager.handleNewContent(chunk, "import");
|
|
49
|
+
}
|
|
50
|
+
const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
|
|
51
|
+
expect(mapInNewSession.core.isStreaming()).toBe(true);
|
|
52
|
+
newSession.node.syncManager.handleNewContent(lastChunk, "import");
|
|
53
|
+
expect(mapInNewSession.core.isStreaming()).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
test("loading a large content update should be streaming until all chunks are sent", async () => {
|
|
56
|
+
const client = setupTestNode({
|
|
57
|
+
connected: true,
|
|
58
|
+
});
|
|
59
|
+
const group = client.node.createGroup();
|
|
60
|
+
const map = group.createMap();
|
|
61
|
+
map.set("hello", "world", "trusting");
|
|
62
|
+
const knownState = map.core.knownState();
|
|
63
|
+
await map.core.waitForSync();
|
|
64
|
+
client.disconnect();
|
|
65
|
+
// Generate a large amount of data that requires multiple chunks
|
|
66
|
+
const dataSize = 1 * 1024 * 100;
|
|
67
|
+
const chunkSize = 1024; // 1KB chunks
|
|
68
|
+
const chunks = dataSize / chunkSize;
|
|
69
|
+
const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
|
|
70
|
+
for (let i = 0; i < chunks; i++) {
|
|
71
|
+
const key = `key${i}`;
|
|
72
|
+
map.set(key, value, "trusting");
|
|
73
|
+
}
|
|
74
|
+
const newSession = client.spawnNewSession();
|
|
75
|
+
await loadCoValueOrFail(client.node, group.id);
|
|
76
|
+
const content = map.core.verified.newContentSince(knownState);
|
|
77
|
+
assert(content);
|
|
78
|
+
const lastChunk = content.pop();
|
|
79
|
+
assert(lastChunk);
|
|
80
|
+
const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
|
|
81
|
+
for (const chunk of content) {
|
|
82
|
+
newSession.node.syncManager.handleNewContent(chunk, "import");
|
|
83
|
+
}
|
|
84
|
+
expect(mapInNewSession.core.isStreaming()).toBe(true);
|
|
85
|
+
newSession.node.syncManager.handleNewContent(lastChunk, "import");
|
|
86
|
+
expect(mapInNewSession.core.isStreaming()).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
test("streaming a large value between two clients should be streaming until all chunks are sent", async () => {
|
|
89
|
+
const client = setupTestNode();
|
|
90
|
+
client.connectToSyncServer({
|
|
91
|
+
ourName: "initialClient",
|
|
92
|
+
});
|
|
93
|
+
const streamingClient = client.spawnNewSession();
|
|
94
|
+
streamingClient.connectToSyncServer({
|
|
95
|
+
ourName: "streamingClient",
|
|
96
|
+
});
|
|
97
|
+
const group = client.node.createGroup();
|
|
98
|
+
await group.core.waitForSync();
|
|
99
|
+
client.disconnect();
|
|
100
|
+
const map = group.createMap();
|
|
101
|
+
// Generate a large amount of data that requires multiple chunks
|
|
102
|
+
const dataSize = 1 * 1024 * 100;
|
|
103
|
+
const chunkSize = 1024; // 1KB chunks
|
|
104
|
+
const chunks = dataSize / chunkSize;
|
|
105
|
+
const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
|
|
106
|
+
for (let i = 0; i < chunks; i++) {
|
|
107
|
+
const key = `key${i}`;
|
|
108
|
+
map.set(key, value, "trusting");
|
|
109
|
+
}
|
|
110
|
+
const loadingClient = client.spawnNewSession();
|
|
111
|
+
loadingClient.connectToSyncServer({
|
|
112
|
+
ourName: "loadingClient",
|
|
113
|
+
});
|
|
114
|
+
await loadCoValueOrFail(loadingClient.node, group.id);
|
|
115
|
+
const content = map.core.verified.newContentSince(undefined);
|
|
116
|
+
assert(content);
|
|
117
|
+
const lastChunk = content.pop();
|
|
118
|
+
assert(lastChunk);
|
|
119
|
+
for (const chunk of content) {
|
|
120
|
+
streamingClient.node.syncManager.handleNewContent(chunk, "import");
|
|
121
|
+
}
|
|
122
|
+
await streamingClient.node.syncManager.waitForAllCoValuesSync();
|
|
123
|
+
const mapInLoadingClient = await loadCoValueOrFail(loadingClient.node, map.id);
|
|
124
|
+
expect(mapInLoadingClient.core.isStreaming()).toBe(true);
|
|
125
|
+
streamingClient.node.syncManager.handleNewContent(lastChunk, "import");
|
|
126
|
+
await waitFor(() => {
|
|
127
|
+
expect(mapInLoadingClient.core.knownState()).toEqual(map.core.knownState());
|
|
128
|
+
});
|
|
129
|
+
expect(mapInLoadingClient.core.isStreaming()).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
test("should be false when getting streaming content that is already in the known state", async () => {
|
|
132
|
+
const client = setupTestNode({
|
|
133
|
+
connected: true,
|
|
134
|
+
});
|
|
135
|
+
const group = client.node.createGroup();
|
|
136
|
+
await group.core.waitForSync();
|
|
137
|
+
const map = group.createMap();
|
|
138
|
+
// Generate a large amount of data that requires multiple chunks
|
|
139
|
+
const dataSize = 1 * 1024 * 100;
|
|
140
|
+
const chunkSize = 1024; // 1KB chunks
|
|
141
|
+
const chunks = dataSize / chunkSize;
|
|
142
|
+
const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
|
|
143
|
+
for (let i = 0; i < chunks; i++) {
|
|
144
|
+
const key = `key${i}`;
|
|
145
|
+
map.set(key, value, "trusting");
|
|
146
|
+
}
|
|
147
|
+
await map.core.waitForSync();
|
|
148
|
+
const newSession = client.spawnNewSession();
|
|
149
|
+
await loadCoValueOrFail(newSession.node, map.id);
|
|
150
|
+
const content = map.core.verified.newContentSince(undefined);
|
|
151
|
+
assert(content);
|
|
152
|
+
const lastChunk = content.pop();
|
|
153
|
+
assert(lastChunk);
|
|
154
|
+
for (const chunk of content) {
|
|
155
|
+
newSession.node.syncManager.handleNewContent(chunk, "import");
|
|
156
|
+
}
|
|
157
|
+
const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
|
|
158
|
+
expect(mapInNewSession.core.isStreaming()).toBe(false);
|
|
159
|
+
});
|
|
160
|
+
test("should be false when getting streaming content that's not really streaming", async () => {
|
|
161
|
+
const client = setupTestNode({
|
|
162
|
+
connected: true,
|
|
163
|
+
});
|
|
164
|
+
const group = client.node.createGroup();
|
|
165
|
+
await group.core.waitForSync();
|
|
166
|
+
client.disconnect();
|
|
167
|
+
const map = group.createMap();
|
|
168
|
+
map.set("hello", "world", "trusting");
|
|
169
|
+
const newSession = client.spawnNewSession();
|
|
170
|
+
await loadCoValueOrFail(client.node, group.id);
|
|
171
|
+
const content = map.core.verified.newContentSince(undefined);
|
|
172
|
+
assert(content);
|
|
173
|
+
content[0].expectContentUntil = map.core.knownState().sessions;
|
|
174
|
+
for (const chunk of content) {
|
|
175
|
+
newSession.node.syncManager.handleNewContent(chunk, "import");
|
|
176
|
+
}
|
|
177
|
+
const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
|
|
178
|
+
expect(mapInNewSession.core.isStreaming()).toBe(false);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=coValueCore.isStreaming.test.js.map
|