cojson 0.19.3 → 0.19.5
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 +15 -0
- package/dist/coValues/coList.d.ts +10 -4
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +30 -4
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +3 -2
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +8 -10
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coPlainText.d.ts +2 -1
- package/dist/coValues/coPlainText.d.ts.map +1 -1
- package/dist/coValues/coPlainText.js +5 -2
- package/dist/coValues/coPlainText.js.map +1 -1
- package/dist/coValues/group.d.ts +7 -3
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +70 -24
- package/dist/coValues/group.js.map +1 -1
- package/dist/storage/sqlite/client.d.ts +3 -3
- package/dist/storage/sqlite/client.d.ts.map +1 -1
- package/dist/storage/sqlite/client.js +1 -1
- package/dist/storage/sqlite/client.js.map +1 -1
- package/dist/storage/sqliteAsync/client.d.ts +3 -3
- package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
- package/dist/storage/sqliteAsync/client.js +1 -1
- package/dist/storage/sqliteAsync/client.js.map +1 -1
- package/dist/storage/storageAsync.d.ts.map +1 -1
- package/dist/storage/storageAsync.js +7 -7
- package/dist/storage/storageAsync.js.map +1 -1
- package/dist/storage/storageSync.d.ts.map +1 -1
- package/dist/storage/storageSync.js +7 -7
- package/dist/storage/storageSync.js.map +1 -1
- package/dist/storage/types.d.ts +18 -14
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/tests/coList.test.js +28 -1
- package/dist/tests/coList.test.js.map +1 -1
- package/dist/tests/group.parentGroupCache.test.d.ts +2 -0
- package/dist/tests/group.parentGroupCache.test.d.ts.map +1 -0
- package/dist/tests/group.parentGroupCache.test.js +216 -0
- package/dist/tests/group.parentGroupCache.test.js.map +1 -0
- package/package.json +3 -3
- package/src/coValues/coList.ts +41 -8
- package/src/coValues/coMap.ts +15 -11
- package/src/coValues/coPlainText.ts +6 -2
- package/src/coValues/group.ts +99 -24
- package/src/storage/sqlite/client.ts +6 -3
- package/src/storage/sqliteAsync/client.ts +8 -3
- package/src/storage/storageAsync.ts +8 -5
- package/src/storage/storageSync.ts +8 -5
- package/src/storage/types.ts +43 -37
- package/src/tests/coList.test.ts +36 -3
- package/src/tests/group.parentGroupCache.test.ts +293 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
setupTestAccount,
|
|
4
|
+
setupTestNode,
|
|
5
|
+
hotSleep,
|
|
6
|
+
loadCoValueOrFail,
|
|
7
|
+
} from "./testUtils";
|
|
8
|
+
|
|
9
|
+
let jazzCloud: ReturnType<typeof setupTestNode>;
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
jazzCloud = setupTestNode({ isSyncServer: true });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe("Parent Group Cache", () => {
|
|
16
|
+
describe("Property 1: Parent group cache update correctness", () => {
|
|
17
|
+
test("cache contains correct entries after processing parent group reference transactions", async () => {
|
|
18
|
+
const account = await setupTestAccount({
|
|
19
|
+
connected: true,
|
|
20
|
+
});
|
|
21
|
+
const parentGroup = account.node.createGroup();
|
|
22
|
+
const childGroup = account.node.createGroup();
|
|
23
|
+
|
|
24
|
+
// Initially no parent groups
|
|
25
|
+
expect(childGroup.getParentGroups()).toEqual([]);
|
|
26
|
+
|
|
27
|
+
// Extend parent group
|
|
28
|
+
childGroup.extend(parentGroup);
|
|
29
|
+
expect(childGroup.getParentGroups()).toEqual([parentGroup]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("cache handles multiple updates to same parent group", async () => {
|
|
33
|
+
const account = await setupTestAccount({
|
|
34
|
+
connected: true,
|
|
35
|
+
});
|
|
36
|
+
const parentGroup = account.node.createGroup();
|
|
37
|
+
const childGroup = account.node.createGroup();
|
|
38
|
+
|
|
39
|
+
childGroup.extend(parentGroup);
|
|
40
|
+
expect(childGroup.getParentGroups()).toEqual([parentGroup]);
|
|
41
|
+
|
|
42
|
+
// Revoke and re-extend
|
|
43
|
+
childGroup.revokeExtend(parentGroup);
|
|
44
|
+
expect(childGroup.getParentGroups()).toEqual([]);
|
|
45
|
+
|
|
46
|
+
childGroup.extend(parentGroup, "admin");
|
|
47
|
+
expect(childGroup.getParentGroups()).toEqual([parentGroup]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("cache correctly handles empty state (no parent groups)", async () => {
|
|
51
|
+
const account = await setupTestAccount({
|
|
52
|
+
connected: true,
|
|
53
|
+
});
|
|
54
|
+
const group = account.node.createGroup();
|
|
55
|
+
|
|
56
|
+
expect(group.getParentGroups()).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("cache only contains parent groups that have reference transactions", async () => {
|
|
60
|
+
const account = await setupTestAccount({
|
|
61
|
+
connected: true,
|
|
62
|
+
});
|
|
63
|
+
const parentGroup1 = account.node.createGroup();
|
|
64
|
+
const parentGroup2 = account.node.createGroup();
|
|
65
|
+
const childGroup = account.node.createGroup();
|
|
66
|
+
|
|
67
|
+
// Only extend one parent group
|
|
68
|
+
childGroup.extend(parentGroup1);
|
|
69
|
+
|
|
70
|
+
const parentGroups = childGroup.getParentGroups();
|
|
71
|
+
expect(parentGroups).toHaveLength(1);
|
|
72
|
+
expect(parentGroups[0]?.id).toEqual(parentGroup1.id);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("cache handles multiple parent groups", async () => {
|
|
76
|
+
const account = await setupTestAccount({
|
|
77
|
+
connected: true,
|
|
78
|
+
});
|
|
79
|
+
const parentGroup1 = account.node.createGroup();
|
|
80
|
+
const parentGroup2 = account.node.createGroup();
|
|
81
|
+
const parentGroup3 = account.node.createGroup();
|
|
82
|
+
const childGroup = account.node.createGroup();
|
|
83
|
+
|
|
84
|
+
childGroup.extend(parentGroup1);
|
|
85
|
+
childGroup.extend(parentGroup2);
|
|
86
|
+
childGroup.extend(parentGroup3);
|
|
87
|
+
|
|
88
|
+
const parentGroups = childGroup.getParentGroups();
|
|
89
|
+
expect(parentGroups).toHaveLength(3);
|
|
90
|
+
expect(parentGroups.map((g) => g.id).sort()).toEqual(
|
|
91
|
+
[parentGroup1.id, parentGroup2.id, parentGroup3.id].sort(),
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("Property 2: Parent group cache chronological ordering", () => {
|
|
97
|
+
test("multiple parent group changes are stored in chronological order", async () => {
|
|
98
|
+
const account = await setupTestAccount({
|
|
99
|
+
connected: true,
|
|
100
|
+
});
|
|
101
|
+
const parentGroup = account.node.createGroup();
|
|
102
|
+
const childGroup = account.node.createGroup();
|
|
103
|
+
|
|
104
|
+
const t1 = hotSleep(10);
|
|
105
|
+
childGroup.extend(parentGroup, "reader");
|
|
106
|
+
const t2 = hotSleep(10);
|
|
107
|
+
childGroup.extend(parentGroup, "writer");
|
|
108
|
+
const t3 = hotSleep(10);
|
|
109
|
+
childGroup.extend(parentGroup, "admin");
|
|
110
|
+
|
|
111
|
+
// Check time travel queries return correct historical states
|
|
112
|
+
expect(childGroup.atTime(t1).getParentGroups()).toEqual([]);
|
|
113
|
+
expect(childGroup.atTime(t2).getParentGroups()).toEqual([
|
|
114
|
+
parentGroup.atTime(t2),
|
|
115
|
+
]);
|
|
116
|
+
expect(childGroup.atTime(t3).getParentGroups()).toEqual([
|
|
117
|
+
parentGroup.atTime(t3),
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
// Current state should have admin role
|
|
121
|
+
expect(childGroup.getParentGroups()).toEqual([parentGroup]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("out-of-order transaction processing maintains chronological order", async () => {
|
|
125
|
+
const account = await setupTestAccount({
|
|
126
|
+
connected: true,
|
|
127
|
+
});
|
|
128
|
+
const parentGroup = account.node.createGroup();
|
|
129
|
+
const childGroup = account.node.createGroup();
|
|
130
|
+
|
|
131
|
+
// Create transactions with different timestamps
|
|
132
|
+
const t1 = hotSleep(10);
|
|
133
|
+
const t2 = hotSleep(10);
|
|
134
|
+
|
|
135
|
+
childGroup.core.makeTransaction(
|
|
136
|
+
[
|
|
137
|
+
{
|
|
138
|
+
op: "set",
|
|
139
|
+
key: `parent_${parentGroup.id}`,
|
|
140
|
+
value: "revoked",
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
"private",
|
|
144
|
+
undefined,
|
|
145
|
+
t2,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
childGroup.core.makeTransaction(
|
|
149
|
+
[
|
|
150
|
+
{
|
|
151
|
+
op: "set",
|
|
152
|
+
key: `parent_${parentGroup.id}`,
|
|
153
|
+
value: "extend",
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
"private",
|
|
157
|
+
undefined,
|
|
158
|
+
t1,
|
|
159
|
+
);
|
|
160
|
+
// Verify chronological ordering through time travel
|
|
161
|
+
const groupAtT1 = childGroup.atTime(t1);
|
|
162
|
+
const groupAtT2 = childGroup.atTime(t2);
|
|
163
|
+
|
|
164
|
+
expect(groupAtT1.getParentGroups()).toEqual([parentGroup.atTime(t1)]);
|
|
165
|
+
expect(groupAtT2.getParentGroups()).toEqual([]);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("chronological ordering after rebuild", async () => {
|
|
169
|
+
const account = await setupTestAccount({
|
|
170
|
+
connected: true,
|
|
171
|
+
});
|
|
172
|
+
const parentGroup = account.node.createGroup();
|
|
173
|
+
const childGroup = account.node.createGroup();
|
|
174
|
+
|
|
175
|
+
const t1 = hotSleep(10);
|
|
176
|
+
childGroup.extend(parentGroup, "reader");
|
|
177
|
+
const t2 = hotSleep(10);
|
|
178
|
+
childGroup.extend(parentGroup, "writer");
|
|
179
|
+
|
|
180
|
+
// Rebuild should maintain chronological order
|
|
181
|
+
childGroup.rebuildFromCore();
|
|
182
|
+
|
|
183
|
+
const groupAtT1 = childGroup.atTime(t1);
|
|
184
|
+
const groupAtT2 = childGroup.atTime(t2);
|
|
185
|
+
|
|
186
|
+
expect(groupAtT1.getParentGroups()).toEqual([]);
|
|
187
|
+
expect(groupAtT2.getParentGroups()).toEqual([parentGroup.atTime(t2)]);
|
|
188
|
+
expect(childGroup.getParentGroups()).toEqual([parentGroup]);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe("Property 3: Rebuild round-trip", () => {
|
|
193
|
+
test("cache is cleared after rebuildFromCore", async () => {
|
|
194
|
+
const account = await setupTestAccount({
|
|
195
|
+
connected: true,
|
|
196
|
+
});
|
|
197
|
+
const parentGroup = account.node.createGroup();
|
|
198
|
+
const childGroup = account.node.createGroup();
|
|
199
|
+
|
|
200
|
+
childGroup.extend(parentGroup);
|
|
201
|
+
expect(childGroup.getParentGroups()).toEqual([parentGroup]);
|
|
202
|
+
|
|
203
|
+
// Rebuild should clear cache and rebuild it
|
|
204
|
+
childGroup.rebuildFromCore();
|
|
205
|
+
|
|
206
|
+
// Cache should be rebuilt and still work
|
|
207
|
+
expect(childGroup.getParentGroups()).toEqual([parentGroup]);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("cache is rebuilt correctly after clear", async () => {
|
|
211
|
+
const account = await setupTestAccount({
|
|
212
|
+
connected: true,
|
|
213
|
+
});
|
|
214
|
+
const parentGroup1 = account.node.createGroup();
|
|
215
|
+
const parentGroup2 = account.node.createGroup();
|
|
216
|
+
const childGroup = account.node.createGroup();
|
|
217
|
+
|
|
218
|
+
childGroup.extend(parentGroup1);
|
|
219
|
+
childGroup.extend(parentGroup2);
|
|
220
|
+
|
|
221
|
+
expect(childGroup.getParentGroups()).toHaveLength(2);
|
|
222
|
+
|
|
223
|
+
// Rebuild
|
|
224
|
+
childGroup.rebuildFromCore();
|
|
225
|
+
|
|
226
|
+
// Cache should be rebuilt with all parent groups
|
|
227
|
+
const parentGroups = childGroup.getParentGroups();
|
|
228
|
+
expect(parentGroups).toHaveLength(2);
|
|
229
|
+
expect(parentGroups.map((g) => g.id).sort()).toEqual(
|
|
230
|
+
[parentGroup1.id, parentGroup2.id].sort(),
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("parent group lookups work correctly after rebuild", async () => {
|
|
235
|
+
const alice = await setupTestAccount({
|
|
236
|
+
connected: true,
|
|
237
|
+
});
|
|
238
|
+
const bob = await setupTestAccount({
|
|
239
|
+
connected: true,
|
|
240
|
+
});
|
|
241
|
+
const parentGroup = alice.node.createGroup();
|
|
242
|
+
const childGroup = alice.node.createGroup();
|
|
243
|
+
|
|
244
|
+
parentGroup.addMember(
|
|
245
|
+
await loadCoValueOrFail(alice.node, bob.accountID),
|
|
246
|
+
"writer",
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
childGroup.extend(parentGroup);
|
|
250
|
+
|
|
251
|
+
// Check role inheritance before rebuild
|
|
252
|
+
expect(childGroup.roleOf(bob.accountID)).toEqual("writer");
|
|
253
|
+
|
|
254
|
+
// Rebuild
|
|
255
|
+
childGroup.rebuildFromCore();
|
|
256
|
+
|
|
257
|
+
// Role inheritance should still work after rebuild
|
|
258
|
+
expect(childGroup.roleOf(bob.accountID)).toEqual("writer");
|
|
259
|
+
expect(childGroup.getParentGroups()).toEqual([parentGroup]);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("rebuild maintains chronological ordering", async () => {
|
|
263
|
+
const alice = await setupTestAccount({
|
|
264
|
+
connected: true,
|
|
265
|
+
});
|
|
266
|
+
const bob = await setupTestAccount({
|
|
267
|
+
connected: true,
|
|
268
|
+
});
|
|
269
|
+
const parentGroup = alice.node.createGroup();
|
|
270
|
+
const childGroup = alice.node.createGroup();
|
|
271
|
+
|
|
272
|
+
parentGroup.addMember(
|
|
273
|
+
await loadCoValueOrFail(alice.node, bob.accountID),
|
|
274
|
+
"writer",
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
const t1 = hotSleep(10);
|
|
278
|
+
childGroup.extend(parentGroup, "reader");
|
|
279
|
+
const t2 = hotSleep(10);
|
|
280
|
+
childGroup.extend(parentGroup, "writer");
|
|
281
|
+
|
|
282
|
+
// Rebuild
|
|
283
|
+
childGroup.rebuildFromCore();
|
|
284
|
+
|
|
285
|
+
// Chronological ordering should be maintained
|
|
286
|
+
expect(childGroup.atTime(t1).roleOf(bob.accountID)).toEqual(undefined);
|
|
287
|
+
expect(childGroup.atTime(t2).roleOf(bob.accountID)).toEqual("reader");
|
|
288
|
+
expect(childGroup.atTime(Date.now()).roleOf(bob.accountID)).toEqual(
|
|
289
|
+
"writer",
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
});
|