@graffiti-garden/api 0.6.3 → 1.0.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/README.md +0 -4
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +3 -3
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +3 -3
- package/dist/src/1-api.d.ts +198 -330
- package/dist/src/1-api.d.ts.map +1 -1
- package/dist/src/2-types.d.ts +52 -161
- package/dist/src/2-types.d.ts.map +1 -1
- package/dist/src/3-errors.d.ts +2 -2
- package/dist/src/3-errors.d.ts.map +1 -1
- package/dist/tests/crud.d.ts +1 -1
- package/dist/tests/crud.d.ts.map +1 -1
- package/dist/tests/discover.d.ts +1 -1
- package/dist/tests/discover.d.ts.map +1 -1
- package/dist/tests/index.d.ts +0 -4
- package/dist/tests/index.d.ts.map +1 -1
- package/dist/tests/utils.d.ts +3 -3
- package/dist/tests/utils.d.ts.map +1 -1
- package/dist/tests.mjs +3 -1269
- package/dist/tests.mjs.map +4 -4
- package/package.json +6 -7
- package/src/1-api.ts +203 -338
- package/src/2-types.ts +53 -166
- package/src/3-errors.ts +6 -6
- package/tests/crud.ts +42 -320
- package/tests/discover.ts +51 -349
- package/tests/index.ts +2 -4
- package/tests/utils.ts +4 -4
- package/dist/tests/channel-stats.d.ts +0 -3
- package/dist/tests/channel-stats.d.ts.map +0 -1
- package/dist/tests/orphans.d.ts +0 -3
- package/dist/tests/orphans.d.ts.map +0 -1
- package/tests/channel-stats.ts +0 -129
- package/tests/orphans.ts +0 -128
package/dist/tests.mjs
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
// tests/crud.ts
|
|
2
|
-
import { it, expect, describe, beforeAll } from "vitest";
|
|
3
|
-
import {
|
|
4
|
-
GraffitiErrorNotFound,
|
|
5
|
-
GraffitiErrorSchemaMismatch,
|
|
6
|
-
GraffitiErrorInvalidSchema,
|
|
7
|
-
GraffitiErrorForbidden,
|
|
8
|
-
GraffitiErrorPatchTestFailed,
|
|
9
|
-
GraffitiErrorPatchError
|
|
10
|
-
} from "@graffiti-garden/api";
|
|
11
|
-
|
|
12
1
|
// tests/utils.ts
|
|
13
2
|
import { assert } from "vitest";
|
|
14
3
|
function randomString() {
|
|
@@ -22,7 +11,7 @@ function randomValue() {
|
|
|
22
11
|
[randomString()]: randomString()
|
|
23
12
|
};
|
|
24
13
|
}
|
|
25
|
-
function
|
|
14
|
+
function randomPostObject() {
|
|
26
15
|
return {
|
|
27
16
|
value: randomValue(),
|
|
28
17
|
channels: [randomString(), randomString()]
|
|
@@ -36,7 +25,7 @@ async function nextStreamValue(iterator) {
|
|
|
36
25
|
}
|
|
37
26
|
function continueStream(graffiti, streamReturn, type, session) {
|
|
38
27
|
if (type === "cursor") {
|
|
39
|
-
return graffiti.
|
|
28
|
+
return graffiti.continueDiscover(
|
|
40
29
|
streamReturn.cursor,
|
|
41
30
|
session
|
|
42
31
|
);
|
|
@@ -44,1265 +33,10 @@ function continueStream(graffiti, streamReturn, type, session) {
|
|
|
44
33
|
return streamReturn.continue();
|
|
45
34
|
}
|
|
46
35
|
}
|
|
47
|
-
|
|
48
|
-
// tests/crud.ts
|
|
49
|
-
var graffitiCRUDTests = (useGraffiti, useSession1, useSession2) => {
|
|
50
|
-
describe.concurrent(
|
|
51
|
-
"CRUD",
|
|
52
|
-
{
|
|
53
|
-
timeout: 2e4
|
|
54
|
-
},
|
|
55
|
-
() => {
|
|
56
|
-
let graffiti;
|
|
57
|
-
let session;
|
|
58
|
-
let session1;
|
|
59
|
-
let session2;
|
|
60
|
-
beforeAll(async () => {
|
|
61
|
-
graffiti = useGraffiti();
|
|
62
|
-
session1 = await useSession1();
|
|
63
|
-
session = session1;
|
|
64
|
-
session2 = await useSession2();
|
|
65
|
-
});
|
|
66
|
-
it("put, get, delete", async () => {
|
|
67
|
-
const value = {
|
|
68
|
-
something: "hello, world~ c:"
|
|
69
|
-
};
|
|
70
|
-
const channels = [randomString(), randomString()];
|
|
71
|
-
const previous = await graffiti.put({ value, channels }, session);
|
|
72
|
-
expect(previous.value).toEqual({});
|
|
73
|
-
expect(previous.channels).toEqual([]);
|
|
74
|
-
expect(previous.allowed).toEqual([]);
|
|
75
|
-
expect(previous.actor).toEqual(session.actor);
|
|
76
|
-
const gotten = await graffiti.get(previous, {});
|
|
77
|
-
expect(gotten.value).toEqual(value);
|
|
78
|
-
expect(gotten.channels).toEqual([]);
|
|
79
|
-
expect(gotten.allowed).toBeUndefined();
|
|
80
|
-
expect(gotten.url).toEqual(previous.url);
|
|
81
|
-
expect(gotten.actor).toEqual(previous.actor);
|
|
82
|
-
expect(gotten.lastModified).toEqual(previous.lastModified);
|
|
83
|
-
const newValue = {
|
|
84
|
-
something: "goodbye, world~ :c"
|
|
85
|
-
};
|
|
86
|
-
const beforeReplaced = await graffiti.put(
|
|
87
|
-
{
|
|
88
|
-
url: previous.url,
|
|
89
|
-
value: newValue,
|
|
90
|
-
channels: []
|
|
91
|
-
},
|
|
92
|
-
session
|
|
93
|
-
);
|
|
94
|
-
expect(beforeReplaced.value).toEqual(value);
|
|
95
|
-
expect(beforeReplaced.url).toEqual(previous.url);
|
|
96
|
-
expect(beforeReplaced.actor).toEqual(previous.actor);
|
|
97
|
-
expect(beforeReplaced.lastModified).toBeGreaterThanOrEqual(
|
|
98
|
-
gotten.lastModified
|
|
99
|
-
);
|
|
100
|
-
const afterReplaced = await graffiti.get(previous, {});
|
|
101
|
-
expect(afterReplaced.value).toEqual(newValue);
|
|
102
|
-
expect(afterReplaced.lastModified).toEqual(beforeReplaced.lastModified);
|
|
103
|
-
const beforeDeleted = await graffiti.delete(afterReplaced, session);
|
|
104
|
-
expect(beforeDeleted.value).toEqual(newValue);
|
|
105
|
-
expect(beforeDeleted.lastModified).toBeGreaterThanOrEqual(
|
|
106
|
-
beforeReplaced.lastModified
|
|
107
|
-
);
|
|
108
|
-
await expect(graffiti.get(afterReplaced, {})).rejects.toBeInstanceOf(
|
|
109
|
-
GraffitiErrorNotFound
|
|
110
|
-
);
|
|
111
|
-
await expect(graffiti.delete(beforeDeleted, session)).rejects.toThrow(
|
|
112
|
-
GraffitiErrorNotFound
|
|
113
|
-
);
|
|
114
|
-
await expect(
|
|
115
|
-
graffiti.put(
|
|
116
|
-
{ url: beforeDeleted.url, value: {}, channels: [] },
|
|
117
|
-
session
|
|
118
|
-
)
|
|
119
|
-
).rejects.toThrow(GraffitiErrorNotFound);
|
|
120
|
-
});
|
|
121
|
-
it("put, delete, patch with wrong actor", async () => {
|
|
122
|
-
await expect(
|
|
123
|
-
graffiti.put(
|
|
124
|
-
{ value: {}, channels: [], actor: session2.actor },
|
|
125
|
-
session1
|
|
126
|
-
)
|
|
127
|
-
).rejects.toThrow(GraffitiErrorForbidden);
|
|
128
|
-
const putted = await graffiti.put(
|
|
129
|
-
{ value: {}, channels: [] },
|
|
130
|
-
session2
|
|
131
|
-
);
|
|
132
|
-
await expect(
|
|
133
|
-
graffiti.put(
|
|
134
|
-
{
|
|
135
|
-
url: putted.url,
|
|
136
|
-
value: {},
|
|
137
|
-
channels: []
|
|
138
|
-
},
|
|
139
|
-
session1
|
|
140
|
-
)
|
|
141
|
-
).rejects.toThrow(GraffitiErrorForbidden);
|
|
142
|
-
await expect(graffiti.delete(putted, session1)).rejects.toThrow(
|
|
143
|
-
GraffitiErrorForbidden
|
|
144
|
-
);
|
|
145
|
-
await expect(graffiti.patch({}, putted, session1)).rejects.toThrow(
|
|
146
|
-
GraffitiErrorForbidden
|
|
147
|
-
);
|
|
148
|
-
});
|
|
149
|
-
it("put, patch, delete object that is not allowed", async () => {
|
|
150
|
-
const putted = await graffiti.put(
|
|
151
|
-
{
|
|
152
|
-
value: {},
|
|
153
|
-
channels: [],
|
|
154
|
-
allowed: []
|
|
155
|
-
},
|
|
156
|
-
session1
|
|
157
|
-
);
|
|
158
|
-
await expect(
|
|
159
|
-
graffiti.put(
|
|
160
|
-
{
|
|
161
|
-
url: putted.url,
|
|
162
|
-
value: {},
|
|
163
|
-
channels: []
|
|
164
|
-
},
|
|
165
|
-
session2
|
|
166
|
-
)
|
|
167
|
-
).rejects.toThrow(GraffitiErrorNotFound);
|
|
168
|
-
await expect(graffiti.patch({}, putted, session2)).rejects.toThrow(
|
|
169
|
-
GraffitiErrorNotFound
|
|
170
|
-
);
|
|
171
|
-
await expect(graffiti.delete(putted, session2)).rejects.toThrow(
|
|
172
|
-
GraffitiErrorNotFound
|
|
173
|
-
);
|
|
174
|
-
});
|
|
175
|
-
it("put and get with schema", async () => {
|
|
176
|
-
const schema = {
|
|
177
|
-
properties: {
|
|
178
|
-
value: {
|
|
179
|
-
properties: {
|
|
180
|
-
something: {
|
|
181
|
-
type: "string"
|
|
182
|
-
},
|
|
183
|
-
another: {
|
|
184
|
-
type: "array",
|
|
185
|
-
items: {
|
|
186
|
-
type: "number"
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
deeper: {
|
|
190
|
-
type: "object",
|
|
191
|
-
properties: {
|
|
192
|
-
deepProp: {
|
|
193
|
-
type: "string"
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
required: ["deepProp"]
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
required: ["another", "deeper"]
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
const goodValue = {
|
|
204
|
-
something: "hello",
|
|
205
|
-
another: [1, 2, 3],
|
|
206
|
-
deeper: {
|
|
207
|
-
deepProp: "hello"
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
const putted = await graffiti.put(
|
|
211
|
-
{
|
|
212
|
-
value: goodValue,
|
|
213
|
-
channels: []
|
|
214
|
-
},
|
|
215
|
-
session
|
|
216
|
-
);
|
|
217
|
-
const gotten = await graffiti.get(putted, schema);
|
|
218
|
-
expect(gotten.value.something).toEqual(goodValue.something);
|
|
219
|
-
expect(gotten.value.another).toEqual(goodValue.another);
|
|
220
|
-
expect(gotten.value.another[0]).toEqual(1);
|
|
221
|
-
expect(gotten.value.deeper.deepProp).toEqual(goodValue.deeper.deepProp);
|
|
222
|
-
});
|
|
223
|
-
it("put and get with invalid schema", async () => {
|
|
224
|
-
const putted = await graffiti.put(
|
|
225
|
-
{ value: {}, channels: [] },
|
|
226
|
-
session
|
|
227
|
-
);
|
|
228
|
-
await expect(
|
|
229
|
-
graffiti.get(putted, {
|
|
230
|
-
properties: {
|
|
231
|
-
value: {
|
|
232
|
-
//@ts-ignore
|
|
233
|
-
type: "asdf"
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
})
|
|
237
|
-
).rejects.toThrow(GraffitiErrorInvalidSchema);
|
|
238
|
-
});
|
|
239
|
-
it("put and get with wrong schema", async () => {
|
|
240
|
-
const putted = await graffiti.put(
|
|
241
|
-
{
|
|
242
|
-
value: {
|
|
243
|
-
hello: "world"
|
|
244
|
-
},
|
|
245
|
-
channels: []
|
|
246
|
-
},
|
|
247
|
-
session
|
|
248
|
-
);
|
|
249
|
-
await expect(
|
|
250
|
-
graffiti.get(putted, {
|
|
251
|
-
properties: {
|
|
252
|
-
value: {
|
|
253
|
-
properties: {
|
|
254
|
-
hello: {
|
|
255
|
-
type: "number"
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
})
|
|
261
|
-
).rejects.toThrow(GraffitiErrorSchemaMismatch);
|
|
262
|
-
});
|
|
263
|
-
it("put and get with empty access control", async () => {
|
|
264
|
-
const value = {
|
|
265
|
-
um: "hi"
|
|
266
|
-
};
|
|
267
|
-
const allowed = [randomString()];
|
|
268
|
-
const channels = [randomString()];
|
|
269
|
-
const putted = await graffiti.put(
|
|
270
|
-
{ value, allowed, channels },
|
|
271
|
-
session1
|
|
272
|
-
);
|
|
273
|
-
const gotten = await graffiti.get(putted, {}, session1);
|
|
274
|
-
expect(gotten.value).toEqual(value);
|
|
275
|
-
expect(gotten.allowed).toEqual(allowed);
|
|
276
|
-
expect(gotten.channels).toEqual(channels);
|
|
277
|
-
await expect(graffiti.get(putted, {})).rejects.toBeInstanceOf(
|
|
278
|
-
GraffitiErrorNotFound
|
|
279
|
-
);
|
|
280
|
-
await expect(graffiti.get(putted, {}, session2)).rejects.toBeInstanceOf(
|
|
281
|
-
GraffitiErrorNotFound
|
|
282
|
-
);
|
|
283
|
-
});
|
|
284
|
-
it("put and get with specific access control", async () => {
|
|
285
|
-
const value = {
|
|
286
|
-
um: "hi"
|
|
287
|
-
};
|
|
288
|
-
const allowed = [randomString(), session2.actor, randomString()];
|
|
289
|
-
const channels = [randomString()];
|
|
290
|
-
const putted = await graffiti.put(
|
|
291
|
-
{
|
|
292
|
-
value,
|
|
293
|
-
allowed,
|
|
294
|
-
channels
|
|
295
|
-
},
|
|
296
|
-
session1
|
|
297
|
-
);
|
|
298
|
-
const gotten = await graffiti.get(putted, {}, session1);
|
|
299
|
-
expect(gotten.value).toEqual(value);
|
|
300
|
-
expect(gotten.allowed).toEqual(allowed);
|
|
301
|
-
expect(gotten.channels).toEqual(channels);
|
|
302
|
-
await expect(graffiti.get(putted, {})).rejects.toBeInstanceOf(
|
|
303
|
-
GraffitiErrorNotFound
|
|
304
|
-
);
|
|
305
|
-
const gotten2 = await graffiti.get(putted, {}, session2);
|
|
306
|
-
expect(gotten2.value).toEqual(value);
|
|
307
|
-
expect(gotten2.allowed).toEqual([session2.actor]);
|
|
308
|
-
expect(gotten2.channels).toEqual([]);
|
|
309
|
-
});
|
|
310
|
-
it("patch value", async () => {
|
|
311
|
-
const value = {
|
|
312
|
-
something: "hello, world~ c:"
|
|
313
|
-
};
|
|
314
|
-
const putted = await graffiti.put({ value, channels: [] }, session);
|
|
315
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
316
|
-
const patch = {
|
|
317
|
-
value: [
|
|
318
|
-
{ op: "replace", path: "/something", value: "goodbye, world~ :c" }
|
|
319
|
-
]
|
|
320
|
-
};
|
|
321
|
-
const beforePatched = await graffiti.patch(patch, putted, session);
|
|
322
|
-
expect(beforePatched.value).toEqual(value);
|
|
323
|
-
expect(beforePatched.lastModified).toBeGreaterThan(putted.lastModified);
|
|
324
|
-
const gotten = await graffiti.get(putted, {});
|
|
325
|
-
expect(gotten.value).toEqual({
|
|
326
|
-
something: "goodbye, world~ :c"
|
|
327
|
-
});
|
|
328
|
-
expect(beforePatched.lastModified).toBe(gotten.lastModified);
|
|
329
|
-
await graffiti.delete(putted, session);
|
|
330
|
-
});
|
|
331
|
-
it("patch deleted object", async () => {
|
|
332
|
-
const putted = await graffiti.put(randomPutObject(), session);
|
|
333
|
-
const deleted = await graffiti.delete(putted, session);
|
|
334
|
-
await expect(
|
|
335
|
-
graffiti.patch({}, putted, session)
|
|
336
|
-
).rejects.toBeInstanceOf(GraffitiErrorNotFound);
|
|
337
|
-
});
|
|
338
|
-
it("deep patch", async () => {
|
|
339
|
-
const value = {
|
|
340
|
-
something: {
|
|
341
|
-
another: {
|
|
342
|
-
somethingElse: "hello"
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
const putted = await graffiti.put(
|
|
347
|
-
{ value, channels: [] },
|
|
348
|
-
session
|
|
349
|
-
);
|
|
350
|
-
const beforePatch = await graffiti.patch(
|
|
351
|
-
{
|
|
352
|
-
value: [
|
|
353
|
-
{
|
|
354
|
-
op: "replace",
|
|
355
|
-
path: "/something/another/somethingElse",
|
|
356
|
-
value: "goodbye"
|
|
357
|
-
}
|
|
358
|
-
]
|
|
359
|
-
},
|
|
360
|
-
putted,
|
|
361
|
-
session
|
|
362
|
-
);
|
|
363
|
-
const gotten = await graffiti.get(putted, {});
|
|
364
|
-
expect(beforePatch.value).toEqual(value);
|
|
365
|
-
expect(gotten.value).toEqual({
|
|
366
|
-
something: {
|
|
367
|
-
another: {
|
|
368
|
-
somethingElse: "goodbye"
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
it("patch channels", async () => {
|
|
374
|
-
const channelsBefore = [randomString()];
|
|
375
|
-
const channelsAfter = [randomString()];
|
|
376
|
-
const putted = await graffiti.put(
|
|
377
|
-
{ value: {}, channels: channelsBefore },
|
|
378
|
-
session
|
|
379
|
-
);
|
|
380
|
-
const patch = {
|
|
381
|
-
channels: [{ op: "replace", path: "/0", value: channelsAfter[0] }]
|
|
382
|
-
};
|
|
383
|
-
const patched = await graffiti.patch(patch, putted, session);
|
|
384
|
-
expect(patched.channels).toEqual(channelsBefore);
|
|
385
|
-
const gotten = await graffiti.get(putted, {}, session);
|
|
386
|
-
expect(gotten.channels).toEqual(channelsAfter);
|
|
387
|
-
await graffiti.delete(putted, session);
|
|
388
|
-
});
|
|
389
|
-
it("patch 'increment' with test", async () => {
|
|
390
|
-
const putted = await graffiti.put(
|
|
391
|
-
{
|
|
392
|
-
value: {
|
|
393
|
-
counter: 1
|
|
394
|
-
},
|
|
395
|
-
channels: []
|
|
396
|
-
},
|
|
397
|
-
session
|
|
398
|
-
);
|
|
399
|
-
const previous = await graffiti.patch(
|
|
400
|
-
{
|
|
401
|
-
value: [
|
|
402
|
-
{ op: "test", path: "/counter", value: 1 },
|
|
403
|
-
{ op: "replace", path: "/counter", value: 2 }
|
|
404
|
-
]
|
|
405
|
-
},
|
|
406
|
-
putted,
|
|
407
|
-
session
|
|
408
|
-
);
|
|
409
|
-
expect(previous.value).toEqual({ counter: 1 });
|
|
410
|
-
const result = await graffiti.get(previous, {
|
|
411
|
-
properties: {
|
|
412
|
-
value: {
|
|
413
|
-
properties: {
|
|
414
|
-
counter: {
|
|
415
|
-
type: "integer"
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
expect(result.value.counter).toEqual(2);
|
|
422
|
-
await expect(
|
|
423
|
-
graffiti.patch(
|
|
424
|
-
{
|
|
425
|
-
value: [
|
|
426
|
-
{ op: "test", path: "/counter", value: 1 },
|
|
427
|
-
{ op: "replace", path: "/counter", value: 3 }
|
|
428
|
-
]
|
|
429
|
-
},
|
|
430
|
-
putted,
|
|
431
|
-
session
|
|
432
|
-
)
|
|
433
|
-
).rejects.toThrow(GraffitiErrorPatchTestFailed);
|
|
434
|
-
});
|
|
435
|
-
it("invalid patch", async () => {
|
|
436
|
-
const object = randomPutObject();
|
|
437
|
-
const putted = await graffiti.put(object, session);
|
|
438
|
-
await expect(
|
|
439
|
-
graffiti.patch(
|
|
440
|
-
{
|
|
441
|
-
value: [
|
|
442
|
-
{ op: "add", path: "/root", value: [] },
|
|
443
|
-
{ op: "add", path: "/root/2", value: 2 }
|
|
444
|
-
// out of bounds
|
|
445
|
-
]
|
|
446
|
-
},
|
|
447
|
-
putted,
|
|
448
|
-
session
|
|
449
|
-
)
|
|
450
|
-
).rejects.toThrow(GraffitiErrorPatchError);
|
|
451
|
-
});
|
|
452
|
-
it("patch channels to be wrong", async () => {
|
|
453
|
-
const object = randomPutObject();
|
|
454
|
-
object.allowed = [randomString()];
|
|
455
|
-
const putted = await graffiti.put(object, session);
|
|
456
|
-
const patches = [
|
|
457
|
-
{
|
|
458
|
-
channels: [{ op: "replace", path: "", value: null }]
|
|
459
|
-
},
|
|
460
|
-
{
|
|
461
|
-
channels: [{ op: "replace", path: "", value: {} }]
|
|
462
|
-
},
|
|
463
|
-
{
|
|
464
|
-
channels: [{ op: "replace", path: "", value: ["hello", ["hi"]] }]
|
|
465
|
-
},
|
|
466
|
-
{
|
|
467
|
-
channels: [{ op: "add", path: "/0", value: 1 }]
|
|
468
|
-
},
|
|
469
|
-
{
|
|
470
|
-
value: [{ op: "replace", path: "", value: "not an object" }]
|
|
471
|
-
},
|
|
472
|
-
{
|
|
473
|
-
value: [{ op: "replace", path: "", value: null }]
|
|
474
|
-
},
|
|
475
|
-
{
|
|
476
|
-
value: [{ op: "replace", path: "", value: [] }]
|
|
477
|
-
},
|
|
478
|
-
{
|
|
479
|
-
allowed: [{ op: "replace", path: "", value: {} }]
|
|
480
|
-
},
|
|
481
|
-
{
|
|
482
|
-
allowed: [{ op: "replace", path: "", value: ["hello", ["hi"]] }]
|
|
483
|
-
}
|
|
484
|
-
];
|
|
485
|
-
for (const patch of patches) {
|
|
486
|
-
await expect(graffiti.patch(patch, putted, session)).rejects.toThrow(
|
|
487
|
-
GraffitiErrorPatchError
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
|
-
const gotten = await graffiti.get(putted, {}, session);
|
|
491
|
-
expect(gotten.value).toEqual(object.value);
|
|
492
|
-
expect(gotten.channels).toEqual(object.channels);
|
|
493
|
-
expect(gotten.allowed).toEqual(object.allowed);
|
|
494
|
-
expect(gotten.lastModified).toEqual(putted.lastModified);
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
);
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
// tests/discover.ts
|
|
501
|
-
import { it as it2, expect as expect2, describe as describe2, assert as assert2, beforeAll as beforeAll2 } from "vitest";
|
|
502
|
-
var graffitiDiscoverTests = (useGraffiti, useSession1, useSession2) => {
|
|
503
|
-
describe2.concurrent("discover", { timeout: 2e4 }, () => {
|
|
504
|
-
let graffiti;
|
|
505
|
-
let session;
|
|
506
|
-
let session1;
|
|
507
|
-
let session2;
|
|
508
|
-
beforeAll2(async () => {
|
|
509
|
-
graffiti = useGraffiti();
|
|
510
|
-
session1 = await useSession1();
|
|
511
|
-
session = session1;
|
|
512
|
-
session2 = await useSession2();
|
|
513
|
-
});
|
|
514
|
-
it2("discover nothing", async () => {
|
|
515
|
-
const iterator = graffiti.discover([], {});
|
|
516
|
-
expect2(await iterator.next()).toHaveProperty("done", true);
|
|
517
|
-
});
|
|
518
|
-
it2("discover single", async () => {
|
|
519
|
-
const object = randomPutObject();
|
|
520
|
-
const putted = await graffiti.put(object, session);
|
|
521
|
-
const queryChannels = [randomString(), object.channels[0]];
|
|
522
|
-
const iterator = graffiti.discover(queryChannels, {});
|
|
523
|
-
const value = await nextStreamValue(iterator);
|
|
524
|
-
expect2(value.value).toEqual(object.value);
|
|
525
|
-
expect2(value.channels).toEqual([object.channels[0]]);
|
|
526
|
-
expect2(value.allowed).toBeUndefined();
|
|
527
|
-
expect2(value.actor).toEqual(session.actor);
|
|
528
|
-
expect2(value.lastModified).toEqual(putted.lastModified);
|
|
529
|
-
const result2 = await iterator.next();
|
|
530
|
-
expect2(result2.done).toBe(true);
|
|
531
|
-
});
|
|
532
|
-
it2("discover wrong channel", async () => {
|
|
533
|
-
const object = randomPutObject();
|
|
534
|
-
await graffiti.put(object, session);
|
|
535
|
-
const iterator = graffiti.discover([randomString()], {});
|
|
536
|
-
await expect2(iterator.next()).resolves.toHaveProperty("done", true);
|
|
537
|
-
});
|
|
538
|
-
it2("discover not allowed", async () => {
|
|
539
|
-
const object = randomPutObject();
|
|
540
|
-
object.allowed = [randomString(), randomString()];
|
|
541
|
-
const putted = await graffiti.put(object, session1);
|
|
542
|
-
const iteratorSession1 = graffiti.discover(
|
|
543
|
-
object.channels,
|
|
544
|
-
{},
|
|
545
|
-
session1
|
|
546
|
-
);
|
|
547
|
-
const value = await nextStreamValue(iteratorSession1);
|
|
548
|
-
expect2(value.value).toEqual(object.value);
|
|
549
|
-
expect2(value.channels).toEqual(object.channels);
|
|
550
|
-
expect2(value.allowed).toEqual(object.allowed);
|
|
551
|
-
expect2(value.actor).toEqual(session1.actor);
|
|
552
|
-
expect2(value.lastModified).toEqual(putted.lastModified);
|
|
553
|
-
const iteratorSession2 = graffiti.discover(object.channels, {}, session2);
|
|
554
|
-
expect2(await iteratorSession2.next()).toHaveProperty("done", true);
|
|
555
|
-
const iteratorNoSession = graffiti.discover(object.channels, {});
|
|
556
|
-
expect2(await iteratorNoSession.next()).toHaveProperty("done", true);
|
|
557
|
-
});
|
|
558
|
-
it2("discover allowed", async () => {
|
|
559
|
-
const object = randomPutObject();
|
|
560
|
-
object.allowed = [randomString(), session2.actor, randomString()];
|
|
561
|
-
const putted = await graffiti.put(object, session1);
|
|
562
|
-
const iteratorSession2 = graffiti.discover(
|
|
563
|
-
object.channels,
|
|
564
|
-
{},
|
|
565
|
-
session2
|
|
566
|
-
);
|
|
567
|
-
const value = await nextStreamValue(iteratorSession2);
|
|
568
|
-
expect2(value.value).toEqual(object.value);
|
|
569
|
-
expect2(value.allowed).toEqual([session2.actor]);
|
|
570
|
-
expect2(value.channels).toEqual(object.channels);
|
|
571
|
-
expect2(value.actor).toEqual(session1.actor);
|
|
572
|
-
expect2(value.lastModified).toEqual(putted.lastModified);
|
|
573
|
-
});
|
|
574
|
-
for (const prop of ["actor", "lastModified"]) {
|
|
575
|
-
it2(`discover for ${prop}`, async () => {
|
|
576
|
-
const object1 = randomPutObject();
|
|
577
|
-
const putted1 = await graffiti.put(object1, session1);
|
|
578
|
-
const object2 = randomPutObject();
|
|
579
|
-
object2.channels = object1.channels;
|
|
580
|
-
await new Promise((r) => setTimeout(r, 20));
|
|
581
|
-
const putted2 = await graffiti.put(object2, session2);
|
|
582
|
-
const iterator = graffiti.discover(object1.channels, {
|
|
583
|
-
properties: {
|
|
584
|
-
[prop]: {
|
|
585
|
-
enum: [putted1[prop]]
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
const value = await nextStreamValue(iterator);
|
|
590
|
-
expect2(value.url).toEqual(putted1.url);
|
|
591
|
-
expect2(value.url).not.toEqual(putted2.url);
|
|
592
|
-
expect2(value.value).toEqual(object1.value);
|
|
593
|
-
await expect2(iterator.next()).resolves.toHaveProperty("done", true);
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
it2("discover with lastModified range", async () => {
|
|
597
|
-
const object = randomPutObject();
|
|
598
|
-
const putted1 = await graffiti.put(object, session);
|
|
599
|
-
await new Promise((r) => setTimeout(r, 20));
|
|
600
|
-
const putted2 = await graffiti.put(object, session);
|
|
601
|
-
expect2(putted1.url).not.toEqual(putted2.url);
|
|
602
|
-
expect2(putted1.lastModified).toBeLessThan(putted2.lastModified);
|
|
603
|
-
const gtIterator = graffiti.discover([object.channels[0]], {
|
|
604
|
-
properties: {
|
|
605
|
-
lastModified: {
|
|
606
|
-
exclusiveMinimum: putted2.lastModified
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
expect2(await gtIterator.next()).toHaveProperty("done", true);
|
|
611
|
-
const gtIteratorEpsilon = graffiti.discover([object.channels[0]], {
|
|
612
|
-
properties: {
|
|
613
|
-
lastModified: {
|
|
614
|
-
exclusiveMinimum: putted2.lastModified - 0.1
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
});
|
|
618
|
-
const value1 = await nextStreamValue(gtIteratorEpsilon);
|
|
619
|
-
expect2(value1.url).toEqual(putted2.url);
|
|
620
|
-
expect2(await gtIteratorEpsilon.next()).toHaveProperty("done", true);
|
|
621
|
-
const gteIterator = graffiti.discover(object.channels, {
|
|
622
|
-
properties: {
|
|
623
|
-
value: {},
|
|
624
|
-
lastModified: {
|
|
625
|
-
minimum: putted2.lastModified
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
});
|
|
629
|
-
const value = await nextStreamValue(gteIterator);
|
|
630
|
-
expect2(value.url).toEqual(putted2.url);
|
|
631
|
-
expect2(await gteIterator.next()).toHaveProperty("done", true);
|
|
632
|
-
const gteIteratorEpsilon = graffiti.discover(object.channels, {
|
|
633
|
-
properties: {
|
|
634
|
-
lastModified: {
|
|
635
|
-
minimum: putted2.lastModified + 0.1
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
expect2(await gteIteratorEpsilon.next()).toHaveProperty("done", true);
|
|
640
|
-
const ltIterator = graffiti.discover(object.channels, {
|
|
641
|
-
properties: {
|
|
642
|
-
lastModified: {
|
|
643
|
-
exclusiveMaximum: putted1.lastModified
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
});
|
|
647
|
-
expect2(await ltIterator.next()).toHaveProperty("done", true);
|
|
648
|
-
const ltIteratorEpsilon = graffiti.discover(object.channels, {
|
|
649
|
-
properties: {
|
|
650
|
-
lastModified: {
|
|
651
|
-
exclusiveMaximum: putted1.lastModified + 0.1
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
const value3 = await nextStreamValue(ltIteratorEpsilon);
|
|
656
|
-
expect2(value3.url).toEqual(putted1.url);
|
|
657
|
-
expect2(await ltIteratorEpsilon.next()).toHaveProperty("done", true);
|
|
658
|
-
const lteIterator = graffiti.discover(object.channels, {
|
|
659
|
-
properties: {
|
|
660
|
-
lastModified: {
|
|
661
|
-
maximum: putted1.lastModified
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
});
|
|
665
|
-
const value2 = await nextStreamValue(lteIterator);
|
|
666
|
-
expect2(value2.url).toEqual(putted1.url);
|
|
667
|
-
expect2(await lteIterator.next()).toHaveProperty("done", true);
|
|
668
|
-
const lteIteratorEpsilon = graffiti.discover(object.channels, {
|
|
669
|
-
properties: {
|
|
670
|
-
lastModified: {
|
|
671
|
-
maximum: putted1.lastModified - 0.1
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
});
|
|
675
|
-
expect2(await lteIteratorEpsilon.next()).toHaveProperty("done", true);
|
|
676
|
-
});
|
|
677
|
-
it2("discover schema allowed, as and not as owner", async () => {
|
|
678
|
-
const object = randomPutObject();
|
|
679
|
-
object.allowed = [randomString(), session2.actor, randomString()];
|
|
680
|
-
await graffiti.put(object, session1);
|
|
681
|
-
const iteratorSession1 = graffiti.discover(
|
|
682
|
-
object.channels,
|
|
683
|
-
{
|
|
684
|
-
properties: {
|
|
685
|
-
allowed: {
|
|
686
|
-
minItems: 3,
|
|
687
|
-
// Make sure session2.actor is in the allow list
|
|
688
|
-
not: {
|
|
689
|
-
items: {
|
|
690
|
-
not: {
|
|
691
|
-
enum: [session2.actor]
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
},
|
|
698
|
-
session1
|
|
699
|
-
);
|
|
700
|
-
const value = await nextStreamValue(iteratorSession1);
|
|
701
|
-
expect2(value.value).toEqual(object.value);
|
|
702
|
-
await expect2(iteratorSession1.next()).resolves.toHaveProperty(
|
|
703
|
-
"done",
|
|
704
|
-
true
|
|
705
|
-
);
|
|
706
|
-
const iteratorSession2BigAllow = graffiti.discover(
|
|
707
|
-
object.channels,
|
|
708
|
-
{
|
|
709
|
-
properties: {
|
|
710
|
-
allowed: {
|
|
711
|
-
minItems: 3
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
},
|
|
715
|
-
session2
|
|
716
|
-
);
|
|
717
|
-
await expect2(iteratorSession2BigAllow.next()).resolves.toHaveProperty(
|
|
718
|
-
"done",
|
|
719
|
-
true
|
|
720
|
-
);
|
|
721
|
-
const iteratorSession2PeekOther = graffiti.discover(
|
|
722
|
-
object.channels,
|
|
723
|
-
{
|
|
724
|
-
properties: {
|
|
725
|
-
allowed: {
|
|
726
|
-
not: {
|
|
727
|
-
items: {
|
|
728
|
-
not: {
|
|
729
|
-
enum: [object.channels[0]]
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
},
|
|
736
|
-
session2
|
|
737
|
-
);
|
|
738
|
-
await expect2(iteratorSession2PeekOther.next()).resolves.toHaveProperty(
|
|
739
|
-
"done",
|
|
740
|
-
true
|
|
741
|
-
);
|
|
742
|
-
const iteratorSession2SmallAllowPeekSelf = graffiti.discover(
|
|
743
|
-
object.channels,
|
|
744
|
-
{
|
|
745
|
-
properties: {
|
|
746
|
-
allowed: {
|
|
747
|
-
maxItems: 1,
|
|
748
|
-
not: {
|
|
749
|
-
items: {
|
|
750
|
-
not: {
|
|
751
|
-
enum: [session2.actor]
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
},
|
|
758
|
-
session2
|
|
759
|
-
);
|
|
760
|
-
const value2 = await nextStreamValue(
|
|
761
|
-
iteratorSession2SmallAllowPeekSelf
|
|
762
|
-
);
|
|
763
|
-
expect2(value2.value).toEqual(object.value);
|
|
764
|
-
await expect2(
|
|
765
|
-
iteratorSession2SmallAllowPeekSelf.next()
|
|
766
|
-
).resolves.toHaveProperty("done", true);
|
|
767
|
-
});
|
|
768
|
-
it2("discover schema channels, as and not as owner", async () => {
|
|
769
|
-
const object = randomPutObject();
|
|
770
|
-
object.channels = [randomString(), randomString(), randomString()];
|
|
771
|
-
await graffiti.put(object, session1);
|
|
772
|
-
const iteratorSession1 = graffiti.discover(
|
|
773
|
-
[object.channels[0], object.channels[2]],
|
|
774
|
-
{
|
|
775
|
-
properties: {
|
|
776
|
-
channels: {
|
|
777
|
-
minItems: 3,
|
|
778
|
-
// Make sure session2.actor is in the allow list
|
|
779
|
-
not: {
|
|
780
|
-
items: {
|
|
781
|
-
not: {
|
|
782
|
-
enum: [object.channels[1]]
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
},
|
|
789
|
-
session1
|
|
790
|
-
);
|
|
791
|
-
const value = await nextStreamValue(iteratorSession1);
|
|
792
|
-
expect2(value.value).toEqual(object.value);
|
|
793
|
-
await expect2(iteratorSession1.next()).resolves.toHaveProperty(
|
|
794
|
-
"done",
|
|
795
|
-
true
|
|
796
|
-
);
|
|
797
|
-
const iteratorSession2BigAllow = graffiti.discover(
|
|
798
|
-
[object.channels[0], object.channels[2]],
|
|
799
|
-
{
|
|
800
|
-
properties: {
|
|
801
|
-
channels: {
|
|
802
|
-
minItems: 3
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
},
|
|
806
|
-
session2
|
|
807
|
-
);
|
|
808
|
-
await expect2(iteratorSession2BigAllow.next()).resolves.toHaveProperty(
|
|
809
|
-
"done",
|
|
810
|
-
true
|
|
811
|
-
);
|
|
812
|
-
const iteratorSession2PeekOther = graffiti.discover(
|
|
813
|
-
[object.channels[0], object.channels[2]],
|
|
814
|
-
{
|
|
815
|
-
properties: {
|
|
816
|
-
channels: {
|
|
817
|
-
not: {
|
|
818
|
-
items: {
|
|
819
|
-
not: {
|
|
820
|
-
enum: [object.channels[1]]
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
},
|
|
827
|
-
session2
|
|
828
|
-
);
|
|
829
|
-
await expect2(iteratorSession2PeekOther.next()).resolves.toHaveProperty(
|
|
830
|
-
"done",
|
|
831
|
-
true
|
|
832
|
-
);
|
|
833
|
-
const iteratorSession2SmallAllowPeekSelf = graffiti.discover(
|
|
834
|
-
[object.channels[0], object.channels[2]],
|
|
835
|
-
{
|
|
836
|
-
properties: {
|
|
837
|
-
allowed: {
|
|
838
|
-
maxItems: 2,
|
|
839
|
-
not: {
|
|
840
|
-
items: {
|
|
841
|
-
not: {
|
|
842
|
-
enum: [object.channels[2]]
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
},
|
|
849
|
-
session2
|
|
850
|
-
);
|
|
851
|
-
const value2 = await nextStreamValue(
|
|
852
|
-
iteratorSession2SmallAllowPeekSelf
|
|
853
|
-
);
|
|
854
|
-
expect2(value2.value).toEqual(object.value);
|
|
855
|
-
await expect2(
|
|
856
|
-
iteratorSession2SmallAllowPeekSelf.next()
|
|
857
|
-
).resolves.toHaveProperty("done", true);
|
|
858
|
-
});
|
|
859
|
-
it2("discover query for empty allowed", async () => {
|
|
860
|
-
const publicO = randomPutObject();
|
|
861
|
-
const publicSchema = {
|
|
862
|
-
not: {
|
|
863
|
-
required: ["allowed"]
|
|
864
|
-
}
|
|
865
|
-
};
|
|
866
|
-
await graffiti.put(publicO, session1);
|
|
867
|
-
const iterator = graffiti.discover(
|
|
868
|
-
publicO.channels,
|
|
869
|
-
publicSchema,
|
|
870
|
-
session1
|
|
871
|
-
);
|
|
872
|
-
const value = await nextStreamValue(iterator);
|
|
873
|
-
expect2(value.value).toEqual(publicO.value);
|
|
874
|
-
expect2(value.allowed).toBeUndefined();
|
|
875
|
-
await expect2(iterator.next()).resolves.toHaveProperty("done", true);
|
|
876
|
-
const restricted = randomPutObject();
|
|
877
|
-
restricted.allowed = [];
|
|
878
|
-
await graffiti.put(restricted, session1);
|
|
879
|
-
const iterator2 = graffiti.discover(
|
|
880
|
-
restricted.channels,
|
|
881
|
-
publicSchema,
|
|
882
|
-
session1
|
|
883
|
-
);
|
|
884
|
-
await expect2(iterator2.next()).resolves.toHaveProperty("done", true);
|
|
885
|
-
});
|
|
886
|
-
it2("discover query for values", async () => {
|
|
887
|
-
const object1 = randomPutObject();
|
|
888
|
-
object1.value = { test: randomString() };
|
|
889
|
-
await graffiti.put(object1, session);
|
|
890
|
-
const object2 = randomPutObject();
|
|
891
|
-
object2.channels = object1.channels;
|
|
892
|
-
object2.value = { test: randomString(), something: randomString() };
|
|
893
|
-
await graffiti.put(object2, session);
|
|
894
|
-
const object3 = randomPutObject();
|
|
895
|
-
object3.channels = object1.channels;
|
|
896
|
-
object3.value = { other: randomString(), something: randomString() };
|
|
897
|
-
await graffiti.put(object3, session);
|
|
898
|
-
const counts = /* @__PURE__ */ new Map();
|
|
899
|
-
for (const property of ["test", "something", "other"]) {
|
|
900
|
-
let count = 0;
|
|
901
|
-
for await (const result of graffiti.discover(object1.channels, {
|
|
902
|
-
properties: {
|
|
903
|
-
value: {
|
|
904
|
-
required: [property]
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
})) {
|
|
908
|
-
assert2(!result.error, "result has error");
|
|
909
|
-
if (property in result.object.value) {
|
|
910
|
-
count++;
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
counts.set(property, count);
|
|
914
|
-
}
|
|
915
|
-
expect2(counts.get("test")).toBe(2);
|
|
916
|
-
expect2(counts.get("something")).toBe(2);
|
|
917
|
-
expect2(counts.get("other")).toBe(1);
|
|
918
|
-
});
|
|
919
|
-
for (const continueType of ["cursor", "continue"]) {
|
|
920
|
-
describe2(`continue discover with ${continueType}`, () => {
|
|
921
|
-
it2("discover for deleted content", async () => {
|
|
922
|
-
const object = randomPutObject();
|
|
923
|
-
const putted = await graffiti.put(object, session);
|
|
924
|
-
const iterator1 = graffiti.discover(object.channels, {});
|
|
925
|
-
const value1 = await nextStreamValue(iterator1);
|
|
926
|
-
expect2(value1.value).toEqual(object.value);
|
|
927
|
-
const returnValue = await iterator1.next();
|
|
928
|
-
assert2(returnValue.done, "value2 is not done");
|
|
929
|
-
const deleted = await graffiti.delete(putted, session);
|
|
930
|
-
const iterator = graffiti.discover(object.channels, {});
|
|
931
|
-
await expect2(iterator.next()).resolves.toHaveProperty("done", true);
|
|
932
|
-
const tombIterator = continueStream(
|
|
933
|
-
graffiti,
|
|
934
|
-
returnValue.value,
|
|
935
|
-
continueType
|
|
936
|
-
);
|
|
937
|
-
const value = await tombIterator.next();
|
|
938
|
-
assert2(!value.done && !value.value.error, "value is done");
|
|
939
|
-
assert2(value.value.tombstone, "value is not tombstone");
|
|
940
|
-
expect2(value.value.object.url).toEqual(putted.url);
|
|
941
|
-
await expect2(tombIterator.next()).resolves.toHaveProperty(
|
|
942
|
-
"done",
|
|
943
|
-
true
|
|
944
|
-
);
|
|
945
|
-
});
|
|
946
|
-
it2("discover for replaced channels", async () => {
|
|
947
|
-
async function runTest() {
|
|
948
|
-
const object1 = randomPutObject();
|
|
949
|
-
const putted = await graffiti.put(object1, session);
|
|
950
|
-
const iterator3 = graffiti.discover(object1.channels, {});
|
|
951
|
-
const value3 = await nextStreamValue(iterator3);
|
|
952
|
-
expect2(value3.value).toEqual(object1.value);
|
|
953
|
-
const returnValue = await iterator3.next();
|
|
954
|
-
assert2(returnValue.done, "value2 is not done");
|
|
955
|
-
const object2 = randomPutObject();
|
|
956
|
-
const replaced = await graffiti.put(
|
|
957
|
-
{
|
|
958
|
-
...object2,
|
|
959
|
-
url: putted.url
|
|
960
|
-
},
|
|
961
|
-
session
|
|
962
|
-
);
|
|
963
|
-
const iterator1 = graffiti.discover(object1.channels, {});
|
|
964
|
-
const iterator2 = graffiti.discover(object2.channels, {});
|
|
965
|
-
const tombIterator = continueStream(
|
|
966
|
-
graffiti,
|
|
967
|
-
returnValue.value,
|
|
968
|
-
continueType
|
|
969
|
-
);
|
|
970
|
-
if (putted.lastModified === replaced.lastModified) {
|
|
971
|
-
const value1 = await iterator1.next();
|
|
972
|
-
const value22 = await iterator2.next();
|
|
973
|
-
const value32 = await tombIterator.next();
|
|
974
|
-
expect2(value1.done || value22.done).toBe(true);
|
|
975
|
-
expect2(value1.done && value22.done).toBe(false);
|
|
976
|
-
assert2(!value32.done && !value32.value.error, "value is done");
|
|
977
|
-
expect2(value32.value.tombstone || value22.done).toBe(true);
|
|
978
|
-
expect2(value32.value.tombstone && value22.done).toBe(false);
|
|
979
|
-
return;
|
|
980
|
-
}
|
|
981
|
-
const value5 = await iterator1.next();
|
|
982
|
-
assert2(value5.done, "value5 is not done");
|
|
983
|
-
const value4 = await tombIterator.next();
|
|
984
|
-
assert2(!value4.done && !value4.value.error, "value is done");
|
|
985
|
-
assert2(value4.value.tombstone, "value is not tombstone");
|
|
986
|
-
expect2(value4.value.object.url).toEqual(putted.url);
|
|
987
|
-
expect2(value4.value.object.lastModified).toEqual(
|
|
988
|
-
replaced.lastModified
|
|
989
|
-
);
|
|
990
|
-
const value2 = await nextStreamValue(iterator2);
|
|
991
|
-
await expect2(iterator2.next()).resolves.toHaveProperty(
|
|
992
|
-
"done",
|
|
993
|
-
true
|
|
994
|
-
);
|
|
995
|
-
expect2(value2.value).toEqual(object2.value);
|
|
996
|
-
expect2(value2.channels).toEqual(object2.channels);
|
|
997
|
-
expect2(value2.lastModified).toEqual(replaced.lastModified);
|
|
998
|
-
const patched = await graffiti.patch(
|
|
999
|
-
{
|
|
1000
|
-
channels: [
|
|
1001
|
-
{ op: "replace", path: "", value: object1.channels }
|
|
1002
|
-
]
|
|
1003
|
-
},
|
|
1004
|
-
replaced,
|
|
1005
|
-
session
|
|
1006
|
-
);
|
|
1007
|
-
const tombIterator2 = continueStream(
|
|
1008
|
-
graffiti,
|
|
1009
|
-
value5.value,
|
|
1010
|
-
continueType
|
|
1011
|
-
);
|
|
1012
|
-
let result;
|
|
1013
|
-
for await (const value of tombIterator2) {
|
|
1014
|
-
if (value.error) continue;
|
|
1015
|
-
if (!result) {
|
|
1016
|
-
result = value;
|
|
1017
|
-
continue;
|
|
1018
|
-
}
|
|
1019
|
-
if (value.object.lastModified > result.object.lastModified || value.object.lastModified === result.object.lastModified && !value.tombstone && result.tombstone) {
|
|
1020
|
-
result = value;
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
assert2(result, "result is not defined");
|
|
1024
|
-
assert2(!result.tombstone, "result is tombstone");
|
|
1025
|
-
expect2(result.object.url).toEqual(replaced.url);
|
|
1026
|
-
expect2(result.object.lastModified).toEqual(patched.lastModified);
|
|
1027
|
-
expect2(result.object.channels).toEqual(object1.channels);
|
|
1028
|
-
expect2(result.object.value).toEqual(object2.value);
|
|
1029
|
-
}
|
|
1030
|
-
await Promise.allSettled(Array.from({ length: 20 }, () => runTest()));
|
|
1031
|
-
});
|
|
1032
|
-
it2("discover for patched allowed", async () => {
|
|
1033
|
-
const object = randomPutObject();
|
|
1034
|
-
const putted = await graffiti.put(object, session);
|
|
1035
|
-
const iterator1 = graffiti.discover(object.channels, {});
|
|
1036
|
-
const value1 = await nextStreamValue(iterator1);
|
|
1037
|
-
expect2(value1.value).toEqual(object.value);
|
|
1038
|
-
const returnValue = await iterator1.next();
|
|
1039
|
-
assert2(returnValue.done, "value2 is not done");
|
|
1040
|
-
await graffiti.patch(
|
|
1041
|
-
{
|
|
1042
|
-
allowed: [{ op: "add", path: "", value: [] }]
|
|
1043
|
-
},
|
|
1044
|
-
putted,
|
|
1045
|
-
session
|
|
1046
|
-
);
|
|
1047
|
-
const iterator2 = graffiti.discover(object.channels, {});
|
|
1048
|
-
expect2(await iterator2.next()).toHaveProperty("done", true);
|
|
1049
|
-
const iterator = continueStream(
|
|
1050
|
-
graffiti,
|
|
1051
|
-
returnValue.value,
|
|
1052
|
-
continueType
|
|
1053
|
-
);
|
|
1054
|
-
const value = await iterator.next();
|
|
1055
|
-
assert2(!value.done && !value.value.error, "value is done");
|
|
1056
|
-
assert2(value.value.tombstone, "value is not tombstone");
|
|
1057
|
-
expect2(value.value.object.url).toEqual(putted.url);
|
|
1058
|
-
await expect2(iterator.next()).resolves.toHaveProperty("done", true);
|
|
1059
|
-
});
|
|
1060
|
-
it2("put concurrently and discover one", async () => {
|
|
1061
|
-
const object = randomPutObject();
|
|
1062
|
-
const putted = await graffiti.put(object, session);
|
|
1063
|
-
const putPromises = Array(99).fill(0).map(
|
|
1064
|
-
() => graffiti.put(
|
|
1065
|
-
{
|
|
1066
|
-
...object,
|
|
1067
|
-
url: putted.url
|
|
1068
|
-
},
|
|
1069
|
-
session
|
|
1070
|
-
)
|
|
1071
|
-
);
|
|
1072
|
-
await Promise.all(putPromises);
|
|
1073
|
-
const iterator = graffiti.discover(object.channels, {});
|
|
1074
|
-
let tombstoneCount = 0;
|
|
1075
|
-
let valueCount = 0;
|
|
1076
|
-
for await (const result of iterator) {
|
|
1077
|
-
assert2(!result.error, "result has error");
|
|
1078
|
-
if (result.tombstone) {
|
|
1079
|
-
tombstoneCount++;
|
|
1080
|
-
} else {
|
|
1081
|
-
valueCount++;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
expect2(tombstoneCount).toBe(0);
|
|
1085
|
-
expect2(valueCount).toBe(1);
|
|
1086
|
-
});
|
|
1087
|
-
});
|
|
1088
|
-
}
|
|
1089
|
-
});
|
|
1090
|
-
};
|
|
1091
|
-
|
|
1092
|
-
// tests/orphans.ts
|
|
1093
|
-
import { it as it3, expect as expect3, describe as describe3, assert as assert3, beforeAll as beforeAll3 } from "vitest";
|
|
1094
|
-
var graffitiOrphanTests = (useGraffiti, useSession1, useSession2) => {
|
|
1095
|
-
describe3("recoverOrphans", { timeout: 2e4 }, () => {
|
|
1096
|
-
let graffiti;
|
|
1097
|
-
let session;
|
|
1098
|
-
let session1;
|
|
1099
|
-
let session2;
|
|
1100
|
-
beforeAll3(async () => {
|
|
1101
|
-
graffiti = useGraffiti();
|
|
1102
|
-
session1 = await useSession1();
|
|
1103
|
-
session = session1;
|
|
1104
|
-
session2 = await useSession2();
|
|
1105
|
-
});
|
|
1106
|
-
it3("list orphans", async () => {
|
|
1107
|
-
const existingOrphans = [];
|
|
1108
|
-
const orphanIterator1 = graffiti.recoverOrphans({}, session);
|
|
1109
|
-
for await (const orphan of orphanIterator1) {
|
|
1110
|
-
if (orphan.error) continue;
|
|
1111
|
-
existingOrphans.push(orphan.object.url);
|
|
1112
|
-
}
|
|
1113
|
-
const object = randomPutObject();
|
|
1114
|
-
object.channels = [];
|
|
1115
|
-
const putted = await graffiti.put(object, session);
|
|
1116
|
-
const orphanIterator2 = graffiti.recoverOrphans({}, session);
|
|
1117
|
-
let numResults = 0;
|
|
1118
|
-
for await (const orphan of orphanIterator2) {
|
|
1119
|
-
if (orphan.error) continue;
|
|
1120
|
-
assert3(!orphan.tombstone, "orphan is tombstone");
|
|
1121
|
-
if (orphan.object.url === putted.url) {
|
|
1122
|
-
numResults++;
|
|
1123
|
-
expect3(orphan.object.lastModified).toBe(putted.lastModified);
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
expect3(numResults).toBe(1);
|
|
1127
|
-
});
|
|
1128
|
-
for (const continueType of ["continue", "cursor"]) {
|
|
1129
|
-
describe3(`continue orphans with ${continueType}`, () => {
|
|
1130
|
-
it3("replaced orphan, no longer", async () => {
|
|
1131
|
-
const object = randomPutObject();
|
|
1132
|
-
object.channels = [];
|
|
1133
|
-
const putOrphan = await graffiti.put(object, session);
|
|
1134
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
1135
|
-
expect3(Object.keys(object.value).length).toBeGreaterThanOrEqual(1);
|
|
1136
|
-
expect3(Object.keys(object.value)[0]).toBeTypeOf("string");
|
|
1137
|
-
const iterator1 = graffiti.recoverOrphans(
|
|
1138
|
-
{
|
|
1139
|
-
properties: {
|
|
1140
|
-
value: {
|
|
1141
|
-
properties: {
|
|
1142
|
-
[Object.keys(object.value)[0]]: {
|
|
1143
|
-
type: "string"
|
|
1144
|
-
}
|
|
1145
|
-
},
|
|
1146
|
-
required: [Object.keys(object.value)[0]]
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
},
|
|
1150
|
-
session
|
|
1151
|
-
);
|
|
1152
|
-
const value1 = await nextStreamValue(iterator1);
|
|
1153
|
-
expect3(value1.value).toEqual(object.value);
|
|
1154
|
-
const returnValue = await iterator1.next();
|
|
1155
|
-
assert3(returnValue.done, "value2 is not done");
|
|
1156
|
-
const putNotOrphan = await graffiti.put(
|
|
1157
|
-
{
|
|
1158
|
-
...putOrphan,
|
|
1159
|
-
...object,
|
|
1160
|
-
channels: [randomString()]
|
|
1161
|
-
},
|
|
1162
|
-
session
|
|
1163
|
-
);
|
|
1164
|
-
expect3(putNotOrphan.url).toBe(putOrphan.url);
|
|
1165
|
-
expect3(putNotOrphan.lastModified).toBeGreaterThan(
|
|
1166
|
-
putOrphan.lastModified
|
|
1167
|
-
);
|
|
1168
|
-
const orphanIterator = graffiti.recoverOrphans({}, session);
|
|
1169
|
-
let numResults = 0;
|
|
1170
|
-
for await (const orphan of orphanIterator) {
|
|
1171
|
-
if (orphan.error) continue;
|
|
1172
|
-
if (orphan.object.url === putOrphan.url) {
|
|
1173
|
-
numResults++;
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
expect3(numResults).toBe(0);
|
|
1177
|
-
const iterator2 = continueStream(
|
|
1178
|
-
graffiti,
|
|
1179
|
-
returnValue.value,
|
|
1180
|
-
continueType,
|
|
1181
|
-
session
|
|
1182
|
-
);
|
|
1183
|
-
const value2 = await iterator2.next();
|
|
1184
|
-
assert3(
|
|
1185
|
-
!value2.done && !value2.value.error,
|
|
1186
|
-
"value2 is done or has error"
|
|
1187
|
-
);
|
|
1188
|
-
assert3(value2.value.tombstone, "value2 is not tombstone");
|
|
1189
|
-
expect3(value2.value.object.url).toBe(putOrphan.url);
|
|
1190
|
-
await expect3(iterator2.next()).resolves.toHaveProperty("done", true);
|
|
1191
|
-
});
|
|
1192
|
-
});
|
|
1193
|
-
}
|
|
1194
|
-
});
|
|
1195
|
-
};
|
|
1196
|
-
|
|
1197
|
-
// tests/channel-stats.ts
|
|
1198
|
-
import { it as it4, expect as expect4, describe as describe4, assert as assert4, beforeAll as beforeAll4 } from "vitest";
|
|
1199
|
-
var graffitiChannelStatsTests = (useGraffiti, useSession1, useSession2) => {
|
|
1200
|
-
describe4("channel stats", { timeout: 2e4 }, () => {
|
|
1201
|
-
let graffiti;
|
|
1202
|
-
let session;
|
|
1203
|
-
let session1;
|
|
1204
|
-
let session2;
|
|
1205
|
-
beforeAll4(async () => {
|
|
1206
|
-
graffiti = useGraffiti();
|
|
1207
|
-
session1 = await useSession1();
|
|
1208
|
-
session = session1;
|
|
1209
|
-
session2 = await useSession2();
|
|
1210
|
-
});
|
|
1211
|
-
it4("list channels", async () => {
|
|
1212
|
-
const existingChannels = /* @__PURE__ */ new Map();
|
|
1213
|
-
const channelIterator1 = graffiti.channelStats(session);
|
|
1214
|
-
for await (const channel of channelIterator1) {
|
|
1215
|
-
if (channel.error) continue;
|
|
1216
|
-
existingChannels.set(channel.value.channel, channel.value.count);
|
|
1217
|
-
}
|
|
1218
|
-
const channels = [randomString(), randomString(), randomString()];
|
|
1219
|
-
for (let i = 0; i < 3; i++) {
|
|
1220
|
-
for (let j = 0; j < i + 1; j++) {
|
|
1221
|
-
await graffiti.put(
|
|
1222
|
-
{
|
|
1223
|
-
value: {
|
|
1224
|
-
index: j
|
|
1225
|
-
},
|
|
1226
|
-
channels: channels.slice(0, i + 1)
|
|
1227
|
-
},
|
|
1228
|
-
session
|
|
1229
|
-
);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
await graffiti.put(
|
|
1233
|
-
{ value: { index: 3 }, channels: [channels[2]] },
|
|
1234
|
-
session
|
|
1235
|
-
);
|
|
1236
|
-
const channelIterator2 = graffiti.channelStats(session);
|
|
1237
|
-
let newChannels = /* @__PURE__ */ new Map();
|
|
1238
|
-
for await (const channel of channelIterator2) {
|
|
1239
|
-
if (channel.error) continue;
|
|
1240
|
-
newChannels.set(channel.value.channel, channel.value.count);
|
|
1241
|
-
}
|
|
1242
|
-
newChannels = new Map(
|
|
1243
|
-
Array.from(newChannels).filter(
|
|
1244
|
-
([channel, count]) => !existingChannels.has(channel)
|
|
1245
|
-
)
|
|
1246
|
-
);
|
|
1247
|
-
expect4(newChannels.size).toBe(3);
|
|
1248
|
-
expect4(newChannels.get(channels[0])).toBe(6);
|
|
1249
|
-
expect4(newChannels.get(channels[1])).toBe(5);
|
|
1250
|
-
expect4(newChannels.get(channels[2])).toBe(4);
|
|
1251
|
-
});
|
|
1252
|
-
it4("list channels with deleted channel", async () => {
|
|
1253
|
-
const channels = [randomString(), randomString(), randomString()];
|
|
1254
|
-
const before = await graffiti.put(
|
|
1255
|
-
{
|
|
1256
|
-
value: { index: 2 },
|
|
1257
|
-
channels: channels.slice(1)
|
|
1258
|
-
},
|
|
1259
|
-
session
|
|
1260
|
-
);
|
|
1261
|
-
const first = await graffiti.put(
|
|
1262
|
-
{ value: { index: 0 }, channels },
|
|
1263
|
-
session
|
|
1264
|
-
);
|
|
1265
|
-
await graffiti.delete(first, session);
|
|
1266
|
-
const second = await graffiti.put(
|
|
1267
|
-
{
|
|
1268
|
-
value: { index: 1 },
|
|
1269
|
-
channels: channels.slice(2)
|
|
1270
|
-
},
|
|
1271
|
-
session
|
|
1272
|
-
);
|
|
1273
|
-
const channelIterator = graffiti.channelStats(session);
|
|
1274
|
-
let got1 = 0;
|
|
1275
|
-
let got2 = 0;
|
|
1276
|
-
for await (const result of channelIterator) {
|
|
1277
|
-
if (result.error) continue;
|
|
1278
|
-
const { channel, count, lastModified } = result.value;
|
|
1279
|
-
assert4(
|
|
1280
|
-
channel !== channels[0],
|
|
1281
|
-
"There should not be an object in channel[0]"
|
|
1282
|
-
);
|
|
1283
|
-
if (channel === channels[1]) {
|
|
1284
|
-
expect4(count).toBe(1);
|
|
1285
|
-
expect4(lastModified).toBe(before.lastModified);
|
|
1286
|
-
got1++;
|
|
1287
|
-
} else if (channel === channels[2]) {
|
|
1288
|
-
expect4(count).toBe(2);
|
|
1289
|
-
expect4(lastModified).toBe(second.lastModified);
|
|
1290
|
-
got2++;
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
expect4(got1).toBe(1);
|
|
1294
|
-
expect4(got2).toBe(1);
|
|
1295
|
-
});
|
|
1296
|
-
});
|
|
1297
|
-
};
|
|
1298
36
|
export {
|
|
1299
37
|
continueStream,
|
|
1300
|
-
graffitiCRUDTests,
|
|
1301
|
-
graffitiChannelStatsTests,
|
|
1302
|
-
graffitiDiscoverTests,
|
|
1303
|
-
graffitiOrphanTests,
|
|
1304
38
|
nextStreamValue,
|
|
1305
|
-
|
|
39
|
+
randomPostObject,
|
|
1306
40
|
randomString,
|
|
1307
41
|
randomValue
|
|
1308
42
|
};
|