@graffiti-garden/api 0.1.8 → 0.1.9

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.
@@ -11,588 +11,608 @@ export const graffitiDiscoverTests = (
11
11
  useSession1: () => GraffitiSession,
12
12
  useSession2: () => GraffitiSession,
13
13
  ) => {
14
- describe("discover", () => {
15
- it("discover nothing", async () => {
16
- const graffiti = useGraffiti();
17
- const iterator = graffiti.discover([], {});
18
- expect(await iterator.next()).toHaveProperty("done", true);
19
- });
20
-
21
- it("discover single", async () => {
22
- const graffiti = useGraffiti();
23
- const session = useSession1();
24
- const object = randomPutObject();
25
-
26
- const putted = await graffiti.put(object, session);
27
-
28
- const queryChannels = [randomString(), object.channels[0]];
29
- const iterator = graffiti.discover(queryChannels, {});
30
- const value = await nextStreamValue(iterator);
31
- expect(value.value).toEqual(object.value);
32
- expect(value.channels).toEqual([object.channels[0]]);
33
- expect(value.allowed).toBeUndefined();
34
- expect(value.actor).toEqual(session.actor);
35
- expect(value.tombstone).toBe(false);
36
- expect(value.lastModified).toEqual(putted.lastModified);
37
- const result2 = await iterator.next();
38
- expect(result2.done).toBe(true);
39
- });
40
-
41
- it("discover wrong channel", async () => {
42
- const graffiti = useGraffiti();
43
- const session = useSession1();
44
- const object = randomPutObject();
45
- await graffiti.put(object, session);
46
- const iterator = graffiti.discover([randomString()], {});
47
- await expect(iterator.next()).resolves.toHaveProperty("done", true);
48
- });
49
-
50
- it("discover not allowed", async () => {
51
- const graffiti = useGraffiti();
52
- const session1 = useSession1();
53
- const session2 = useSession2();
54
-
55
- const object = randomPutObject();
56
- object.allowed = [randomString(), randomString()];
57
- const putted = await graffiti.put(object, session1);
58
-
59
- const iteratorSession1 = graffiti.discover(object.channels, {}, session1);
60
- const value = await nextStreamValue(iteratorSession1);
61
- expect(value.value).toEqual(object.value);
62
- expect(value.channels).toEqual(object.channels);
63
- expect(value.allowed).toEqual(object.allowed);
64
- expect(value.actor).toEqual(session1.actor);
65
- expect(value.tombstone).toBe(false);
66
- expect(value.lastModified).toEqual(putted.lastModified);
67
-
68
- const iteratorSession2 = graffiti.discover(object.channels, {}, session2);
69
- expect(await iteratorSession2.next()).toHaveProperty("done", true);
70
-
71
- const iteratorNoSession = graffiti.discover(object.channels, {});
72
- expect(await iteratorNoSession.next()).toHaveProperty("done", true);
73
- });
74
-
75
- it("discover allowed", async () => {
76
- const graffiti = useGraffiti();
77
- const session1 = useSession1();
78
- const session2 = useSession2();
79
-
80
- const object = randomPutObject();
81
- object.allowed = [randomString(), session2.actor, randomString()];
82
- const putted = await graffiti.put(object, session1);
83
-
84
- const iteratorSession2 = graffiti.discover(object.channels, {}, session2);
85
- const value = await nextStreamValue(iteratorSession2);
86
- expect(value.value).toEqual(object.value);
87
- expect(value.allowed).toEqual([session2.actor]);
88
- expect(value.channels).toEqual(object.channels);
89
- expect(value.actor).toEqual(session1.actor);
90
- expect(value.tombstone).toBe(false);
91
- expect(value.lastModified).toEqual(putted.lastModified);
92
- });
93
-
94
- for (const prop of ["name", "actor", "lastModified"] as const) {
95
- it(`discover for ${prop}`, async () => {
14
+ describe(
15
+ "discover",
16
+ () => {
17
+ it("discover nothing", async () => {
18
+ const graffiti = useGraffiti();
19
+ const iterator = graffiti.discover([], {});
20
+ expect(await iterator.next()).toHaveProperty("done", true);
21
+ });
22
+
23
+ it("discover single", async () => {
24
+ const graffiti = useGraffiti();
25
+ const session = useSession1();
26
+ const object = randomPutObject();
27
+
28
+ const putted = await graffiti.put(object, session);
29
+
30
+ const queryChannels = [randomString(), object.channels[0]];
31
+ const iterator = graffiti.discover(queryChannels, {});
32
+ const value = await nextStreamValue(iterator);
33
+ expect(value.value).toEqual(object.value);
34
+ expect(value.channels).toEqual([object.channels[0]]);
35
+ expect(value.allowed).toBeUndefined();
36
+ expect(value.actor).toEqual(session.actor);
37
+ expect(value.tombstone).toBe(false);
38
+ expect(value.lastModified).toEqual(putted.lastModified);
39
+ const result2 = await iterator.next();
40
+ expect(result2.done).toBe(true);
41
+ });
42
+
43
+ it("discover wrong channel", async () => {
44
+ const graffiti = useGraffiti();
45
+ const session = useSession1();
46
+ const object = randomPutObject();
47
+ await graffiti.put(object, session);
48
+ const iterator = graffiti.discover([randomString()], {});
49
+ await expect(iterator.next()).resolves.toHaveProperty("done", true);
50
+ });
51
+
52
+ it("discover not allowed", async () => {
96
53
  const graffiti = useGraffiti();
97
54
  const session1 = useSession1();
98
55
  const session2 = useSession2();
99
56
 
100
- const object1 = randomPutObject();
101
- const putted1 = await graffiti.put(object1, session1);
57
+ const object = randomPutObject();
58
+ object.allowed = [randomString(), randomString()];
59
+ const putted = await graffiti.put(object, session1);
102
60
 
103
- const object2 = randomPutObject();
104
- object2.channels = object1.channels;
105
- // Make sure the lastModified is different for the query
61
+ const iteratorSession1 = graffiti.discover(
62
+ object.channels,
63
+ {},
64
+ session1,
65
+ );
66
+ const value = await nextStreamValue(iteratorSession1);
67
+ expect(value.value).toEqual(object.value);
68
+ expect(value.channels).toEqual(object.channels);
69
+ expect(value.allowed).toEqual(object.allowed);
70
+ expect(value.actor).toEqual(session1.actor);
71
+ expect(value.tombstone).toBe(false);
72
+ expect(value.lastModified).toEqual(putted.lastModified);
73
+
74
+ const iteratorSession2 = graffiti.discover(
75
+ object.channels,
76
+ {},
77
+ session2,
78
+ );
79
+ expect(await iteratorSession2.next()).toHaveProperty("done", true);
80
+
81
+ const iteratorNoSession = graffiti.discover(object.channels, {});
82
+ expect(await iteratorNoSession.next()).toHaveProperty("done", true);
83
+ });
84
+
85
+ it("discover allowed", async () => {
86
+ const graffiti = useGraffiti();
87
+ const session1 = useSession1();
88
+ const session2 = useSession2();
89
+
90
+ const object = randomPutObject();
91
+ object.allowed = [randomString(), session2.actor, randomString()];
92
+ const putted = await graffiti.put(object, session1);
93
+
94
+ const iteratorSession2 = graffiti.discover(
95
+ object.channels,
96
+ {},
97
+ session2,
98
+ );
99
+ const value = await nextStreamValue(iteratorSession2);
100
+ expect(value.value).toEqual(object.value);
101
+ expect(value.allowed).toEqual([session2.actor]);
102
+ expect(value.channels).toEqual(object.channels);
103
+ expect(value.actor).toEqual(session1.actor);
104
+ expect(value.tombstone).toBe(false);
105
+ expect(value.lastModified).toEqual(putted.lastModified);
106
+ });
107
+
108
+ for (const prop of ["name", "actor", "lastModified"] as const) {
109
+ it(`discover for ${prop}`, async () => {
110
+ const graffiti = useGraffiti();
111
+ const session1 = useSession1();
112
+ const session2 = useSession2();
113
+
114
+ const object1 = randomPutObject();
115
+ const putted1 = await graffiti.put(object1, session1);
116
+
117
+ const object2 = randomPutObject();
118
+ object2.channels = object1.channels;
119
+ // Make sure the lastModified is different for the query
120
+ await new Promise((r) => setTimeout(r, 20));
121
+ const putted2 = await graffiti.put(object2, session2);
122
+
123
+ const iterator = graffiti.discover(object1.channels, {
124
+ properties: {
125
+ [prop]: {
126
+ enum: [putted1[prop]],
127
+ },
128
+ },
129
+ });
130
+
131
+ const value = await nextStreamValue(iterator);
132
+ expect(value.name).toEqual(putted1.name);
133
+ expect(value.name).not.toEqual(putted2.name);
134
+ expect(value.value).toEqual(object1.value);
135
+ await expect(iterator.next()).resolves.toHaveProperty("done", true);
136
+ });
137
+ }
138
+
139
+ it("discover with lastModified range", async () => {
140
+ const graffiti = useGraffiti();
141
+ const session = useSession1();
142
+
143
+ const object = randomPutObject();
144
+ const putted1 = await graffiti.put(object, session);
145
+ // Make sure the lastModified is different
106
146
  await new Promise((r) => setTimeout(r, 20));
107
- const putted2 = await graffiti.put(object2, session2);
147
+ const putted2 = await graffiti.put(object, session);
108
148
 
109
- const iterator = graffiti.discover(object1.channels, {
149
+ expect(putted1.name).not.toEqual(putted2.name);
150
+ expect(putted1.lastModified).toBeLessThan(putted2.lastModified);
151
+
152
+ const gtIterator = graffiti.discover([object.channels[0]], {
110
153
  properties: {
111
- [prop]: {
112
- enum: [putted1[prop]],
154
+ lastModified: {
155
+ minimum: putted2.lastModified,
156
+ exclusiveMinimum: true,
113
157
  },
114
158
  },
115
159
  });
116
-
117
- const value = await nextStreamValue(iterator);
118
- expect(value.name).toEqual(putted1.name);
119
- expect(value.name).not.toEqual(putted2.name);
120
- expect(value.value).toEqual(object1.value);
121
- await expect(iterator.next()).resolves.toHaveProperty("done", true);
122
- });
123
- }
124
-
125
- it("discover with lastModified range", async () => {
126
- const graffiti = useGraffiti();
127
- const session = useSession1();
128
-
129
- const object = randomPutObject();
130
- const putted1 = await graffiti.put(object, session);
131
- // Make sure the lastModified is different
132
- await new Promise((r) => setTimeout(r, 20));
133
- const putted2 = await graffiti.put(object, session);
134
-
135
- expect(putted1.name).not.toEqual(putted2.name);
136
- expect(putted1.lastModified).toBeLessThan(putted2.lastModified);
137
-
138
- const gtIterator = graffiti.discover([object.channels[0]], {
139
- properties: {
140
- lastModified: {
141
- minimum: putted2.lastModified,
142
- exclusiveMinimum: true,
143
- },
144
- },
145
- });
146
- expect(await gtIterator.next()).toHaveProperty("done", true);
147
- const gtIteratorEpsilon = graffiti.discover([object.channels[0]], {
148
- properties: {
149
- lastModified: {
150
- minimum: putted2.lastModified - 0.1,
151
- exclusiveMinimum: true,
160
+ expect(await gtIterator.next()).toHaveProperty("done", true);
161
+ const gtIteratorEpsilon = graffiti.discover([object.channels[0]], {
162
+ properties: {
163
+ lastModified: {
164
+ minimum: putted2.lastModified - 0.1,
165
+ exclusiveMinimum: true,
166
+ },
152
167
  },
153
- },
154
- });
155
- const value1 = await nextStreamValue(gtIteratorEpsilon);
156
- expect(value1.name).toEqual(putted2.name);
157
- expect(await gtIteratorEpsilon.next()).toHaveProperty("done", true);
158
- const gteIterator = graffiti.discover(object.channels, {
159
- properties: {
160
- value: {},
161
- lastModified: {
162
- minimum: putted2.lastModified,
168
+ });
169
+ const value1 = await nextStreamValue(gtIteratorEpsilon);
170
+ expect(value1.name).toEqual(putted2.name);
171
+ expect(await gtIteratorEpsilon.next()).toHaveProperty("done", true);
172
+ const gteIterator = graffiti.discover(object.channels, {
173
+ properties: {
174
+ value: {},
175
+ lastModified: {
176
+ minimum: putted2.lastModified,
177
+ },
163
178
  },
164
- },
165
- });
166
- const value = await nextStreamValue(gteIterator);
167
- expect(value.name).toEqual(putted2.name);
168
- expect(await gteIterator.next()).toHaveProperty("done", true);
169
- const gteIteratorEpsilon = graffiti.discover(object.channels, {
170
- properties: {
171
- lastModified: {
172
- minimum: putted2.lastModified + 0.1,
179
+ });
180
+ const value = await nextStreamValue(gteIterator);
181
+ expect(value.name).toEqual(putted2.name);
182
+ expect(await gteIterator.next()).toHaveProperty("done", true);
183
+ const gteIteratorEpsilon = graffiti.discover(object.channels, {
184
+ properties: {
185
+ lastModified: {
186
+ minimum: putted2.lastModified + 0.1,
187
+ },
173
188
  },
174
- },
175
- });
176
- expect(await gteIteratorEpsilon.next()).toHaveProperty("done", true);
189
+ });
190
+ expect(await gteIteratorEpsilon.next()).toHaveProperty("done", true);
177
191
 
178
- const ltIterator = graffiti.discover(object.channels, {
179
- properties: {
180
- lastModified: {
181
- maximum: putted1.lastModified,
182
- exclusiveMaximum: true,
192
+ const ltIterator = graffiti.discover(object.channels, {
193
+ properties: {
194
+ lastModified: {
195
+ maximum: putted1.lastModified,
196
+ exclusiveMaximum: true,
197
+ },
183
198
  },
184
- },
185
- });
186
- expect(await ltIterator.next()).toHaveProperty("done", true);
199
+ });
200
+ expect(await ltIterator.next()).toHaveProperty("done", true);
187
201
 
188
- const ltIteratorEpsilon = graffiti.discover(object.channels, {
189
- properties: {
190
- lastModified: {
191
- maximum: putted1.lastModified + 0.1,
192
- exclusiveMaximum: true,
202
+ const ltIteratorEpsilon = graffiti.discover(object.channels, {
203
+ properties: {
204
+ lastModified: {
205
+ maximum: putted1.lastModified + 0.1,
206
+ exclusiveMaximum: true,
207
+ },
193
208
  },
194
- },
195
- });
196
- const value3 = await nextStreamValue(ltIteratorEpsilon);
197
- expect(value3.name).toEqual(putted1.name);
198
- expect(await ltIteratorEpsilon.next()).toHaveProperty("done", true);
199
-
200
- const lteIterator = graffiti.discover(object.channels, {
201
- properties: {
202
- lastModified: {
203
- maximum: putted1.lastModified,
209
+ });
210
+ const value3 = await nextStreamValue(ltIteratorEpsilon);
211
+ expect(value3.name).toEqual(putted1.name);
212
+ expect(await ltIteratorEpsilon.next()).toHaveProperty("done", true);
213
+
214
+ const lteIterator = graffiti.discover(object.channels, {
215
+ properties: {
216
+ lastModified: {
217
+ maximum: putted1.lastModified,
218
+ },
204
219
  },
205
- },
206
- });
207
- const value2 = await nextStreamValue(lteIterator);
208
- expect(value2.name).toEqual(putted1.name);
209
- expect(await lteIterator.next()).toHaveProperty("done", true);
210
-
211
- const lteIteratorEpsilon = graffiti.discover(object.channels, {
212
- properties: {
213
- lastModified: {
214
- maximum: putted1.lastModified - 0.1,
220
+ });
221
+ const value2 = await nextStreamValue(lteIterator);
222
+ expect(value2.name).toEqual(putted1.name);
223
+ expect(await lteIterator.next()).toHaveProperty("done", true);
224
+
225
+ const lteIteratorEpsilon = graffiti.discover(object.channels, {
226
+ properties: {
227
+ lastModified: {
228
+ maximum: putted1.lastModified - 0.1,
229
+ },
215
230
  },
216
- },
231
+ });
232
+ expect(await lteIteratorEpsilon.next()).toHaveProperty("done", true);
217
233
  });
218
- expect(await lteIteratorEpsilon.next()).toHaveProperty("done", true);
219
- });
220
234
 
221
- it("discover schema allowed, as and not as owner", async () => {
222
- const graffiti = useGraffiti();
223
- const session1 = useSession1();
224
- const session2 = useSession2();
235
+ it("discover schema allowed, as and not as owner", async () => {
236
+ const graffiti = useGraffiti();
237
+ const session1 = useSession1();
238
+ const session2 = useSession2();
225
239
 
226
- const object = randomPutObject();
227
- object.allowed = [randomString(), session2.actor, randomString()];
228
- await graffiti.put(object, session1);
240
+ const object = randomPutObject();
241
+ object.allowed = [randomString(), session2.actor, randomString()];
242
+ await graffiti.put(object, session1);
229
243
 
230
- const iteratorSession1 = graffiti.discover(
231
- object.channels,
232
- {
233
- properties: {
234
- allowed: {
235
- minItems: 3,
236
- // Make sure session2.actor is in the allow list
237
- not: {
238
- items: {
239
- not: {
240
- enum: [session2.actor],
244
+ const iteratorSession1 = graffiti.discover(
245
+ object.channels,
246
+ {
247
+ properties: {
248
+ allowed: {
249
+ minItems: 3,
250
+ // Make sure session2.actor is in the allow list
251
+ not: {
252
+ items: {
253
+ not: {
254
+ enum: [session2.actor],
255
+ },
241
256
  },
242
257
  },
243
258
  },
244
259
  },
245
260
  },
246
- },
247
- session1,
248
- );
249
- const value = await nextStreamValue(iteratorSession1);
250
- expect(value.value).toEqual(object.value);
251
- await expect(iteratorSession1.next()).resolves.toHaveProperty(
252
- "done",
253
- true,
254
- );
255
-
256
- const iteratorSession2BigAllow = graffiti.discover(
257
- object.channels,
258
- {
259
- properties: {
260
- allowed: {
261
- minItems: 3,
261
+ session1,
262
+ );
263
+ const value = await nextStreamValue(iteratorSession1);
264
+ expect(value.value).toEqual(object.value);
265
+ await expect(iteratorSession1.next()).resolves.toHaveProperty(
266
+ "done",
267
+ true,
268
+ );
269
+
270
+ const iteratorSession2BigAllow = graffiti.discover(
271
+ object.channels,
272
+ {
273
+ properties: {
274
+ allowed: {
275
+ minItems: 3,
276
+ },
262
277
  },
263
278
  },
264
- },
265
- session2,
266
- );
267
- await expect(iteratorSession2BigAllow.next()).resolves.toHaveProperty(
268
- "done",
269
- true,
270
- );
271
- const iteratorSession2PeekOther = graffiti.discover(
272
- object.channels,
273
- {
274
- properties: {
275
- allowed: {
276
- not: {
277
- items: {
278
- not: {
279
- enum: [object.channels[0]],
279
+ session2,
280
+ );
281
+ await expect(iteratorSession2BigAllow.next()).resolves.toHaveProperty(
282
+ "done",
283
+ true,
284
+ );
285
+ const iteratorSession2PeekOther = graffiti.discover(
286
+ object.channels,
287
+ {
288
+ properties: {
289
+ allowed: {
290
+ not: {
291
+ items: {
292
+ not: {
293
+ enum: [object.channels[0]],
294
+ },
280
295
  },
281
296
  },
282
297
  },
283
298
  },
284
299
  },
285
- },
286
- session2,
287
- );
288
- await expect(iteratorSession2PeekOther.next()).resolves.toHaveProperty(
289
- "done",
290
- true,
291
- );
292
- const iteratorSession2SmallAllowPeekSelf = graffiti.discover(
293
- object.channels,
294
- {
295
- properties: {
296
- allowed: {
297
- maxItems: 1,
298
- not: {
299
- items: {
300
- not: {
301
- enum: [session2.actor],
300
+ session2,
301
+ );
302
+ await expect(iteratorSession2PeekOther.next()).resolves.toHaveProperty(
303
+ "done",
304
+ true,
305
+ );
306
+ const iteratorSession2SmallAllowPeekSelf = graffiti.discover(
307
+ object.channels,
308
+ {
309
+ properties: {
310
+ allowed: {
311
+ maxItems: 1,
312
+ not: {
313
+ items: {
314
+ not: {
315
+ enum: [session2.actor],
316
+ },
302
317
  },
303
318
  },
304
319
  },
305
320
  },
306
321
  },
307
- },
308
- session2,
309
- );
310
- const value2 = await nextStreamValue(iteratorSession2SmallAllowPeekSelf);
311
- expect(value2.value).toEqual(object.value);
312
- await expect(
313
- iteratorSession2SmallAllowPeekSelf.next(),
314
- ).resolves.toHaveProperty("done", true);
315
- });
316
-
317
- it("discover schema channels, as and not as owner", async () => {
318
- const graffiti = useGraffiti();
319
- const session1 = useSession1();
320
- const session2 = useSession2();
321
-
322
- const object = randomPutObject();
323
- object.channels = [randomString(), randomString(), randomString()];
324
- await graffiti.put(object, session1);
325
-
326
- const iteratorSession1 = graffiti.discover(
327
- [object.channels[0], object.channels[2]],
328
- {
329
- properties: {
330
- channels: {
331
- minItems: 3,
332
- // Make sure session2.actor is in the allow list
333
- not: {
334
- items: {
335
- not: {
336
- enum: [object.channels[1]],
322
+ session2,
323
+ );
324
+ const value2 = await nextStreamValue(
325
+ iteratorSession2SmallAllowPeekSelf,
326
+ );
327
+ expect(value2.value).toEqual(object.value);
328
+ await expect(
329
+ iteratorSession2SmallAllowPeekSelf.next(),
330
+ ).resolves.toHaveProperty("done", true);
331
+ });
332
+
333
+ it("discover schema channels, as and not as owner", async () => {
334
+ const graffiti = useGraffiti();
335
+ const session1 = useSession1();
336
+ const session2 = useSession2();
337
+
338
+ const object = randomPutObject();
339
+ object.channels = [randomString(), randomString(), randomString()];
340
+ await graffiti.put(object, session1);
341
+
342
+ const iteratorSession1 = graffiti.discover(
343
+ [object.channels[0], object.channels[2]],
344
+ {
345
+ properties: {
346
+ channels: {
347
+ minItems: 3,
348
+ // Make sure session2.actor is in the allow list
349
+ not: {
350
+ items: {
351
+ not: {
352
+ enum: [object.channels[1]],
353
+ },
337
354
  },
338
355
  },
339
356
  },
340
357
  },
341
358
  },
342
- },
343
- session1,
344
- );
345
- const value = await nextStreamValue(iteratorSession1);
346
- expect(value.value).toEqual(object.value);
347
- await expect(iteratorSession1.next()).resolves.toHaveProperty(
348
- "done",
349
- true,
350
- );
351
-
352
- const iteratorSession2BigAllow = graffiti.discover(
353
- [object.channels[0], object.channels[2]],
354
- {
355
- properties: {
356
- channels: {
357
- minItems: 3,
359
+ session1,
360
+ );
361
+ const value = await nextStreamValue(iteratorSession1);
362
+ expect(value.value).toEqual(object.value);
363
+ await expect(iteratorSession1.next()).resolves.toHaveProperty(
364
+ "done",
365
+ true,
366
+ );
367
+
368
+ const iteratorSession2BigAllow = graffiti.discover(
369
+ [object.channels[0], object.channels[2]],
370
+ {
371
+ properties: {
372
+ channels: {
373
+ minItems: 3,
374
+ },
358
375
  },
359
376
  },
360
- },
361
- session2,
362
- );
363
- await expect(iteratorSession2BigAllow.next()).resolves.toHaveProperty(
364
- "done",
365
- true,
366
- );
367
- const iteratorSession2PeekOther = graffiti.discover(
368
- [object.channels[0], object.channels[2]],
369
- {
370
- properties: {
371
- channels: {
372
- not: {
373
- items: {
374
- not: {
375
- enum: [object.channels[1]],
377
+ session2,
378
+ );
379
+ await expect(iteratorSession2BigAllow.next()).resolves.toHaveProperty(
380
+ "done",
381
+ true,
382
+ );
383
+ const iteratorSession2PeekOther = graffiti.discover(
384
+ [object.channels[0], object.channels[2]],
385
+ {
386
+ properties: {
387
+ channels: {
388
+ not: {
389
+ items: {
390
+ not: {
391
+ enum: [object.channels[1]],
392
+ },
376
393
  },
377
394
  },
378
395
  },
379
396
  },
380
397
  },
381
- },
382
- session2,
383
- );
384
- await expect(iteratorSession2PeekOther.next()).resolves.toHaveProperty(
385
- "done",
386
- true,
387
- );
388
- const iteratorSession2SmallAllowPeekSelf = graffiti.discover(
389
- [object.channels[0], object.channels[2]],
390
- {
391
- properties: {
392
- allowed: {
393
- maxItems: 2,
394
- not: {
395
- items: {
396
- not: {
397
- enum: [object.channels[2]],
398
+ session2,
399
+ );
400
+ await expect(iteratorSession2PeekOther.next()).resolves.toHaveProperty(
401
+ "done",
402
+ true,
403
+ );
404
+ const iteratorSession2SmallAllowPeekSelf = graffiti.discover(
405
+ [object.channels[0], object.channels[2]],
406
+ {
407
+ properties: {
408
+ allowed: {
409
+ maxItems: 2,
410
+ not: {
411
+ items: {
412
+ not: {
413
+ enum: [object.channels[2]],
414
+ },
398
415
  },
399
416
  },
400
417
  },
401
418
  },
402
419
  },
403
- },
404
- session2,
405
- );
406
- const value2 = await nextStreamValue(iteratorSession2SmallAllowPeekSelf);
407
- expect(value2.value).toEqual(object.value);
408
- await expect(
409
- iteratorSession2SmallAllowPeekSelf.next(),
410
- ).resolves.toHaveProperty("done", true);
411
- });
412
-
413
- it("discover query for empty allowed", async () => {
414
- const graffiti = useGraffiti();
415
- const session1 = useSession1();
416
-
417
- const publicO = randomPutObject();
418
-
419
- const publicSchema = {
420
- not: {
421
- required: ["allowed"],
422
- },
423
- } satisfies JSONSchema4;
424
-
425
- await graffiti.put(publicO, session1);
426
- const iterator = graffiti.discover(
427
- publicO.channels,
428
- publicSchema,
429
- session1,
430
- );
431
- const value = await nextStreamValue(iterator);
432
- expect(value.value).toEqual(publicO.value);
433
- expect(value.allowed).toBeUndefined();
434
- await expect(iterator.next()).resolves.toHaveProperty("done", true);
435
-
436
- const restricted = randomPutObject();
437
- restricted.allowed = [];
438
- await graffiti.put(restricted, session1);
439
- const iterator2 = graffiti.discover(
440
- restricted.channels,
441
- publicSchema,
442
- session1,
443
- );
444
- await expect(iterator2.next()).resolves.toHaveProperty("done", true);
445
- });
446
-
447
- it("discover query for values", async () => {
448
- const graffiti = useGraffiti();
449
- const session = useSession1();
450
-
451
- const object1 = randomPutObject();
452
- object1.value = { test: randomString() };
453
- await graffiti.put(object1, session);
454
-
455
- const object2 = randomPutObject();
456
- object2.channels = object1.channels;
457
- object2.value = { test: randomString(), something: randomString() };
458
- await graffiti.put(object2, session);
459
-
460
- const object3 = randomPutObject();
461
- object3.channels = object1.channels;
462
- object3.value = { other: randomString(), something: randomString() };
463
- await graffiti.put(object3, session);
464
-
465
- const counts = new Map<string, number>();
466
- for (const property of ["test", "something", "other"] as const) {
467
- let count = 0;
468
- for await (const result of graffiti.discover(object1.channels, {
469
- properties: {
470
- value: {
471
- required: [property],
472
- },
420
+ session2,
421
+ );
422
+ const value2 = await nextStreamValue(
423
+ iteratorSession2SmallAllowPeekSelf,
424
+ );
425
+ expect(value2.value).toEqual(object.value);
426
+ await expect(
427
+ iteratorSession2SmallAllowPeekSelf.next(),
428
+ ).resolves.toHaveProperty("done", true);
429
+ });
430
+
431
+ it("discover query for empty allowed", async () => {
432
+ const graffiti = useGraffiti();
433
+ const session1 = useSession1();
434
+
435
+ const publicO = randomPutObject();
436
+
437
+ const publicSchema = {
438
+ not: {
439
+ required: ["allowed"],
473
440
  },
474
- })) {
475
- assert(!result.error, "result has error");
476
- if (property in result.value.value) {
477
- count++;
478
- }
479
- }
480
- counts.set(property, count);
481
- }
441
+ } satisfies JSONSchema4;
482
442
 
483
- expect(counts.get("test")).toBe(2);
484
- expect(counts.get("something")).toBe(2);
485
- expect(counts.get("other")).toBe(1);
486
- });
487
-
488
- it("discover for deleted content", async () => {
489
- const graffiti = useGraffiti();
490
- const session = useSession1();
491
-
492
- const object = randomPutObject();
493
- const putted = await graffiti.put(object, session);
494
- const deleted = await graffiti.delete(putted, session);
495
-
496
- const iterator = graffiti.discover(object.channels, {});
497
- const value = await nextStreamValue(iterator);
498
- expect(value.tombstone).toBe(true);
499
- expect(value.value).toEqual(object.value);
500
- expect(value.channels).toEqual(object.channels);
501
- expect(value.actor).toEqual(session.actor);
502
- expect(value.lastModified).toEqual(deleted.lastModified);
503
- await expect(iterator.next()).resolves.toHaveProperty("done", true);
504
- });
505
-
506
- it("discover for replaced channels", async () => {
507
- // Do this a bunch to check for concurrency issues
508
- for (let i = 0; i < 10; i++) {
443
+ await graffiti.put(publicO, session1);
444
+ const iterator = graffiti.discover(
445
+ publicO.channels,
446
+ publicSchema,
447
+ session1,
448
+ );
449
+ const value = await nextStreamValue(iterator);
450
+ expect(value.value).toEqual(publicO.value);
451
+ expect(value.allowed).toBeUndefined();
452
+ await expect(iterator.next()).resolves.toHaveProperty("done", true);
453
+
454
+ const restricted = randomPutObject();
455
+ restricted.allowed = [];
456
+ await graffiti.put(restricted, session1);
457
+ const iterator2 = graffiti.discover(
458
+ restricted.channels,
459
+ publicSchema,
460
+ session1,
461
+ );
462
+ await expect(iterator2.next()).resolves.toHaveProperty("done", true);
463
+ });
464
+
465
+ it("discover query for values", async () => {
509
466
  const graffiti = useGraffiti();
510
467
  const session = useSession1();
511
468
 
512
469
  const object1 = randomPutObject();
513
- const putted = await graffiti.put(object1, session);
470
+ object1.value = { test: randomString() };
471
+ await graffiti.put(object1, session);
472
+
514
473
  const object2 = randomPutObject();
515
- const replaced = await graffiti.put(
474
+ object2.channels = object1.channels;
475
+ object2.value = { test: randomString(), something: randomString() };
476
+ await graffiti.put(object2, session);
477
+
478
+ const object3 = randomPutObject();
479
+ object3.channels = object1.channels;
480
+ object3.value = { other: randomString(), something: randomString() };
481
+ await graffiti.put(object3, session);
482
+
483
+ const counts = new Map<string, number>();
484
+ for (const property of ["test", "something", "other"] as const) {
485
+ let count = 0;
486
+ for await (const result of graffiti.discover(object1.channels, {
487
+ properties: {
488
+ value: {
489
+ required: [property],
490
+ },
491
+ },
492
+ })) {
493
+ assert(!result.error, "result has error");
494
+ if (property in result.value.value) {
495
+ count++;
496
+ }
497
+ }
498
+ counts.set(property, count);
499
+ }
500
+
501
+ expect(counts.get("test")).toBe(2);
502
+ expect(counts.get("something")).toBe(2);
503
+ expect(counts.get("other")).toBe(1);
504
+ });
505
+
506
+ it("discover for deleted content", async () => {
507
+ const graffiti = useGraffiti();
508
+ const session = useSession1();
509
+
510
+ const object = randomPutObject();
511
+ const putted = await graffiti.put(object, session);
512
+ const deleted = await graffiti.delete(putted, session);
513
+
514
+ const iterator = graffiti.discover(object.channels, {});
515
+ const value = await nextStreamValue(iterator);
516
+ expect(value.tombstone).toBe(true);
517
+ expect(value.value).toEqual(object.value);
518
+ expect(value.channels).toEqual(object.channels);
519
+ expect(value.actor).toEqual(session.actor);
520
+ expect(value.lastModified).toEqual(deleted.lastModified);
521
+ await expect(iterator.next()).resolves.toHaveProperty("done", true);
522
+ });
523
+
524
+ it("discover for replaced channels", async () => {
525
+ // Do this a bunch to check for concurrency issues
526
+ for (let i = 0; i < 10; i++) {
527
+ const graffiti = useGraffiti();
528
+ const session = useSession1();
529
+
530
+ const object1 = randomPutObject();
531
+ const putted = await graffiti.put(object1, session);
532
+ const object2 = randomPutObject();
533
+ const replaced = await graffiti.put(
534
+ {
535
+ ...putted,
536
+ ...object2,
537
+ },
538
+ session,
539
+ );
540
+
541
+ const iterator1 = graffiti.discover(object1.channels, {});
542
+ const value1 = await nextStreamValue(iterator1);
543
+ await expect(iterator1.next()).resolves.toHaveProperty("done", true);
544
+
545
+ const iterator2 = graffiti.discover(object2.channels, {});
546
+ const value2 = await nextStreamValue(iterator2);
547
+ await expect(iterator2.next()).resolves.toHaveProperty("done", true);
548
+
549
+ // If they have the same timestamp, except
550
+ // only one to have a tombstone
551
+ if (putted.lastModified === replaced.lastModified) {
552
+ expect(value1.tombstone || value2.tombstone).toBe(true);
553
+ expect(value1.tombstone && value2.tombstone).toBe(false);
554
+ } else {
555
+ expect(value1.tombstone).toBe(true);
556
+ expect(value1.value).toEqual(object1.value);
557
+ expect(value1.channels).toEqual(object1.channels);
558
+ expect(value1.lastModified).toEqual(replaced.lastModified);
559
+
560
+ expect(value2.tombstone).toBe(false);
561
+ expect(value2.value).toEqual(object2.value);
562
+ expect(value2.channels).toEqual(object2.channels);
563
+ expect(value2.lastModified).toEqual(replaced.lastModified);
564
+ }
565
+ }
566
+ });
567
+
568
+ it("discover for patched allowed", async () => {
569
+ const graffiti = useGraffiti();
570
+ const session = useSession1();
571
+ const object = randomPutObject();
572
+ const putted = await graffiti.put(object, session);
573
+ await graffiti.patch(
516
574
  {
517
- ...putted,
518
- ...object2,
575
+ allowed: [{ op: "add", path: "", value: [] }],
519
576
  },
577
+ putted,
520
578
  session,
521
579
  );
580
+ const iterator = graffiti.discover(object.channels, {});
581
+ const value = await nextStreamValue(iterator);
582
+ expect(value.tombstone).toBe(true);
583
+ expect(value.value).toEqual(object.value);
584
+ expect(value.channels).toEqual(object.channels);
585
+ expect(value.allowed).toBeUndefined();
586
+ await expect(iterator.next()).resolves.toHaveProperty("done", true);
587
+ });
522
588
 
523
- const iterator1 = graffiti.discover(object1.channels, {});
524
- const value1 = await nextStreamValue(iterator1);
525
- await expect(iterator1.next()).resolves.toHaveProperty("done", true);
589
+ it("put concurrently and discover one", async () => {
590
+ const graffiti = useGraffiti();
591
+ const session = useSession1();
526
592
 
527
- const iterator2 = graffiti.discover(object2.channels, {});
528
- const value2 = await nextStreamValue(iterator2);
529
- await expect(iterator2.next()).resolves.toHaveProperty("done", true);
593
+ const object = randomPutObject();
594
+ object.name = randomString();
530
595
 
531
- // If they have the same timestamp, except
532
- // only one to have a tombstone
533
- if (putted.lastModified === replaced.lastModified) {
534
- expect(value1.tombstone || value2.tombstone).toBe(true);
535
- expect(value1.tombstone && value2.tombstone).toBe(false);
536
- } else {
537
- expect(value1.tombstone).toBe(true);
538
- expect(value1.value).toEqual(object1.value);
539
- expect(value1.channels).toEqual(object1.channels);
540
- expect(value1.lastModified).toEqual(replaced.lastModified);
541
-
542
- expect(value2.tombstone).toBe(false);
543
- expect(value2.value).toEqual(object2.value);
544
- expect(value2.channels).toEqual(object2.channels);
545
- expect(value2.lastModified).toEqual(replaced.lastModified);
546
- }
547
- }
548
- });
549
-
550
- it("discover for patched allowed", async () => {
551
- const graffiti = useGraffiti();
552
- const session = useSession1();
553
- const object = randomPutObject();
554
- const putted = await graffiti.put(object, session);
555
- await graffiti.patch(
556
- {
557
- allowed: [{ op: "add", path: "", value: [] }],
558
- },
559
- putted,
560
- session,
561
- );
562
- const iterator = graffiti.discover(object.channels, {});
563
- const value = await nextStreamValue(iterator);
564
- expect(value.tombstone).toBe(true);
565
- expect(value.value).toEqual(object.value);
566
- expect(value.channels).toEqual(object.channels);
567
- expect(value.allowed).toBeUndefined();
568
- await expect(iterator.next()).resolves.toHaveProperty("done", true);
569
- });
570
-
571
- it("put concurrently and discover one", async () => {
572
- const graffiti = useGraffiti();
573
- const session = useSession1();
574
-
575
- const object = randomPutObject();
576
- object.name = randomString();
577
-
578
- const putPromises = Array(100)
579
- .fill(0)
580
- .map(() => graffiti.put(object, session));
581
- await Promise.all(putPromises);
582
-
583
- const iterator = graffiti.discover(object.channels, {});
584
- let tombstoneCount = 0;
585
- let valueCount = 0;
586
- for await (const result of iterator) {
587
- assert(!result.error, "result has error");
588
- if (result.value.tombstone) {
589
- tombstoneCount++;
590
- } else {
591
- valueCount++;
596
+ const putPromises = Array(100)
597
+ .fill(0)
598
+ .map(() => graffiti.put(object, session));
599
+ await Promise.all(putPromises);
600
+
601
+ const iterator = graffiti.discover(object.channels, {});
602
+ let tombstoneCount = 0;
603
+ let valueCount = 0;
604
+ for await (const result of iterator) {
605
+ assert(!result.error, "result has error");
606
+ if (result.value.tombstone) {
607
+ tombstoneCount++;
608
+ } else {
609
+ valueCount++;
610
+ }
592
611
  }
593
- }
594
- expect(tombstoneCount).toBe(99);
595
- expect(valueCount).toBe(1);
596
- });
597
- });
612
+ expect(tombstoneCount).toBe(99);
613
+ expect(valueCount).toBe(1);
614
+ });
615
+ },
616
+ { timeout: 20000 },
617
+ );
598
618
  };