cojson 0.19.22 → 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 +54 -0
- 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 +67 -3
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +289 -12
- 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 +2 -2
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +2 -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 +12 -0
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +14 -0
- 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 +7 -0
- package/dist/storage/storageAsync.d.ts.map +1 -1
- package/dist/storage/storageAsync.js +48 -0
- package/dist/storage/storageAsync.js.map +1 -1
- package/dist/storage/storageSync.d.ts +6 -0
- package/dist/storage/storageSync.d.ts.map +1 -1
- package/dist/storage/storageSync.js +42 -0
- package/dist/storage/storageSync.js.map +1 -1
- package/dist/storage/types.d.ts +59 -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.map +1 -1
- package/dist/sync.js +44 -11
- package/dist/sync.js.map +1 -1
- 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 +5 -6
- package/dist/tests/GarbageCollector.test.js.map +1 -1
- package/dist/tests/StorageApiAsync.test.js +484 -152
- package/dist/tests/StorageApiAsync.test.js.map +1 -1
- package/dist/tests/StorageApiSync.test.js +505 -136
- package/dist/tests/StorageApiSync.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/coValueCore.loadFromStorage.test.js +3 -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 +3 -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.mesh.test.js +3 -2
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +3 -2
- package/dist/tests/sync.storage.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/testStorage.d.ts +3 -0
- package/dist/tests/testStorage.d.ts.map +1 -1
- package/dist/tests/testStorage.js +14 -0
- package/dist/tests/testStorage.js.map +1 -1
- package/dist/tests/testUtils.d.ts +6 -3
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +17 -3
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +6 -16
- package/src/coValueContentMessage.ts +0 -14
- package/src/coValueCore/SessionMap.ts +43 -1
- package/src/coValueCore/coValueCore.ts +400 -8
- 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 +2 -0
- package/src/ids.ts +11 -1
- package/src/localNode.ts +15 -0
- 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 +62 -0
- package/src/storage/storageSync.ts +58 -0
- package/src/storage/types.ts +69 -0
- package/src/sync.ts +51 -11
- package/src/tests/DeletedCoValuesEraserScheduler.test.ts +185 -0
- package/src/tests/GarbageCollector.test.ts +6 -10
- package/src/tests/StorageApiAsync.test.ts +572 -162
- package/src/tests/StorageApiSync.test.ts +580 -143
- package/src/tests/WasmCrypto.test.ts +8 -3
- package/src/tests/coValueCore.loadFromStorage.test.ts +6 -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 +6 -0
- package/src/tests/sync.deleted.test.ts +294 -0
- package/src/tests/sync.mesh.test.ts +5 -2
- package/src/tests/sync.storage.test.ts +5 -2
- package/src/tests/sync.test.ts +5 -2
- package/src/tests/testStorage.ts +28 -1
- package/src/tests/testUtils.ts +28 -9
- 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 {
|
|
4
|
-
import {
|
|
5
|
-
import { afterEach, describe, expect, onTestFinished, test, vi } from "vitest";
|
|
6
|
-
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
7
|
-
import { LocalNode, logger } from "../exports.js";
|
|
8
|
-
import { createAsyncStorage } from "./testStorage.js";
|
|
9
|
-
import { SyncMessagesLog, loadCoValueOrFail, randomAgentAndSessionID, waitFor, } from "./testUtils.js";
|
|
1
|
+
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { logger } from "../exports.js";
|
|
3
|
+
import { createAsyncStorage, getAllCoValuesWaitingForDelete, getCoValueStoredSessions, getDbPath, } from "./testStorage.js";
|
|
4
|
+
import { SyncMessagesLog, fillCoMapWithLargeData, loadCoValueOrFail, setupTestNode, waitFor, } from "./testUtils.js";
|
|
10
5
|
import { emptyKnownState } from "../knownState.js";
|
|
11
|
-
const crypto = await WasmCrypto.create();
|
|
12
6
|
/**
|
|
13
7
|
* Helper function that gets new content since a known state, throwing if:
|
|
14
8
|
* - The coValue is not verified
|
|
@@ -24,63 +18,34 @@ function getNewContentSince(coValue, knownState) {
|
|
|
24
18
|
}
|
|
25
19
|
return contentMessage;
|
|
26
20
|
}
|
|
27
|
-
async function createFixturesNode(customDbPath) {
|
|
28
|
-
const [admin, session] = randomAgentAndSessionID();
|
|
29
|
-
const node = new LocalNode(admin.agentSecret, session, crypto);
|
|
30
|
-
// Create a unique database file for each test
|
|
31
|
-
const dbPath = customDbPath ?? join(tmpdir(), `test-${randomUUID()}.db`);
|
|
32
|
-
const storage = await createAsyncStorage({
|
|
33
|
-
filename: dbPath,
|
|
34
|
-
nodeName: "test",
|
|
35
|
-
storageName: "test-storage",
|
|
36
|
-
});
|
|
37
|
-
onTestFinished(() => {
|
|
38
|
-
try {
|
|
39
|
-
unlinkSync(dbPath);
|
|
40
|
-
}
|
|
41
|
-
catch { }
|
|
42
|
-
});
|
|
43
|
-
onTestFinished(async () => {
|
|
44
|
-
await node.gracefulShutdown();
|
|
45
|
-
});
|
|
46
|
-
node.setStorage(storage);
|
|
47
|
-
return {
|
|
48
|
-
fixturesNode: node,
|
|
49
|
-
dbPath,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
async function createTestNode(dbPath) {
|
|
53
|
-
const [admin, session] = randomAgentAndSessionID();
|
|
54
|
-
const node = new LocalNode(admin.agentSecret, session, crypto);
|
|
55
|
-
const storage = await createAsyncStorage({
|
|
56
|
-
filename: dbPath,
|
|
57
|
-
nodeName: "test",
|
|
58
|
-
storageName: "test-storage",
|
|
59
|
-
});
|
|
60
|
-
onTestFinished(async () => {
|
|
61
|
-
await node.gracefulShutdown();
|
|
62
|
-
await storage.close();
|
|
63
|
-
});
|
|
64
|
-
return {
|
|
65
|
-
node,
|
|
66
|
-
storage,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
21
|
afterEach(() => {
|
|
70
22
|
SyncMessagesLog.clear();
|
|
23
|
+
vi.useRealTimers();
|
|
71
24
|
});
|
|
72
25
|
describe("StorageApiAsync", () => {
|
|
73
26
|
describe("getKnownState", () => {
|
|
74
27
|
test("should return known state for existing coValue ID", async () => {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
28
|
+
const fixtures = setupTestNode();
|
|
29
|
+
await fixtures.addAsyncStorage({
|
|
30
|
+
ourName: "test",
|
|
31
|
+
storageName: "test-storage",
|
|
32
|
+
});
|
|
33
|
+
const client = setupTestNode();
|
|
34
|
+
const { storage } = await client.addAsyncStorage({
|
|
35
|
+
ourName: "test",
|
|
36
|
+
storageName: "test-storage",
|
|
37
|
+
});
|
|
38
|
+
const id = fixtures.node.createGroup().id;
|
|
78
39
|
const knownState = storage.getKnownState(id);
|
|
79
40
|
expect(knownState).toEqual(emptyKnownState(id));
|
|
80
41
|
expect(storage.getKnownState(id)).toBe(knownState); // Should return same instance
|
|
81
42
|
});
|
|
82
43
|
test("should return different known states for different coValue IDs", async () => {
|
|
83
|
-
const
|
|
44
|
+
const client = setupTestNode();
|
|
45
|
+
const { storage } = await client.addAsyncStorage({
|
|
46
|
+
ourName: "test",
|
|
47
|
+
storageName: "test-storage",
|
|
48
|
+
});
|
|
84
49
|
const id1 = "test-id-1";
|
|
85
50
|
const id2 = "test-id-2";
|
|
86
51
|
const knownState1 = storage.getKnownState(id1);
|
|
@@ -90,7 +55,11 @@ describe("StorageApiAsync", () => {
|
|
|
90
55
|
});
|
|
91
56
|
describe("load", () => {
|
|
92
57
|
test("should handle non-existent coValue gracefully", async () => {
|
|
93
|
-
const
|
|
58
|
+
const client = setupTestNode();
|
|
59
|
+
const { storage } = await client.addAsyncStorage({
|
|
60
|
+
ourName: "test",
|
|
61
|
+
storageName: "test-storage",
|
|
62
|
+
});
|
|
94
63
|
const id = "non-existent-id";
|
|
95
64
|
const callback = vi.fn();
|
|
96
65
|
const done = vi.fn();
|
|
@@ -105,12 +74,23 @@ describe("StorageApiAsync", () => {
|
|
|
105
74
|
expect(afterLoadKnownState).toEqual(initialKnownState);
|
|
106
75
|
});
|
|
107
76
|
test("should load coValue with header only successfully", async () => {
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
|
|
77
|
+
const dbPath = getDbPath();
|
|
78
|
+
const fixtures = setupTestNode();
|
|
79
|
+
await fixtures.addAsyncStorage({
|
|
80
|
+
ourName: "test",
|
|
81
|
+
storageName: "test-storage",
|
|
82
|
+
filename: dbPath,
|
|
83
|
+
});
|
|
84
|
+
const client = setupTestNode();
|
|
85
|
+
const { storage } = await client.addAsyncStorage({
|
|
86
|
+
ourName: "test",
|
|
87
|
+
storageName: "test-storage",
|
|
88
|
+
filename: dbPath,
|
|
89
|
+
});
|
|
90
|
+
const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
|
|
111
91
|
const done = vi.fn();
|
|
112
92
|
// Create a real group and get its content message
|
|
113
|
-
const group =
|
|
93
|
+
const group = fixtures.node.createGroup();
|
|
114
94
|
await group.core.waitForSync();
|
|
115
95
|
// Get initial known state
|
|
116
96
|
const initialKnownState = storage.getKnownState(group.id);
|
|
@@ -125,16 +105,27 @@ describe("StorageApiAsync", () => {
|
|
|
125
105
|
// Verify that storage known state is updated after load
|
|
126
106
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
127
107
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
128
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
108
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
129
109
|
expect(groupOnNode.core.verified.header).toEqual(group.core.verified.header);
|
|
130
110
|
});
|
|
131
111
|
test("should load coValue with sessions and transactions successfully", async () => {
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
|
|
112
|
+
const dbPath = getDbPath();
|
|
113
|
+
const fixtures = setupTestNode();
|
|
114
|
+
await fixtures.addAsyncStorage({
|
|
115
|
+
ourName: "test",
|
|
116
|
+
storageName: "test-storage",
|
|
117
|
+
filename: dbPath,
|
|
118
|
+
});
|
|
119
|
+
const client = setupTestNode();
|
|
120
|
+
const { storage } = await client.addAsyncStorage({
|
|
121
|
+
ourName: "test",
|
|
122
|
+
storageName: "test-storage",
|
|
123
|
+
filename: dbPath,
|
|
124
|
+
});
|
|
125
|
+
const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
|
|
135
126
|
const done = vi.fn();
|
|
136
127
|
// Create a real group and add a member to create transactions
|
|
137
|
-
const group =
|
|
128
|
+
const group = fixtures.node.createGroup();
|
|
138
129
|
group.addMember("everyone", "reader");
|
|
139
130
|
await group.core.waitForSync();
|
|
140
131
|
// Get initial known state
|
|
@@ -145,23 +136,31 @@ describe("StorageApiAsync", () => {
|
|
|
145
136
|
id: group.id,
|
|
146
137
|
header: group.core.verified.header,
|
|
147
138
|
new: expect.objectContaining({
|
|
148
|
-
[
|
|
139
|
+
[fixtures.node.currentSessionID]: expect.any(Object),
|
|
149
140
|
}),
|
|
150
141
|
}));
|
|
151
142
|
expect(done).toHaveBeenCalledWith(true);
|
|
152
143
|
// Verify that storage known state is updated after load
|
|
153
144
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
154
145
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
155
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
146
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
156
147
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
157
148
|
});
|
|
158
149
|
});
|
|
159
150
|
describe("store", () => {
|
|
160
151
|
test("should store new coValue with header successfully", async () => {
|
|
161
|
-
const
|
|
162
|
-
|
|
152
|
+
const fixtures = setupTestNode();
|
|
153
|
+
await fixtures.addAsyncStorage({
|
|
154
|
+
ourName: "test",
|
|
155
|
+
storageName: "test-storage",
|
|
156
|
+
});
|
|
157
|
+
const client = setupTestNode();
|
|
158
|
+
const storage = await createAsyncStorage({
|
|
159
|
+
nodeName: "test",
|
|
160
|
+
storageName: "test-storage",
|
|
161
|
+
});
|
|
163
162
|
// Create a real group and get its content message
|
|
164
|
-
const group =
|
|
163
|
+
const group = fixtures.node.createGroup();
|
|
165
164
|
const contentMessage = getNewContentSince(group.core, emptyKnownState(group.id));
|
|
166
165
|
const correctionCallback = vi.fn();
|
|
167
166
|
// Get initial known state
|
|
@@ -172,15 +171,23 @@ describe("StorageApiAsync", () => {
|
|
|
172
171
|
// Verify that storage known state is updated after store
|
|
173
172
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
174
173
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
175
|
-
|
|
176
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
174
|
+
client.addStorage({ storage });
|
|
175
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
177
176
|
expect(groupOnNode.core.verified.header).toEqual(group.core.verified.header);
|
|
178
177
|
});
|
|
179
178
|
test("should store coValue with transactions successfully", async () => {
|
|
180
|
-
const
|
|
181
|
-
|
|
179
|
+
const fixtures = setupTestNode();
|
|
180
|
+
await fixtures.addAsyncStorage({
|
|
181
|
+
ourName: "test",
|
|
182
|
+
storageName: "test-storage",
|
|
183
|
+
});
|
|
184
|
+
const client = setupTestNode();
|
|
185
|
+
const storage = await createAsyncStorage({
|
|
186
|
+
nodeName: "test",
|
|
187
|
+
storageName: "test-storage",
|
|
188
|
+
});
|
|
182
189
|
// Create a real group and add a member to create transactions
|
|
183
|
-
const group =
|
|
190
|
+
const group = fixtures.node.createGroup();
|
|
184
191
|
const knownState = group.core.knownState();
|
|
185
192
|
group.addMember("everyone", "reader");
|
|
186
193
|
const contentMessage = getNewContentSince(group.core, emptyKnownState(group.id));
|
|
@@ -193,14 +200,22 @@ describe("StorageApiAsync", () => {
|
|
|
193
200
|
// Verify that storage known state is updated after store
|
|
194
201
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
195
202
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
196
|
-
|
|
197
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
203
|
+
client.addStorage({ storage });
|
|
204
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
198
205
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
199
206
|
});
|
|
200
207
|
test("should handle invalid assumption on header presence with correction", async () => {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
208
|
+
const fixtures = setupTestNode();
|
|
209
|
+
await fixtures.addAsyncStorage({
|
|
210
|
+
ourName: "test",
|
|
211
|
+
storageName: "test-storage",
|
|
212
|
+
});
|
|
213
|
+
const client = setupTestNode();
|
|
214
|
+
const storage = await createAsyncStorage({
|
|
215
|
+
nodeName: "test",
|
|
216
|
+
storageName: "test-storage",
|
|
217
|
+
});
|
|
218
|
+
const group = fixtures.node.createGroup();
|
|
204
219
|
const knownState = group.core.knownState();
|
|
205
220
|
group.addMember("everyone", "reader");
|
|
206
221
|
const contentMessage = getNewContentSince(group.core, knownState);
|
|
@@ -217,14 +232,22 @@ describe("StorageApiAsync", () => {
|
|
|
217
232
|
// Verify that storage known state is updated after store with correction
|
|
218
233
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
219
234
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
220
|
-
|
|
221
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
235
|
+
client.addStorage({ storage });
|
|
236
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
222
237
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
223
238
|
});
|
|
224
239
|
test("should handle invalid assumption on new content with correction", async () => {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
240
|
+
const fixtures = setupTestNode();
|
|
241
|
+
await fixtures.addAsyncStorage({
|
|
242
|
+
ourName: "test",
|
|
243
|
+
storageName: "test-storage",
|
|
244
|
+
});
|
|
245
|
+
const client = setupTestNode();
|
|
246
|
+
const storage = await createAsyncStorage({
|
|
247
|
+
nodeName: "test",
|
|
248
|
+
storageName: "test-storage",
|
|
249
|
+
});
|
|
250
|
+
const group = fixtures.node.createGroup();
|
|
228
251
|
const initialContent = getNewContentSince(group.core, emptyKnownState(group.id));
|
|
229
252
|
const initialKnownState = group.core.knownState();
|
|
230
253
|
group.addMember("everyone", "reader");
|
|
@@ -245,14 +268,22 @@ describe("StorageApiAsync", () => {
|
|
|
245
268
|
// Verify that storage known state is updated after store with correction
|
|
246
269
|
const finalKnownState = storage.getKnownState(group.id);
|
|
247
270
|
expect(finalKnownState).toEqual(group.core.knownState());
|
|
248
|
-
|
|
249
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
271
|
+
client.addStorage({ storage });
|
|
272
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
250
273
|
expect(groupOnNode.get("everyone")).toEqual("writer");
|
|
251
274
|
});
|
|
252
275
|
test("should log an error when the correction callback returns undefined", async () => {
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
276
|
+
const fixtures = setupTestNode();
|
|
277
|
+
await fixtures.addAsyncStorage({
|
|
278
|
+
ourName: "test",
|
|
279
|
+
storageName: "test-storage",
|
|
280
|
+
});
|
|
281
|
+
const client = setupTestNode();
|
|
282
|
+
const { storage } = await client.addAsyncStorage({
|
|
283
|
+
ourName: "test",
|
|
284
|
+
storageName: "test-storage",
|
|
285
|
+
});
|
|
286
|
+
const group = fixtures.node.createGroup();
|
|
256
287
|
const knownState = group.core.knownState();
|
|
257
288
|
group.addMember("everyone", "writer");
|
|
258
289
|
const contentMessage = getNewContentSince(group.core, knownState);
|
|
@@ -277,9 +308,17 @@ describe("StorageApiAsync", () => {
|
|
|
277
308
|
errorSpy.mockClear();
|
|
278
309
|
});
|
|
279
310
|
test("should log an error when the correction callback returns an invalid content message", async () => {
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
311
|
+
const fixtures = setupTestNode();
|
|
312
|
+
await fixtures.addAsyncStorage({
|
|
313
|
+
ourName: "test",
|
|
314
|
+
storageName: "test-storage",
|
|
315
|
+
});
|
|
316
|
+
const client = setupTestNode();
|
|
317
|
+
const { storage } = await client.addAsyncStorage({
|
|
318
|
+
ourName: "test",
|
|
319
|
+
storageName: "test-storage",
|
|
320
|
+
});
|
|
321
|
+
const group = fixtures.node.createGroup();
|
|
283
322
|
const knownState = group.core.knownState();
|
|
284
323
|
group.addMember("everyone", "writer");
|
|
285
324
|
const contentMessage = getNewContentSince(group.core, knownState);
|
|
@@ -308,17 +347,21 @@ describe("StorageApiAsync", () => {
|
|
|
308
347
|
errorSpy.mockClear();
|
|
309
348
|
});
|
|
310
349
|
test("should handle invalid assumption when pushing multiple transactions with correction", async () => {
|
|
311
|
-
const
|
|
312
|
-
const
|
|
350
|
+
const client = setupTestNode();
|
|
351
|
+
const storage = await createAsyncStorage({
|
|
352
|
+
nodeName: "test",
|
|
353
|
+
storageName: "test-storage",
|
|
354
|
+
});
|
|
355
|
+
const core = client.node.createCoValue({
|
|
313
356
|
type: "comap",
|
|
314
357
|
ruleset: { type: "unsafeAllowAll" },
|
|
315
358
|
meta: null,
|
|
316
|
-
...crypto.createdNowUnique(),
|
|
359
|
+
...client.node.crypto.createdNowUnique(),
|
|
317
360
|
});
|
|
318
361
|
core.makeTransaction([{ count: 1 }], "trusting");
|
|
319
362
|
await core.waitForSync();
|
|
320
363
|
// Add storage later
|
|
321
|
-
|
|
364
|
+
client.addStorage({ storage });
|
|
322
365
|
core.makeTransaction([{ count: 2 }], "trusting");
|
|
323
366
|
core.makeTransaction([{ count: 3 }], "trusting");
|
|
324
367
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
@@ -338,24 +381,28 @@ describe("StorageApiAsync", () => {
|
|
|
338
381
|
`);
|
|
339
382
|
});
|
|
340
383
|
test("should handle invalid assumption when pushing multiple transactions on different coValues with correction", async () => {
|
|
341
|
-
const
|
|
342
|
-
const
|
|
384
|
+
const client = setupTestNode();
|
|
385
|
+
const storage = await createAsyncStorage({
|
|
386
|
+
nodeName: "test",
|
|
387
|
+
storageName: "test-storage",
|
|
388
|
+
});
|
|
389
|
+
const core = client.node.createCoValue({
|
|
343
390
|
type: "comap",
|
|
344
391
|
ruleset: { type: "unsafeAllowAll" },
|
|
345
392
|
meta: null,
|
|
346
|
-
...crypto.createdNowUnique(),
|
|
393
|
+
...client.node.crypto.createdNowUnique(),
|
|
347
394
|
});
|
|
348
|
-
const core2 = node.createCoValue({
|
|
395
|
+
const core2 = client.node.createCoValue({
|
|
349
396
|
type: "comap",
|
|
350
397
|
ruleset: { type: "unsafeAllowAll" },
|
|
351
398
|
meta: null,
|
|
352
|
-
...crypto.createdNowUnique(),
|
|
399
|
+
...client.node.crypto.createdNowUnique(),
|
|
353
400
|
});
|
|
354
401
|
core.makeTransaction([{ count: 1 }], "trusting");
|
|
355
402
|
core2.makeTransaction([{ count: 1 }], "trusting");
|
|
356
403
|
await core.waitForSync();
|
|
357
404
|
// Add storage later
|
|
358
|
-
|
|
405
|
+
client.addStorage({ storage });
|
|
359
406
|
core.makeTransaction([{ count: 2 }], "trusting");
|
|
360
407
|
core2.makeTransaction([{ count: 2 }], "trusting");
|
|
361
408
|
core.makeTransaction([{ count: 3 }], "trusting");
|
|
@@ -388,24 +435,28 @@ describe("StorageApiAsync", () => {
|
|
|
388
435
|
`);
|
|
389
436
|
});
|
|
390
437
|
test("should handle close while pushing multiple transactions on different coValues with an invalid assumption", async () => {
|
|
391
|
-
const
|
|
392
|
-
const
|
|
438
|
+
const client = setupTestNode();
|
|
439
|
+
const storage = await createAsyncStorage({
|
|
440
|
+
nodeName: "test",
|
|
441
|
+
storageName: "test-storage",
|
|
442
|
+
});
|
|
443
|
+
const core = client.node.createCoValue({
|
|
393
444
|
type: "comap",
|
|
394
445
|
ruleset: { type: "unsafeAllowAll" },
|
|
395
446
|
meta: null,
|
|
396
|
-
...crypto.createdNowUnique(),
|
|
447
|
+
...client.node.crypto.createdNowUnique(),
|
|
397
448
|
});
|
|
398
|
-
const core2 = node.createCoValue({
|
|
449
|
+
const core2 = client.node.createCoValue({
|
|
399
450
|
type: "comap",
|
|
400
451
|
ruleset: { type: "unsafeAllowAll" },
|
|
401
452
|
meta: null,
|
|
402
|
-
...crypto.createdNowUnique(),
|
|
453
|
+
...client.node.crypto.createdNowUnique(),
|
|
403
454
|
});
|
|
404
455
|
core.makeTransaction([{ count: 1 }], "trusting");
|
|
405
456
|
core2.makeTransaction([{ count: 1 }], "trusting");
|
|
406
457
|
await core.waitForSync();
|
|
407
458
|
// Add storage later
|
|
408
|
-
|
|
459
|
+
client.addStorage({ storage });
|
|
409
460
|
core.makeTransaction([{ count: 2 }], "trusting");
|
|
410
461
|
core2.makeTransaction([{ count: 2 }], "trusting");
|
|
411
462
|
core.makeTransaction([{ count: 3 }], "trusting");
|
|
@@ -438,14 +489,29 @@ describe("StorageApiAsync", () => {
|
|
|
438
489
|
expect(storage.getKnownState(core.id)).toEqual(knownState);
|
|
439
490
|
});
|
|
440
491
|
test("should handle multiple sessions correctly", async () => {
|
|
441
|
-
const
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
492
|
+
const dbPath = getDbPath();
|
|
493
|
+
const fixtures = setupTestNode();
|
|
494
|
+
await fixtures.addAsyncStorage({
|
|
495
|
+
ourName: "test",
|
|
496
|
+
storageName: "test-storage",
|
|
497
|
+
filename: dbPath,
|
|
498
|
+
});
|
|
499
|
+
const fixture2 = setupTestNode();
|
|
500
|
+
await fixture2.addAsyncStorage({
|
|
501
|
+
ourName: "test",
|
|
502
|
+
storageName: "test-storage",
|
|
503
|
+
filename: dbPath,
|
|
504
|
+
});
|
|
505
|
+
const client = setupTestNode();
|
|
506
|
+
const storage = await createAsyncStorage({
|
|
507
|
+
nodeName: "test",
|
|
508
|
+
storageName: "test-storage",
|
|
509
|
+
});
|
|
510
|
+
const coValue = fixtures.node.createCoValue({
|
|
445
511
|
type: "comap",
|
|
446
512
|
ruleset: { type: "unsafeAllowAll" },
|
|
447
513
|
meta: null,
|
|
448
|
-
...crypto.createdNowUnique(),
|
|
514
|
+
...fixtures.node.crypto.createdNowUnique(),
|
|
449
515
|
});
|
|
450
516
|
coValue.makeTransaction([
|
|
451
517
|
{
|
|
@@ -453,7 +519,7 @@ describe("StorageApiAsync", () => {
|
|
|
453
519
|
},
|
|
454
520
|
], "trusting");
|
|
455
521
|
await coValue.waitForSync();
|
|
456
|
-
const mapOnNode2 = await loadCoValueOrFail(
|
|
522
|
+
const mapOnNode2 = await loadCoValueOrFail(fixture2.node, coValue.id);
|
|
457
523
|
coValue.makeTransaction([
|
|
458
524
|
{
|
|
459
525
|
count: 2,
|
|
@@ -464,22 +530,215 @@ describe("StorageApiAsync", () => {
|
|
|
464
530
|
const correctionCallback = vi.fn();
|
|
465
531
|
await storage.store(contentMessage, correctionCallback);
|
|
466
532
|
await storage.waitForSync(mapOnNode2.id, mapOnNode2.core);
|
|
467
|
-
|
|
468
|
-
const finalMap = await loadCoValueOrFail(node, mapOnNode2.id);
|
|
533
|
+
client.addStorage({ storage });
|
|
534
|
+
const finalMap = await loadCoValueOrFail(client.node, mapOnNode2.id);
|
|
469
535
|
expect(finalMap.core.knownState()).toEqual(knownState);
|
|
470
536
|
});
|
|
471
537
|
});
|
|
538
|
+
describe("delete flow", () => {
|
|
539
|
+
test("deleteCoValue enqueues the coValue for erasure", async () => {
|
|
540
|
+
const storage = await createAsyncStorage({
|
|
541
|
+
nodeName: "test",
|
|
542
|
+
storageName: "test-storage",
|
|
543
|
+
});
|
|
544
|
+
const client = setupTestNode();
|
|
545
|
+
client.addStorage({ storage });
|
|
546
|
+
const group = client.node.createGroup();
|
|
547
|
+
const map = group.createMap();
|
|
548
|
+
map.core.deleteCoValue();
|
|
549
|
+
await map.core.waitForSync();
|
|
550
|
+
expect(await getAllCoValuesWaitingForDelete(storage)).toContain(map.id);
|
|
551
|
+
});
|
|
552
|
+
test("background erasure doesn't run if not enabled", async () => {
|
|
553
|
+
const dbPath = getDbPath();
|
|
554
|
+
const node = setupTestNode();
|
|
555
|
+
const { storage } = await node.addAsyncStorage({
|
|
556
|
+
ourName: "test",
|
|
557
|
+
storageName: "test-storage",
|
|
558
|
+
filename: dbPath,
|
|
559
|
+
});
|
|
560
|
+
const group = node.node.createGroup();
|
|
561
|
+
const map = group.createMap();
|
|
562
|
+
map.set("k", "v");
|
|
563
|
+
await map.core.waitForSync();
|
|
564
|
+
vi.useFakeTimers();
|
|
565
|
+
map.core.deleteCoValue();
|
|
566
|
+
await map.core.waitForSync();
|
|
567
|
+
await vi.runAllTimersAsync();
|
|
568
|
+
expect(await getAllCoValuesWaitingForDelete(storage)).toContain(map.id);
|
|
569
|
+
});
|
|
570
|
+
test("background erasure runs if enabled", async () => {
|
|
571
|
+
const dbPath = getDbPath();
|
|
572
|
+
const node = setupTestNode();
|
|
573
|
+
const { storage } = await node.addAsyncStorage({
|
|
574
|
+
ourName: "test",
|
|
575
|
+
storageName: "test-storage",
|
|
576
|
+
filename: dbPath,
|
|
577
|
+
});
|
|
578
|
+
vi.useFakeTimers();
|
|
579
|
+
node.node.enableDeletedCoValuesErasure();
|
|
580
|
+
const group = node.node.createGroup();
|
|
581
|
+
const map = group.createMap();
|
|
582
|
+
map.set("k", "v");
|
|
583
|
+
await map.core.waitForSync();
|
|
584
|
+
map.core.deleteCoValue();
|
|
585
|
+
await map.core.waitForSync();
|
|
586
|
+
await vi.runAllTimersAsync();
|
|
587
|
+
expect(await getAllCoValuesWaitingForDelete(storage)).not.toContain(map.id);
|
|
588
|
+
const sessionIDs = await getCoValueStoredSessions(storage, map.id);
|
|
589
|
+
expect(sessionIDs).toHaveLength(1);
|
|
590
|
+
expect(sessionIDs[0]).toMatch(/_session_d[1-9A-HJ-NP-Za-km-z]+\$$/); // Delete session format
|
|
591
|
+
});
|
|
592
|
+
test("eraseAllDeletedCoValues deletes history but preserves tombstone", async () => {
|
|
593
|
+
const dbPath = getDbPath();
|
|
594
|
+
const node = setupTestNode();
|
|
595
|
+
const { storage } = await node.addAsyncStorage({
|
|
596
|
+
ourName: "test",
|
|
597
|
+
storageName: "test-storage",
|
|
598
|
+
filename: dbPath,
|
|
599
|
+
});
|
|
600
|
+
const group = node.node.createGroup();
|
|
601
|
+
const map = group.createMap();
|
|
602
|
+
map.set("k", "v");
|
|
603
|
+
await map.core.waitForSync();
|
|
604
|
+
map.core.deleteCoValue();
|
|
605
|
+
await map.core.waitForSync();
|
|
606
|
+
await waitFor(async () => {
|
|
607
|
+
const queued = await getAllCoValuesWaitingForDelete(storage);
|
|
608
|
+
expect(queued).toContain(map.id);
|
|
609
|
+
return true;
|
|
610
|
+
});
|
|
611
|
+
await storage.eraseAllDeletedCoValues();
|
|
612
|
+
const queued = await getAllCoValuesWaitingForDelete(storage);
|
|
613
|
+
expect(queued).not.toContain(map.id);
|
|
614
|
+
// Tombstone preserved + history erased when loaded from storage
|
|
615
|
+
const client = setupTestNode();
|
|
616
|
+
const { storage: clientStorage } = await client.addAsyncStorage({
|
|
617
|
+
ourName: "test",
|
|
618
|
+
storageName: "test-storage",
|
|
619
|
+
filename: dbPath,
|
|
620
|
+
});
|
|
621
|
+
const loaded = await loadCoValueOrFail(client.node, map.id);
|
|
622
|
+
expect(loaded.core.isDeleted).toBe(true);
|
|
623
|
+
expect(loaded.get("k")).toBeUndefined();
|
|
624
|
+
const sessionIDs = await getCoValueStoredSessions(clientStorage, map.id);
|
|
625
|
+
expect(sessionIDs).toHaveLength(1);
|
|
626
|
+
expect(sessionIDs[0]).toMatch(/_session_d[1-9A-HJ-NP-Za-km-z]+\$$/); // Delete session format
|
|
627
|
+
});
|
|
628
|
+
test("eraseAllDeletedCoValues does not break when called while a coValue is streaming from storage", async () => {
|
|
629
|
+
const dbPath = getDbPath();
|
|
630
|
+
const node = setupTestNode();
|
|
631
|
+
const { storage } = await node.addAsyncStorage({
|
|
632
|
+
ourName: "test",
|
|
633
|
+
storageName: "test-storage",
|
|
634
|
+
filename: dbPath,
|
|
635
|
+
});
|
|
636
|
+
const group = node.node.createGroup();
|
|
637
|
+
const map = group.createMap();
|
|
638
|
+
fillCoMapWithLargeData(map);
|
|
639
|
+
await map.core.waitForSync();
|
|
640
|
+
map.core.deleteCoValue();
|
|
641
|
+
await map.core.waitForSync();
|
|
642
|
+
storage.close();
|
|
643
|
+
const newSession = node.spawnNewSession();
|
|
644
|
+
const { storage: newStorage } = await newSession.addAsyncStorage({
|
|
645
|
+
ourName: "test",
|
|
646
|
+
storageName: "test-storage",
|
|
647
|
+
filename: dbPath,
|
|
648
|
+
});
|
|
649
|
+
const callback = vi.fn();
|
|
650
|
+
const loadPromise = new Promise((resolve) => {
|
|
651
|
+
newStorage.load(map.id, callback, resolve);
|
|
652
|
+
});
|
|
653
|
+
await newStorage.eraseAllDeletedCoValues();
|
|
654
|
+
expect(await loadPromise).toBe(true);
|
|
655
|
+
});
|
|
656
|
+
test("load interrupts eraseAllDeletedCoValues mid-run (resolves early, leaves some queued)", async () => {
|
|
657
|
+
const dbPath = getDbPath();
|
|
658
|
+
const node = setupTestNode();
|
|
659
|
+
const { storage } = await node.addAsyncStorage({
|
|
660
|
+
ourName: "test",
|
|
661
|
+
storageName: "test-storage",
|
|
662
|
+
filename: dbPath,
|
|
663
|
+
});
|
|
664
|
+
const group = node.node.createGroup();
|
|
665
|
+
const map1 = group.createMap();
|
|
666
|
+
map1.set("k", "v");
|
|
667
|
+
await map1.core.waitForSync();
|
|
668
|
+
const map2 = group.createMap();
|
|
669
|
+
map2.set("k", "v");
|
|
670
|
+
await map2.core.waitForSync();
|
|
671
|
+
map1.core.deleteCoValue();
|
|
672
|
+
map2.core.deleteCoValue();
|
|
673
|
+
await map1.core.waitForSync();
|
|
674
|
+
await map2.core.waitForSync();
|
|
675
|
+
await waitFor(async () => {
|
|
676
|
+
const queued = await getAllCoValuesWaitingForDelete(storage);
|
|
677
|
+
expect(queued).toEqual(expect.arrayContaining([map1.id, map2.id]));
|
|
678
|
+
return true;
|
|
679
|
+
});
|
|
680
|
+
let releaseBarrier;
|
|
681
|
+
const barrier = new Promise((resolve) => {
|
|
682
|
+
releaseBarrier = resolve;
|
|
683
|
+
});
|
|
684
|
+
let firstTxStartedResolve;
|
|
685
|
+
const firstTxStarted = new Promise((resolve) => {
|
|
686
|
+
firstTxStartedResolve = resolve;
|
|
687
|
+
});
|
|
688
|
+
// @ts-expect-error - dbClient is private
|
|
689
|
+
const dbClient = storage.dbClient;
|
|
690
|
+
const originalTransaction = dbClient.transaction.bind(dbClient);
|
|
691
|
+
let txCalls = 0;
|
|
692
|
+
const txSpy = vi
|
|
693
|
+
.spyOn(dbClient, "transaction")
|
|
694
|
+
.mockImplementation(async (callback) => {
|
|
695
|
+
txCalls += 1;
|
|
696
|
+
return originalTransaction(async (tx) => {
|
|
697
|
+
if (txCalls === 1) {
|
|
698
|
+
firstTxStartedResolve();
|
|
699
|
+
await barrier;
|
|
700
|
+
}
|
|
701
|
+
return callback(tx);
|
|
702
|
+
});
|
|
703
|
+
});
|
|
704
|
+
const erasePromise = storage.eraseAllDeletedCoValues();
|
|
705
|
+
// Ensure the eraser is in-flight and inside its first transaction.
|
|
706
|
+
await firstTxStarted;
|
|
707
|
+
// Trigger interruption. We don't await the load immediately to avoid doing
|
|
708
|
+
// DB reads while the transaction is being held open by the barrier.
|
|
709
|
+
const loadDone = new Promise((resolve) => {
|
|
710
|
+
void storage.load("non-existent-id", () => { }, resolve);
|
|
711
|
+
});
|
|
712
|
+
releaseBarrier();
|
|
713
|
+
await erasePromise;
|
|
714
|
+
await loadDone;
|
|
715
|
+
const queuedAfter = await getAllCoValuesWaitingForDelete(storage);
|
|
716
|
+
expect(queuedAfter).toHaveLength(1);
|
|
717
|
+
txSpy.mockRestore();
|
|
718
|
+
});
|
|
719
|
+
});
|
|
472
720
|
describe("dependencies", () => {
|
|
473
721
|
test("should push dependencies before the coValue", async () => {
|
|
474
|
-
const
|
|
475
|
-
const
|
|
722
|
+
const dbPath = getDbPath();
|
|
723
|
+
const fixtures = setupTestNode();
|
|
724
|
+
await fixtures.addAsyncStorage({
|
|
725
|
+
ourName: "test",
|
|
726
|
+
storageName: "test-storage",
|
|
727
|
+
filename: dbPath,
|
|
728
|
+
});
|
|
729
|
+
const client = setupTestNode();
|
|
730
|
+
const storage = await createAsyncStorage({
|
|
731
|
+
nodeName: "test",
|
|
732
|
+
storageName: "test-storage",
|
|
733
|
+
filename: dbPath,
|
|
734
|
+
});
|
|
476
735
|
// Create a group and a map owned by that group to create dependencies
|
|
477
|
-
const group =
|
|
736
|
+
const group = fixtures.node.createGroup();
|
|
478
737
|
group.addMember("everyone", "reader");
|
|
479
738
|
const map = group.createMap({ test: "value" });
|
|
480
739
|
await group.core.waitForSync();
|
|
481
740
|
await map.core.waitForSync();
|
|
482
|
-
const callback = vi.fn((content) => node.syncManager.handleNewContent(content, "storage"));
|
|
741
|
+
const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
|
|
483
742
|
const done = vi.fn();
|
|
484
743
|
// Get initial known states
|
|
485
744
|
const initialGroupKnownState = storage.getKnownState(group.id);
|
|
@@ -501,20 +760,31 @@ describe("StorageApiAsync", () => {
|
|
|
501
760
|
const updatedMapKnownState = storage.getKnownState(map.id);
|
|
502
761
|
expect(updatedGroupKnownState).toEqual(group.core.knownState());
|
|
503
762
|
expect(updatedMapKnownState).toEqual(map.core.knownState());
|
|
504
|
-
|
|
505
|
-
const mapOnNode = await loadCoValueOrFail(node, map.id);
|
|
763
|
+
client.addStorage({ storage });
|
|
764
|
+
const mapOnNode = await loadCoValueOrFail(client.node, map.id);
|
|
506
765
|
expect(mapOnNode.get("test")).toEqual("value");
|
|
507
766
|
});
|
|
508
767
|
test("should handle dependencies that are already loaded correctly", async () => {
|
|
509
|
-
const
|
|
510
|
-
const
|
|
768
|
+
const dbPath = getDbPath();
|
|
769
|
+
const fixtures = setupTestNode();
|
|
770
|
+
await fixtures.addAsyncStorage({
|
|
771
|
+
ourName: "test",
|
|
772
|
+
storageName: "test-storage",
|
|
773
|
+
filename: dbPath,
|
|
774
|
+
});
|
|
775
|
+
const client = setupTestNode();
|
|
776
|
+
const storage = await createAsyncStorage({
|
|
777
|
+
nodeName: "test",
|
|
778
|
+
storageName: "test-storage",
|
|
779
|
+
filename: dbPath,
|
|
780
|
+
});
|
|
511
781
|
// Create a group and a map owned by that group
|
|
512
|
-
const group =
|
|
782
|
+
const group = fixtures.node.createGroup();
|
|
513
783
|
group.addMember("everyone", "reader");
|
|
514
784
|
const map = group.createMap({ test: "value" });
|
|
515
785
|
await group.core.waitForSync();
|
|
516
786
|
await map.core.waitForSync();
|
|
517
|
-
const callback = vi.fn((content) => node.syncManager.handleNewContent(content, "storage"));
|
|
787
|
+
const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
|
|
518
788
|
const done = vi.fn();
|
|
519
789
|
// Get initial known states
|
|
520
790
|
const initialGroupKnownState = storage.getKnownState(group.id);
|
|
@@ -539,20 +809,31 @@ describe("StorageApiAsync", () => {
|
|
|
539
809
|
// Verify map known state is updated after second load
|
|
540
810
|
const finalMapKnownState = storage.getKnownState(map.id);
|
|
541
811
|
expect(finalMapKnownState).toEqual(map.core.knownState());
|
|
542
|
-
|
|
543
|
-
const mapOnNode = await loadCoValueOrFail(node, map.id);
|
|
812
|
+
client.addStorage({ storage });
|
|
813
|
+
const mapOnNode = await loadCoValueOrFail(client.node, map.id);
|
|
544
814
|
expect(mapOnNode.get("test")).toEqual("value");
|
|
545
815
|
});
|
|
546
816
|
test("should load dependencies again if they were unmounted", async () => {
|
|
547
|
-
const
|
|
548
|
-
const
|
|
817
|
+
const dbPath = getDbPath();
|
|
818
|
+
const fixtures = setupTestNode();
|
|
819
|
+
await fixtures.addAsyncStorage({
|
|
820
|
+
ourName: "test",
|
|
821
|
+
storageName: "test-storage",
|
|
822
|
+
filename: dbPath,
|
|
823
|
+
});
|
|
824
|
+
const client = setupTestNode();
|
|
825
|
+
const { storage } = await client.addAsyncStorage({
|
|
826
|
+
ourName: "test",
|
|
827
|
+
storageName: "test-storage",
|
|
828
|
+
filename: dbPath,
|
|
829
|
+
});
|
|
549
830
|
// Create a group and a map owned by that group
|
|
550
|
-
const group =
|
|
831
|
+
const group = fixtures.node.createGroup();
|
|
551
832
|
group.addMember("everyone", "reader");
|
|
552
833
|
const map = group.createMap({ test: "value" });
|
|
553
834
|
await group.core.waitForSync();
|
|
554
835
|
await map.core.waitForSync();
|
|
555
|
-
const callback = vi.fn((content) => node.syncManager.handleNewContent(content, "storage"));
|
|
836
|
+
const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
|
|
556
837
|
const done = vi.fn();
|
|
557
838
|
// Load the map (and its group)
|
|
558
839
|
await storage.load(map.id, callback, done);
|
|
@@ -571,26 +852,37 @@ describe("StorageApiAsync", () => {
|
|
|
571
852
|
id: map.id,
|
|
572
853
|
}));
|
|
573
854
|
expect(done).toHaveBeenCalledWith(true);
|
|
574
|
-
|
|
575
|
-
const mapOnNode = await loadCoValueOrFail(node, map.id);
|
|
855
|
+
client.addStorage({ storage });
|
|
856
|
+
const mapOnNode = await loadCoValueOrFail(client.node, map.id);
|
|
576
857
|
expect(mapOnNode.get("test")).toEqual("value");
|
|
577
858
|
});
|
|
578
859
|
});
|
|
579
860
|
describe("waitForSync", () => {
|
|
580
861
|
test("should resolve when the coValue is already synced", async () => {
|
|
581
|
-
const
|
|
582
|
-
const
|
|
862
|
+
const dbPath = getDbPath();
|
|
863
|
+
const fixtures = setupTestNode();
|
|
864
|
+
await fixtures.addAsyncStorage({
|
|
865
|
+
ourName: "test",
|
|
866
|
+
storageName: "test-storage",
|
|
867
|
+
filename: dbPath,
|
|
868
|
+
});
|
|
869
|
+
const client = setupTestNode();
|
|
870
|
+
const storage = await createAsyncStorage({
|
|
871
|
+
nodeName: "test",
|
|
872
|
+
storageName: "test-storage",
|
|
873
|
+
filename: dbPath,
|
|
874
|
+
});
|
|
583
875
|
// Create a group and add a member
|
|
584
|
-
const group =
|
|
876
|
+
const group = fixtures.node.createGroup();
|
|
585
877
|
group.addMember("everyone", "reader");
|
|
586
878
|
await group.core.waitForSync();
|
|
587
879
|
// Store the group in storage
|
|
588
880
|
const contentMessage = getNewContentSince(group.core, emptyKnownState(group.id));
|
|
589
881
|
const correctionCallback = vi.fn();
|
|
590
882
|
await storage.store(contentMessage, correctionCallback);
|
|
591
|
-
|
|
883
|
+
client.addStorage({ storage });
|
|
592
884
|
// Load the group on the new node
|
|
593
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
885
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
594
886
|
// Wait for sync should resolve immediately since the coValue is already synced
|
|
595
887
|
await expect(storage.waitForSync(group.id, groupOnNode.core)).resolves.toBeUndefined();
|
|
596
888
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
@@ -598,16 +890,30 @@ describe("StorageApiAsync", () => {
|
|
|
598
890
|
});
|
|
599
891
|
describe("close", () => {
|
|
600
892
|
test("should close without throwing an error", async () => {
|
|
601
|
-
const
|
|
893
|
+
const storage = await createAsyncStorage({
|
|
894
|
+
nodeName: "test",
|
|
895
|
+
storageName: "test-storage",
|
|
896
|
+
});
|
|
602
897
|
expect(() => storage.close()).not.toThrow();
|
|
603
898
|
});
|
|
604
899
|
});
|
|
605
900
|
describe("loadKnownState", () => {
|
|
606
901
|
test("should return cached knownState if available", async () => {
|
|
607
|
-
const
|
|
608
|
-
const
|
|
902
|
+
const dbPath = getDbPath();
|
|
903
|
+
const fixtures = setupTestNode();
|
|
904
|
+
await fixtures.addAsyncStorage({
|
|
905
|
+
ourName: "test",
|
|
906
|
+
storageName: "test-storage",
|
|
907
|
+
filename: dbPath,
|
|
908
|
+
});
|
|
909
|
+
const client = setupTestNode();
|
|
910
|
+
const { storage } = await client.addAsyncStorage({
|
|
911
|
+
ourName: "test",
|
|
912
|
+
storageName: "test-storage",
|
|
913
|
+
filename: dbPath,
|
|
914
|
+
});
|
|
609
915
|
// Create a group to have data in the database
|
|
610
|
-
const group =
|
|
916
|
+
const group = fixtures.node.createGroup();
|
|
611
917
|
group.addMember("everyone", "reader");
|
|
612
918
|
await group.core.waitForSync();
|
|
613
919
|
// First call should hit the database and cache the result
|
|
@@ -624,17 +930,32 @@ describe("StorageApiAsync", () => {
|
|
|
624
930
|
expect(result2).toEqual(result1);
|
|
625
931
|
});
|
|
626
932
|
test("should return undefined for non-existent CoValue", async () => {
|
|
627
|
-
const
|
|
933
|
+
const client = setupTestNode();
|
|
934
|
+
const { storage } = await client.addAsyncStorage({
|
|
935
|
+
ourName: "test",
|
|
936
|
+
storageName: "test-storage",
|
|
937
|
+
});
|
|
628
938
|
const result = await new Promise((resolve) => {
|
|
629
939
|
storage.loadKnownState("co_nonexistent", resolve);
|
|
630
940
|
});
|
|
631
941
|
expect(result).toBeUndefined();
|
|
632
942
|
});
|
|
633
943
|
test("should deduplicate concurrent requests for the same ID", async () => {
|
|
634
|
-
const
|
|
635
|
-
const
|
|
944
|
+
const dbPath = getDbPath();
|
|
945
|
+
const fixtures = setupTestNode();
|
|
946
|
+
await fixtures.addAsyncStorage({
|
|
947
|
+
ourName: "test",
|
|
948
|
+
storageName: "test-storage",
|
|
949
|
+
filename: dbPath,
|
|
950
|
+
});
|
|
951
|
+
const client = setupTestNode();
|
|
952
|
+
const { storage } = await client.addAsyncStorage({
|
|
953
|
+
ourName: "test",
|
|
954
|
+
storageName: "test-storage",
|
|
955
|
+
filename: dbPath,
|
|
956
|
+
});
|
|
636
957
|
// Create a group to have data in the database
|
|
637
|
-
const group =
|
|
958
|
+
const group = fixtures.node.createGroup();
|
|
638
959
|
group.addMember("everyone", "reader");
|
|
639
960
|
await group.core.waitForSync();
|
|
640
961
|
// Clear the cache to force database access
|
|
@@ -662,10 +983,21 @@ describe("StorageApiAsync", () => {
|
|
|
662
983
|
expect(dbClientSpy).toHaveBeenCalledTimes(1);
|
|
663
984
|
});
|
|
664
985
|
test("should use cache and not query database when cache is populated", async () => {
|
|
665
|
-
const
|
|
666
|
-
const
|
|
986
|
+
const dbPath = getDbPath();
|
|
987
|
+
const fixtures = setupTestNode();
|
|
988
|
+
await fixtures.addAsyncStorage({
|
|
989
|
+
ourName: "test",
|
|
990
|
+
storageName: "test-storage",
|
|
991
|
+
filename: dbPath,
|
|
992
|
+
});
|
|
993
|
+
const client = setupTestNode();
|
|
994
|
+
const { storage } = await client.addAsyncStorage({
|
|
995
|
+
ourName: "test",
|
|
996
|
+
storageName: "test-storage",
|
|
997
|
+
filename: dbPath,
|
|
998
|
+
});
|
|
667
999
|
// Create a group to have data in the database
|
|
668
|
-
const group =
|
|
1000
|
+
const group = fixtures.node.createGroup();
|
|
669
1001
|
group.addMember("everyone", "reader");
|
|
670
1002
|
await group.core.waitForSync();
|
|
671
1003
|
// Spy on the database client to track calls
|