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,23 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { afterEach, describe, expect, onTestFinished, test, vi } from "vitest";
|
|
6
|
-
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
7
|
-
import { CoID, LocalNode, RawCoMap, logger } from "../exports.js";
|
|
1
|
+
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { CoID, RawCoMap, logger } from "../exports.js";
|
|
8
3
|
import { CoValueCore } from "../exports.js";
|
|
9
4
|
import { NewContentMessage } from "../sync.js";
|
|
10
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
createAsyncStorage,
|
|
7
|
+
getAllCoValuesWaitingForDelete,
|
|
8
|
+
getCoValueStoredSessions,
|
|
9
|
+
getDbPath,
|
|
10
|
+
} from "./testStorage.js";
|
|
11
11
|
import {
|
|
12
12
|
SyncMessagesLog,
|
|
13
|
+
fillCoMapWithLargeData,
|
|
13
14
|
loadCoValueOrFail,
|
|
14
|
-
|
|
15
|
+
setupTestNode,
|
|
15
16
|
waitFor,
|
|
16
17
|
} from "./testUtils.js";
|
|
17
18
|
import { CoValueKnownState, emptyKnownState } from "../knownState.js";
|
|
18
19
|
|
|
19
|
-
const crypto = await WasmCrypto.create();
|
|
20
|
-
|
|
21
20
|
/**
|
|
22
21
|
* Helper function that gets new content since a known state, throwing if:
|
|
23
22
|
* - The coValue is not verified
|
|
@@ -40,68 +39,27 @@ function getNewContentSince(
|
|
|
40
39
|
return contentMessage;
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
async function createFixturesNode(customDbPath?: string) {
|
|
44
|
-
const [admin, session] = randomAgentAndSessionID();
|
|
45
|
-
const node = new LocalNode(admin.agentSecret, session, crypto);
|
|
46
|
-
|
|
47
|
-
// Create a unique database file for each test
|
|
48
|
-
const dbPath = customDbPath ?? join(tmpdir(), `test-${randomUUID()}.db`);
|
|
49
|
-
const storage = await createAsyncStorage({
|
|
50
|
-
filename: dbPath,
|
|
51
|
-
nodeName: "test",
|
|
52
|
-
storageName: "test-storage",
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
onTestFinished(() => {
|
|
56
|
-
try {
|
|
57
|
-
unlinkSync(dbPath);
|
|
58
|
-
} catch {}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
onTestFinished(async () => {
|
|
62
|
-
await node.gracefulShutdown();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
node.setStorage(storage);
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
fixturesNode: node,
|
|
69
|
-
dbPath,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function createTestNode(dbPath?: string) {
|
|
74
|
-
const [admin, session] = randomAgentAndSessionID();
|
|
75
|
-
const node = new LocalNode(admin.agentSecret, session, crypto);
|
|
76
|
-
|
|
77
|
-
const storage = await createAsyncStorage({
|
|
78
|
-
filename: dbPath,
|
|
79
|
-
nodeName: "test",
|
|
80
|
-
storageName: "test-storage",
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
onTestFinished(async () => {
|
|
84
|
-
await node.gracefulShutdown();
|
|
85
|
-
await storage.close();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
node,
|
|
90
|
-
storage,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
42
|
afterEach(() => {
|
|
95
43
|
SyncMessagesLog.clear();
|
|
44
|
+
vi.useRealTimers();
|
|
96
45
|
});
|
|
97
46
|
|
|
98
47
|
describe("StorageApiAsync", () => {
|
|
99
48
|
describe("getKnownState", () => {
|
|
100
49
|
test("should return known state for existing coValue ID", async () => {
|
|
101
|
-
const
|
|
102
|
-
|
|
50
|
+
const fixtures = setupTestNode();
|
|
51
|
+
await fixtures.addAsyncStorage({
|
|
52
|
+
ourName: "test",
|
|
53
|
+
storageName: "test-storage",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const client = setupTestNode();
|
|
57
|
+
const { storage } = await client.addAsyncStorage({
|
|
58
|
+
ourName: "test",
|
|
59
|
+
storageName: "test-storage",
|
|
60
|
+
});
|
|
103
61
|
|
|
104
|
-
const id =
|
|
62
|
+
const id = fixtures.node.createGroup().id;
|
|
105
63
|
const knownState = storage.getKnownState(id);
|
|
106
64
|
|
|
107
65
|
expect(knownState).toEqual(emptyKnownState(id));
|
|
@@ -109,7 +67,11 @@ describe("StorageApiAsync", () => {
|
|
|
109
67
|
});
|
|
110
68
|
|
|
111
69
|
test("should return different known states for different coValue IDs", async () => {
|
|
112
|
-
const
|
|
70
|
+
const client = setupTestNode();
|
|
71
|
+
const { storage } = await client.addAsyncStorage({
|
|
72
|
+
ourName: "test",
|
|
73
|
+
storageName: "test-storage",
|
|
74
|
+
});
|
|
113
75
|
const id1 = "test-id-1";
|
|
114
76
|
const id2 = "test-id-2";
|
|
115
77
|
|
|
@@ -122,7 +84,11 @@ describe("StorageApiAsync", () => {
|
|
|
122
84
|
|
|
123
85
|
describe("load", () => {
|
|
124
86
|
test("should handle non-existent coValue gracefully", async () => {
|
|
125
|
-
const
|
|
87
|
+
const client = setupTestNode();
|
|
88
|
+
const { storage } = await client.addAsyncStorage({
|
|
89
|
+
ourName: "test",
|
|
90
|
+
storageName: "test-storage",
|
|
91
|
+
});
|
|
126
92
|
const id = "non-existent-id";
|
|
127
93
|
const callback = vi.fn();
|
|
128
94
|
const done = vi.fn();
|
|
@@ -142,15 +108,27 @@ describe("StorageApiAsync", () => {
|
|
|
142
108
|
});
|
|
143
109
|
|
|
144
110
|
test("should load coValue with header only successfully", async () => {
|
|
145
|
-
const
|
|
146
|
-
const
|
|
111
|
+
const dbPath = getDbPath();
|
|
112
|
+
const fixtures = setupTestNode();
|
|
113
|
+
await fixtures.addAsyncStorage({
|
|
114
|
+
ourName: "test",
|
|
115
|
+
storageName: "test-storage",
|
|
116
|
+
filename: dbPath,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const client = setupTestNode();
|
|
120
|
+
const { storage } = await client.addAsyncStorage({
|
|
121
|
+
ourName: "test",
|
|
122
|
+
storageName: "test-storage",
|
|
123
|
+
filename: dbPath,
|
|
124
|
+
});
|
|
147
125
|
const callback = vi.fn((content) =>
|
|
148
|
-
node.syncManager.handleNewContent(content, "storage"),
|
|
126
|
+
client.node.syncManager.handleNewContent(content, "storage"),
|
|
149
127
|
);
|
|
150
128
|
const done = vi.fn();
|
|
151
129
|
|
|
152
130
|
// Create a real group and get its content message
|
|
153
|
-
const group =
|
|
131
|
+
const group = fixtures.node.createGroup();
|
|
154
132
|
await group.core.waitForSync();
|
|
155
133
|
|
|
156
134
|
// Get initial known state
|
|
@@ -172,7 +150,7 @@ describe("StorageApiAsync", () => {
|
|
|
172
150
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
173
151
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
174
152
|
|
|
175
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
153
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
176
154
|
|
|
177
155
|
expect(groupOnNode.core.verified.header).toEqual(
|
|
178
156
|
group.core.verified.header,
|
|
@@ -180,15 +158,27 @@ describe("StorageApiAsync", () => {
|
|
|
180
158
|
});
|
|
181
159
|
|
|
182
160
|
test("should load coValue with sessions and transactions successfully", async () => {
|
|
183
|
-
const
|
|
184
|
-
const
|
|
161
|
+
const dbPath = getDbPath();
|
|
162
|
+
const fixtures = setupTestNode();
|
|
163
|
+
await fixtures.addAsyncStorage({
|
|
164
|
+
ourName: "test",
|
|
165
|
+
storageName: "test-storage",
|
|
166
|
+
filename: dbPath,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const client = setupTestNode();
|
|
170
|
+
const { storage } = await client.addAsyncStorage({
|
|
171
|
+
ourName: "test",
|
|
172
|
+
storageName: "test-storage",
|
|
173
|
+
filename: dbPath,
|
|
174
|
+
});
|
|
185
175
|
const callback = vi.fn((content) =>
|
|
186
|
-
node.syncManager.handleNewContent(content, "storage"),
|
|
176
|
+
client.node.syncManager.handleNewContent(content, "storage"),
|
|
187
177
|
);
|
|
188
178
|
const done = vi.fn();
|
|
189
179
|
|
|
190
180
|
// Create a real group and add a member to create transactions
|
|
191
|
-
const group =
|
|
181
|
+
const group = fixtures.node.createGroup();
|
|
192
182
|
group.addMember("everyone", "reader");
|
|
193
183
|
await group.core.waitForSync();
|
|
194
184
|
|
|
@@ -203,7 +193,7 @@ describe("StorageApiAsync", () => {
|
|
|
203
193
|
id: group.id,
|
|
204
194
|
header: group.core.verified.header,
|
|
205
195
|
new: expect.objectContaining({
|
|
206
|
-
[
|
|
196
|
+
[fixtures.node.currentSessionID]: expect.any(Object),
|
|
207
197
|
}),
|
|
208
198
|
}),
|
|
209
199
|
);
|
|
@@ -213,17 +203,26 @@ describe("StorageApiAsync", () => {
|
|
|
213
203
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
214
204
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
215
205
|
|
|
216
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
206
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
217
207
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
218
208
|
});
|
|
219
209
|
});
|
|
220
210
|
|
|
221
211
|
describe("store", () => {
|
|
222
212
|
test("should store new coValue with header successfully", async () => {
|
|
223
|
-
const
|
|
224
|
-
|
|
213
|
+
const fixtures = setupTestNode();
|
|
214
|
+
await fixtures.addAsyncStorage({
|
|
215
|
+
ourName: "test",
|
|
216
|
+
storageName: "test-storage",
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const client = setupTestNode();
|
|
220
|
+
const storage = await createAsyncStorage({
|
|
221
|
+
nodeName: "test",
|
|
222
|
+
storageName: "test-storage",
|
|
223
|
+
});
|
|
225
224
|
// Create a real group and get its content message
|
|
226
|
-
const group =
|
|
225
|
+
const group = fixtures.node.createGroup();
|
|
227
226
|
const contentMessage = getNewContentSince(
|
|
228
227
|
group.core,
|
|
229
228
|
emptyKnownState(group.id),
|
|
@@ -241,9 +240,9 @@ describe("StorageApiAsync", () => {
|
|
|
241
240
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
242
241
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
243
242
|
|
|
244
|
-
|
|
243
|
+
client.addStorage({ storage });
|
|
245
244
|
|
|
246
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
245
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
247
246
|
|
|
248
247
|
expect(groupOnNode.core.verified.header).toEqual(
|
|
249
248
|
group.core.verified.header,
|
|
@@ -251,11 +250,20 @@ describe("StorageApiAsync", () => {
|
|
|
251
250
|
});
|
|
252
251
|
|
|
253
252
|
test("should store coValue with transactions successfully", async () => {
|
|
254
|
-
const
|
|
255
|
-
|
|
253
|
+
const fixtures = setupTestNode();
|
|
254
|
+
await fixtures.addAsyncStorage({
|
|
255
|
+
ourName: "test",
|
|
256
|
+
storageName: "test-storage",
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const client = setupTestNode();
|
|
260
|
+
const storage = await createAsyncStorage({
|
|
261
|
+
nodeName: "test",
|
|
262
|
+
storageName: "test-storage",
|
|
263
|
+
});
|
|
256
264
|
|
|
257
265
|
// Create a real group and add a member to create transactions
|
|
258
|
-
const group =
|
|
266
|
+
const group = fixtures.node.createGroup();
|
|
259
267
|
const knownState = group.core.knownState();
|
|
260
268
|
|
|
261
269
|
group.addMember("everyone", "reader");
|
|
@@ -277,17 +285,26 @@ describe("StorageApiAsync", () => {
|
|
|
277
285
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
278
286
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
279
287
|
|
|
280
|
-
|
|
288
|
+
client.addStorage({ storage });
|
|
281
289
|
|
|
282
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
290
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
283
291
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
284
292
|
});
|
|
285
293
|
|
|
286
294
|
test("should handle invalid assumption on header presence with correction", async () => {
|
|
287
|
-
const
|
|
288
|
-
|
|
295
|
+
const fixtures = setupTestNode();
|
|
296
|
+
await fixtures.addAsyncStorage({
|
|
297
|
+
ourName: "test",
|
|
298
|
+
storageName: "test-storage",
|
|
299
|
+
});
|
|
289
300
|
|
|
290
|
-
const
|
|
301
|
+
const client = setupTestNode();
|
|
302
|
+
const storage = await createAsyncStorage({
|
|
303
|
+
nodeName: "test",
|
|
304
|
+
storageName: "test-storage",
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const group = fixtures.node.createGroup();
|
|
291
308
|
const knownState = group.core.knownState();
|
|
292
309
|
|
|
293
310
|
group.addMember("everyone", "reader");
|
|
@@ -311,17 +328,26 @@ describe("StorageApiAsync", () => {
|
|
|
311
328
|
const updatedKnownState = storage.getKnownState(group.id);
|
|
312
329
|
expect(updatedKnownState).toEqual(group.core.knownState());
|
|
313
330
|
|
|
314
|
-
|
|
315
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
331
|
+
client.addStorage({ storage });
|
|
332
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
316
333
|
|
|
317
334
|
expect(groupOnNode.get("everyone")).toEqual("reader");
|
|
318
335
|
});
|
|
319
336
|
|
|
320
337
|
test("should handle invalid assumption on new content with correction", async () => {
|
|
321
|
-
const
|
|
322
|
-
|
|
338
|
+
const fixtures = setupTestNode();
|
|
339
|
+
await fixtures.addAsyncStorage({
|
|
340
|
+
ourName: "test",
|
|
341
|
+
storageName: "test-storage",
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const client = setupTestNode();
|
|
345
|
+
const storage = await createAsyncStorage({
|
|
346
|
+
nodeName: "test",
|
|
347
|
+
storageName: "test-storage",
|
|
348
|
+
});
|
|
323
349
|
|
|
324
|
-
const group =
|
|
350
|
+
const group = fixtures.node.createGroup();
|
|
325
351
|
|
|
326
352
|
const initialContent = getNewContentSince(
|
|
327
353
|
group.core,
|
|
@@ -357,17 +383,26 @@ describe("StorageApiAsync", () => {
|
|
|
357
383
|
const finalKnownState = storage.getKnownState(group.id);
|
|
358
384
|
expect(finalKnownState).toEqual(group.core.knownState());
|
|
359
385
|
|
|
360
|
-
|
|
361
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
386
|
+
client.addStorage({ storage });
|
|
387
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
362
388
|
|
|
363
389
|
expect(groupOnNode.get("everyone")).toEqual("writer");
|
|
364
390
|
});
|
|
365
391
|
|
|
366
392
|
test("should log an error when the correction callback returns undefined", async () => {
|
|
367
|
-
const
|
|
368
|
-
|
|
393
|
+
const fixtures = setupTestNode();
|
|
394
|
+
await fixtures.addAsyncStorage({
|
|
395
|
+
ourName: "test",
|
|
396
|
+
storageName: "test-storage",
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const client = setupTestNode();
|
|
400
|
+
const { storage } = await client.addAsyncStorage({
|
|
401
|
+
ourName: "test",
|
|
402
|
+
storageName: "test-storage",
|
|
403
|
+
});
|
|
369
404
|
|
|
370
|
-
const group =
|
|
405
|
+
const group = fixtures.node.createGroup();
|
|
371
406
|
|
|
372
407
|
const knownState = group.core.knownState();
|
|
373
408
|
group.addMember("everyone", "writer");
|
|
@@ -404,10 +439,19 @@ describe("StorageApiAsync", () => {
|
|
|
404
439
|
});
|
|
405
440
|
|
|
406
441
|
test("should log an error when the correction callback returns an invalid content message", async () => {
|
|
407
|
-
const
|
|
408
|
-
|
|
442
|
+
const fixtures = setupTestNode();
|
|
443
|
+
await fixtures.addAsyncStorage({
|
|
444
|
+
ourName: "test",
|
|
445
|
+
storageName: "test-storage",
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const client = setupTestNode();
|
|
449
|
+
const { storage } = await client.addAsyncStorage({
|
|
450
|
+
ourName: "test",
|
|
451
|
+
storageName: "test-storage",
|
|
452
|
+
});
|
|
409
453
|
|
|
410
|
-
const group =
|
|
454
|
+
const group = fixtures.node.createGroup();
|
|
411
455
|
|
|
412
456
|
const knownState = group.core.knownState();
|
|
413
457
|
group.addMember("everyone", "writer");
|
|
@@ -449,13 +493,17 @@ describe("StorageApiAsync", () => {
|
|
|
449
493
|
});
|
|
450
494
|
|
|
451
495
|
test("should handle invalid assumption when pushing multiple transactions with correction", async () => {
|
|
452
|
-
const
|
|
496
|
+
const client = setupTestNode();
|
|
497
|
+
const storage = await createAsyncStorage({
|
|
498
|
+
nodeName: "test",
|
|
499
|
+
storageName: "test-storage",
|
|
500
|
+
});
|
|
453
501
|
|
|
454
|
-
const core = node.createCoValue({
|
|
502
|
+
const core = client.node.createCoValue({
|
|
455
503
|
type: "comap",
|
|
456
504
|
ruleset: { type: "unsafeAllowAll" },
|
|
457
505
|
meta: null,
|
|
458
|
-
...crypto.createdNowUnique(),
|
|
506
|
+
...client.node.crypto.createdNowUnique(),
|
|
459
507
|
});
|
|
460
508
|
|
|
461
509
|
core.makeTransaction([{ count: 1 }], "trusting");
|
|
@@ -463,7 +511,7 @@ describe("StorageApiAsync", () => {
|
|
|
463
511
|
await core.waitForSync();
|
|
464
512
|
|
|
465
513
|
// Add storage later
|
|
466
|
-
|
|
514
|
+
client.addStorage({ storage });
|
|
467
515
|
|
|
468
516
|
core.makeTransaction([{ count: 2 }], "trusting");
|
|
469
517
|
core.makeTransaction([{ count: 3 }], "trusting");
|
|
@@ -492,20 +540,24 @@ describe("StorageApiAsync", () => {
|
|
|
492
540
|
});
|
|
493
541
|
|
|
494
542
|
test("should handle invalid assumption when pushing multiple transactions on different coValues with correction", async () => {
|
|
495
|
-
const
|
|
543
|
+
const client = setupTestNode();
|
|
544
|
+
const storage = await createAsyncStorage({
|
|
545
|
+
nodeName: "test",
|
|
546
|
+
storageName: "test-storage",
|
|
547
|
+
});
|
|
496
548
|
|
|
497
|
-
const core = node.createCoValue({
|
|
549
|
+
const core = client.node.createCoValue({
|
|
498
550
|
type: "comap",
|
|
499
551
|
ruleset: { type: "unsafeAllowAll" },
|
|
500
552
|
meta: null,
|
|
501
|
-
...crypto.createdNowUnique(),
|
|
553
|
+
...client.node.crypto.createdNowUnique(),
|
|
502
554
|
});
|
|
503
555
|
|
|
504
|
-
const core2 = node.createCoValue({
|
|
556
|
+
const core2 = client.node.createCoValue({
|
|
505
557
|
type: "comap",
|
|
506
558
|
ruleset: { type: "unsafeAllowAll" },
|
|
507
559
|
meta: null,
|
|
508
|
-
...crypto.createdNowUnique(),
|
|
560
|
+
...client.node.crypto.createdNowUnique(),
|
|
509
561
|
});
|
|
510
562
|
|
|
511
563
|
core.makeTransaction([{ count: 1 }], "trusting");
|
|
@@ -514,7 +566,7 @@ describe("StorageApiAsync", () => {
|
|
|
514
566
|
await core.waitForSync();
|
|
515
567
|
|
|
516
568
|
// Add storage later
|
|
517
|
-
|
|
569
|
+
client.addStorage({ storage });
|
|
518
570
|
|
|
519
571
|
core.makeTransaction([{ count: 2 }], "trusting");
|
|
520
572
|
core2.makeTransaction([{ count: 2 }], "trusting");
|
|
@@ -556,20 +608,24 @@ describe("StorageApiAsync", () => {
|
|
|
556
608
|
});
|
|
557
609
|
|
|
558
610
|
test("should handle close while pushing multiple transactions on different coValues with an invalid assumption", async () => {
|
|
559
|
-
const
|
|
611
|
+
const client = setupTestNode();
|
|
612
|
+
const storage = await createAsyncStorage({
|
|
613
|
+
nodeName: "test",
|
|
614
|
+
storageName: "test-storage",
|
|
615
|
+
});
|
|
560
616
|
|
|
561
|
-
const core = node.createCoValue({
|
|
617
|
+
const core = client.node.createCoValue({
|
|
562
618
|
type: "comap",
|
|
563
619
|
ruleset: { type: "unsafeAllowAll" },
|
|
564
620
|
meta: null,
|
|
565
|
-
...crypto.createdNowUnique(),
|
|
621
|
+
...client.node.crypto.createdNowUnique(),
|
|
566
622
|
});
|
|
567
623
|
|
|
568
|
-
const core2 = node.createCoValue({
|
|
624
|
+
const core2 = client.node.createCoValue({
|
|
569
625
|
type: "comap",
|
|
570
626
|
ruleset: { type: "unsafeAllowAll" },
|
|
571
627
|
meta: null,
|
|
572
|
-
...crypto.createdNowUnique(),
|
|
628
|
+
...client.node.crypto.createdNowUnique(),
|
|
573
629
|
});
|
|
574
630
|
|
|
575
631
|
core.makeTransaction([{ count: 1 }], "trusting");
|
|
@@ -578,7 +634,7 @@ describe("StorageApiAsync", () => {
|
|
|
578
634
|
await core.waitForSync();
|
|
579
635
|
|
|
580
636
|
// Add storage later
|
|
581
|
-
|
|
637
|
+
client.addStorage({ storage });
|
|
582
638
|
|
|
583
639
|
core.makeTransaction([{ count: 2 }], "trusting");
|
|
584
640
|
core2.makeTransaction([{ count: 2 }], "trusting");
|
|
@@ -623,15 +679,33 @@ describe("StorageApiAsync", () => {
|
|
|
623
679
|
});
|
|
624
680
|
|
|
625
681
|
test("should handle multiple sessions correctly", async () => {
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
const
|
|
682
|
+
const dbPath = getDbPath();
|
|
683
|
+
|
|
684
|
+
const fixtures = setupTestNode();
|
|
685
|
+
await fixtures.addAsyncStorage({
|
|
686
|
+
ourName: "test",
|
|
687
|
+
storageName: "test-storage",
|
|
688
|
+
filename: dbPath,
|
|
689
|
+
});
|
|
629
690
|
|
|
630
|
-
const
|
|
691
|
+
const fixture2 = setupTestNode();
|
|
692
|
+
await fixture2.addAsyncStorage({
|
|
693
|
+
ourName: "test",
|
|
694
|
+
storageName: "test-storage",
|
|
695
|
+
filename: dbPath,
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
const client = setupTestNode();
|
|
699
|
+
const storage = await createAsyncStorage({
|
|
700
|
+
nodeName: "test",
|
|
701
|
+
storageName: "test-storage",
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
const coValue = fixtures.node.createCoValue({
|
|
631
705
|
type: "comap",
|
|
632
706
|
ruleset: { type: "unsafeAllowAll" },
|
|
633
707
|
meta: null,
|
|
634
|
-
...crypto.createdNowUnique(),
|
|
708
|
+
...fixtures.node.crypto.createdNowUnique(),
|
|
635
709
|
});
|
|
636
710
|
|
|
637
711
|
coValue.makeTransaction(
|
|
@@ -646,7 +720,7 @@ describe("StorageApiAsync", () => {
|
|
|
646
720
|
await coValue.waitForSync();
|
|
647
721
|
|
|
648
722
|
const mapOnNode2 = await loadCoValueOrFail(
|
|
649
|
-
|
|
723
|
+
fixture2.node,
|
|
650
724
|
coValue.id as CoID<RawCoMap>,
|
|
651
725
|
);
|
|
652
726
|
|
|
@@ -670,27 +744,284 @@ describe("StorageApiAsync", () => {
|
|
|
670
744
|
await storage.store(contentMessage, correctionCallback);
|
|
671
745
|
await storage.waitForSync(mapOnNode2.id, mapOnNode2.core);
|
|
672
746
|
|
|
673
|
-
|
|
747
|
+
client.addStorage({ storage });
|
|
674
748
|
|
|
675
|
-
const finalMap = await loadCoValueOrFail(node, mapOnNode2.id);
|
|
749
|
+
const finalMap = await loadCoValueOrFail(client.node, mapOnNode2.id);
|
|
676
750
|
expect(finalMap.core.knownState()).toEqual(knownState);
|
|
677
751
|
});
|
|
678
752
|
});
|
|
679
753
|
|
|
754
|
+
describe("delete flow", () => {
|
|
755
|
+
test("deleteCoValue enqueues the coValue for erasure", async () => {
|
|
756
|
+
const storage = await createAsyncStorage({
|
|
757
|
+
nodeName: "test",
|
|
758
|
+
storageName: "test-storage",
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
const client = setupTestNode();
|
|
762
|
+
client.addStorage({ storage });
|
|
763
|
+
|
|
764
|
+
const group = client.node.createGroup();
|
|
765
|
+
const map = group.createMap();
|
|
766
|
+
map.core.deleteCoValue();
|
|
767
|
+
await map.core.waitForSync();
|
|
768
|
+
|
|
769
|
+
expect(await getAllCoValuesWaitingForDelete(storage)).toContain(map.id);
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
test("background erasure doesn't run if not enabled", async () => {
|
|
773
|
+
const dbPath = getDbPath();
|
|
774
|
+
|
|
775
|
+
const node = setupTestNode();
|
|
776
|
+
const { storage } = await node.addAsyncStorage({
|
|
777
|
+
ourName: "test",
|
|
778
|
+
storageName: "test-storage",
|
|
779
|
+
filename: dbPath,
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
const group = node.node.createGroup();
|
|
783
|
+
const map = group.createMap();
|
|
784
|
+
map.set("k", "v");
|
|
785
|
+
await map.core.waitForSync();
|
|
786
|
+
|
|
787
|
+
vi.useFakeTimers();
|
|
788
|
+
|
|
789
|
+
map.core.deleteCoValue();
|
|
790
|
+
await map.core.waitForSync();
|
|
791
|
+
|
|
792
|
+
await vi.runAllTimersAsync();
|
|
793
|
+
|
|
794
|
+
expect(await getAllCoValuesWaitingForDelete(storage)).toContain(map.id);
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
test("background erasure runs if enabled", async () => {
|
|
798
|
+
const dbPath = getDbPath();
|
|
799
|
+
|
|
800
|
+
const node = setupTestNode();
|
|
801
|
+
const { storage } = await node.addAsyncStorage({
|
|
802
|
+
ourName: "test",
|
|
803
|
+
storageName: "test-storage",
|
|
804
|
+
filename: dbPath,
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
vi.useFakeTimers();
|
|
808
|
+
node.node.enableDeletedCoValuesErasure();
|
|
809
|
+
|
|
810
|
+
const group = node.node.createGroup();
|
|
811
|
+
const map = group.createMap();
|
|
812
|
+
map.set("k", "v");
|
|
813
|
+
await map.core.waitForSync();
|
|
814
|
+
|
|
815
|
+
map.core.deleteCoValue();
|
|
816
|
+
await map.core.waitForSync();
|
|
817
|
+
|
|
818
|
+
await vi.runAllTimersAsync();
|
|
819
|
+
|
|
820
|
+
expect(await getAllCoValuesWaitingForDelete(storage)).not.toContain(
|
|
821
|
+
map.id,
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
const sessionIDs = await getCoValueStoredSessions(storage, map.id);
|
|
825
|
+
|
|
826
|
+
expect(sessionIDs).toHaveLength(1);
|
|
827
|
+
expect(sessionIDs[0]).toMatch(/_session_d[1-9A-HJ-NP-Za-km-z]+\$$/); // Delete session format
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
test("eraseAllDeletedCoValues deletes history but preserves tombstone", async () => {
|
|
831
|
+
const dbPath = getDbPath();
|
|
832
|
+
|
|
833
|
+
const node = setupTestNode();
|
|
834
|
+
const { storage } = await node.addAsyncStorage({
|
|
835
|
+
ourName: "test",
|
|
836
|
+
storageName: "test-storage",
|
|
837
|
+
filename: dbPath,
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
const group = node.node.createGroup();
|
|
841
|
+
const map = group.createMap();
|
|
842
|
+
map.set("k", "v");
|
|
843
|
+
await map.core.waitForSync();
|
|
844
|
+
|
|
845
|
+
map.core.deleteCoValue();
|
|
846
|
+
await map.core.waitForSync();
|
|
847
|
+
|
|
848
|
+
await waitFor(async () => {
|
|
849
|
+
const queued = await getAllCoValuesWaitingForDelete(storage);
|
|
850
|
+
expect(queued).toContain(map.id);
|
|
851
|
+
return true;
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
await storage.eraseAllDeletedCoValues();
|
|
855
|
+
|
|
856
|
+
const queued = await getAllCoValuesWaitingForDelete(storage);
|
|
857
|
+
expect(queued).not.toContain(map.id);
|
|
858
|
+
|
|
859
|
+
// Tombstone preserved + history erased when loaded from storage
|
|
860
|
+
const client = setupTestNode();
|
|
861
|
+
const { storage: clientStorage } = await client.addAsyncStorage({
|
|
862
|
+
ourName: "test",
|
|
863
|
+
storageName: "test-storage",
|
|
864
|
+
filename: dbPath,
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
const loaded = await loadCoValueOrFail(
|
|
868
|
+
client.node,
|
|
869
|
+
map.id as CoID<RawCoMap>,
|
|
870
|
+
);
|
|
871
|
+
|
|
872
|
+
expect(loaded.core.isDeleted).toBe(true);
|
|
873
|
+
expect(loaded.get("k")).toBeUndefined();
|
|
874
|
+
|
|
875
|
+
const sessionIDs = await getCoValueStoredSessions(clientStorage, map.id);
|
|
876
|
+
|
|
877
|
+
expect(sessionIDs).toHaveLength(1);
|
|
878
|
+
expect(sessionIDs[0]).toMatch(/_session_d[1-9A-HJ-NP-Za-km-z]+\$$/); // Delete session format
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
test("eraseAllDeletedCoValues does not break when called while a coValue is streaming from storage", async () => {
|
|
882
|
+
const dbPath = getDbPath();
|
|
883
|
+
|
|
884
|
+
const node = setupTestNode();
|
|
885
|
+
const { storage } = await node.addAsyncStorage({
|
|
886
|
+
ourName: "test",
|
|
887
|
+
storageName: "test-storage",
|
|
888
|
+
filename: dbPath,
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
const group = node.node.createGroup();
|
|
892
|
+
const map = group.createMap();
|
|
893
|
+
fillCoMapWithLargeData(map);
|
|
894
|
+
await map.core.waitForSync();
|
|
895
|
+
map.core.deleteCoValue();
|
|
896
|
+
await map.core.waitForSync();
|
|
897
|
+
|
|
898
|
+
storage.close();
|
|
899
|
+
|
|
900
|
+
const newSession = node.spawnNewSession();
|
|
901
|
+
const { storage: newStorage } = await newSession.addAsyncStorage({
|
|
902
|
+
ourName: "test",
|
|
903
|
+
storageName: "test-storage",
|
|
904
|
+
filename: dbPath,
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
const callback = vi.fn();
|
|
908
|
+
|
|
909
|
+
const loadPromise = new Promise((resolve) => {
|
|
910
|
+
newStorage.load(map.id, callback, resolve);
|
|
911
|
+
});
|
|
912
|
+
await newStorage.eraseAllDeletedCoValues();
|
|
913
|
+
|
|
914
|
+
expect(await loadPromise).toBe(true);
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
test("load interrupts eraseAllDeletedCoValues mid-run (resolves early, leaves some queued)", async () => {
|
|
918
|
+
const dbPath = getDbPath();
|
|
919
|
+
|
|
920
|
+
const node = setupTestNode();
|
|
921
|
+
const { storage } = await node.addAsyncStorage({
|
|
922
|
+
ourName: "test",
|
|
923
|
+
storageName: "test-storage",
|
|
924
|
+
filename: dbPath,
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
const group = node.node.createGroup();
|
|
928
|
+
|
|
929
|
+
const map1 = group.createMap();
|
|
930
|
+
map1.set("k", "v");
|
|
931
|
+
await map1.core.waitForSync();
|
|
932
|
+
|
|
933
|
+
const map2 = group.createMap();
|
|
934
|
+
map2.set("k", "v");
|
|
935
|
+
await map2.core.waitForSync();
|
|
936
|
+
|
|
937
|
+
map1.core.deleteCoValue();
|
|
938
|
+
map2.core.deleteCoValue();
|
|
939
|
+
await map1.core.waitForSync();
|
|
940
|
+
await map2.core.waitForSync();
|
|
941
|
+
|
|
942
|
+
await waitFor(async () => {
|
|
943
|
+
const queued = await getAllCoValuesWaitingForDelete(storage);
|
|
944
|
+
expect(queued).toEqual(expect.arrayContaining([map1.id, map2.id]));
|
|
945
|
+
return true;
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
let releaseBarrier!: () => void;
|
|
949
|
+
const barrier = new Promise<void>((resolve) => {
|
|
950
|
+
releaseBarrier = resolve;
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
let firstTxStartedResolve!: () => void;
|
|
954
|
+
const firstTxStarted = new Promise<void>((resolve) => {
|
|
955
|
+
firstTxStartedResolve = resolve;
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
// @ts-expect-error - dbClient is private
|
|
959
|
+
const dbClient = storage.dbClient;
|
|
960
|
+
const originalTransaction = dbClient.transaction.bind(dbClient);
|
|
961
|
+
|
|
962
|
+
let txCalls = 0;
|
|
963
|
+
const txSpy = vi
|
|
964
|
+
.spyOn(dbClient, "transaction")
|
|
965
|
+
.mockImplementation(async (callback) => {
|
|
966
|
+
txCalls += 1;
|
|
967
|
+
return originalTransaction(async (tx) => {
|
|
968
|
+
if (txCalls === 1) {
|
|
969
|
+
firstTxStartedResolve();
|
|
970
|
+
await barrier;
|
|
971
|
+
}
|
|
972
|
+
return callback(tx);
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
const erasePromise = storage.eraseAllDeletedCoValues();
|
|
977
|
+
|
|
978
|
+
// Ensure the eraser is in-flight and inside its first transaction.
|
|
979
|
+
await firstTxStarted;
|
|
980
|
+
|
|
981
|
+
// Trigger interruption. We don't await the load immediately to avoid doing
|
|
982
|
+
// DB reads while the transaction is being held open by the barrier.
|
|
983
|
+
const loadDone = new Promise<boolean>((resolve) => {
|
|
984
|
+
void storage.load("non-existent-id", () => {}, resolve);
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
releaseBarrier();
|
|
988
|
+
|
|
989
|
+
await erasePromise;
|
|
990
|
+
await loadDone;
|
|
991
|
+
|
|
992
|
+
const queuedAfter = await getAllCoValuesWaitingForDelete(storage);
|
|
993
|
+
expect(queuedAfter).toHaveLength(1);
|
|
994
|
+
|
|
995
|
+
txSpy.mockRestore();
|
|
996
|
+
});
|
|
997
|
+
});
|
|
998
|
+
|
|
680
999
|
describe("dependencies", () => {
|
|
681
1000
|
test("should push dependencies before the coValue", async () => {
|
|
682
|
-
const
|
|
683
|
-
const
|
|
1001
|
+
const dbPath = getDbPath();
|
|
1002
|
+
const fixtures = setupTestNode();
|
|
1003
|
+
await fixtures.addAsyncStorage({
|
|
1004
|
+
ourName: "test",
|
|
1005
|
+
storageName: "test-storage",
|
|
1006
|
+
filename: dbPath,
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
const client = setupTestNode();
|
|
1010
|
+
const storage = await createAsyncStorage({
|
|
1011
|
+
nodeName: "test",
|
|
1012
|
+
storageName: "test-storage",
|
|
1013
|
+
filename: dbPath,
|
|
1014
|
+
});
|
|
684
1015
|
|
|
685
1016
|
// Create a group and a map owned by that group to create dependencies
|
|
686
|
-
const group =
|
|
1017
|
+
const group = fixtures.node.createGroup();
|
|
687
1018
|
group.addMember("everyone", "reader");
|
|
688
1019
|
const map = group.createMap({ test: "value" });
|
|
689
1020
|
await group.core.waitForSync();
|
|
690
1021
|
await map.core.waitForSync();
|
|
691
1022
|
|
|
692
1023
|
const callback = vi.fn((content) =>
|
|
693
|
-
node.syncManager.handleNewContent(content, "storage"),
|
|
1024
|
+
client.node.syncManager.handleNewContent(content, "storage"),
|
|
694
1025
|
);
|
|
695
1026
|
const done = vi.fn();
|
|
696
1027
|
|
|
@@ -725,24 +1056,36 @@ describe("StorageApiAsync", () => {
|
|
|
725
1056
|
expect(updatedGroupKnownState).toEqual(group.core.knownState());
|
|
726
1057
|
expect(updatedMapKnownState).toEqual(map.core.knownState());
|
|
727
1058
|
|
|
728
|
-
|
|
729
|
-
const mapOnNode = await loadCoValueOrFail(node, map.id);
|
|
1059
|
+
client.addStorage({ storage });
|
|
1060
|
+
const mapOnNode = await loadCoValueOrFail(client.node, map.id);
|
|
730
1061
|
expect(mapOnNode.get("test")).toEqual("value");
|
|
731
1062
|
});
|
|
732
1063
|
|
|
733
1064
|
test("should handle dependencies that are already loaded correctly", async () => {
|
|
734
|
-
const
|
|
735
|
-
const
|
|
1065
|
+
const dbPath = getDbPath();
|
|
1066
|
+
const fixtures = setupTestNode();
|
|
1067
|
+
await fixtures.addAsyncStorage({
|
|
1068
|
+
ourName: "test",
|
|
1069
|
+
storageName: "test-storage",
|
|
1070
|
+
filename: dbPath,
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
const client = setupTestNode();
|
|
1074
|
+
const storage = await createAsyncStorage({
|
|
1075
|
+
nodeName: "test",
|
|
1076
|
+
storageName: "test-storage",
|
|
1077
|
+
filename: dbPath,
|
|
1078
|
+
});
|
|
736
1079
|
|
|
737
1080
|
// Create a group and a map owned by that group
|
|
738
|
-
const group =
|
|
1081
|
+
const group = fixtures.node.createGroup();
|
|
739
1082
|
group.addMember("everyone", "reader");
|
|
740
1083
|
const map = group.createMap({ test: "value" });
|
|
741
1084
|
await group.core.waitForSync();
|
|
742
1085
|
await map.core.waitForSync();
|
|
743
1086
|
|
|
744
1087
|
const callback = vi.fn((content) =>
|
|
745
|
-
node.syncManager.handleNewContent(content, "storage"),
|
|
1088
|
+
client.node.syncManager.handleNewContent(content, "storage"),
|
|
746
1089
|
);
|
|
747
1090
|
const done = vi.fn();
|
|
748
1091
|
|
|
@@ -778,24 +1121,36 @@ describe("StorageApiAsync", () => {
|
|
|
778
1121
|
const finalMapKnownState = storage.getKnownState(map.id);
|
|
779
1122
|
expect(finalMapKnownState).toEqual(map.core.knownState());
|
|
780
1123
|
|
|
781
|
-
|
|
782
|
-
const mapOnNode = await loadCoValueOrFail(node, map.id);
|
|
1124
|
+
client.addStorage({ storage });
|
|
1125
|
+
const mapOnNode = await loadCoValueOrFail(client.node, map.id);
|
|
783
1126
|
expect(mapOnNode.get("test")).toEqual("value");
|
|
784
1127
|
});
|
|
785
1128
|
|
|
786
1129
|
test("should load dependencies again if they were unmounted", async () => {
|
|
787
|
-
const
|
|
788
|
-
const
|
|
1130
|
+
const dbPath = getDbPath();
|
|
1131
|
+
const fixtures = setupTestNode();
|
|
1132
|
+
await fixtures.addAsyncStorage({
|
|
1133
|
+
ourName: "test",
|
|
1134
|
+
storageName: "test-storage",
|
|
1135
|
+
filename: dbPath,
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
const client = setupTestNode();
|
|
1139
|
+
const { storage } = await client.addAsyncStorage({
|
|
1140
|
+
ourName: "test",
|
|
1141
|
+
storageName: "test-storage",
|
|
1142
|
+
filename: dbPath,
|
|
1143
|
+
});
|
|
789
1144
|
|
|
790
1145
|
// Create a group and a map owned by that group
|
|
791
|
-
const group =
|
|
1146
|
+
const group = fixtures.node.createGroup();
|
|
792
1147
|
group.addMember("everyone", "reader");
|
|
793
1148
|
const map = group.createMap({ test: "value" });
|
|
794
1149
|
await group.core.waitForSync();
|
|
795
1150
|
await map.core.waitForSync();
|
|
796
1151
|
|
|
797
1152
|
const callback = vi.fn((content) =>
|
|
798
|
-
node.syncManager.handleNewContent(content, "storage"),
|
|
1153
|
+
client.node.syncManager.handleNewContent(content, "storage"),
|
|
799
1154
|
);
|
|
800
1155
|
const done = vi.fn();
|
|
801
1156
|
|
|
@@ -827,19 +1182,31 @@ describe("StorageApiAsync", () => {
|
|
|
827
1182
|
|
|
828
1183
|
expect(done).toHaveBeenCalledWith(true);
|
|
829
1184
|
|
|
830
|
-
|
|
831
|
-
const mapOnNode = await loadCoValueOrFail(node, map.id);
|
|
1185
|
+
client.addStorage({ storage });
|
|
1186
|
+
const mapOnNode = await loadCoValueOrFail(client.node, map.id);
|
|
832
1187
|
expect(mapOnNode.get("test")).toEqual("value");
|
|
833
1188
|
});
|
|
834
1189
|
});
|
|
835
1190
|
|
|
836
1191
|
describe("waitForSync", () => {
|
|
837
1192
|
test("should resolve when the coValue is already synced", async () => {
|
|
838
|
-
const
|
|
839
|
-
const
|
|
1193
|
+
const dbPath = getDbPath();
|
|
1194
|
+
const fixtures = setupTestNode();
|
|
1195
|
+
await fixtures.addAsyncStorage({
|
|
1196
|
+
ourName: "test",
|
|
1197
|
+
storageName: "test-storage",
|
|
1198
|
+
filename: dbPath,
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
const client = setupTestNode();
|
|
1202
|
+
const storage = await createAsyncStorage({
|
|
1203
|
+
nodeName: "test",
|
|
1204
|
+
storageName: "test-storage",
|
|
1205
|
+
filename: dbPath,
|
|
1206
|
+
});
|
|
840
1207
|
|
|
841
1208
|
// Create a group and add a member
|
|
842
|
-
const group =
|
|
1209
|
+
const group = fixtures.node.createGroup();
|
|
843
1210
|
group.addMember("everyone", "reader");
|
|
844
1211
|
await group.core.waitForSync();
|
|
845
1212
|
|
|
@@ -851,10 +1218,10 @@ describe("StorageApiAsync", () => {
|
|
|
851
1218
|
const correctionCallback = vi.fn();
|
|
852
1219
|
await storage.store(contentMessage, correctionCallback);
|
|
853
1220
|
|
|
854
|
-
|
|
1221
|
+
client.addStorage({ storage });
|
|
855
1222
|
|
|
856
1223
|
// Load the group on the new node
|
|
857
|
-
const groupOnNode = await loadCoValueOrFail(node, group.id);
|
|
1224
|
+
const groupOnNode = await loadCoValueOrFail(client.node, group.id);
|
|
858
1225
|
|
|
859
1226
|
// Wait for sync should resolve immediately since the coValue is already synced
|
|
860
1227
|
await expect(
|
|
@@ -867,7 +1234,10 @@ describe("StorageApiAsync", () => {
|
|
|
867
1234
|
|
|
868
1235
|
describe("close", () => {
|
|
869
1236
|
test("should close without throwing an error", async () => {
|
|
870
|
-
const
|
|
1237
|
+
const storage = await createAsyncStorage({
|
|
1238
|
+
nodeName: "test",
|
|
1239
|
+
storageName: "test-storage",
|
|
1240
|
+
});
|
|
871
1241
|
|
|
872
1242
|
expect(() => storage.close()).not.toThrow();
|
|
873
1243
|
});
|
|
@@ -875,11 +1245,23 @@ describe("StorageApiAsync", () => {
|
|
|
875
1245
|
|
|
876
1246
|
describe("loadKnownState", () => {
|
|
877
1247
|
test("should return cached knownState if available", async () => {
|
|
878
|
-
const
|
|
879
|
-
const
|
|
1248
|
+
const dbPath = getDbPath();
|
|
1249
|
+
const fixtures = setupTestNode();
|
|
1250
|
+
await fixtures.addAsyncStorage({
|
|
1251
|
+
ourName: "test",
|
|
1252
|
+
storageName: "test-storage",
|
|
1253
|
+
filename: dbPath,
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
const client = setupTestNode();
|
|
1257
|
+
const { storage } = await client.addAsyncStorage({
|
|
1258
|
+
ourName: "test",
|
|
1259
|
+
storageName: "test-storage",
|
|
1260
|
+
filename: dbPath,
|
|
1261
|
+
});
|
|
880
1262
|
|
|
881
1263
|
// Create a group to have data in the database
|
|
882
|
-
const group =
|
|
1264
|
+
const group = fixtures.node.createGroup();
|
|
883
1265
|
group.addMember("everyone", "reader");
|
|
884
1266
|
await group.core.waitForSync();
|
|
885
1267
|
|
|
@@ -905,7 +1287,11 @@ describe("StorageApiAsync", () => {
|
|
|
905
1287
|
});
|
|
906
1288
|
|
|
907
1289
|
test("should return undefined for non-existent CoValue", async () => {
|
|
908
|
-
const
|
|
1290
|
+
const client = setupTestNode();
|
|
1291
|
+
const { storage } = await client.addAsyncStorage({
|
|
1292
|
+
ourName: "test",
|
|
1293
|
+
storageName: "test-storage",
|
|
1294
|
+
});
|
|
909
1295
|
|
|
910
1296
|
const result = await new Promise<CoValueKnownState | undefined>(
|
|
911
1297
|
(resolve) => {
|
|
@@ -917,11 +1303,23 @@ describe("StorageApiAsync", () => {
|
|
|
917
1303
|
});
|
|
918
1304
|
|
|
919
1305
|
test("should deduplicate concurrent requests for the same ID", async () => {
|
|
920
|
-
const
|
|
921
|
-
const
|
|
1306
|
+
const dbPath = getDbPath();
|
|
1307
|
+
const fixtures = setupTestNode();
|
|
1308
|
+
await fixtures.addAsyncStorage({
|
|
1309
|
+
ourName: "test",
|
|
1310
|
+
storageName: "test-storage",
|
|
1311
|
+
filename: dbPath,
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
const client = setupTestNode();
|
|
1315
|
+
const { storage } = await client.addAsyncStorage({
|
|
1316
|
+
ourName: "test",
|
|
1317
|
+
storageName: "test-storage",
|
|
1318
|
+
filename: dbPath,
|
|
1319
|
+
});
|
|
922
1320
|
|
|
923
1321
|
// Create a group to have data in the database
|
|
924
|
-
const group =
|
|
1322
|
+
const group = fixtures.node.createGroup();
|
|
925
1323
|
group.addMember("everyone", "reader");
|
|
926
1324
|
await group.core.waitForSync();
|
|
927
1325
|
|
|
@@ -959,11 +1357,23 @@ describe("StorageApiAsync", () => {
|
|
|
959
1357
|
});
|
|
960
1358
|
|
|
961
1359
|
test("should use cache and not query database when cache is populated", async () => {
|
|
962
|
-
const
|
|
963
|
-
const
|
|
1360
|
+
const dbPath = getDbPath();
|
|
1361
|
+
const fixtures = setupTestNode();
|
|
1362
|
+
await fixtures.addAsyncStorage({
|
|
1363
|
+
ourName: "test",
|
|
1364
|
+
storageName: "test-storage",
|
|
1365
|
+
filename: dbPath,
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
const client = setupTestNode();
|
|
1369
|
+
const { storage } = await client.addAsyncStorage({
|
|
1370
|
+
ourName: "test",
|
|
1371
|
+
storageName: "test-storage",
|
|
1372
|
+
filename: dbPath,
|
|
1373
|
+
});
|
|
964
1374
|
|
|
965
1375
|
// Create a group to have data in the database
|
|
966
|
-
const group =
|
|
1376
|
+
const group = fixtures.node.createGroup();
|
|
967
1377
|
group.addMember("everyone", "reader");
|
|
968
1378
|
await group.core.waitForSync();
|
|
969
1379
|
|