@elevasis/core 0.34.1 → 0.35.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/dist/auth/index.d.ts +74 -2
- package/dist/auth/index.js +65 -30
- package/dist/index.d.ts +60 -2
- package/dist/index.js +52 -1
- package/dist/knowledge/index.d.ts +12 -0
- package/dist/organization-model/index.d.ts +60 -2
- package/dist/organization-model/index.js +52 -1
- package/dist/test-utils/index.d.ts +12 -0
- package/dist/test-utils/index.js +51 -0
- package/package.json +1 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +69 -30
- package/src/auth/multi-tenancy/index.ts +29 -26
- package/src/auth/multi-tenancy/org-id.test.ts +139 -0
- package/src/auth/multi-tenancy/org-id.ts +112 -0
- package/src/business/acquisition/api-schemas.test.ts +456 -28
- package/src/business/acquisition/ontology-validation.ts +715 -23
- package/src/execution/engine/tools/platform/storage/__tests__/storage.test.ts +997 -998
- package/src/organization-model/__tests__/domains/systems.test.ts +61 -15
- package/src/organization-model/__tests__/domains/topology.test.ts +23 -0
- package/src/organization-model/__tests__/schema.test.ts +112 -0
- package/src/organization-model/domains/systems.ts +44 -0
- package/src/organization-model/domains/topology.ts +18 -1
- package/src/organization-model/published.ts +19 -1
- package/src/organization-model/schema-refinements.ts +23 -0
- package/src/organization-model/types.ts +17 -1
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/registry/__tests__/validation.test.ts +254 -15
- package/src/platform/registry/index.ts +28 -15
- package/src/platform/registry/validation.ts +180 -2
- package/src/reference/_generated/contracts.md +44 -0
- package/src/supabase/__tests__/helpers.test.ts +92 -51
- package/src/supabase/helpers.ts +40 -20
- package/src/supabase/index.ts +52 -52
|
@@ -1,998 +1,997 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from '
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
vi.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
vi.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
expect(tool.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
expect(uploadCall.file).
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
expect(toolingError.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
expect(tool.
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
expect(error).
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
expect(tool.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
expect(result.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
expect(
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
expect(result.
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
expect(error).
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
expect(tool.
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
})
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
expect(error).
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
expect(tool.
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
expect(error).
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
mockStorageService.
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
expect(results).
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
expect(results[2].
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
describe('
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
})
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
createStorageUploadTool,
|
|
4
|
+
createStorageSignedUrlTool,
|
|
5
|
+
createStorageDownloadTool,
|
|
6
|
+
createStorageDeleteTool,
|
|
7
|
+
createStorageListTool,
|
|
8
|
+
StorageUploadInputSchema,
|
|
9
|
+
StorageSignedUrlInputSchema,
|
|
10
|
+
StorageDownloadInputSchema,
|
|
11
|
+
StorageDeleteInputSchema,
|
|
12
|
+
StorageListInputSchema
|
|
13
|
+
} from '../index'
|
|
14
|
+
import { ToolingError } from '../../../types'
|
|
15
|
+
import { getToolServices } from '../../../registry'
|
|
16
|
+
import { createMockExecutionContext } from '../../../../test-utils/mocks'
|
|
17
|
+
|
|
18
|
+
vi.mock('../../../registry', () => ({
|
|
19
|
+
getToolServices: vi.fn()
|
|
20
|
+
}))
|
|
21
|
+
|
|
22
|
+
describe('Storage Platform Tools', () => {
|
|
23
|
+
const mockConfig = {
|
|
24
|
+
name: 'test_storage_tool',
|
|
25
|
+
description: 'Test storage tool description'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const mockContext = createMockExecutionContext({
|
|
29
|
+
organizationId: 'org-123',
|
|
30
|
+
executionId: 'exec-456',
|
|
31
|
+
resourceId: 'agent-789'
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const mockStorageService = {
|
|
35
|
+
upload: vi.fn(),
|
|
36
|
+
download: vi.fn(),
|
|
37
|
+
createSignedUrl: vi.fn(),
|
|
38
|
+
delete: vi.fn(),
|
|
39
|
+
list: vi.fn()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
vi.clearAllMocks()
|
|
44
|
+
vi.mocked(getToolServices).mockReturnValue({
|
|
45
|
+
commandQueueService: null,
|
|
46
|
+
taskSchedulerService: null,
|
|
47
|
+
notificationsService: null,
|
|
48
|
+
emailService: null,
|
|
49
|
+
credentialsService: null,
|
|
50
|
+
integrationService: null,
|
|
51
|
+
leadService: null,
|
|
52
|
+
storageService: mockStorageService
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
describe('createStorageUploadTool', () => {
|
|
57
|
+
describe('tool creation', () => {
|
|
58
|
+
it('creates tool with correct name and description', () => {
|
|
59
|
+
const tool = createStorageUploadTool({ name: 'upload_file', description: 'Upload file' })
|
|
60
|
+
|
|
61
|
+
expect(tool.name).toBe('upload_file')
|
|
62
|
+
expect(tool.description).toBe('Upload file')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('uses StorageUploadInputSchema as inputSchema', () => {
|
|
66
|
+
const tool = createStorageUploadTool(mockConfig)
|
|
67
|
+
|
|
68
|
+
expect(tool.inputSchema).toBe(StorageUploadInputSchema)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('execute', () => {
|
|
73
|
+
it('uploads file with organizationId from context', async () => {
|
|
74
|
+
mockStorageService.upload.mockResolvedValue({
|
|
75
|
+
path: 'files/test.pdf',
|
|
76
|
+
fullPath: 'org-123/files/test.pdf',
|
|
77
|
+
size: 1024
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const tool = createStorageUploadTool(mockConfig)
|
|
81
|
+
const result = await tool.execute({
|
|
82
|
+
input: {
|
|
83
|
+
bucket: 'org-files',
|
|
84
|
+
path: 'files/test.pdf',
|
|
85
|
+
content: Buffer.from('test content').toString('base64'),
|
|
86
|
+
contentType: 'application/pdf'
|
|
87
|
+
},
|
|
88
|
+
executionContext: mockContext
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
expect(mockStorageService.upload).toHaveBeenCalledWith({
|
|
92
|
+
organizationId: 'org-123',
|
|
93
|
+
bucket: 'org-files',
|
|
94
|
+
path: 'files/test.pdf',
|
|
95
|
+
file: expect.any(Buffer),
|
|
96
|
+
contentType: 'application/pdf',
|
|
97
|
+
upsert: undefined
|
|
98
|
+
})
|
|
99
|
+
expect(result).toEqual({
|
|
100
|
+
success: true,
|
|
101
|
+
path: 'files/test.pdf',
|
|
102
|
+
fullPath: 'org-123/files/test.pdf'
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('passes upsert flag when provided', async () => {
|
|
107
|
+
mockStorageService.upload.mockResolvedValue({
|
|
108
|
+
path: 'files/test.pdf',
|
|
109
|
+
fullPath: 'org-123/files/test.pdf',
|
|
110
|
+
size: 1024
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const tool = createStorageUploadTool(mockConfig)
|
|
114
|
+
await tool.execute({
|
|
115
|
+
input: {
|
|
116
|
+
bucket: 'org-files',
|
|
117
|
+
path: 'files/test.pdf',
|
|
118
|
+
content: Buffer.from('test').toString('base64'),
|
|
119
|
+
contentType: 'application/pdf',
|
|
120
|
+
upsert: true
|
|
121
|
+
},
|
|
122
|
+
executionContext: mockContext
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
expect(mockStorageService.upload).toHaveBeenCalledWith(
|
|
126
|
+
expect.objectContaining({
|
|
127
|
+
upsert: true
|
|
128
|
+
})
|
|
129
|
+
)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('decodes base64 content to Buffer', async () => {
|
|
133
|
+
mockStorageService.upload.mockResolvedValue({
|
|
134
|
+
path: 'files/test.pdf',
|
|
135
|
+
fullPath: 'org-123/files/test.pdf',
|
|
136
|
+
size: 12
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
const originalContent = 'test content'
|
|
140
|
+
const base64Content = Buffer.from(originalContent).toString('base64')
|
|
141
|
+
|
|
142
|
+
const tool = createStorageUploadTool(mockConfig)
|
|
143
|
+
await tool.execute({
|
|
144
|
+
input: {
|
|
145
|
+
bucket: 'org-files',
|
|
146
|
+
path: 'files/test.pdf',
|
|
147
|
+
content: base64Content,
|
|
148
|
+
contentType: 'application/pdf'
|
|
149
|
+
},
|
|
150
|
+
executionContext: mockContext
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
const uploadCall = mockStorageService.upload.mock.calls[0][0]
|
|
154
|
+
expect(uploadCall.file).toBeInstanceOf(Buffer)
|
|
155
|
+
expect(uploadCall.file.toString()).toBe(originalContent)
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
describe('error handling', () => {
|
|
160
|
+
it('throws Error when executionContext is missing', async () => {
|
|
161
|
+
const tool = createStorageUploadTool(mockConfig)
|
|
162
|
+
|
|
163
|
+
await expect(
|
|
164
|
+
tool.execute({
|
|
165
|
+
input: {
|
|
166
|
+
bucket: 'test',
|
|
167
|
+
path: 'test.pdf',
|
|
168
|
+
content: 'dGVzdA==',
|
|
169
|
+
contentType: 'application/pdf'
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
).rejects.toThrow('ExecutionContext is unavailable')
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('throws ToolingError when storageService is unavailable', async () => {
|
|
176
|
+
vi.mocked(getToolServices).mockReturnValue({
|
|
177
|
+
commandQueueService: null,
|
|
178
|
+
taskSchedulerService: null,
|
|
179
|
+
notificationsService: null,
|
|
180
|
+
emailService: null,
|
|
181
|
+
credentialsService: null,
|
|
182
|
+
integrationService: null,
|
|
183
|
+
leadService: null,
|
|
184
|
+
storageService: null
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const tool = createStorageUploadTool(mockConfig)
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
await tool.execute({
|
|
191
|
+
input: {
|
|
192
|
+
bucket: 'test',
|
|
193
|
+
path: 'test.pdf',
|
|
194
|
+
content: 'dGVzdA==',
|
|
195
|
+
contentType: 'application/pdf'
|
|
196
|
+
},
|
|
197
|
+
executionContext: mockContext
|
|
198
|
+
})
|
|
199
|
+
expect.fail('Should have thrown')
|
|
200
|
+
} catch (error) {
|
|
201
|
+
expect(error).toBeInstanceOf(ToolingError)
|
|
202
|
+
const toolingError = error as ToolingError
|
|
203
|
+
expect(toolingError.errorType).toBe('service_unavailable')
|
|
204
|
+
expect(toolingError.message).toBe('StorageService is unavailable')
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('propagates errors from storageService', async () => {
|
|
209
|
+
mockStorageService.upload.mockRejectedValue(new Error('Upload failed'))
|
|
210
|
+
|
|
211
|
+
const tool = createStorageUploadTool(mockConfig)
|
|
212
|
+
|
|
213
|
+
await expect(
|
|
214
|
+
tool.execute({
|
|
215
|
+
input: {
|
|
216
|
+
bucket: 'test',
|
|
217
|
+
path: 'test.pdf',
|
|
218
|
+
content: 'dGVzdA==',
|
|
219
|
+
contentType: 'application/pdf'
|
|
220
|
+
},
|
|
221
|
+
executionContext: mockContext
|
|
222
|
+
})
|
|
223
|
+
).rejects.toThrow('Upload failed')
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe('createStorageSignedUrlTool', () => {
|
|
229
|
+
describe('tool creation', () => {
|
|
230
|
+
it('creates tool with correct name and description', () => {
|
|
231
|
+
const tool = createStorageSignedUrlTool({
|
|
232
|
+
name: 'get_signed_url',
|
|
233
|
+
description: 'Get signed URL'
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
expect(tool.name).toBe('get_signed_url')
|
|
237
|
+
expect(tool.description).toBe('Get signed URL')
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('uses StorageSignedUrlInputSchema as inputSchema', () => {
|
|
241
|
+
const tool = createStorageSignedUrlTool(mockConfig)
|
|
242
|
+
|
|
243
|
+
expect(tool.inputSchema).toBe(StorageSignedUrlInputSchema)
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
describe('execute', () => {
|
|
248
|
+
it('creates signed URL with organizationId from context', async () => {
|
|
249
|
+
const expiresAt = new Date('2025-01-24T12:00:00Z')
|
|
250
|
+
mockStorageService.createSignedUrl.mockResolvedValue({
|
|
251
|
+
signedUrl: 'https://storage.supabase.co/signed/test-url',
|
|
252
|
+
path: 'files/test.pdf',
|
|
253
|
+
expiresAt
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
const tool = createStorageSignedUrlTool(mockConfig)
|
|
257
|
+
const result = await tool.execute({
|
|
258
|
+
input: {
|
|
259
|
+
bucket: 'org-files',
|
|
260
|
+
path: 'files/test.pdf',
|
|
261
|
+
expiresIn: 3600
|
|
262
|
+
},
|
|
263
|
+
executionContext: mockContext
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
expect(mockStorageService.createSignedUrl).toHaveBeenCalledWith({
|
|
267
|
+
organizationId: 'org-123',
|
|
268
|
+
bucket: 'org-files',
|
|
269
|
+
path: 'files/test.pdf',
|
|
270
|
+
expiresIn: 3600
|
|
271
|
+
})
|
|
272
|
+
expect(result).toEqual({
|
|
273
|
+
success: true,
|
|
274
|
+
signedUrl: 'https://storage.supabase.co/signed/test-url',
|
|
275
|
+
expiresAt: '2025-01-24T12:00:00.000Z'
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('uses default expiresIn when not provided (via schema)', async () => {
|
|
280
|
+
const expiresAt = new Date('2025-01-25T12:00:00Z')
|
|
281
|
+
mockStorageService.createSignedUrl.mockResolvedValue({
|
|
282
|
+
signedUrl: 'https://storage.supabase.co/signed/test-url',
|
|
283
|
+
path: 'files/test.pdf',
|
|
284
|
+
expiresAt
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
const tool = createStorageSignedUrlTool(mockConfig)
|
|
288
|
+
// Parse input through schema to get default
|
|
289
|
+
const parsedInput = StorageSignedUrlInputSchema.parse({
|
|
290
|
+
bucket: 'org-files',
|
|
291
|
+
path: 'files/test.pdf'
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
await tool.execute({
|
|
295
|
+
input: parsedInput,
|
|
296
|
+
executionContext: mockContext
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
expect(mockStorageService.createSignedUrl).toHaveBeenCalledWith(
|
|
300
|
+
expect.objectContaining({
|
|
301
|
+
expiresIn: 86400 // Default 24 hours
|
|
302
|
+
})
|
|
303
|
+
)
|
|
304
|
+
})
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
describe('error handling', () => {
|
|
308
|
+
it('throws Error when executionContext is missing', async () => {
|
|
309
|
+
const tool = createStorageSignedUrlTool(mockConfig)
|
|
310
|
+
|
|
311
|
+
await expect(
|
|
312
|
+
tool.execute({
|
|
313
|
+
input: {
|
|
314
|
+
bucket: 'org-files',
|
|
315
|
+
path: 'files/test.pdf',
|
|
316
|
+
expiresIn: 3600
|
|
317
|
+
}
|
|
318
|
+
})
|
|
319
|
+
).rejects.toThrow('ExecutionContext is unavailable')
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('throws ToolingError when storageService is unavailable', async () => {
|
|
323
|
+
vi.mocked(getToolServices).mockReturnValue({
|
|
324
|
+
commandQueueService: null,
|
|
325
|
+
taskSchedulerService: null,
|
|
326
|
+
notificationsService: null,
|
|
327
|
+
emailService: null,
|
|
328
|
+
credentialsService: null,
|
|
329
|
+
integrationService: null,
|
|
330
|
+
leadService: null,
|
|
331
|
+
storageService: null
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
const tool = createStorageSignedUrlTool(mockConfig)
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
await tool.execute({
|
|
338
|
+
input: {
|
|
339
|
+
bucket: 'org-files',
|
|
340
|
+
path: 'files/test.pdf',
|
|
341
|
+
expiresIn: 3600
|
|
342
|
+
},
|
|
343
|
+
executionContext: mockContext
|
|
344
|
+
})
|
|
345
|
+
expect.fail('Should have thrown')
|
|
346
|
+
} catch (error) {
|
|
347
|
+
expect(error).toBeInstanceOf(ToolingError)
|
|
348
|
+
expect((error as ToolingError).errorType).toBe('service_unavailable')
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
describe('createStorageDownloadTool', () => {
|
|
355
|
+
describe('tool creation', () => {
|
|
356
|
+
it('creates tool with correct name and description', () => {
|
|
357
|
+
const tool = createStorageDownloadTool({
|
|
358
|
+
name: 'download_file',
|
|
359
|
+
description: 'Download file'
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
expect(tool.name).toBe('download_file')
|
|
363
|
+
expect(tool.description).toBe('Download file')
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it('uses StorageDownloadInputSchema as inputSchema', () => {
|
|
367
|
+
const tool = createStorageDownloadTool(mockConfig)
|
|
368
|
+
|
|
369
|
+
expect(tool.inputSchema).toBe(StorageDownloadInputSchema)
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
describe('execute', () => {
|
|
374
|
+
it('downloads file and returns base64 content', async () => {
|
|
375
|
+
const fileContent = 'test file content'
|
|
376
|
+
const mockBlob = new Blob([fileContent], { type: 'application/pdf' })
|
|
377
|
+
mockStorageService.download.mockResolvedValue(mockBlob)
|
|
378
|
+
|
|
379
|
+
const tool = createStorageDownloadTool(mockConfig)
|
|
380
|
+
const result = await tool.execute({
|
|
381
|
+
input: {
|
|
382
|
+
bucket: 'org-files',
|
|
383
|
+
path: 'files/test.pdf'
|
|
384
|
+
},
|
|
385
|
+
executionContext: mockContext
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
expect(mockStorageService.download).toHaveBeenCalledWith({
|
|
389
|
+
organizationId: 'org-123',
|
|
390
|
+
bucket: 'org-files',
|
|
391
|
+
path: 'files/test.pdf'
|
|
392
|
+
})
|
|
393
|
+
expect(result.success).toBe(true)
|
|
394
|
+
expect(result.content).toBeTruthy()
|
|
395
|
+
// Verify base64 decodes back to original content
|
|
396
|
+
expect(Buffer.from(result.content, 'base64').toString()).toBe(fileContent)
|
|
397
|
+
expect(result.contentType).toBe('application/pdf')
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
it('handles blob without type', async () => {
|
|
401
|
+
const mockBlob = new Blob(['content'])
|
|
402
|
+
Object.defineProperty(mockBlob, 'type', { value: '' })
|
|
403
|
+
mockStorageService.download.mockResolvedValue(mockBlob)
|
|
404
|
+
|
|
405
|
+
const tool = createStorageDownloadTool(mockConfig)
|
|
406
|
+
const result = await tool.execute({
|
|
407
|
+
input: {
|
|
408
|
+
bucket: 'org-files',
|
|
409
|
+
path: 'files/test.bin'
|
|
410
|
+
},
|
|
411
|
+
executionContext: mockContext
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
expect(result.success).toBe(true)
|
|
415
|
+
expect(result.contentType).toBeUndefined()
|
|
416
|
+
})
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
describe('error handling', () => {
|
|
420
|
+
it('throws Error when executionContext is missing', async () => {
|
|
421
|
+
const tool = createStorageDownloadTool(mockConfig)
|
|
422
|
+
|
|
423
|
+
await expect(
|
|
424
|
+
tool.execute({
|
|
425
|
+
input: {
|
|
426
|
+
bucket: 'org-files',
|
|
427
|
+
path: 'files/test.pdf'
|
|
428
|
+
}
|
|
429
|
+
})
|
|
430
|
+
).rejects.toThrow('ExecutionContext is unavailable')
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
it('throws ToolingError when storageService is unavailable', async () => {
|
|
434
|
+
vi.mocked(getToolServices).mockReturnValue({
|
|
435
|
+
commandQueueService: null,
|
|
436
|
+
taskSchedulerService: null,
|
|
437
|
+
notificationsService: null,
|
|
438
|
+
emailService: null,
|
|
439
|
+
credentialsService: null,
|
|
440
|
+
integrationService: null,
|
|
441
|
+
leadService: null,
|
|
442
|
+
storageService: null
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
const tool = createStorageDownloadTool(mockConfig)
|
|
446
|
+
|
|
447
|
+
try {
|
|
448
|
+
await tool.execute({
|
|
449
|
+
input: {
|
|
450
|
+
bucket: 'org-files',
|
|
451
|
+
path: 'files/test.pdf'
|
|
452
|
+
},
|
|
453
|
+
executionContext: mockContext
|
|
454
|
+
})
|
|
455
|
+
expect.fail('Should have thrown')
|
|
456
|
+
} catch (error) {
|
|
457
|
+
expect(error).toBeInstanceOf(ToolingError)
|
|
458
|
+
expect((error as ToolingError).errorType).toBe('service_unavailable')
|
|
459
|
+
}
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
it('propagates errors from storageService', async () => {
|
|
463
|
+
mockStorageService.download.mockRejectedValue(new Error('File not found'))
|
|
464
|
+
|
|
465
|
+
const tool = createStorageDownloadTool(mockConfig)
|
|
466
|
+
|
|
467
|
+
await expect(
|
|
468
|
+
tool.execute({
|
|
469
|
+
input: {
|
|
470
|
+
bucket: 'org-files',
|
|
471
|
+
path: 'files/nonexistent.pdf'
|
|
472
|
+
},
|
|
473
|
+
executionContext: mockContext
|
|
474
|
+
})
|
|
475
|
+
).rejects.toThrow('File not found')
|
|
476
|
+
})
|
|
477
|
+
})
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
describe('createStorageDeleteTool', () => {
|
|
481
|
+
describe('tool creation', () => {
|
|
482
|
+
it('creates tool with correct name and description', () => {
|
|
483
|
+
const tool = createStorageDeleteTool({
|
|
484
|
+
name: 'delete_file',
|
|
485
|
+
description: 'Delete file'
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
expect(tool.name).toBe('delete_file')
|
|
489
|
+
expect(tool.description).toBe('Delete file')
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
it('uses StorageDeleteInputSchema as inputSchema', () => {
|
|
493
|
+
const tool = createStorageDeleteTool(mockConfig)
|
|
494
|
+
|
|
495
|
+
expect(tool.inputSchema).toBe(StorageDeleteInputSchema)
|
|
496
|
+
})
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
describe('execute', () => {
|
|
500
|
+
it('deletes file with organizationId from context', async () => {
|
|
501
|
+
mockStorageService.delete.mockResolvedValue(undefined)
|
|
502
|
+
|
|
503
|
+
const tool = createStorageDeleteTool(mockConfig)
|
|
504
|
+
const result = await tool.execute({
|
|
505
|
+
input: {
|
|
506
|
+
bucket: 'org-files',
|
|
507
|
+
path: 'files/test.pdf'
|
|
508
|
+
},
|
|
509
|
+
executionContext: mockContext
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
expect(mockStorageService.delete).toHaveBeenCalledWith({
|
|
513
|
+
organizationId: 'org-123',
|
|
514
|
+
bucket: 'org-files',
|
|
515
|
+
path: 'files/test.pdf'
|
|
516
|
+
})
|
|
517
|
+
expect(result).toEqual({ success: true })
|
|
518
|
+
})
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
describe('error handling', () => {
|
|
522
|
+
it('throws Error when executionContext is missing', async () => {
|
|
523
|
+
const tool = createStorageDeleteTool(mockConfig)
|
|
524
|
+
|
|
525
|
+
await expect(
|
|
526
|
+
tool.execute({
|
|
527
|
+
input: {
|
|
528
|
+
bucket: 'org-files',
|
|
529
|
+
path: 'files/test.pdf'
|
|
530
|
+
}
|
|
531
|
+
})
|
|
532
|
+
).rejects.toThrow('ExecutionContext is unavailable')
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
it('throws ToolingError when storageService is unavailable', async () => {
|
|
536
|
+
vi.mocked(getToolServices).mockReturnValue({
|
|
537
|
+
commandQueueService: null,
|
|
538
|
+
taskSchedulerService: null,
|
|
539
|
+
notificationsService: null,
|
|
540
|
+
emailService: null,
|
|
541
|
+
credentialsService: null,
|
|
542
|
+
integrationService: null,
|
|
543
|
+
leadService: null,
|
|
544
|
+
storageService: null
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
const tool = createStorageDeleteTool(mockConfig)
|
|
548
|
+
|
|
549
|
+
try {
|
|
550
|
+
await tool.execute({
|
|
551
|
+
input: {
|
|
552
|
+
bucket: 'org-files',
|
|
553
|
+
path: 'files/test.pdf'
|
|
554
|
+
},
|
|
555
|
+
executionContext: mockContext
|
|
556
|
+
})
|
|
557
|
+
expect.fail('Should have thrown')
|
|
558
|
+
} catch (error) {
|
|
559
|
+
expect(error).toBeInstanceOf(ToolingError)
|
|
560
|
+
expect((error as ToolingError).errorType).toBe('service_unavailable')
|
|
561
|
+
}
|
|
562
|
+
})
|
|
563
|
+
|
|
564
|
+
it('propagates errors from storageService', async () => {
|
|
565
|
+
mockStorageService.delete.mockRejectedValue(new Error('Permission denied'))
|
|
566
|
+
|
|
567
|
+
const tool = createStorageDeleteTool(mockConfig)
|
|
568
|
+
|
|
569
|
+
await expect(
|
|
570
|
+
tool.execute({
|
|
571
|
+
input: {
|
|
572
|
+
bucket: 'org-files',
|
|
573
|
+
path: 'files/protected.pdf'
|
|
574
|
+
},
|
|
575
|
+
executionContext: mockContext
|
|
576
|
+
})
|
|
577
|
+
).rejects.toThrow('Permission denied')
|
|
578
|
+
})
|
|
579
|
+
})
|
|
580
|
+
})
|
|
581
|
+
|
|
582
|
+
describe('createStorageListTool', () => {
|
|
583
|
+
describe('tool creation', () => {
|
|
584
|
+
it('creates tool with correct name and description', () => {
|
|
585
|
+
const tool = createStorageListTool({
|
|
586
|
+
name: 'list_files',
|
|
587
|
+
description: 'List files'
|
|
588
|
+
})
|
|
589
|
+
|
|
590
|
+
expect(tool.name).toBe('list_files')
|
|
591
|
+
expect(tool.description).toBe('List files')
|
|
592
|
+
})
|
|
593
|
+
|
|
594
|
+
it('uses StorageListInputSchema as inputSchema', () => {
|
|
595
|
+
const tool = createStorageListTool(mockConfig)
|
|
596
|
+
|
|
597
|
+
expect(tool.inputSchema).toBe(StorageListInputSchema)
|
|
598
|
+
})
|
|
599
|
+
})
|
|
600
|
+
|
|
601
|
+
describe('execute', () => {
|
|
602
|
+
it('lists files with organizationId from context', async () => {
|
|
603
|
+
mockStorageService.list.mockResolvedValue(['file1.pdf', 'file2.png', 'file3.doc'])
|
|
604
|
+
|
|
605
|
+
const tool = createStorageListTool(mockConfig)
|
|
606
|
+
const result = await tool.execute({
|
|
607
|
+
input: {
|
|
608
|
+
bucket: 'org-files',
|
|
609
|
+
prefix: 'proposals/'
|
|
610
|
+
},
|
|
611
|
+
executionContext: mockContext
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
expect(mockStorageService.list).toHaveBeenCalledWith({
|
|
615
|
+
organizationId: 'org-123',
|
|
616
|
+
bucket: 'org-files',
|
|
617
|
+
prefix: 'proposals/'
|
|
618
|
+
})
|
|
619
|
+
expect(result).toEqual({
|
|
620
|
+
success: true,
|
|
621
|
+
files: ['file1.pdf', 'file2.png', 'file3.doc']
|
|
622
|
+
})
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
it('lists files without prefix', async () => {
|
|
626
|
+
mockStorageService.list.mockResolvedValue(['file1.pdf'])
|
|
627
|
+
|
|
628
|
+
const tool = createStorageListTool(mockConfig)
|
|
629
|
+
await tool.execute({
|
|
630
|
+
input: {
|
|
631
|
+
bucket: 'org-files'
|
|
632
|
+
},
|
|
633
|
+
executionContext: mockContext
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
expect(mockStorageService.list).toHaveBeenCalledWith({
|
|
637
|
+
organizationId: 'org-123',
|
|
638
|
+
bucket: 'org-files',
|
|
639
|
+
prefix: undefined
|
|
640
|
+
})
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
it('returns empty array when no files found', async () => {
|
|
644
|
+
mockStorageService.list.mockResolvedValue([])
|
|
645
|
+
|
|
646
|
+
const tool = createStorageListTool(mockConfig)
|
|
647
|
+
const result = await tool.execute({
|
|
648
|
+
input: {
|
|
649
|
+
bucket: 'org-files',
|
|
650
|
+
prefix: 'empty-folder/'
|
|
651
|
+
},
|
|
652
|
+
executionContext: mockContext
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
expect(result).toEqual({
|
|
656
|
+
success: true,
|
|
657
|
+
files: []
|
|
658
|
+
})
|
|
659
|
+
})
|
|
660
|
+
})
|
|
661
|
+
|
|
662
|
+
describe('error handling', () => {
|
|
663
|
+
it('throws Error when executionContext is missing', async () => {
|
|
664
|
+
const tool = createStorageListTool(mockConfig)
|
|
665
|
+
|
|
666
|
+
await expect(
|
|
667
|
+
tool.execute({
|
|
668
|
+
input: {
|
|
669
|
+
bucket: 'org-files'
|
|
670
|
+
}
|
|
671
|
+
})
|
|
672
|
+
).rejects.toThrow('ExecutionContext is unavailable')
|
|
673
|
+
})
|
|
674
|
+
|
|
675
|
+
it('throws ToolingError when storageService is unavailable', async () => {
|
|
676
|
+
vi.mocked(getToolServices).mockReturnValue({
|
|
677
|
+
commandQueueService: null,
|
|
678
|
+
taskSchedulerService: null,
|
|
679
|
+
notificationsService: null,
|
|
680
|
+
emailService: null,
|
|
681
|
+
credentialsService: null,
|
|
682
|
+
integrationService: null,
|
|
683
|
+
leadService: null,
|
|
684
|
+
storageService: null
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
const tool = createStorageListTool(mockConfig)
|
|
688
|
+
|
|
689
|
+
try {
|
|
690
|
+
await tool.execute({
|
|
691
|
+
input: {
|
|
692
|
+
bucket: 'org-files'
|
|
693
|
+
},
|
|
694
|
+
executionContext: mockContext
|
|
695
|
+
})
|
|
696
|
+
expect.fail('Should have thrown')
|
|
697
|
+
} catch (error) {
|
|
698
|
+
expect(error).toBeInstanceOf(ToolingError)
|
|
699
|
+
expect((error as ToolingError).errorType).toBe('service_unavailable')
|
|
700
|
+
}
|
|
701
|
+
})
|
|
702
|
+
|
|
703
|
+
it('propagates errors from storageService', async () => {
|
|
704
|
+
mockStorageService.list.mockRejectedValue(new Error('Bucket not found'))
|
|
705
|
+
|
|
706
|
+
const tool = createStorageListTool(mockConfig)
|
|
707
|
+
|
|
708
|
+
await expect(
|
|
709
|
+
tool.execute({
|
|
710
|
+
input: {
|
|
711
|
+
bucket: 'nonexistent-bucket'
|
|
712
|
+
},
|
|
713
|
+
executionContext: mockContext
|
|
714
|
+
})
|
|
715
|
+
).rejects.toThrow('Bucket not found')
|
|
716
|
+
})
|
|
717
|
+
})
|
|
718
|
+
})
|
|
719
|
+
|
|
720
|
+
describe('concurrent execution', () => {
|
|
721
|
+
it('handles multiple concurrent operations', async () => {
|
|
722
|
+
mockStorageService.upload.mockResolvedValue({
|
|
723
|
+
path: 'file1.pdf',
|
|
724
|
+
fullPath: 'org-123/file1.pdf',
|
|
725
|
+
size: 100
|
|
726
|
+
})
|
|
727
|
+
mockStorageService.list.mockResolvedValue(['file1.pdf', 'file2.pdf'])
|
|
728
|
+
mockStorageService.createSignedUrl.mockResolvedValue({
|
|
729
|
+
signedUrl: 'https://signed-url',
|
|
730
|
+
path: 'file1.pdf',
|
|
731
|
+
expiresAt: new Date()
|
|
732
|
+
})
|
|
733
|
+
|
|
734
|
+
const uploadTool = createStorageUploadTool({
|
|
735
|
+
name: 'upload',
|
|
736
|
+
description: 'Upload'
|
|
737
|
+
})
|
|
738
|
+
const listTool = createStorageListTool({
|
|
739
|
+
name: 'list',
|
|
740
|
+
description: 'List'
|
|
741
|
+
})
|
|
742
|
+
const signedUrlTool = createStorageSignedUrlTool({
|
|
743
|
+
name: 'signed_url',
|
|
744
|
+
description: 'Signed URL'
|
|
745
|
+
})
|
|
746
|
+
|
|
747
|
+
const results = await Promise.all([
|
|
748
|
+
uploadTool.execute({
|
|
749
|
+
input: {
|
|
750
|
+
bucket: 'files',
|
|
751
|
+
path: 'file1.pdf',
|
|
752
|
+
content: 'dGVzdA==',
|
|
753
|
+
contentType: 'application/pdf'
|
|
754
|
+
},
|
|
755
|
+
executionContext: mockContext
|
|
756
|
+
}),
|
|
757
|
+
listTool.execute({
|
|
758
|
+
input: { bucket: 'files' },
|
|
759
|
+
executionContext: mockContext
|
|
760
|
+
}),
|
|
761
|
+
signedUrlTool.execute({
|
|
762
|
+
input: {
|
|
763
|
+
bucket: 'files',
|
|
764
|
+
path: 'file1.pdf',
|
|
765
|
+
expiresIn: 3600
|
|
766
|
+
},
|
|
767
|
+
executionContext: mockContext
|
|
768
|
+
})
|
|
769
|
+
])
|
|
770
|
+
|
|
771
|
+
expect(results).toHaveLength(3)
|
|
772
|
+
expect(results[0]).toEqual({
|
|
773
|
+
success: true,
|
|
774
|
+
path: 'file1.pdf',
|
|
775
|
+
fullPath: 'org-123/file1.pdf'
|
|
776
|
+
})
|
|
777
|
+
expect(results[1]).toEqual({
|
|
778
|
+
success: true,
|
|
779
|
+
files: ['file1.pdf', 'file2.pdf']
|
|
780
|
+
})
|
|
781
|
+
expect(results[2].success).toBe(true)
|
|
782
|
+
expect(results[2].signedUrl).toBe('https://signed-url')
|
|
783
|
+
})
|
|
784
|
+
})
|
|
785
|
+
})
|
|
786
|
+
|
|
787
|
+
describe('Storage Input Schemas', () => {
|
|
788
|
+
describe('StorageUploadInputSchema', () => {
|
|
789
|
+
it('accepts valid upload input with all required fields', () => {
|
|
790
|
+
const result = StorageUploadInputSchema.safeParse({
|
|
791
|
+
bucket: 'org-files',
|
|
792
|
+
path: 'files/test.pdf',
|
|
793
|
+
content: 'dGVzdA==',
|
|
794
|
+
contentType: 'application/pdf'
|
|
795
|
+
})
|
|
796
|
+
|
|
797
|
+
expect(result.success).toBe(true)
|
|
798
|
+
})
|
|
799
|
+
|
|
800
|
+
it('accepts optional upsert flag', () => {
|
|
801
|
+
const result = StorageUploadInputSchema.safeParse({
|
|
802
|
+
bucket: 'org-files',
|
|
803
|
+
path: 'files/test.pdf',
|
|
804
|
+
content: 'dGVzdA==',
|
|
805
|
+
contentType: 'application/pdf',
|
|
806
|
+
upsert: true
|
|
807
|
+
})
|
|
808
|
+
|
|
809
|
+
expect(result.success).toBe(true)
|
|
810
|
+
if (result.success) {
|
|
811
|
+
expect(result.data.upsert).toBe(true)
|
|
812
|
+
}
|
|
813
|
+
})
|
|
814
|
+
|
|
815
|
+
it('rejects missing bucket', () => {
|
|
816
|
+
const result = StorageUploadInputSchema.safeParse({
|
|
817
|
+
path: 'files/test.pdf',
|
|
818
|
+
content: 'dGVzdA==',
|
|
819
|
+
contentType: 'application/pdf'
|
|
820
|
+
})
|
|
821
|
+
|
|
822
|
+
expect(result.success).toBe(false)
|
|
823
|
+
})
|
|
824
|
+
|
|
825
|
+
it('rejects missing path', () => {
|
|
826
|
+
const result = StorageUploadInputSchema.safeParse({
|
|
827
|
+
bucket: 'org-files',
|
|
828
|
+
content: 'dGVzdA==',
|
|
829
|
+
contentType: 'application/pdf'
|
|
830
|
+
})
|
|
831
|
+
|
|
832
|
+
expect(result.success).toBe(false)
|
|
833
|
+
})
|
|
834
|
+
|
|
835
|
+
it('rejects missing content', () => {
|
|
836
|
+
const result = StorageUploadInputSchema.safeParse({
|
|
837
|
+
bucket: 'org-files',
|
|
838
|
+
path: 'files/test.pdf',
|
|
839
|
+
contentType: 'application/pdf'
|
|
840
|
+
})
|
|
841
|
+
|
|
842
|
+
expect(result.success).toBe(false)
|
|
843
|
+
})
|
|
844
|
+
|
|
845
|
+
it('rejects missing contentType', () => {
|
|
846
|
+
const result = StorageUploadInputSchema.safeParse({
|
|
847
|
+
bucket: 'org-files',
|
|
848
|
+
path: 'files/test.pdf',
|
|
849
|
+
content: 'dGVzdA=='
|
|
850
|
+
})
|
|
851
|
+
|
|
852
|
+
expect(result.success).toBe(false)
|
|
853
|
+
})
|
|
854
|
+
})
|
|
855
|
+
|
|
856
|
+
describe('StorageSignedUrlInputSchema', () => {
|
|
857
|
+
it('accepts valid signed URL input', () => {
|
|
858
|
+
const result = StorageSignedUrlInputSchema.safeParse({
|
|
859
|
+
bucket: 'org-files',
|
|
860
|
+
path: 'files/test.pdf',
|
|
861
|
+
expiresIn: 3600
|
|
862
|
+
})
|
|
863
|
+
|
|
864
|
+
expect(result.success).toBe(true)
|
|
865
|
+
})
|
|
866
|
+
|
|
867
|
+
it('uses default expiresIn when not provided', () => {
|
|
868
|
+
const result = StorageSignedUrlInputSchema.safeParse({
|
|
869
|
+
bucket: 'org-files',
|
|
870
|
+
path: 'files/test.pdf'
|
|
871
|
+
})
|
|
872
|
+
|
|
873
|
+
expect(result.success).toBe(true)
|
|
874
|
+
if (result.success) {
|
|
875
|
+
expect(result.data.expiresIn).toBe(86400) // Default 24 hours
|
|
876
|
+
}
|
|
877
|
+
})
|
|
878
|
+
|
|
879
|
+
it('accepts custom expiresIn value', () => {
|
|
880
|
+
const result = StorageSignedUrlInputSchema.safeParse({
|
|
881
|
+
bucket: 'org-files',
|
|
882
|
+
path: 'files/test.pdf',
|
|
883
|
+
expiresIn: 604800 // 7 days
|
|
884
|
+
})
|
|
885
|
+
|
|
886
|
+
expect(result.success).toBe(true)
|
|
887
|
+
if (result.success) {
|
|
888
|
+
expect(result.data.expiresIn).toBe(604800)
|
|
889
|
+
}
|
|
890
|
+
})
|
|
891
|
+
|
|
892
|
+
it('rejects missing bucket', () => {
|
|
893
|
+
const result = StorageSignedUrlInputSchema.safeParse({
|
|
894
|
+
path: 'files/test.pdf',
|
|
895
|
+
expiresIn: 3600
|
|
896
|
+
})
|
|
897
|
+
|
|
898
|
+
expect(result.success).toBe(false)
|
|
899
|
+
})
|
|
900
|
+
|
|
901
|
+
it('rejects missing path', () => {
|
|
902
|
+
const result = StorageSignedUrlInputSchema.safeParse({
|
|
903
|
+
bucket: 'org-files',
|
|
904
|
+
expiresIn: 3600
|
|
905
|
+
})
|
|
906
|
+
|
|
907
|
+
expect(result.success).toBe(false)
|
|
908
|
+
})
|
|
909
|
+
})
|
|
910
|
+
|
|
911
|
+
describe('StorageDownloadInputSchema', () => {
|
|
912
|
+
it('accepts valid download input', () => {
|
|
913
|
+
const result = StorageDownloadInputSchema.safeParse({
|
|
914
|
+
bucket: 'org-files',
|
|
915
|
+
path: 'files/test.pdf'
|
|
916
|
+
})
|
|
917
|
+
|
|
918
|
+
expect(result.success).toBe(true)
|
|
919
|
+
})
|
|
920
|
+
|
|
921
|
+
it('rejects missing bucket', () => {
|
|
922
|
+
const result = StorageDownloadInputSchema.safeParse({
|
|
923
|
+
path: 'files/test.pdf'
|
|
924
|
+
})
|
|
925
|
+
|
|
926
|
+
expect(result.success).toBe(false)
|
|
927
|
+
})
|
|
928
|
+
|
|
929
|
+
it('rejects missing path', () => {
|
|
930
|
+
const result = StorageDownloadInputSchema.safeParse({
|
|
931
|
+
bucket: 'org-files'
|
|
932
|
+
})
|
|
933
|
+
|
|
934
|
+
expect(result.success).toBe(false)
|
|
935
|
+
})
|
|
936
|
+
})
|
|
937
|
+
|
|
938
|
+
describe('StorageDeleteInputSchema', () => {
|
|
939
|
+
it('accepts valid delete input', () => {
|
|
940
|
+
const result = StorageDeleteInputSchema.safeParse({
|
|
941
|
+
bucket: 'org-files',
|
|
942
|
+
path: 'files/test.pdf'
|
|
943
|
+
})
|
|
944
|
+
|
|
945
|
+
expect(result.success).toBe(true)
|
|
946
|
+
})
|
|
947
|
+
|
|
948
|
+
it('rejects missing bucket', () => {
|
|
949
|
+
const result = StorageDeleteInputSchema.safeParse({
|
|
950
|
+
path: 'files/test.pdf'
|
|
951
|
+
})
|
|
952
|
+
|
|
953
|
+
expect(result.success).toBe(false)
|
|
954
|
+
})
|
|
955
|
+
|
|
956
|
+
it('rejects missing path', () => {
|
|
957
|
+
const result = StorageDeleteInputSchema.safeParse({
|
|
958
|
+
bucket: 'org-files'
|
|
959
|
+
})
|
|
960
|
+
|
|
961
|
+
expect(result.success).toBe(false)
|
|
962
|
+
})
|
|
963
|
+
})
|
|
964
|
+
|
|
965
|
+
describe('StorageListInputSchema', () => {
|
|
966
|
+
it('accepts valid list input with prefix', () => {
|
|
967
|
+
const result = StorageListInputSchema.safeParse({
|
|
968
|
+
bucket: 'org-files',
|
|
969
|
+
prefix: 'proposals/'
|
|
970
|
+
})
|
|
971
|
+
|
|
972
|
+
expect(result.success).toBe(true)
|
|
973
|
+
if (result.success) {
|
|
974
|
+
expect(result.data.prefix).toBe('proposals/')
|
|
975
|
+
}
|
|
976
|
+
})
|
|
977
|
+
|
|
978
|
+
it('accepts list input without prefix', () => {
|
|
979
|
+
const result = StorageListInputSchema.safeParse({
|
|
980
|
+
bucket: 'org-files'
|
|
981
|
+
})
|
|
982
|
+
|
|
983
|
+
expect(result.success).toBe(true)
|
|
984
|
+
if (result.success) {
|
|
985
|
+
expect(result.data.prefix).toBeUndefined()
|
|
986
|
+
}
|
|
987
|
+
})
|
|
988
|
+
|
|
989
|
+
it('rejects missing bucket', () => {
|
|
990
|
+
const result = StorageListInputSchema.safeParse({
|
|
991
|
+
prefix: 'proposals/'
|
|
992
|
+
})
|
|
993
|
+
|
|
994
|
+
expect(result.success).toBe(false)
|
|
995
|
+
})
|
|
996
|
+
})
|
|
997
|
+
})
|