@graffiti-garden/api 1.0.0 → 1.0.1

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/dist/tests.mjs CHANGED
@@ -1,3 +1,12 @@
1
+ // tests/crud.ts
2
+ import { it, expect, describe, beforeAll } from "vitest";
3
+ import {
4
+ GraffitiErrorNotFound,
5
+ GraffitiErrorSchemaMismatch,
6
+ GraffitiErrorInvalidSchema,
7
+ GraffitiErrorForbidden
8
+ } from "@graffiti-garden/api";
9
+
1
10
  // tests/utils.ts
2
11
  import { assert } from "vitest";
3
12
  function randomString() {
@@ -33,8 +42,682 @@ function continueStream(graffiti, streamReturn, type, session) {
33
42
  return streamReturn.continue();
34
43
  }
35
44
  }
45
+
46
+ // tests/crud.ts
47
+ var graffitiCRUDTests = (useGraffiti, useSession1, useSession2) => {
48
+ describe.concurrent(
49
+ "CRUD",
50
+ {
51
+ timeout: 2e4
52
+ },
53
+ () => {
54
+ let graffiti;
55
+ let session;
56
+ let session1;
57
+ let session2;
58
+ beforeAll(async () => {
59
+ graffiti = useGraffiti();
60
+ session1 = await useSession1();
61
+ session = session1;
62
+ session2 = await useSession2();
63
+ });
64
+ it("post, get, delete", async () => {
65
+ const value = {
66
+ something: "hello, world~ c:"
67
+ };
68
+ const channels = [randomString(), randomString()];
69
+ const previous = await graffiti.post({ value, channels }, session);
70
+ expect(previous.value).toEqual(value);
71
+ expect(previous.channels).toEqual(channels);
72
+ expect(previous.allowed).toEqual(void 0);
73
+ expect(previous.actor).toEqual(session.actor);
74
+ const gotten = await graffiti.get(previous, {});
75
+ expect(gotten.value).toEqual(value);
76
+ expect(gotten.channels).toEqual([]);
77
+ expect(gotten.allowed).toBeUndefined();
78
+ expect(gotten.url).toEqual(previous.url);
79
+ expect(gotten.actor).toEqual(previous.actor);
80
+ await graffiti.delete(gotten, session);
81
+ await expect(graffiti.get(gotten, {})).rejects.toBeInstanceOf(
82
+ GraffitiErrorNotFound
83
+ );
84
+ await expect(graffiti.delete(gotten, session)).rejects.toThrow(
85
+ GraffitiErrorNotFound
86
+ );
87
+ });
88
+ it("post then delete with wrong actor", async () => {
89
+ const posted = await graffiti.post(
90
+ { value: {}, channels: [] },
91
+ session2
92
+ );
93
+ await expect(graffiti.delete(posted, session1)).rejects.toThrow(
94
+ GraffitiErrorForbidden
95
+ );
96
+ });
97
+ it("post and get with schema", async () => {
98
+ const schema = {
99
+ properties: {
100
+ value: {
101
+ properties: {
102
+ something: {
103
+ type: "string"
104
+ },
105
+ another: {
106
+ type: "array",
107
+ items: {
108
+ type: "number"
109
+ }
110
+ },
111
+ deeper: {
112
+ type: "object",
113
+ properties: {
114
+ deepProp: {
115
+ type: "string"
116
+ }
117
+ },
118
+ required: ["deepProp"]
119
+ }
120
+ },
121
+ required: ["another", "deeper"]
122
+ }
123
+ }
124
+ };
125
+ const goodValue = {
126
+ something: "hello",
127
+ another: [1, 2, 3],
128
+ deeper: {
129
+ deepProp: "hello"
130
+ }
131
+ };
132
+ const posted = await graffiti.post(
133
+ {
134
+ value: goodValue,
135
+ channels: []
136
+ },
137
+ session
138
+ );
139
+ const gotten = await graffiti.get(posted, schema);
140
+ expect(gotten.value.something).toEqual(goodValue.something);
141
+ expect(gotten.value.another).toEqual(goodValue.another);
142
+ expect(gotten.value.another[0]).toEqual(1);
143
+ expect(gotten.value.deeper.deepProp).toEqual(goodValue.deeper.deepProp);
144
+ });
145
+ it("post and get with invalid schema", async () => {
146
+ const posted = await graffiti.post(
147
+ { value: {}, channels: [] },
148
+ session
149
+ );
150
+ await expect(
151
+ graffiti.get(posted, {
152
+ properties: {
153
+ value: {
154
+ //@ts-ignore
155
+ type: "asdf"
156
+ }
157
+ }
158
+ })
159
+ ).rejects.toThrow(GraffitiErrorInvalidSchema);
160
+ });
161
+ it("post and get with wrong schema", async () => {
162
+ const posted = await graffiti.post(
163
+ {
164
+ value: {
165
+ hello: "world"
166
+ },
167
+ channels: []
168
+ },
169
+ session
170
+ );
171
+ await expect(
172
+ graffiti.get(posted, {
173
+ properties: {
174
+ value: {
175
+ properties: {
176
+ hello: {
177
+ type: "number"
178
+ }
179
+ }
180
+ }
181
+ }
182
+ })
183
+ ).rejects.toThrow(GraffitiErrorSchemaMismatch);
184
+ });
185
+ it("post and get with empty access control", async () => {
186
+ const value = {
187
+ um: "hi"
188
+ };
189
+ const allowed = [randomString()];
190
+ const channels = [randomString()];
191
+ const posted = await graffiti.post(
192
+ { value, allowed, channels },
193
+ session1
194
+ );
195
+ const gotten = await graffiti.get(posted, {}, session1);
196
+ expect(gotten.url).toEqual(posted.url);
197
+ expect(gotten.actor).toEqual(session1.actor);
198
+ expect(gotten.value).toEqual(value);
199
+ expect(gotten.allowed).toEqual(allowed);
200
+ expect(gotten.channels).toEqual(channels);
201
+ await expect(graffiti.get(posted, {})).rejects.toBeInstanceOf(
202
+ GraffitiErrorNotFound
203
+ );
204
+ await expect(graffiti.get(posted, {}, session2)).rejects.toBeInstanceOf(
205
+ GraffitiErrorNotFound
206
+ );
207
+ });
208
+ it("post and get with specific access control", async () => {
209
+ const value = {
210
+ um: "hi"
211
+ };
212
+ const allowed = [randomString(), session2.actor, randomString()];
213
+ const channels = [randomString()];
214
+ const posted = await graffiti.post(
215
+ {
216
+ value,
217
+ allowed,
218
+ channels
219
+ },
220
+ session1
221
+ );
222
+ const gotten = await graffiti.get(posted, {}, session1);
223
+ expect(gotten.url).toEqual(posted.url);
224
+ expect(gotten.actor).toEqual(session1.actor);
225
+ expect(gotten.value).toEqual(value);
226
+ expect(gotten.allowed).toEqual(allowed);
227
+ expect(gotten.channels).toEqual(channels);
228
+ await expect(graffiti.get(posted, {})).rejects.toBeInstanceOf(
229
+ GraffitiErrorNotFound
230
+ );
231
+ const gotten2 = await graffiti.get(posted, {}, session2);
232
+ expect(gotten.url).toEqual(posted.url);
233
+ expect(gotten.actor).toEqual(session1.actor);
234
+ expect(gotten2.value).toEqual(value);
235
+ expect(gotten2.allowed).toEqual([session2.actor]);
236
+ expect(gotten2.channels).toEqual([]);
237
+ });
238
+ }
239
+ );
240
+ };
241
+
242
+ // tests/discover.ts
243
+ import { it as it2, expect as expect2, describe as describe2, assert as assert2, beforeAll as beforeAll2 } from "vitest";
244
+ var graffitiDiscoverTests = (useGraffiti, useSession1, useSession2) => {
245
+ describe2.concurrent("discover", { timeout: 2e4 }, () => {
246
+ let graffiti;
247
+ let session;
248
+ let session1;
249
+ let session2;
250
+ beforeAll2(async () => {
251
+ graffiti = useGraffiti();
252
+ session1 = await useSession1();
253
+ session = session1;
254
+ session2 = await useSession2();
255
+ });
256
+ it2("discover nothing", async () => {
257
+ const iterator = graffiti.discover([], {});
258
+ expect2(await iterator.next()).toHaveProperty("done", true);
259
+ });
260
+ it2("discover single", async () => {
261
+ const object = randomPostObject();
262
+ const posted = await graffiti.post(object, session);
263
+ const queryChannels = [randomString(), object.channels[0]];
264
+ const iterator = graffiti.discover(queryChannels, {});
265
+ const value = await nextStreamValue(iterator);
266
+ expect2(value.url).toEqual(posted.url);
267
+ expect2(value.value).toEqual(object.value);
268
+ expect2(value.channels).toEqual([object.channels[0]]);
269
+ expect2(value.allowed).toBeUndefined();
270
+ expect2(value.actor).toEqual(session.actor);
271
+ const result2 = await iterator.next();
272
+ expect2(result2.done).toBe(true);
273
+ });
274
+ it2("discover wrong channel", async () => {
275
+ const object = randomPostObject();
276
+ await graffiti.post(object, session);
277
+ const iterator = graffiti.discover([randomString()], {});
278
+ await expect2(iterator.next()).resolves.toHaveProperty("done", true);
279
+ });
280
+ it2("discover not allowed", async () => {
281
+ const object = randomPostObject();
282
+ object.allowed = [randomString(), randomString()];
283
+ const posted = await graffiti.post(object, session1);
284
+ const iteratorSession1 = graffiti.discover(
285
+ object.channels,
286
+ {},
287
+ session1
288
+ );
289
+ const value = await nextStreamValue(iteratorSession1);
290
+ expect2(value.url).toEqual(posted.url);
291
+ expect2(value.value).toEqual(object.value);
292
+ expect2(value.channels).toEqual(object.channels);
293
+ expect2(value.allowed).toEqual(object.allowed);
294
+ expect2(value.actor).toEqual(session1.actor);
295
+ const iteratorSession2 = graffiti.discover(object.channels, {}, session2);
296
+ expect2(await iteratorSession2.next()).toHaveProperty("done", true);
297
+ const iteratorNoSession = graffiti.discover(object.channels, {});
298
+ expect2(await iteratorNoSession.next()).toHaveProperty("done", true);
299
+ });
300
+ it2("discover allowed", async () => {
301
+ const object = randomPostObject();
302
+ object.allowed = [randomString(), session2.actor, randomString()];
303
+ const posted = await graffiti.post(object, session1);
304
+ const iteratorSession2 = graffiti.discover(
305
+ object.channels,
306
+ {},
307
+ session2
308
+ );
309
+ const value = await nextStreamValue(iteratorSession2);
310
+ expect2(value.url).toEqual(posted.url);
311
+ expect2(value.value).toEqual(object.value);
312
+ expect2(value.allowed).toEqual([session2.actor]);
313
+ expect2(value.channels).toEqual(object.channels);
314
+ expect2(value.actor).toEqual(session1.actor);
315
+ });
316
+ it2("discover for actor", async () => {
317
+ const object1 = randomPostObject();
318
+ const posted1 = await graffiti.post(object1, session1);
319
+ const object2 = randomPostObject();
320
+ object2.channels = object1.channels;
321
+ const posted2 = await graffiti.post(object2, session2);
322
+ const iterator = graffiti.discover(object1.channels, {
323
+ properties: {
324
+ actor: { const: posted1.actor }
325
+ }
326
+ });
327
+ const value = await nextStreamValue(iterator);
328
+ expect2(value.url).toEqual(posted1.url);
329
+ expect2(value.url).not.toEqual(posted2.url);
330
+ expect2(value.value).toEqual(object1.value);
331
+ await expect2(iterator.next()).resolves.toHaveProperty("done", true);
332
+ });
333
+ it2("discover schema allowed, as and not as owner", async () => {
334
+ const object = randomPostObject();
335
+ object.allowed = [randomString(), session2.actor, randomString()];
336
+ await graffiti.post(object, session1);
337
+ const iteratorSession1 = graffiti.discover(
338
+ object.channels,
339
+ {
340
+ properties: {
341
+ allowed: {
342
+ minItems: 3,
343
+ // Make sure session2.actor is in the allow list
344
+ not: {
345
+ items: {
346
+ not: { const: session2.actor }
347
+ }
348
+ }
349
+ }
350
+ }
351
+ },
352
+ session1
353
+ );
354
+ const value = await nextStreamValue(iteratorSession1);
355
+ expect2(value.value).toEqual(object.value);
356
+ await expect2(iteratorSession1.next()).resolves.toHaveProperty(
357
+ "done",
358
+ true
359
+ );
360
+ const iteratorSession2BigAllow = graffiti.discover(
361
+ object.channels,
362
+ {
363
+ properties: {
364
+ allowed: {
365
+ minItems: 3
366
+ }
367
+ }
368
+ },
369
+ session2
370
+ );
371
+ await expect2(iteratorSession2BigAllow.next()).resolves.toHaveProperty(
372
+ "done",
373
+ true
374
+ );
375
+ const iteratorSession2PeekOther = graffiti.discover(
376
+ object.channels,
377
+ {
378
+ properties: {
379
+ allowed: {
380
+ not: {
381
+ items: {
382
+ not: { const: object.allowed[0] }
383
+ }
384
+ }
385
+ }
386
+ }
387
+ },
388
+ session2
389
+ );
390
+ await expect2(iteratorSession2PeekOther.next()).resolves.toHaveProperty(
391
+ "done",
392
+ true
393
+ );
394
+ const iteratorSession2SmallAllowPeekSelf = graffiti.discover(
395
+ object.channels,
396
+ {
397
+ properties: {
398
+ allowed: {
399
+ maxItems: 1,
400
+ not: {
401
+ items: {
402
+ not: { const: session2.actor }
403
+ }
404
+ }
405
+ }
406
+ }
407
+ },
408
+ session2
409
+ );
410
+ const value2 = await nextStreamValue(
411
+ iteratorSession2SmallAllowPeekSelf
412
+ );
413
+ expect2(value2.value).toEqual(object.value);
414
+ await expect2(
415
+ iteratorSession2SmallAllowPeekSelf.next()
416
+ ).resolves.toHaveProperty("done", true);
417
+ });
418
+ it2("discover schema channels, as and not as owner", async () => {
419
+ const object = randomPostObject();
420
+ object.channels = [randomString(), randomString(), randomString()];
421
+ await graffiti.post(object, session1);
422
+ const iteratorSession1 = graffiti.discover(
423
+ [object.channels[0], object.channels[2]],
424
+ {
425
+ properties: {
426
+ channels: {
427
+ minItems: 3,
428
+ // Make sure channel 1 is in the allow list
429
+ not: {
430
+ items: {
431
+ not: { const: object.channels[1] }
432
+ }
433
+ }
434
+ }
435
+ }
436
+ },
437
+ session1
438
+ );
439
+ const value = await nextStreamValue(iteratorSession1);
440
+ expect2(value.value).toEqual(object.value);
441
+ await expect2(iteratorSession1.next()).resolves.toHaveProperty(
442
+ "done",
443
+ true
444
+ );
445
+ const iteratorSession2BigAllow = graffiti.discover(
446
+ [object.channels[0], object.channels[2]],
447
+ {
448
+ properties: {
449
+ channels: {
450
+ minItems: 3
451
+ }
452
+ }
453
+ },
454
+ session2
455
+ );
456
+ await expect2(iteratorSession2BigAllow.next()).resolves.toHaveProperty(
457
+ "done",
458
+ true
459
+ );
460
+ const iteratorSession2PeekOther = graffiti.discover(
461
+ [object.channels[0], object.channels[2]],
462
+ {
463
+ properties: {
464
+ channels: {
465
+ not: {
466
+ items: {
467
+ not: { const: object.channels[1] }
468
+ }
469
+ }
470
+ }
471
+ }
472
+ },
473
+ session2
474
+ );
475
+ await expect2(iteratorSession2PeekOther.next()).resolves.toHaveProperty(
476
+ "done",
477
+ true
478
+ );
479
+ const iteratorSession2SmallAllowPeekSelf = graffiti.discover(
480
+ [object.channels[0], object.channels[2]],
481
+ {
482
+ properties: {
483
+ allowed: {
484
+ maxItems: 2,
485
+ not: {
486
+ items: {
487
+ not: { const: object.channels[2] }
488
+ }
489
+ }
490
+ }
491
+ }
492
+ },
493
+ session2
494
+ );
495
+ const value2 = await nextStreamValue(
496
+ iteratorSession2SmallAllowPeekSelf
497
+ );
498
+ expect2(value2.value).toEqual(object.value);
499
+ await expect2(
500
+ iteratorSession2SmallAllowPeekSelf.next()
501
+ ).resolves.toHaveProperty("done", true);
502
+ });
503
+ it2("discover query for empty allowed", async () => {
504
+ const publicO = randomPostObject();
505
+ const publicSchema = {
506
+ not: {
507
+ required: ["allowed"]
508
+ }
509
+ };
510
+ await graffiti.post(publicO, session1);
511
+ const iterator = graffiti.discover(
512
+ publicO.channels,
513
+ publicSchema,
514
+ session1
515
+ );
516
+ const value = await nextStreamValue(iterator);
517
+ expect2(value.value).toEqual(publicO.value);
518
+ expect2(value.allowed).toBeUndefined();
519
+ await expect2(iterator.next()).resolves.toHaveProperty("done", true);
520
+ const restricted = randomPostObject();
521
+ restricted.allowed = [];
522
+ await graffiti.post(restricted, session1);
523
+ const iterator2 = graffiti.discover(
524
+ restricted.channels,
525
+ publicSchema,
526
+ session1
527
+ );
528
+ await expect2(iterator2.next()).resolves.toHaveProperty("done", true);
529
+ });
530
+ it2("discover query for values", async () => {
531
+ const object1 = randomPostObject();
532
+ object1.value = { test: randomString() };
533
+ await graffiti.post(object1, session);
534
+ const object2 = randomPostObject();
535
+ object2.channels = object1.channels;
536
+ object2.value = { test: randomString(), something: randomString() };
537
+ await graffiti.post(object2, session);
538
+ const object3 = randomPostObject();
539
+ object3.channels = object1.channels;
540
+ object3.value = { other: randomString(), something: randomString() };
541
+ await graffiti.post(object3, session);
542
+ const counts = /* @__PURE__ */ new Map();
543
+ for (const property of ["test", "something", "other"]) {
544
+ let count = 0;
545
+ for await (const result of graffiti.discover(object1.channels, {
546
+ properties: {
547
+ value: {
548
+ required: [property]
549
+ }
550
+ }
551
+ })) {
552
+ assert2(!result.error, "result has error");
553
+ if (property in result.object.value) {
554
+ count++;
555
+ }
556
+ }
557
+ counts.set(property, count);
558
+ }
559
+ expect2(counts.get("test")).toBe(2);
560
+ expect2(counts.get("something")).toBe(2);
561
+ expect2(counts.get("other")).toBe(1);
562
+ });
563
+ for (const continueType of ["cursor", "continue"]) {
564
+ describe2(`continue discover with ${continueType}`, () => {
565
+ it2("discover for deleted content", async () => {
566
+ const object = randomPostObject();
567
+ const posted = await graffiti.post(object, session);
568
+ const iterator1 = graffiti.discover(object.channels, {});
569
+ const value1 = await nextStreamValue(iterator1);
570
+ expect2(value1.value).toEqual(object.value);
571
+ const returnValue = await iterator1.next();
572
+ assert2(returnValue.done, "value2 is not done");
573
+ await graffiti.delete(posted, session);
574
+ const iterator = graffiti.discover(object.channels, {});
575
+ await expect2(iterator.next()).resolves.toHaveProperty("done", true);
576
+ const tombIterator = continueStream(
577
+ graffiti,
578
+ returnValue.value,
579
+ continueType
580
+ );
581
+ const value = await tombIterator.next();
582
+ assert2(!value.done && !value.value.error, "value is done");
583
+ assert2(value.value.tombstone, "value is not tombstone");
584
+ expect2(value.value.object.url).toEqual(posted.url);
585
+ await expect2(tombIterator.next()).resolves.toHaveProperty(
586
+ "done",
587
+ true
588
+ );
589
+ });
590
+ });
591
+ }
592
+ });
593
+ };
594
+
595
+ // tests/media.ts
596
+ import {
597
+ GraffitiErrorNotAcceptable,
598
+ GraffitiErrorNotFound as GraffitiErrorNotFound2,
599
+ GraffitiErrorTooLarge
600
+ } from "@graffiti-garden/api";
601
+ import { it as it3, expect as expect3, describe as describe3, beforeAll as beforeAll3 } from "vitest";
602
+ var graffitiMediaTests = (useGraffiti, useSession1, useSession2) => {
603
+ describe3.concurrent(
604
+ "media",
605
+ {
606
+ timeout: 2e4
607
+ },
608
+ () => {
609
+ let graffiti;
610
+ let session;
611
+ let session1;
612
+ let session2;
613
+ beforeAll3(async () => {
614
+ graffiti = useGraffiti();
615
+ session1 = await useSession1();
616
+ session = session1;
617
+ session2 = await useSession2();
618
+ });
619
+ it3("post, get, delete media", async () => {
620
+ const text = randomString();
621
+ const data = new Blob([text], { type: "text/plain" });
622
+ const mediaUrl = await graffiti.postMedia({ data }, session);
623
+ const media = await graffiti.getMedia(mediaUrl, {});
624
+ expect3(await media.data.text()).toEqual(text);
625
+ expect3(media.data.type).toEqual("text/plain");
626
+ expect3(media.allowed).toBeUndefined();
627
+ expect3(media.actor).toBe(session.actor);
628
+ await graffiti.deleteMedia(mediaUrl, session);
629
+ await expect3(graffiti.getMedia(mediaUrl, {})).rejects.toThrow(
630
+ GraffitiErrorNotFound2
631
+ );
632
+ });
633
+ it3("acceptable type", async () => {
634
+ const text = randomString();
635
+ const data = new Blob([text], { type: "text/plain" });
636
+ const mediaUrl = await graffiti.postMedia({ data }, session);
637
+ const media = await graffiti.getMedia(mediaUrl, {
638
+ accept: "text/*"
639
+ });
640
+ expect3(await media.data.text()).toEqual(text);
641
+ expect3(media.data.type).toEqual("text/plain");
642
+ expect3(media.allowed).toBeUndefined();
643
+ expect3(media.actor).toBe(session.actor);
644
+ });
645
+ it3("unacceptable type", async () => {
646
+ const text = randomString();
647
+ const data = new Blob([text], { type: "text/plain" });
648
+ const mediaUrl = await graffiti.postMedia({ data }, session);
649
+ await expect3(
650
+ graffiti.getMedia(mediaUrl, {
651
+ accept: "image/*"
652
+ })
653
+ ).rejects.toThrow(GraffitiErrorNotAcceptable);
654
+ });
655
+ it3("acceptable size", async () => {
656
+ const text = randomString();
657
+ const data = new Blob([text], { type: "text/plain" });
658
+ const mediaUrl = await graffiti.postMedia({ data }, session);
659
+ const media = await graffiti.getMedia(mediaUrl, {
660
+ maxBytes: data.size
661
+ });
662
+ expect3(await media.data.text()).toEqual(text);
663
+ expect3(media.data.type).toEqual("text/plain");
664
+ expect3(media.allowed).toBeUndefined();
665
+ expect3(media.actor).toBe(session.actor);
666
+ });
667
+ it3("unacceptable size", async () => {
668
+ const text = randomString();
669
+ const data = new Blob([text], { type: "text/plain" });
670
+ const mediaUrl = await graffiti.postMedia({ data }, session);
671
+ await expect3(
672
+ graffiti.getMedia(mediaUrl, {
673
+ maxBytes: data.size - 1
674
+ })
675
+ ).rejects.toThrow(GraffitiErrorTooLarge);
676
+ });
677
+ it3("empty allowed", async () => {
678
+ const text = randomString();
679
+ const data = new Blob([text], { type: "text/plain" });
680
+ const allowed = [];
681
+ const mediaUrl = await graffiti.postMedia({ data, allowed }, session1);
682
+ const media = await graffiti.getMedia(mediaUrl, {}, session1);
683
+ expect3(await media.data.text()).toEqual(text);
684
+ expect3(media.data.type).toEqual("text/plain");
685
+ expect3(media.allowed).toEqual([]);
686
+ expect3(media.actor).toBe(session1.actor);
687
+ await expect3(graffiti.getMedia(mediaUrl, {}, session2)).rejects.toThrow(
688
+ GraffitiErrorNotFound2
689
+ );
690
+ await expect3(graffiti.getMedia(mediaUrl, {})).rejects.toThrow(
691
+ GraffitiErrorNotFound2
692
+ );
693
+ });
694
+ it3("allowed", async () => {
695
+ const text = randomString();
696
+ const data = new Blob([text], { type: "text/plain" });
697
+ const allowed = [randomString(), session2.actor, randomString()];
698
+ const mediaUrl = await graffiti.postMedia({ data, allowed }, session1);
699
+ const media = await graffiti.getMedia(mediaUrl, {}, session1);
700
+ expect3(await media.data.text()).toEqual(text);
701
+ expect3(media.data.type).toEqual("text/plain");
702
+ expect3(media.allowed).toEqual(allowed);
703
+ expect3(media.actor).toBe(session1.actor);
704
+ const media2 = await graffiti.getMedia(mediaUrl, {}, session2);
705
+ expect3(await media2.data.text()).toEqual(text);
706
+ expect3(media2.data.type).toEqual("text/plain");
707
+ expect3(media2.allowed).toEqual([session2.actor]);
708
+ expect3(media2.actor).toBe(session1.actor);
709
+ await expect3(graffiti.getMedia(mediaUrl, {})).rejects.toThrow(
710
+ GraffitiErrorNotFound2
711
+ );
712
+ });
713
+ }
714
+ );
715
+ };
36
716
  export {
37
717
  continueStream,
718
+ graffitiCRUDTests,
719
+ graffitiDiscoverTests,
720
+ graffitiMediaTests,
38
721
  nextStreamValue,
39
722
  randomPostObject,
40
723
  randomString,