cojson 0.19.21 → 0.20.0
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 +67 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.d.ts +42 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.js +261 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.js.map +1 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.d.ts +18 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.js +37 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.js.map +1 -0
- package/dist/CojsonMessageChannel/index.d.ts +3 -0
- package/dist/CojsonMessageChannel/index.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/index.js +2 -0
- package/dist/CojsonMessageChannel/index.js.map +1 -0
- package/dist/CojsonMessageChannel/types.d.ts +149 -0
- package/dist/CojsonMessageChannel/types.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/types.js +36 -0
- package/dist/CojsonMessageChannel/types.js.map +1 -0
- package/dist/GarbageCollector.d.ts +4 -2
- package/dist/GarbageCollector.d.ts.map +1 -1
- package/dist/GarbageCollector.js +5 -3
- package/dist/GarbageCollector.js.map +1 -1
- package/dist/SyncStateManager.d.ts +3 -3
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +4 -4
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValueContentMessage.d.ts +0 -2
- package/dist/coValueContentMessage.d.ts.map +1 -1
- package/dist/coValueContentMessage.js +0 -8
- package/dist/coValueContentMessage.js.map +1 -1
- package/dist/coValueCore/SessionMap.d.ts +4 -2
- package/dist/coValueCore/SessionMap.d.ts.map +1 -1
- package/dist/coValueCore/SessionMap.js +30 -0
- package/dist/coValueCore/SessionMap.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +86 -4
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +318 -17
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +6 -1
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +9 -0
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/coValues/coList.d.ts +3 -2
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +3 -6
- package/dist/coValues/group.js.map +1 -1
- package/dist/config.d.ts +0 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -8
- package/dist/config.js.map +1 -1
- package/dist/crypto/NapiCrypto.d.ts +1 -2
- package/dist/crypto/NapiCrypto.d.ts.map +1 -1
- package/dist/crypto/NapiCrypto.js +19 -4
- package/dist/crypto/NapiCrypto.js.map +1 -1
- package/dist/crypto/RNCrypto.d.ts.map +1 -1
- package/dist/crypto/RNCrypto.js +19 -4
- package/dist/crypto/RNCrypto.js.map +1 -1
- package/dist/crypto/WasmCrypto.d.ts +11 -4
- package/dist/crypto/WasmCrypto.d.ts.map +1 -1
- package/dist/crypto/WasmCrypto.js +52 -10
- package/dist/crypto/WasmCrypto.js.map +1 -1
- package/dist/crypto/WasmCryptoEdge.d.ts +1 -0
- package/dist/crypto/WasmCryptoEdge.d.ts.map +1 -1
- package/dist/crypto/WasmCryptoEdge.js +4 -1
- package/dist/crypto/WasmCryptoEdge.js.map +1 -1
- package/dist/crypto/crypto.d.ts +3 -3
- package/dist/crypto/crypto.d.ts.map +1 -1
- package/dist/crypto/crypto.js +6 -1
- package/dist/crypto/crypto.js.map +1 -1
- package/dist/exports.d.ts +3 -2
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +3 -1
- package/dist/exports.js.map +1 -1
- package/dist/ids.d.ts +4 -1
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +4 -0
- package/dist/ids.js.map +1 -1
- package/dist/knownState.d.ts +2 -0
- package/dist/knownState.d.ts.map +1 -1
- package/dist/localNode.d.ts +13 -3
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +17 -2
- package/dist/localNode.js.map +1 -1
- package/dist/platformUtils.d.ts +3 -0
- package/dist/platformUtils.d.ts.map +1 -0
- package/dist/platformUtils.js +24 -0
- package/dist/platformUtils.js.map +1 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.d.ts +30 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.d.ts.map +1 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.js +84 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.js.map +1 -0
- package/dist/storage/sqlite/client.d.ts +3 -0
- package/dist/storage/sqlite/client.d.ts.map +1 -1
- package/dist/storage/sqlite/client.js +44 -0
- package/dist/storage/sqlite/client.js.map +1 -1
- package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -1
- package/dist/storage/sqlite/sqliteMigrations.js +7 -0
- package/dist/storage/sqlite/sqliteMigrations.js.map +1 -1
- package/dist/storage/sqliteAsync/client.d.ts +3 -0
- package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
- package/dist/storage/sqliteAsync/client.js +42 -0
- package/dist/storage/sqliteAsync/client.js.map +1 -1
- package/dist/storage/storageAsync.d.ts +15 -3
- package/dist/storage/storageAsync.d.ts.map +1 -1
- package/dist/storage/storageAsync.js +60 -3
- package/dist/storage/storageAsync.js.map +1 -1
- package/dist/storage/storageSync.d.ts +14 -3
- package/dist/storage/storageSync.d.ts.map +1 -1
- package/dist/storage/storageSync.js +54 -3
- package/dist/storage/storageSync.js.map +1 -1
- package/dist/storage/types.d.ts +64 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +12 -1
- package/dist/storage/types.js.map +1 -1
- package/dist/sync.d.ts +6 -0
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +69 -15
- package/dist/sync.js.map +1 -1
- package/dist/tests/CojsonMessageChannel.test.d.ts +2 -0
- package/dist/tests/CojsonMessageChannel.test.d.ts.map +1 -0
- package/dist/tests/CojsonMessageChannel.test.js +236 -0
- package/dist/tests/CojsonMessageChannel.test.js.map +1 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts +2 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts.map +1 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.js +149 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.js.map +1 -0
- package/dist/tests/GarbageCollector.test.js +91 -18
- package/dist/tests/GarbageCollector.test.js.map +1 -1
- package/dist/tests/StorageApiAsync.test.js +510 -146
- package/dist/tests/StorageApiAsync.test.js.map +1 -1
- package/dist/tests/StorageApiSync.test.js +531 -130
- package/dist/tests/StorageApiSync.test.js.map +1 -1
- package/dist/tests/SyncManager.processQueues.test.js +1 -1
- package/dist/tests/SyncManager.processQueues.test.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +1 -1
- package/dist/tests/SyncStateManager.test.js.map +1 -1
- package/dist/tests/WasmCrypto.test.js +6 -3
- package/dist/tests/WasmCrypto.test.js.map +1 -1
- package/dist/tests/coPlainText.test.js +1 -1
- package/dist/tests/coPlainText.test.js.map +1 -1
- package/dist/tests/coValueCore.loadFromStorage.test.js +4 -0
- package/dist/tests/coValueCore.loadFromStorage.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +34 -13
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/coreWasm.test.js +127 -4
- package/dist/tests/coreWasm.test.js.map +1 -1
- package/dist/tests/crypto.test.js +89 -93
- package/dist/tests/crypto.test.js.map +1 -1
- package/dist/tests/deleteCoValue.test.d.ts +2 -0
- package/dist/tests/deleteCoValue.test.d.ts.map +1 -0
- package/dist/tests/deleteCoValue.test.js +313 -0
- package/dist/tests/deleteCoValue.test.js.map +1 -0
- package/dist/tests/group.removeMember.test.js +18 -30
- package/dist/tests/group.removeMember.test.js.map +1 -1
- package/dist/tests/knownState.lazyLoading.test.js +4 -0
- package/dist/tests/knownState.lazyLoading.test.js.map +1 -1
- package/dist/tests/sync.deleted.test.d.ts +2 -0
- package/dist/tests/sync.deleted.test.d.ts.map +1 -0
- package/dist/tests/sync.deleted.test.js +214 -0
- package/dist/tests/sync.deleted.test.js.map +1 -0
- package/dist/tests/sync.garbageCollection.test.js +56 -32
- package/dist/tests/sync.garbageCollection.test.js.map +1 -1
- package/dist/tests/sync.load.test.js +3 -5
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +4 -3
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.peerReconciliation.test.js +3 -3
- package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +12 -11
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.storageAsync.test.js +7 -7
- package/dist/tests/sync.storageAsync.test.js.map +1 -1
- package/dist/tests/sync.test.js +3 -2
- package/dist/tests/sync.test.js.map +1 -1
- package/dist/tests/sync.tracking.test.js +35 -4
- package/dist/tests/sync.tracking.test.js.map +1 -1
- package/dist/tests/testStorage.d.ts +3 -0
- package/dist/tests/testStorage.d.ts.map +1 -1
- package/dist/tests/testStorage.js +16 -2
- package/dist/tests/testStorage.js.map +1 -1
- package/dist/tests/testUtils.d.ts +29 -4
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +84 -9
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +6 -16
- package/src/CojsonMessageChannel/CojsonMessageChannel.ts +332 -0
- package/src/CojsonMessageChannel/MessagePortOutgoingChannel.ts +52 -0
- package/src/CojsonMessageChannel/index.ts +9 -0
- package/src/CojsonMessageChannel/types.ts +200 -0
- package/src/GarbageCollector.ts +5 -5
- package/src/SyncStateManager.ts +6 -6
- package/src/coValueContentMessage.ts +0 -14
- package/src/coValueCore/SessionMap.ts +43 -1
- package/src/coValueCore/coValueCore.ts +430 -15
- package/src/coValueCore/verifiedState.ts +26 -3
- package/src/coValues/coList.ts +5 -3
- package/src/coValues/group.ts +5 -6
- package/src/config.ts +0 -9
- package/src/crypto/NapiCrypto.ts +29 -13
- package/src/crypto/RNCrypto.ts +29 -11
- package/src/crypto/WasmCrypto.ts +67 -20
- package/src/crypto/WasmCryptoEdge.ts +5 -1
- package/src/crypto/crypto.ts +16 -4
- package/src/exports.ts +3 -0
- package/src/ids.ts +11 -1
- package/src/localNode.ts +18 -5
- package/src/platformUtils.ts +26 -0
- package/src/storage/DeletedCoValuesEraserScheduler.ts +124 -0
- package/src/storage/sqlite/client.ts +77 -0
- package/src/storage/sqlite/sqliteMigrations.ts +7 -0
- package/src/storage/sqliteAsync/client.ts +75 -0
- package/src/storage/storageAsync.ts +77 -4
- package/src/storage/storageSync.ts +73 -4
- package/src/storage/types.ts +75 -0
- package/src/sync.ts +84 -15
- package/src/tests/CojsonMessageChannel.test.ts +306 -0
- package/src/tests/DeletedCoValuesEraserScheduler.test.ts +185 -0
- package/src/tests/GarbageCollector.test.ts +119 -22
- package/src/tests/StorageApiAsync.test.ts +615 -156
- package/src/tests/StorageApiSync.test.ts +623 -137
- package/src/tests/SyncManager.processQueues.test.ts +1 -1
- package/src/tests/SyncStateManager.test.ts +1 -1
- package/src/tests/WasmCrypto.test.ts +8 -3
- package/src/tests/coPlainText.test.ts +1 -1
- package/src/tests/coValueCore.loadFromStorage.test.ts +8 -0
- package/src/tests/coValueCore.test.ts +49 -14
- package/src/tests/coreWasm.test.ts +319 -10
- package/src/tests/crypto.test.ts +141 -150
- package/src/tests/deleteCoValue.test.ts +528 -0
- package/src/tests/group.removeMember.test.ts +35 -35
- package/src/tests/knownState.lazyLoading.test.ts +8 -0
- package/src/tests/sync.deleted.test.ts +294 -0
- package/src/tests/sync.garbageCollection.test.ts +69 -36
- package/src/tests/sync.load.test.ts +3 -5
- package/src/tests/sync.mesh.test.ts +6 -3
- package/src/tests/sync.peerReconciliation.test.ts +3 -3
- package/src/tests/sync.storage.test.ts +14 -11
- package/src/tests/sync.storageAsync.test.ts +7 -7
- package/src/tests/sync.test.ts +5 -2
- package/src/tests/sync.tracking.test.ts +54 -4
- package/src/tests/testStorage.ts +30 -3
- package/src/tests/testUtils.ts +113 -15
- package/dist/crypto/PureJSCrypto.d.ts +0 -77
- package/dist/crypto/PureJSCrypto.d.ts.map +0 -1
- package/dist/crypto/PureJSCrypto.js +0 -236
- package/dist/crypto/PureJSCrypto.js.map +0 -1
- package/dist/tests/PureJSCrypto.test.d.ts +0 -2
- package/dist/tests/PureJSCrypto.test.d.ts.map +0 -1
- package/dist/tests/PureJSCrypto.test.js +0 -145
- package/dist/tests/PureJSCrypto.test.js.map +0 -1
- package/src/crypto/PureJSCrypto.ts +0 -429
- package/src/tests/PureJSCrypto.test.ts +0 -217
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { describe, expect, onTestFinished, test, vi } from "vitest";
|
|
6
|
-
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
7
|
-
import { LocalNode, logger } from "../exports.js";
|
|
1
|
+
import { describe, expect, test, vi, afterEach } from "vitest";
|
|
2
|
+
import { logger } from "../exports.js";
|
|
8
3
|
import { emptyKnownState } from "../knownState.js";
|
|
9
|
-
import { createSyncStorage } from "./testStorage.js";
|
|
10
|
-
import { loadCoValueOrFail,
|
|
11
|
-
const crypto = await WasmCrypto.create();
|
|
4
|
+
import { createSyncStorage, getAllCoValuesWaitingForDelete, getCoValueStoredSessions, getDbPath, } from "./testStorage.js";
|
|
5
|
+
import { fillCoMapWithLargeData, loadCoValueOrFail, setupTestNode, } from "./testUtils.js";
|
|
12
6
|
/**
|
|
13
7
|
* Helper function that gets new content since a known state, throwing if:
|
|
14
8
|
* - The coValue is not verified
|
|
@@ -24,53 +18,33 @@ function getNewContentSince(coValue, knownState) {
|
|
|
24
18
|
}
|
|
25
19
|
return contentMessage;
|
|
26
20
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// Create a unique database file for each test
|
|
31
|
-
const dbPath = customDbPath ?? join(tmpdir(), `test-${randomUUID()}.db`);
|
|
32
|
-
const storage = createSyncStorage({
|
|
33
|
-
filename: dbPath,
|
|
34
|
-
nodeName: "test",
|
|
35
|
-
storageName: "test-storage",
|
|
36
|
-
});
|
|
37
|
-
onTestFinished(() => {
|
|
38
|
-
try {
|
|
39
|
-
unlinkSync(dbPath);
|
|
40
|
-
}
|
|
41
|
-
catch { }
|
|
42
|
-
});
|
|
43
|
-
node.setStorage(storage);
|
|
44
|
-
return {
|
|
45
|
-
fixturesNode: node,
|
|
46
|
-
dbPath,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
async function createTestNode(dbPath) {
|
|
50
|
-
const [admin, session] = randomAgentAndSessionID();
|
|
51
|
-
const node = new LocalNode(admin.agentSecret, session, crypto);
|
|
52
|
-
const storage = createSyncStorage({
|
|
53
|
-
filename: dbPath,
|
|
54
|
-
nodeName: "test",
|
|
55
|
-
storageName: "test-storage",
|
|
56
|
-
});
|
|
57
|
-
return {
|
|
58
|
-
node,
|
|
59
|
-
storage,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
vi.useRealTimers();
|
|
23
|
+
});
|
|
62
24
|
describe("StorageApiSync", () => {
|
|
63
25
|
describe("getKnownState", () => {
|
|
64
26
|
test("should return empty known state for new coValue ID and cache the result", async () => {
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
27
|
+
const fixtures = setupTestNode();
|
|
28
|
+
const client = setupTestNode();
|
|
29
|
+
const { storage } = client.addStorage({
|
|
30
|
+
storage: createSyncStorage({
|
|
31
|
+
nodeName: "test",
|
|
32
|
+
storageName: "test-storage",
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
const id = fixtures.node.createGroup().id;
|
|
68
36
|
const knownState = storage.getKnownState(id);
|
|
69
37
|
expect(knownState).toEqual(emptyKnownState(id));
|
|
70
38
|
expect(storage.getKnownState(id)).toBe(knownState); // Should return same instance
|
|
71
39
|
});
|
|
72
40
|
test("should return separate known state instances for different coValue IDs", async () => {
|
|
73
|
-
const
|
|
41
|
+
const client = setupTestNode();
|
|
42
|
+
const { storage } = client.addStorage({
|
|
43
|
+
storage: createSyncStorage({
|
|
44
|
+
nodeName: "test",
|
|
45
|
+
storageName: "test-storage",
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
74
48
|
const id1 = "test-id-1";
|
|
75
49
|
const id2 = "test-id-2";
|
|
76
50
|
const knownState1 = storage.getKnownState(id1);
|
|
@@ -80,7 +54,13 @@ describe("StorageApiSync", () => {
|
|
|
80
54
|
});
|
|
81
55
|
describe("load", () => {
|
|
82
56
|
test("should fail gracefully when loading non-existent coValue and preserve known state", async () => {
|
|
83
|
-
const
|
|
57
|
+
const client = setupTestNode();
|
|
58
|
+
const { storage } = client.addStorage({
|
|
59
|
+
storage: createSyncStorage({
|
|
60
|
+
nodeName: "test",
|
|
61
|
+
storageName: "test-storage",
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
84
64
|
const id = "non-existent-id";
|
|
85
65
|
const callback = vi.fn();
|
|
86
66
|
const done = vi.fn();
|
|
@@ -95,12 +75,27 @@ describe("StorageApiSync", () => {
|
|
|
95
75
|
expect(afterLoadKnownState).toEqual(initialKnownState);
|
|
96
76
|
});
|
|
97
77
|
test("should successfully load coValue with header and update known state", async () => {
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
78
|
+
const dbPath = getDbPath();
|
|
79
|
+
const fixtures = setupTestNode();
|
|
80
|
+
fixtures.addStorage({
|
|
81
|
+
storage: createSyncStorage({
|
|
82
|
+
filename: dbPath,
|
|
83
|
+
nodeName: "test",
|
|
84
|
+
storageName: "test-storage",
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
const client = setupTestNode();
|
|
88
|
+
const { storage } = client.addStorage({
|
|
89
|
+
storage: createSyncStorage({
|
|
90
|
+
filename: dbPath,
|
|
91
|
+
nodeName: "test",
|
|
92
|
+
storageName: "test-storage",
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
95
|
+
const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
|
|
101
96
|
const done = vi.fn();
|
|
102
97
|
// Create a real group and get its content message
|
|
103
|
-
const group =
|
|
98
|
+
const group = fixtures.node.createGroup();
|
|
104
99
|
await group.core.waitForSync();
|
|
105
100
|
// Get initial known state
|
|
106
101
|
const initialKnownState = storage.getKnownState(group.id);
|
|
@@ -115,16 +110,31 @@ describe("StorageApiSync", () => {
|
|
|
115
110
|
// Verify that storage known state is updated after load
|
|
116
111
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
117
112
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
118
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
113
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
119
114
|
expect(groupOnNode.core.verified.header).toEqual(group.core.verified.header);
|
|
120
115
|
});
|
|
121
116
|
test("should successfully load coValue with transactions and update known state", async () => {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
117
|
+
const dbPath = getDbPath();
|
|
118
|
+
const fixtures = setupTestNode();
|
|
119
|
+
fixtures.addStorage({
|
|
120
|
+
storage: createSyncStorage({
|
|
121
|
+
filename: dbPath,
|
|
122
|
+
nodeName: "test",
|
|
123
|
+
storageName: "test-storage",
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
const client = setupTestNode();
|
|
127
|
+
const { storage } = client.addStorage({
|
|
128
|
+
storage: createSyncStorage({
|
|
129
|
+
filename: dbPath,
|
|
130
|
+
nodeName: "test",
|
|
131
|
+
storageName: "test-storage",
|
|
132
|
+
}),
|
|
133
|
+
});
|
|
134
|
+
const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
|
|
125
135
|
const done = vi.fn();
|
|
126
136
|
// Create a real group and add a member to create transactions
|
|
127
|
-
const group =
|
|
137
|
+
const group = fixtures.node.createGroup();
|
|
128
138
|
group.addMember("everyone", "reader");
|
|
129
139
|
await group.core.waitForSync();
|
|
130
140
|
// Get initial known state
|
|
@@ -135,23 +145,35 @@ describe("StorageApiSync", () => {
|
|
|
135
145
|
id: group.id,
|
|
136
146
|
header: group.core.verified.header,
|
|
137
147
|
new: expect.objectContaining({
|
|
138
|
-
[
|
|
148
|
+
[fixtures.node.currentSessionID]: expect.any(Object),
|
|
139
149
|
}),
|
|
140
150
|
}));
|
|
141
151
|
expect(done).toHaveBeenCalledWith(true);
|
|
142
152
|
// Verify that storage known state is updated after load
|
|
143
153
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
144
154
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
145
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
155
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
146
156
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
147
157
|
});
|
|
148
158
|
});
|
|
149
159
|
describe("store", () => {
|
|
150
160
|
test("should successfully store new coValue with header and update known state", async () => {
|
|
151
|
-
const
|
|
152
|
-
|
|
161
|
+
const fixtures = setupTestNode();
|
|
162
|
+
fixtures.addStorage({
|
|
163
|
+
storage: createSyncStorage({
|
|
164
|
+
nodeName: "test",
|
|
165
|
+
storageName: "test-storage",
|
|
166
|
+
}),
|
|
167
|
+
});
|
|
168
|
+
const client = setupTestNode();
|
|
169
|
+
const { storage } = client.addStorage({
|
|
170
|
+
storage: createSyncStorage({
|
|
171
|
+
nodeName: "test",
|
|
172
|
+
storageName: "test-storage",
|
|
173
|
+
}),
|
|
174
|
+
});
|
|
153
175
|
// Create a real group and get its content message
|
|
154
|
-
const group =
|
|
176
|
+
const group = fixtures.node.createGroup();
|
|
155
177
|
const contentMessage = getNewContentSince(group.core, emptyKnownState(group.id));
|
|
156
178
|
const correctionCallback = vi.fn();
|
|
157
179
|
// Get initial known state
|
|
@@ -161,15 +183,27 @@ describe("StorageApiSync", () => {
|
|
|
161
183
|
// Verify that storage known state is updated after store
|
|
162
184
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
163
185
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
164
|
-
|
|
165
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
186
|
+
client.addStorage({ storage });
|
|
187
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
166
188
|
expect(groupOnNode.core.verified.header).toEqual(group.core.verified.header);
|
|
167
189
|
});
|
|
168
190
|
test("should successfully store coValue with transactions and update known state", async () => {
|
|
169
|
-
const
|
|
170
|
-
|
|
191
|
+
const fixtures = setupTestNode();
|
|
192
|
+
fixtures.addStorage({
|
|
193
|
+
storage: createSyncStorage({
|
|
194
|
+
nodeName: "test",
|
|
195
|
+
storageName: "test-storage",
|
|
196
|
+
}),
|
|
197
|
+
});
|
|
198
|
+
const client = setupTestNode();
|
|
199
|
+
const { storage } = client.addStorage({
|
|
200
|
+
storage: createSyncStorage({
|
|
201
|
+
nodeName: "test",
|
|
202
|
+
storageName: "test-storage",
|
|
203
|
+
}),
|
|
204
|
+
});
|
|
171
205
|
// Create a real group and add a member to create transactions
|
|
172
|
-
const group =
|
|
206
|
+
const group = fixtures.node.createGroup();
|
|
173
207
|
group.addMember("everyone", "reader");
|
|
174
208
|
const contentMessage = getNewContentSince(group.core, emptyKnownState(group.id));
|
|
175
209
|
const correctionCallback = vi.fn();
|
|
@@ -180,14 +214,26 @@ describe("StorageApiSync", () => {
|
|
|
180
214
|
// Verify that storage known state is updated after store
|
|
181
215
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
182
216
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
183
|
-
|
|
184
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
217
|
+
client.addStorage({ storage });
|
|
218
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
185
219
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
186
220
|
});
|
|
187
221
|
test("should handle correction when header assumption is invalid", async () => {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
222
|
+
const fixtures = setupTestNode();
|
|
223
|
+
fixtures.addStorage({
|
|
224
|
+
storage: createSyncStorage({
|
|
225
|
+
nodeName: "test",
|
|
226
|
+
storageName: "test-storage",
|
|
227
|
+
}),
|
|
228
|
+
});
|
|
229
|
+
const client = setupTestNode();
|
|
230
|
+
const { storage } = client.addStorage({
|
|
231
|
+
storage: createSyncStorage({
|
|
232
|
+
nodeName: "test",
|
|
233
|
+
storageName: "test-storage",
|
|
234
|
+
}),
|
|
235
|
+
});
|
|
236
|
+
const group = fixtures.node.createGroup();
|
|
191
237
|
const knownState = group.core.knownState();
|
|
192
238
|
group.addMember("everyone", "reader");
|
|
193
239
|
const contentMessage = getNewContentSince(group.core, knownState);
|
|
@@ -204,14 +250,26 @@ describe("StorageApiSync", () => {
|
|
|
204
250
|
// Verify that storage known state is updated after store with correction
|
|
205
251
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
206
252
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
207
|
-
|
|
208
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
253
|
+
client.addStorage({ storage });
|
|
254
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
209
255
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
210
256
|
});
|
|
211
257
|
test("should handle correction when new content assumption is invalid", async () => {
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
258
|
+
const fixtures = setupTestNode();
|
|
259
|
+
fixtures.addStorage({
|
|
260
|
+
storage: createSyncStorage({
|
|
261
|
+
nodeName: "test",
|
|
262
|
+
storageName: "test-storage",
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
const client = setupTestNode();
|
|
266
|
+
const { storage } = client.addStorage({
|
|
267
|
+
storage: createSyncStorage({
|
|
268
|
+
nodeName: "test",
|
|
269
|
+
storageName: "test-storage",
|
|
270
|
+
}),
|
|
271
|
+
});
|
|
272
|
+
const group = fixtures.node.createGroup();
|
|
215
273
|
const initialContent = getNewContentSince(group.core, emptyKnownState(group.id));
|
|
216
274
|
const initialKnownState = group.core.knownState();
|
|
217
275
|
group.addMember("everyone", "reader");
|
|
@@ -235,14 +293,26 @@ describe("StorageApiSync", () => {
|
|
|
235
293
|
// Verify that storage known state is updated after store with correction
|
|
236
294
|
const finalKnownState = storage.getKnownState(group.id);
|
|
237
295
|
expect(finalKnownState).toEqual(group.core.knownState());
|
|
238
|
-
|
|
239
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
296
|
+
client.addStorage({ storage });
|
|
297
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
240
298
|
expect(groupOnNode.get("everyone")).toEqual("writer");
|
|
241
299
|
});
|
|
242
300
|
test("should log error and fail when correction callback returns undefined", async () => {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
301
|
+
const fixtures = setupTestNode();
|
|
302
|
+
fixtures.addStorage({
|
|
303
|
+
storage: createSyncStorage({
|
|
304
|
+
nodeName: "test",
|
|
305
|
+
storageName: "test-storage",
|
|
306
|
+
}),
|
|
307
|
+
});
|
|
308
|
+
const client = setupTestNode();
|
|
309
|
+
const { storage } = client.addStorage({
|
|
310
|
+
storage: createSyncStorage({
|
|
311
|
+
nodeName: "test",
|
|
312
|
+
storageName: "test-storage",
|
|
313
|
+
}),
|
|
314
|
+
});
|
|
315
|
+
const group = fixtures.node.createGroup();
|
|
246
316
|
const knownState = group.core.knownState();
|
|
247
317
|
group.addMember("everyone", "writer");
|
|
248
318
|
const contentMessage = getNewContentSince(group.core, knownState);
|
|
@@ -265,9 +335,21 @@ describe("StorageApiSync", () => {
|
|
|
265
335
|
errorSpy.mockClear();
|
|
266
336
|
});
|
|
267
337
|
test("should log error and fail when correction callback returns invalid content message", async () => {
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
338
|
+
const fixtures = setupTestNode();
|
|
339
|
+
fixtures.addStorage({
|
|
340
|
+
storage: createSyncStorage({
|
|
341
|
+
nodeName: "test",
|
|
342
|
+
storageName: "test-storage",
|
|
343
|
+
}),
|
|
344
|
+
});
|
|
345
|
+
const client = setupTestNode();
|
|
346
|
+
const { storage } = client.addStorage({
|
|
347
|
+
storage: createSyncStorage({
|
|
348
|
+
nodeName: "test",
|
|
349
|
+
storageName: "test-storage",
|
|
350
|
+
}),
|
|
351
|
+
});
|
|
352
|
+
const group = fixtures.node.createGroup();
|
|
271
353
|
const knownState = group.core.knownState();
|
|
272
354
|
group.addMember("everyone", "writer");
|
|
273
355
|
const contentMessage = getNewContentSince(group.core, knownState);
|
|
@@ -288,14 +370,35 @@ describe("StorageApiSync", () => {
|
|
|
288
370
|
errorSpy.mockClear();
|
|
289
371
|
});
|
|
290
372
|
test("should successfully store coValue with multiple sessions", async () => {
|
|
291
|
-
const
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
373
|
+
const dbPath = getDbPath();
|
|
374
|
+
const fixtures = setupTestNode();
|
|
375
|
+
fixtures.addStorage({
|
|
376
|
+
storage: createSyncStorage({
|
|
377
|
+
filename: dbPath,
|
|
378
|
+
nodeName: "test",
|
|
379
|
+
storageName: "test-storage",
|
|
380
|
+
}),
|
|
381
|
+
});
|
|
382
|
+
const fixture2 = setupTestNode();
|
|
383
|
+
fixture2.addStorage({
|
|
384
|
+
storage: createSyncStorage({
|
|
385
|
+
filename: dbPath,
|
|
386
|
+
nodeName: "test",
|
|
387
|
+
storageName: "test-storage",
|
|
388
|
+
}),
|
|
389
|
+
});
|
|
390
|
+
const client = setupTestNode();
|
|
391
|
+
const { storage } = client.addStorage({
|
|
392
|
+
storage: createSyncStorage({
|
|
393
|
+
nodeName: "test",
|
|
394
|
+
storageName: "test-storage",
|
|
395
|
+
}),
|
|
396
|
+
});
|
|
397
|
+
const coValue = fixtures.node.createCoValue({
|
|
295
398
|
type: "comap",
|
|
296
399
|
ruleset: { type: "unsafeAllowAll" },
|
|
297
400
|
meta: null,
|
|
298
|
-
...crypto.createdNowUnique(),
|
|
401
|
+
...fixtures.node.crypto.createdNowUnique(),
|
|
299
402
|
});
|
|
300
403
|
coValue.makeTransaction([
|
|
301
404
|
{
|
|
@@ -303,7 +406,7 @@ describe("StorageApiSync", () => {
|
|
|
303
406
|
},
|
|
304
407
|
], "trusting");
|
|
305
408
|
await coValue.waitForSync();
|
|
306
|
-
const mapOnNode2 = await loadCoValueOrFail(
|
|
409
|
+
const mapOnNode2 = await loadCoValueOrFail(fixture2.node, coValue.id);
|
|
307
410
|
coValue.makeTransaction([
|
|
308
411
|
{
|
|
309
412
|
count: 2,
|
|
@@ -313,22 +416,164 @@ describe("StorageApiSync", () => {
|
|
|
313
416
|
const contentMessage = getNewContentSince(mapOnNode2.core, emptyKnownState(mapOnNode2.id));
|
|
314
417
|
const correctionCallback = vi.fn();
|
|
315
418
|
storage.store(contentMessage, correctionCallback);
|
|
316
|
-
|
|
317
|
-
const finalMap = await loadCoValueOrFail(node, mapOnNode2.id);
|
|
419
|
+
client.addStorage({ storage });
|
|
420
|
+
const finalMap = await loadCoValueOrFail(client.node, mapOnNode2.id);
|
|
318
421
|
expect(finalMap.core.knownState()).toEqual(knownState);
|
|
319
422
|
});
|
|
320
423
|
});
|
|
424
|
+
describe("delete flow", () => {
|
|
425
|
+
test("deleteCoValue enqueues the coValue for erasure", async () => {
|
|
426
|
+
const client = setupTestNode();
|
|
427
|
+
const { storage } = client.addStorage({
|
|
428
|
+
storage: createSyncStorage({
|
|
429
|
+
nodeName: "test",
|
|
430
|
+
storageName: "test-storage",
|
|
431
|
+
}),
|
|
432
|
+
});
|
|
433
|
+
const group = client.node.createGroup();
|
|
434
|
+
const map = group.createMap();
|
|
435
|
+
map.core.deleteCoValue();
|
|
436
|
+
await map.core.waitForSync();
|
|
437
|
+
const queued = await getAllCoValuesWaitingForDelete(storage);
|
|
438
|
+
expect(queued).toContain(map.id);
|
|
439
|
+
});
|
|
440
|
+
test("background erasure doesn't run if not enabled", async () => {
|
|
441
|
+
const dbPath = getDbPath();
|
|
442
|
+
const node = setupTestNode();
|
|
443
|
+
const { storage } = node.addStorage({
|
|
444
|
+
storage: createSyncStorage({
|
|
445
|
+
filename: dbPath,
|
|
446
|
+
nodeName: "test",
|
|
447
|
+
storageName: "test-storage",
|
|
448
|
+
}),
|
|
449
|
+
});
|
|
450
|
+
const group = node.node.createGroup();
|
|
451
|
+
const map = group.createMap();
|
|
452
|
+
map.set("k", "v");
|
|
453
|
+
await map.core.waitForSync();
|
|
454
|
+
vi.useFakeTimers();
|
|
455
|
+
map.core.deleteCoValue();
|
|
456
|
+
await map.core.waitForSync();
|
|
457
|
+
await vi.advanceTimersByTimeAsync(70000);
|
|
458
|
+
// Queue drained
|
|
459
|
+
expect(await getAllCoValuesWaitingForDelete(storage)).toContain(map.id);
|
|
460
|
+
});
|
|
461
|
+
test("background erasure run if enabled", async () => {
|
|
462
|
+
const dbPath = getDbPath();
|
|
463
|
+
const node = setupTestNode();
|
|
464
|
+
const { storage } = node.addStorage({
|
|
465
|
+
storage: createSyncStorage({
|
|
466
|
+
filename: dbPath,
|
|
467
|
+
nodeName: "test",
|
|
468
|
+
storageName: "test-storage",
|
|
469
|
+
}),
|
|
470
|
+
});
|
|
471
|
+
vi.useFakeTimers();
|
|
472
|
+
node.node.enableDeletedCoValuesErasure();
|
|
473
|
+
const group = node.node.createGroup();
|
|
474
|
+
const map = group.createMap();
|
|
475
|
+
map.set("k", "v");
|
|
476
|
+
await map.core.waitForSync();
|
|
477
|
+
map.core.deleteCoValue();
|
|
478
|
+
await map.core.waitForSync();
|
|
479
|
+
await vi.advanceTimersByTimeAsync(70000);
|
|
480
|
+
// Queue drained
|
|
481
|
+
expect(await getAllCoValuesWaitingForDelete(storage)).not.toContain(map.id);
|
|
482
|
+
const sessionIDs = await getCoValueStoredSessions(storage, map.id);
|
|
483
|
+
expect(sessionIDs).toHaveLength(1);
|
|
484
|
+
expect(sessionIDs[0]).toMatch(/_session_d[1-9A-HJ-NP-Za-km-z]+\$$/); // Delete session format
|
|
485
|
+
});
|
|
486
|
+
test("eraseAllDeletedCoValues deletes history but preserves tombstone", async () => {
|
|
487
|
+
const dbPath = getDbPath();
|
|
488
|
+
const node = setupTestNode();
|
|
489
|
+
const { storage } = node.addStorage({
|
|
490
|
+
storage: createSyncStorage({
|
|
491
|
+
filename: dbPath,
|
|
492
|
+
nodeName: "test",
|
|
493
|
+
storageName: "test-storage",
|
|
494
|
+
}),
|
|
495
|
+
});
|
|
496
|
+
const group = node.node.createGroup();
|
|
497
|
+
const map = group.createMap();
|
|
498
|
+
map.set("k", "v");
|
|
499
|
+
await map.core.waitForSync();
|
|
500
|
+
map.core.deleteCoValue();
|
|
501
|
+
await map.core.waitForSync();
|
|
502
|
+
expect(await getAllCoValuesWaitingForDelete(storage)).toContain(map.id);
|
|
503
|
+
await storage.eraseAllDeletedCoValues();
|
|
504
|
+
// Queue drained
|
|
505
|
+
expect(await getAllCoValuesWaitingForDelete(storage)).not.toContain(map.id);
|
|
506
|
+
// Tombstone preserved + history erased when loaded from storage
|
|
507
|
+
const client = setupTestNode();
|
|
508
|
+
const { storage: clientStorage } = client.addStorage({
|
|
509
|
+
storage: createSyncStorage({
|
|
510
|
+
filename: dbPath,
|
|
511
|
+
nodeName: "test",
|
|
512
|
+
storageName: "test-storage",
|
|
513
|
+
}),
|
|
514
|
+
});
|
|
515
|
+
const loaded = await loadCoValueOrFail(client.node, map.id);
|
|
516
|
+
expect(loaded.core.isDeleted).toBe(true);
|
|
517
|
+
expect(loaded.get("k")).toBeUndefined();
|
|
518
|
+
const sessionIDs = await getCoValueStoredSessions(clientStorage, map.id);
|
|
519
|
+
expect(sessionIDs).toHaveLength(1);
|
|
520
|
+
expect(sessionIDs[0]).toMatch(/_session_d[1-9A-HJ-NP-Za-km-z]+\$$/); // Delete session format
|
|
521
|
+
});
|
|
522
|
+
test("eraseAllDeletedCoValues does not break when called while a coValue is streaming from storage", async () => {
|
|
523
|
+
const dbPath = getDbPath();
|
|
524
|
+
const node = setupTestNode();
|
|
525
|
+
const storage = createSyncStorage({
|
|
526
|
+
filename: dbPath,
|
|
527
|
+
nodeName: "test",
|
|
528
|
+
storageName: "test-storage",
|
|
529
|
+
});
|
|
530
|
+
node.addStorage({ storage });
|
|
531
|
+
const group = node.node.createGroup();
|
|
532
|
+
const map = group.createMap();
|
|
533
|
+
fillCoMapWithLargeData(map);
|
|
534
|
+
await map.core.waitForSync();
|
|
535
|
+
map.core.deleteCoValue();
|
|
536
|
+
await map.core.waitForSync();
|
|
537
|
+
storage.close();
|
|
538
|
+
const newStorage = createSyncStorage({
|
|
539
|
+
filename: dbPath,
|
|
540
|
+
nodeName: "test",
|
|
541
|
+
storageName: "test-storage",
|
|
542
|
+
});
|
|
543
|
+
const callback = vi.fn();
|
|
544
|
+
const loadPromise = new Promise((resolve) => {
|
|
545
|
+
newStorage.load(map.id, callback, resolve);
|
|
546
|
+
});
|
|
547
|
+
await newStorage.eraseAllDeletedCoValues();
|
|
548
|
+
expect(await loadPromise).toBe(true);
|
|
549
|
+
});
|
|
550
|
+
});
|
|
321
551
|
describe("dependencies", () => {
|
|
322
552
|
test("should load dependencies before dependent coValues and update all known states", async () => {
|
|
323
|
-
const
|
|
324
|
-
const
|
|
553
|
+
const dbPath = getDbPath();
|
|
554
|
+
const fixtures = setupTestNode();
|
|
555
|
+
fixtures.addStorage({
|
|
556
|
+
storage: createSyncStorage({
|
|
557
|
+
filename: dbPath,
|
|
558
|
+
nodeName: "test",
|
|
559
|
+
storageName: "test-storage",
|
|
560
|
+
}),
|
|
561
|
+
});
|
|
562
|
+
const client = setupTestNode();
|
|
563
|
+
const { storage } = client.addStorage({
|
|
564
|
+
storage: createSyncStorage({
|
|
565
|
+
filename: dbPath,
|
|
566
|
+
nodeName: "test",
|
|
567
|
+
storageName: "test-storage",
|
|
568
|
+
}),
|
|
569
|
+
});
|
|
325
570
|
// Create a group and a map owned by that group to create dependencies
|
|
326
|
-
const group =
|
|
571
|
+
const group = fixtures.node.createGroup();
|
|
327
572
|
group.addMember("everyone", "reader");
|
|
328
573
|
const map = group.createMap({ test: "value" });
|
|
329
574
|
await group.core.waitForSync();
|
|
330
575
|
await map.core.waitForSync();
|
|
331
|
-
const callback = vi.fn((content) => node.syncManager.handleNewContent(content, "storage"));
|
|
576
|
+
const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
|
|
332
577
|
const done = vi.fn();
|
|
333
578
|
// Get initial known states
|
|
334
579
|
const initialGroupKnownState = storage.getKnownState(group.id);
|
|
@@ -350,20 +595,35 @@ describe("StorageApiSync", () => {
|
|
|
350
595
|
const updatedMapKnownState = storage.getKnownState(map.id);
|
|
351
596
|
expect(updatedGroupKnownState).toEqual(group.core.knownState());
|
|
352
597
|
expect(updatedMapKnownState).toEqual(map.core.knownState());
|
|
353
|
-
|
|
354
|
-
const mapOnNode = await loadCoValueOrFail(node, map.id);
|
|
598
|
+
client.addStorage({ storage });
|
|
599
|
+
const mapOnNode = await loadCoValueOrFail(client.node, map.id);
|
|
355
600
|
expect(mapOnNode.get("test")).toEqual("value");
|
|
356
601
|
});
|
|
357
602
|
test("should skip loading already loaded dependencies", async () => {
|
|
358
|
-
const
|
|
359
|
-
const
|
|
603
|
+
const dbPath = getDbPath();
|
|
604
|
+
const fixtures = setupTestNode();
|
|
605
|
+
fixtures.addStorage({
|
|
606
|
+
storage: createSyncStorage({
|
|
607
|
+
filename: dbPath,
|
|
608
|
+
nodeName: "test",
|
|
609
|
+
storageName: "test-storage",
|
|
610
|
+
}),
|
|
611
|
+
});
|
|
612
|
+
const client = setupTestNode();
|
|
613
|
+
const { storage } = client.addStorage({
|
|
614
|
+
storage: createSyncStorage({
|
|
615
|
+
filename: dbPath,
|
|
616
|
+
nodeName: "test",
|
|
617
|
+
storageName: "test-storage",
|
|
618
|
+
}),
|
|
619
|
+
});
|
|
360
620
|
// Create a group and a map owned by that group
|
|
361
|
-
const group =
|
|
621
|
+
const group = fixtures.node.createGroup();
|
|
362
622
|
group.addMember("everyone", "reader");
|
|
363
623
|
const map = group.createMap({ test: "value" });
|
|
364
624
|
await group.core.waitForSync();
|
|
365
625
|
await map.core.waitForSync();
|
|
366
|
-
const callback = vi.fn((content) => node.syncManager.handleNewContent(content, "storage"));
|
|
626
|
+
const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
|
|
367
627
|
const done = vi.fn();
|
|
368
628
|
// Get initial known states
|
|
369
629
|
const initialGroupKnownState = storage.getKnownState(group.id);
|
|
@@ -388,26 +648,88 @@ describe("StorageApiSync", () => {
|
|
|
388
648
|
// Verify map known state is updated after second load
|
|
389
649
|
const finalMapKnownState = storage.getKnownState(map.id);
|
|
390
650
|
expect(finalMapKnownState).toEqual(map.core.knownState());
|
|
391
|
-
|
|
392
|
-
const mapOnNode = await loadCoValueOrFail(node, map.id);
|
|
651
|
+
client.addStorage({ storage });
|
|
652
|
+
const mapOnNode = await loadCoValueOrFail(client.node, map.id);
|
|
653
|
+
expect(mapOnNode.get("test")).toEqual("value");
|
|
654
|
+
});
|
|
655
|
+
test("should load dependencies again if they were unmounted", async () => {
|
|
656
|
+
const dbPath = getDbPath();
|
|
657
|
+
const fixtures = setupTestNode();
|
|
658
|
+
fixtures.addStorage({
|
|
659
|
+
storage: createSyncStorage({
|
|
660
|
+
filename: dbPath,
|
|
661
|
+
nodeName: "test",
|
|
662
|
+
storageName: "test-storage",
|
|
663
|
+
}),
|
|
664
|
+
});
|
|
665
|
+
const client = setupTestNode();
|
|
666
|
+
const { storage } = client.addStorage({
|
|
667
|
+
storage: createSyncStorage({
|
|
668
|
+
filename: dbPath,
|
|
669
|
+
nodeName: "test",
|
|
670
|
+
storageName: "test-storage",
|
|
671
|
+
}),
|
|
672
|
+
});
|
|
673
|
+
// Create a group and a map owned by that group
|
|
674
|
+
const group = fixtures.node.createGroup();
|
|
675
|
+
group.addMember("everyone", "reader");
|
|
676
|
+
const map = group.createMap({ test: "value" });
|
|
677
|
+
await group.core.waitForSync();
|
|
678
|
+
await map.core.waitForSync();
|
|
679
|
+
const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
|
|
680
|
+
const done = vi.fn();
|
|
681
|
+
// Load the map (and its group)
|
|
682
|
+
await storage.load(map.id, callback, done);
|
|
683
|
+
callback.mockClear();
|
|
684
|
+
done.mockClear();
|
|
685
|
+
// Unmount the map and its group
|
|
686
|
+
storage.onCoValueUnmounted(map.id);
|
|
687
|
+
storage.onCoValueUnmounted(group.id);
|
|
688
|
+
// Load the map. The group dependency should be loaded again
|
|
689
|
+
await storage.load(map.id, callback, done);
|
|
690
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
691
|
+
expect(callback).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
692
|
+
id: group.id,
|
|
693
|
+
}));
|
|
694
|
+
expect(callback).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
|
695
|
+
id: map.id,
|
|
696
|
+
}));
|
|
697
|
+
expect(done).toHaveBeenCalledWith(true);
|
|
698
|
+
client.addStorage({ storage });
|
|
699
|
+
const mapOnNode = await loadCoValueOrFail(client.node, map.id);
|
|
393
700
|
expect(mapOnNode.get("test")).toEqual("value");
|
|
394
701
|
});
|
|
395
702
|
});
|
|
396
703
|
describe("waitForSync", () => {
|
|
397
704
|
test("should resolve immediately when coValue is already synced", async () => {
|
|
398
|
-
const
|
|
399
|
-
const
|
|
705
|
+
const dbPath = getDbPath();
|
|
706
|
+
const fixtures = setupTestNode();
|
|
707
|
+
fixtures.addStorage({
|
|
708
|
+
storage: createSyncStorage({
|
|
709
|
+
filename: dbPath,
|
|
710
|
+
nodeName: "test",
|
|
711
|
+
storageName: "test-storage",
|
|
712
|
+
}),
|
|
713
|
+
});
|
|
714
|
+
const client = setupTestNode();
|
|
715
|
+
const { storage } = client.addStorage({
|
|
716
|
+
storage: createSyncStorage({
|
|
717
|
+
filename: dbPath,
|
|
718
|
+
nodeName: "test",
|
|
719
|
+
storageName: "test-storage",
|
|
720
|
+
}),
|
|
721
|
+
});
|
|
400
722
|
// Create a group and add a member
|
|
401
|
-
const group =
|
|
723
|
+
const group = fixtures.node.createGroup();
|
|
402
724
|
group.addMember("everyone", "reader");
|
|
403
725
|
await group.core.waitForSync();
|
|
404
726
|
// Store the group in storage
|
|
405
727
|
const contentMessage = getNewContentSince(group.core, emptyKnownState(group.id));
|
|
406
728
|
const correctionCallback = vi.fn();
|
|
407
729
|
storage.store(contentMessage, correctionCallback);
|
|
408
|
-
|
|
730
|
+
client.addStorage({ storage });
|
|
409
731
|
// Load the group on the new node
|
|
410
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
732
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
411
733
|
// Wait for sync should resolve immediately since the coValue is already synced
|
|
412
734
|
await expect(storage.waitForSync(group.id, groupOnNode.core)).resolves.toBeUndefined();
|
|
413
735
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
@@ -415,16 +737,37 @@ describe("StorageApiSync", () => {
|
|
|
415
737
|
});
|
|
416
738
|
describe("close", () => {
|
|
417
739
|
test("should close storage without throwing errors", async () => {
|
|
418
|
-
const
|
|
740
|
+
const client = setupTestNode();
|
|
741
|
+
const { storage } = client.addStorage({
|
|
742
|
+
storage: createSyncStorage({
|
|
743
|
+
nodeName: "test",
|
|
744
|
+
storageName: "test-storage",
|
|
745
|
+
}),
|
|
746
|
+
});
|
|
419
747
|
expect(() => storage.close()).not.toThrow();
|
|
420
748
|
});
|
|
421
749
|
});
|
|
422
750
|
describe("loadKnownState", () => {
|
|
423
751
|
test("should return correct knownState structure for existing CoValue", async () => {
|
|
424
|
-
const
|
|
425
|
-
const
|
|
752
|
+
const dbPath = getDbPath();
|
|
753
|
+
const fixtures = setupTestNode();
|
|
754
|
+
fixtures.addStorage({
|
|
755
|
+
storage: createSyncStorage({
|
|
756
|
+
filename: dbPath,
|
|
757
|
+
nodeName: "test",
|
|
758
|
+
storageName: "test-storage",
|
|
759
|
+
}),
|
|
760
|
+
});
|
|
761
|
+
const client = setupTestNode();
|
|
762
|
+
const { storage } = client.addStorage({
|
|
763
|
+
storage: createSyncStorage({
|
|
764
|
+
filename: dbPath,
|
|
765
|
+
nodeName: "test",
|
|
766
|
+
storageName: "test-storage",
|
|
767
|
+
}),
|
|
768
|
+
});
|
|
426
769
|
// Create a group to have data in the database
|
|
427
|
-
const group =
|
|
770
|
+
const group = fixtures.node.createGroup();
|
|
428
771
|
group.addMember("everyone", "reader");
|
|
429
772
|
await group.core.waitForSync();
|
|
430
773
|
const result = await new Promise((resolve) => {
|
|
@@ -436,21 +779,42 @@ describe("StorageApiSync", () => {
|
|
|
436
779
|
expect(result?.sessions).toEqual(group.core.knownState().sessions);
|
|
437
780
|
});
|
|
438
781
|
test("should return undefined for non-existent CoValue", async () => {
|
|
439
|
-
const
|
|
782
|
+
const client = setupTestNode();
|
|
783
|
+
const { storage } = client.addStorage({
|
|
784
|
+
storage: createSyncStorage({
|
|
785
|
+
nodeName: "test",
|
|
786
|
+
storageName: "test-storage",
|
|
787
|
+
}),
|
|
788
|
+
});
|
|
440
789
|
const result = await new Promise((resolve) => {
|
|
441
790
|
storage.loadKnownState("co_nonexistent", resolve);
|
|
442
791
|
});
|
|
443
792
|
expect(result).toBeUndefined();
|
|
444
793
|
});
|
|
445
794
|
test("should handle CoValue with no sessions (header only)", async () => {
|
|
446
|
-
const
|
|
447
|
-
const
|
|
795
|
+
const dbPath = getDbPath();
|
|
796
|
+
const fixtures = setupTestNode();
|
|
797
|
+
fixtures.addStorage({
|
|
798
|
+
storage: createSyncStorage({
|
|
799
|
+
filename: dbPath,
|
|
800
|
+
nodeName: "test",
|
|
801
|
+
storageName: "test-storage",
|
|
802
|
+
}),
|
|
803
|
+
});
|
|
804
|
+
const client = setupTestNode();
|
|
805
|
+
const { storage } = client.addStorage({
|
|
806
|
+
storage: createSyncStorage({
|
|
807
|
+
filename: dbPath,
|
|
808
|
+
nodeName: "test",
|
|
809
|
+
storageName: "test-storage",
|
|
810
|
+
}),
|
|
811
|
+
});
|
|
448
812
|
// Create a CoValue with just a header (no transactions yet)
|
|
449
|
-
const coValue =
|
|
813
|
+
const coValue = fixtures.node.createCoValue({
|
|
450
814
|
type: "comap",
|
|
451
815
|
ruleset: { type: "unsafeAllowAll" },
|
|
452
816
|
meta: null,
|
|
453
|
-
...crypto.createdNowUnique(),
|
|
817
|
+
...fixtures.node.crypto.createdNowUnique(),
|
|
454
818
|
});
|
|
455
819
|
await coValue.waitForSync();
|
|
456
820
|
const result = await new Promise((resolve) => {
|
|
@@ -463,19 +827,41 @@ describe("StorageApiSync", () => {
|
|
|
463
827
|
expect(Object.keys(result?.sessions || {}).length).toBe(0);
|
|
464
828
|
});
|
|
465
829
|
test("should handle CoValue with multiple sessions", async () => {
|
|
466
|
-
const
|
|
467
|
-
const
|
|
468
|
-
|
|
830
|
+
const dbPath = getDbPath();
|
|
831
|
+
const fixtures = setupTestNode();
|
|
832
|
+
fixtures.addStorage({
|
|
833
|
+
storage: createSyncStorage({
|
|
834
|
+
filename: dbPath,
|
|
835
|
+
nodeName: "test",
|
|
836
|
+
storageName: "test-storage",
|
|
837
|
+
}),
|
|
838
|
+
});
|
|
839
|
+
const fixtures2 = setupTestNode();
|
|
840
|
+
fixtures2.addStorage({
|
|
841
|
+
storage: createSyncStorage({
|
|
842
|
+
filename: dbPath,
|
|
843
|
+
nodeName: "test",
|
|
844
|
+
storageName: "test-storage",
|
|
845
|
+
}),
|
|
846
|
+
});
|
|
847
|
+
const client = setupTestNode();
|
|
848
|
+
const { storage } = client.addStorage({
|
|
849
|
+
storage: createSyncStorage({
|
|
850
|
+
filename: dbPath,
|
|
851
|
+
nodeName: "test",
|
|
852
|
+
storageName: "test-storage",
|
|
853
|
+
}),
|
|
854
|
+
});
|
|
469
855
|
// Create a CoValue and have two nodes make transactions
|
|
470
|
-
const coValue =
|
|
856
|
+
const coValue = fixtures.node.createCoValue({
|
|
471
857
|
type: "comap",
|
|
472
858
|
ruleset: { type: "unsafeAllowAll" },
|
|
473
859
|
meta: null,
|
|
474
|
-
...crypto.createdNowUnique(),
|
|
860
|
+
...fixtures.node.crypto.createdNowUnique(),
|
|
475
861
|
});
|
|
476
862
|
coValue.makeTransaction([{ key1: "value1" }], "trusting");
|
|
477
863
|
await coValue.waitForSync();
|
|
478
|
-
const coValueOnNode2 = await loadCoValueOrFail(
|
|
864
|
+
const coValueOnNode2 = await loadCoValueOrFail(fixtures2.node, coValue.id);
|
|
479
865
|
coValueOnNode2.set("key2", "value2", "trusting");
|
|
480
866
|
await coValueOnNode2.core.waitForSync();
|
|
481
867
|
const result = await new Promise((resolve) => {
|
|
@@ -490,10 +876,25 @@ describe("StorageApiSync", () => {
|
|
|
490
876
|
expect(result?.sessions).toEqual(coValueOnNode2.core.knownState().sessions);
|
|
491
877
|
});
|
|
492
878
|
test("should use cache when knownState is cached", async () => {
|
|
493
|
-
const
|
|
494
|
-
const
|
|
879
|
+
const dbPath = getDbPath();
|
|
880
|
+
const fixtures = setupTestNode();
|
|
881
|
+
fixtures.addStorage({
|
|
882
|
+
storage: createSyncStorage({
|
|
883
|
+
filename: dbPath,
|
|
884
|
+
nodeName: "test",
|
|
885
|
+
storageName: "test-storage",
|
|
886
|
+
}),
|
|
887
|
+
});
|
|
888
|
+
const client = setupTestNode();
|
|
889
|
+
const { storage } = client.addStorage({
|
|
890
|
+
storage: createSyncStorage({
|
|
891
|
+
filename: dbPath,
|
|
892
|
+
nodeName: "test",
|
|
893
|
+
storageName: "test-storage",
|
|
894
|
+
}),
|
|
895
|
+
});
|
|
495
896
|
// Create a group to have data in the database
|
|
496
|
-
const group =
|
|
897
|
+
const group = fixtures.node.createGroup();
|
|
497
898
|
group.addMember("everyone", "reader");
|
|
498
899
|
await group.core.waitForSync();
|
|
499
900
|
// First call should hit the database and cache the result
|