@elevasis/core 0.20.0 → 0.22.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/index.d.ts +524 -6
- package/dist/index.js +417 -42
- package/dist/knowledge/index.d.ts +151 -1
- package/dist/organization-model/index.d.ts +524 -6
- package/dist/organization-model/index.js +417 -42
- package/dist/test-utils/index.d.ts +270 -1
- package/dist/test-utils/index.js +407 -41
- package/package.json +5 -5
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +501 -303
- package/src/auth/multi-tenancy/permissions.ts +20 -8
- package/src/business/README.md +2 -2
- package/src/business/acquisition/api-schemas.test.ts +198 -0
- package/src/business/acquisition/api-schemas.ts +250 -9
- package/src/business/acquisition/build-templates.test.ts +28 -0
- package/src/business/acquisition/build-templates.ts +20 -8
- package/src/business/acquisition/index.ts +12 -0
- package/src/business/acquisition/types.ts +6 -1
- package/src/business/clients/api-schemas.test.ts +115 -0
- package/src/business/clients/api-schemas.ts +158 -0
- package/src/business/clients/index.ts +1 -0
- package/src/business/deals/api-schemas.ts +8 -0
- package/src/business/index.ts +5 -2
- package/src/business/projects/types.ts +19 -0
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -8
- package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -12
- package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -3
- package/src/execution/engine/agent/core/types.ts +25 -15
- package/src/execution/engine/agent/index.ts +6 -4
- package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -18
- package/src/execution/engine/index.ts +3 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -41
- package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -0
- package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -0
- package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -0
- package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -0
- package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -0
- package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -0
- package/src/execution/engine/workflow/types.ts +7 -0
- package/src/integrations/credentials/api-schemas.ts +21 -2
- package/src/integrations/credentials/schemas.ts +200 -164
- package/src/organization-model/README.md +10 -3
- package/src/organization-model/__tests__/defaults.test.ts +6 -0
- package/src/organization-model/__tests__/domains/resources.test.ts +188 -0
- package/src/organization-model/__tests__/domains/roles.test.ts +402 -347
- package/src/organization-model/__tests__/domains/systems.test.ts +193 -0
- package/src/organization-model/__tests__/knowledge.test.ts +39 -0
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +7 -4
- package/src/organization-model/__tests__/resolve.test.ts +1 -1
- package/src/organization-model/defaults.ts +24 -3
- package/src/organization-model/domains/knowledge.ts +3 -2
- package/src/organization-model/domains/prospecting.ts +182 -25
- package/src/organization-model/domains/resources.ts +88 -0
- package/src/organization-model/domains/roles.ts +93 -55
- package/src/organization-model/domains/sales.ts +24 -3
- package/src/organization-model/domains/systems.ts +46 -0
- package/src/organization-model/icons.ts +1 -0
- package/src/organization-model/index.ts +2 -0
- package/src/organization-model/organization-model.mdx +33 -14
- package/src/organization-model/published.ts +52 -1
- package/src/organization-model/schema.ts +121 -0
- package/src/organization-model/types.ts +46 -1
- package/src/platform/api/types.ts +38 -35
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/registry/__tests__/resource-registry.test.ts +2051 -2005
- package/src/platform/registry/__tests__/validation.test.ts +1343 -1086
- package/src/platform/registry/index.ts +14 -0
- package/src/platform/registry/resource-registry.ts +40 -2
- package/src/platform/registry/serialization.ts +241 -202
- package/src/platform/registry/serialized-types.ts +1 -0
- package/src/platform/registry/types.ts +411 -361
- package/src/platform/registry/validation.ts +743 -513
- package/src/projects/api-schemas.ts +290 -267
- package/src/reference/_generated/contracts.md +501 -303
- package/src/reference/glossary.md +8 -3
- package/src/server.ts +2 -0
- package/src/supabase/database.types.ts +121 -0
|
@@ -1,2005 +1,2051 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
import { ResourceRegistry } from '../resource-registry'
|
|
4
|
-
import type { RemoteOrgConfig } from '../resource-registry'
|
|
5
|
-
import type { WorkflowDefinition } from '../../../execution/engine/workflow/types'
|
|
6
|
-
import type { AgentDefinition } from '../../../execution/engine/agent/core/types'
|
|
7
|
-
import type { ModelConfig } from '../../../execution/engine/llm/model-info'
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
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
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
expect(registry.getResourceDefinition('org-1', 'workflow-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
expect(result.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
expect(result.agents).
|
|
297
|
-
expect(result.
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
it('
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
expect(
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
expect(result
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
expect(()
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
})
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
expect(()
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
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
|
-
|
|
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
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
})
|
|
600
|
-
}).toThrow('
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
expect(
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
}
|
|
798
|
-
})
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
expect(result.
|
|
804
|
-
expect(result.
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
const
|
|
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
|
-
describe('
|
|
966
|
-
it('finds
|
|
967
|
-
const
|
|
968
|
-
const
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
const registry = new ResourceRegistry({
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
})
|
|
1004
|
-
|
|
1005
|
-
const result = registry.
|
|
1006
|
-
|
|
1007
|
-
expect(result).
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
it('
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
})
|
|
1037
|
-
|
|
1038
|
-
const result = registry.
|
|
1039
|
-
|
|
1040
|
-
expect(result).
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
it('
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
const
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
'
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
const
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
]
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
const registry = new ResourceRegistry({
|
|
1385
|
-
'test-org': {
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
}
|
|
1468
|
-
})
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
const
|
|
1558
|
-
|
|
1559
|
-
const
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
})
|
|
1564
|
-
|
|
1565
|
-
registry.
|
|
1566
|
-
|
|
1567
|
-
expect(
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
expect(
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
expect(() =>
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
registry
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
expect(result.workflows
|
|
1736
|
-
expect(result.
|
|
1737
|
-
})
|
|
1738
|
-
|
|
1739
|
-
it('
|
|
1740
|
-
const
|
|
1741
|
-
|
|
1742
|
-
const registry = new ResourceRegistry({
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
registry.unregisterOrganization('test-org')
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
registry.
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
)
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
)
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
const
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
expect(
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
})
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
})
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
}
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
)
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { ResourceRegistry } from '../resource-registry'
|
|
4
|
+
import type { RemoteOrgConfig } from '../resource-registry'
|
|
5
|
+
import type { WorkflowDefinition } from '../../../execution/engine/workflow/types'
|
|
6
|
+
import type { AgentDefinition } from '../../../execution/engine/agent/core/types'
|
|
7
|
+
import type { ModelConfig } from '../../../execution/engine/llm/model-info'
|
|
8
|
+
import type { ResourceEntry } from '../../../organization-model/domains/resources'
|
|
9
|
+
import type { SystemEntry } from '../../../organization-model/domains/systems'
|
|
10
|
+
|
|
11
|
+
describe('ResourceRegistry', () => {
|
|
12
|
+
// Mock data helpers
|
|
13
|
+
const createMockWorkflow = (resourceId: string, status: 'dev' | 'prod' = 'dev'): WorkflowDefinition => ({
|
|
14
|
+
config: {
|
|
15
|
+
resourceId,
|
|
16
|
+
name: `Workflow ${resourceId}`,
|
|
17
|
+
description: `Test workflow ${resourceId}`,
|
|
18
|
+
version: '1.0.0',
|
|
19
|
+
type: 'workflow',
|
|
20
|
+
status
|
|
21
|
+
},
|
|
22
|
+
contract: {
|
|
23
|
+
inputSchema: z.object({ data: z.string() }),
|
|
24
|
+
outputSchema: z.object({ result: z.boolean() })
|
|
25
|
+
},
|
|
26
|
+
steps: {
|
|
27
|
+
step1: {
|
|
28
|
+
id: 'step1',
|
|
29
|
+
name: 'First Step',
|
|
30
|
+
description: 'First step',
|
|
31
|
+
handler: async () => ({ result: true }),
|
|
32
|
+
inputSchema: z.object({ data: z.string() }),
|
|
33
|
+
outputSchema: z.object({ result: z.boolean() }),
|
|
34
|
+
next: null
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
entryPoint: 'step1'
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const createMockAgent = (resourceId: string, status: 'dev' | 'prod' = 'dev'): AgentDefinition => ({
|
|
41
|
+
config: {
|
|
42
|
+
resourceId,
|
|
43
|
+
name: `Agent ${resourceId}`,
|
|
44
|
+
description: `Test agent ${resourceId}`,
|
|
45
|
+
version: '1.0.0',
|
|
46
|
+
type: 'agent',
|
|
47
|
+
status,
|
|
48
|
+
systemPrompt: 'You are a test agent'
|
|
49
|
+
},
|
|
50
|
+
contract: {
|
|
51
|
+
inputSchema: z.object({ query: z.string() }),
|
|
52
|
+
outputSchema: z.object({ response: z.string() })
|
|
53
|
+
},
|
|
54
|
+
tools: [],
|
|
55
|
+
modelConfig: {
|
|
56
|
+
provider: 'mock',
|
|
57
|
+
apiKey: 'test-key',
|
|
58
|
+
model: 'mock'
|
|
59
|
+
} as ModelConfig
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe('getResourceDefinition', () => {
|
|
63
|
+
it('returns workflow definition by resourceId', () => {
|
|
64
|
+
const workflow = createMockWorkflow('test-workflow')
|
|
65
|
+
const registry = new ResourceRegistry({
|
|
66
|
+
'test-org': { workflows: [workflow] }
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const result = registry.getResourceDefinition('test-org', 'test-workflow')
|
|
70
|
+
|
|
71
|
+
expect(result).toBe(workflow)
|
|
72
|
+
expect(result?.config.type).toBe('workflow')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('returns agent definition by resourceId', () => {
|
|
76
|
+
const agent = createMockAgent('test-agent')
|
|
77
|
+
const registry = new ResourceRegistry({
|
|
78
|
+
'test-org': { agents: [agent] }
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const result = registry.getResourceDefinition('test-org', 'test-agent')
|
|
82
|
+
|
|
83
|
+
expect(result).toBe(agent)
|
|
84
|
+
expect(result?.config.type).toBe('agent')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('returns null for non-existent organization', () => {
|
|
88
|
+
const registry = new ResourceRegistry({})
|
|
89
|
+
|
|
90
|
+
const result = registry.getResourceDefinition('nonexistent-org', 'test-workflow')
|
|
91
|
+
|
|
92
|
+
expect(result).toBeNull()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('returns null for non-existent resource in existing organization', () => {
|
|
96
|
+
const registry = new ResourceRegistry({
|
|
97
|
+
'test-org': { workflows: [] }
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const result = registry.getResourceDefinition('test-org', 'nonexistent-resource')
|
|
101
|
+
|
|
102
|
+
expect(result).toBeNull()
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('handles organization with only workflows', () => {
|
|
106
|
+
const workflow = createMockWorkflow('test-workflow')
|
|
107
|
+
const registry = new ResourceRegistry({
|
|
108
|
+
'test-org': { workflows: [workflow] }
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
const result = registry.getResourceDefinition('test-org', 'test-workflow')
|
|
112
|
+
|
|
113
|
+
expect(result).toBe(workflow)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('handles organization with only agents', () => {
|
|
117
|
+
const agent = createMockAgent('test-agent')
|
|
118
|
+
const registry = new ResourceRegistry({
|
|
119
|
+
'test-org': { agents: [agent] }
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const result = registry.getResourceDefinition('test-org', 'test-agent')
|
|
123
|
+
|
|
124
|
+
expect(result).toBe(agent)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('handles multiple organizations', () => {
|
|
128
|
+
const workflow1 = createMockWorkflow('workflow-1')
|
|
129
|
+
const workflow2 = createMockWorkflow('workflow-2')
|
|
130
|
+
|
|
131
|
+
const registry = new ResourceRegistry({
|
|
132
|
+
'org-1': { workflows: [workflow1] },
|
|
133
|
+
'org-2': { workflows: [workflow2] }
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
expect(registry.getResourceDefinition('org-1', 'workflow-1')).toBe(workflow1)
|
|
137
|
+
expect(registry.getResourceDefinition('org-2', 'workflow-2')).toBe(workflow2)
|
|
138
|
+
expect(registry.getResourceDefinition('org-1', 'workflow-2')).toBeNull()
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe('listResourcesForOrganization', () => {
|
|
143
|
+
it('returns empty list for non-existent organization', () => {
|
|
144
|
+
const registry = new ResourceRegistry({})
|
|
145
|
+
|
|
146
|
+
const result = registry.listResourcesForOrganization('nonexistent-org')
|
|
147
|
+
|
|
148
|
+
expect(result).toEqual({
|
|
149
|
+
workflows: [],
|
|
150
|
+
agents: [],
|
|
151
|
+
total: 0,
|
|
152
|
+
organizationName: 'nonexistent-org',
|
|
153
|
+
environment: undefined
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('lists all workflows for organization', () => {
|
|
158
|
+
const workflow1 = createMockWorkflow('workflow-1')
|
|
159
|
+
const workflow2 = createMockWorkflow('workflow-2')
|
|
160
|
+
|
|
161
|
+
const registry = new ResourceRegistry({
|
|
162
|
+
'test-org': { workflows: [workflow1, workflow2] }
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
166
|
+
|
|
167
|
+
expect(result.workflows).toHaveLength(2)
|
|
168
|
+
expect(result.workflows[0]).toEqual({
|
|
169
|
+
resourceId: 'workflow-1',
|
|
170
|
+
name: 'Workflow workflow-1',
|
|
171
|
+
description: 'Test workflow workflow-1',
|
|
172
|
+
version: '1.0.0',
|
|
173
|
+
type: 'workflow',
|
|
174
|
+
status: 'dev',
|
|
175
|
+
origin: 'local'
|
|
176
|
+
})
|
|
177
|
+
expect(result.agents).toHaveLength(0)
|
|
178
|
+
expect(result.total).toBe(2)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('lists all agents for organization', () => {
|
|
182
|
+
const agent1 = createMockAgent('agent-1')
|
|
183
|
+
const agent2 = createMockAgent('agent-2')
|
|
184
|
+
|
|
185
|
+
const registry = new ResourceRegistry({
|
|
186
|
+
'test-org': { agents: [agent1, agent2] }
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
190
|
+
|
|
191
|
+
expect(result.agents).toHaveLength(2)
|
|
192
|
+
expect(result.agents[0]).toEqual({
|
|
193
|
+
resourceId: 'agent-1',
|
|
194
|
+
name: 'Agent agent-1',
|
|
195
|
+
description: 'Test agent agent-1',
|
|
196
|
+
version: '1.0.0',
|
|
197
|
+
type: 'agent',
|
|
198
|
+
status: 'dev',
|
|
199
|
+
sessionCapable: false,
|
|
200
|
+
origin: 'local'
|
|
201
|
+
})
|
|
202
|
+
expect(result.workflows).toHaveLength(0)
|
|
203
|
+
expect(result.total).toBe(2)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('lists both workflows and agents', () => {
|
|
207
|
+
const workflow = createMockWorkflow('workflow-1')
|
|
208
|
+
const agent = createMockAgent('agent-1')
|
|
209
|
+
|
|
210
|
+
const registry = new ResourceRegistry({
|
|
211
|
+
'test-org': {
|
|
212
|
+
workflows: [workflow],
|
|
213
|
+
agents: [agent]
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
218
|
+
|
|
219
|
+
expect(result.workflows).toHaveLength(1)
|
|
220
|
+
expect(result.agents).toHaveLength(1)
|
|
221
|
+
expect(result.total).toBe(2)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('includes descriptor-derived System and governance metadata', () => {
|
|
225
|
+
const system: SystemEntry = {
|
|
226
|
+
id: 'sys.lead-gen',
|
|
227
|
+
title: 'Lead Generation',
|
|
228
|
+
description: 'Lead acquisition workflows.',
|
|
229
|
+
kind: 'operational',
|
|
230
|
+
governedByKnowledge: [],
|
|
231
|
+
drivesGoals: [],
|
|
232
|
+
status: 'active'
|
|
233
|
+
}
|
|
234
|
+
const resource: ResourceEntry = {
|
|
235
|
+
id: 'workflow-1',
|
|
236
|
+
kind: 'workflow',
|
|
237
|
+
systemId: system.id,
|
|
238
|
+
status: 'active'
|
|
239
|
+
}
|
|
240
|
+
const workflow = createMockWorkflow('workflow-1')
|
|
241
|
+
workflow.config.resource = resource
|
|
242
|
+
|
|
243
|
+
const registry = new ResourceRegistry({
|
|
244
|
+
'test-org': {
|
|
245
|
+
organizationModel: {
|
|
246
|
+
systems: { systems: [system] },
|
|
247
|
+
resources: { entries: [resource] }
|
|
248
|
+
},
|
|
249
|
+
workflows: [workflow]
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
254
|
+
|
|
255
|
+
expect(result.workflows[0]).toMatchObject({
|
|
256
|
+
resourceId: 'workflow-1',
|
|
257
|
+
systemId: 'sys.lead-gen',
|
|
258
|
+
governanceStatus: 'active',
|
|
259
|
+
system: {
|
|
260
|
+
id: 'sys.lead-gen',
|
|
261
|
+
title: 'Lead Generation',
|
|
262
|
+
kind: 'operational',
|
|
263
|
+
status: 'active'
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
it('filters resources by dev environment', () => {
|
|
269
|
+
const devWorkflow = createMockWorkflow('workflow-dev', 'dev')
|
|
270
|
+
const prodWorkflow = createMockWorkflow('workflow-prod', 'prod')
|
|
271
|
+
|
|
272
|
+
const registry = new ResourceRegistry({
|
|
273
|
+
'test-org': { workflows: [devWorkflow, prodWorkflow] }
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
const result = registry.listResourcesForOrganization('test-org', 'dev')
|
|
277
|
+
|
|
278
|
+
expect(result.workflows).toHaveLength(1)
|
|
279
|
+
expect(result.workflows[0].resourceId).toBe('workflow-dev')
|
|
280
|
+
expect(result.workflows[0].status).toBe('dev')
|
|
281
|
+
expect(result.environment).toBe('dev')
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
it('filters resources by prod environment', () => {
|
|
285
|
+
const devAgent = createMockAgent('agent-dev', 'dev')
|
|
286
|
+
const prodAgent = createMockAgent('agent-prod', 'prod')
|
|
287
|
+
|
|
288
|
+
const registry = new ResourceRegistry({
|
|
289
|
+
'test-org': { agents: [devAgent, prodAgent] }
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
const result = registry.listResourcesForOrganization('test-org', 'prod')
|
|
293
|
+
|
|
294
|
+
expect(result.agents).toHaveLength(1)
|
|
295
|
+
expect(result.agents[0].resourceId).toBe('agent-prod')
|
|
296
|
+
expect(result.agents[0].status).toBe('prod')
|
|
297
|
+
expect(result.environment).toBe('prod')
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it('filters both workflows and agents by environment', () => {
|
|
301
|
+
const devWorkflow = createMockWorkflow('workflow-dev', 'dev')
|
|
302
|
+
const prodWorkflow = createMockWorkflow('workflow-prod', 'prod')
|
|
303
|
+
const devAgent = createMockAgent('agent-dev', 'dev')
|
|
304
|
+
const prodAgent = createMockAgent('agent-prod', 'prod')
|
|
305
|
+
|
|
306
|
+
const registry = new ResourceRegistry({
|
|
307
|
+
'test-org': {
|
|
308
|
+
workflows: [devWorkflow, prodWorkflow],
|
|
309
|
+
agents: [devAgent, prodAgent]
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
const devResult = registry.listResourcesForOrganization('test-org', 'dev')
|
|
314
|
+
expect(devResult.total).toBe(2)
|
|
315
|
+
expect(devResult.workflows[0].status).toBe('dev')
|
|
316
|
+
expect(devResult.agents[0].status).toBe('dev')
|
|
317
|
+
|
|
318
|
+
const prodResult = registry.listResourcesForOrganization('test-org', 'prod')
|
|
319
|
+
expect(prodResult.total).toBe(2)
|
|
320
|
+
expect(prodResult.workflows[0].status).toBe('prod')
|
|
321
|
+
expect(prodResult.agents[0].status).toBe('prod')
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
it('handles empty workflows array', () => {
|
|
325
|
+
const registry = new ResourceRegistry({
|
|
326
|
+
'test-org': { workflows: [] }
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
330
|
+
|
|
331
|
+
expect(result.workflows).toEqual([])
|
|
332
|
+
expect(result.total).toBe(0)
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
it('handles empty agents array', () => {
|
|
336
|
+
const registry = new ResourceRegistry({
|
|
337
|
+
'test-org': { agents: [] }
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
341
|
+
|
|
342
|
+
expect(result.agents).toEqual([])
|
|
343
|
+
expect(result.total).toBe(0)
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
it('includes organizationName in result', () => {
|
|
347
|
+
const registry = new ResourceRegistry({
|
|
348
|
+
'test-org': { workflows: [] }
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
352
|
+
|
|
353
|
+
expect(result.organizationName).toBe('test-org')
|
|
354
|
+
})
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
describe('listAllResources', () => {
|
|
358
|
+
it('returns entire registry', () => {
|
|
359
|
+
const workflow = createMockWorkflow('workflow-1')
|
|
360
|
+
const agent = createMockAgent('agent-1')
|
|
361
|
+
|
|
362
|
+
const registryData = {
|
|
363
|
+
'org-1': { workflows: [workflow] },
|
|
364
|
+
'org-2': { agents: [agent] }
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const registry = new ResourceRegistry(registryData)
|
|
368
|
+
|
|
369
|
+
const result = registry.listAllResources()
|
|
370
|
+
|
|
371
|
+
expect(result).toBe(registryData)
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
it('returns empty object for empty registry', () => {
|
|
375
|
+
const registry = new ResourceRegistry({})
|
|
376
|
+
|
|
377
|
+
const result = registry.listAllResources()
|
|
378
|
+
|
|
379
|
+
expect(result).toEqual({})
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
it('returns all organizations and resources', () => {
|
|
383
|
+
const registryData = {
|
|
384
|
+
'org-1': {
|
|
385
|
+
workflows: [createMockWorkflow('w1'), createMockWorkflow('w2')],
|
|
386
|
+
agents: [createMockAgent('a1')]
|
|
387
|
+
},
|
|
388
|
+
'org-2': {
|
|
389
|
+
workflows: [createMockWorkflow('w3')],
|
|
390
|
+
agents: [createMockAgent('a2'), createMockAgent('a3')]
|
|
391
|
+
},
|
|
392
|
+
'org-3': {
|
|
393
|
+
workflows: [],
|
|
394
|
+
agents: []
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const registry = new ResourceRegistry(registryData)
|
|
399
|
+
|
|
400
|
+
const result = registry.listAllResources()
|
|
401
|
+
|
|
402
|
+
expect(Object.keys(result)).toHaveLength(3)
|
|
403
|
+
expect(result['org-1'].workflows).toHaveLength(2)
|
|
404
|
+
expect(result['org-1'].agents).toHaveLength(1)
|
|
405
|
+
expect(result['org-2'].workflows).toHaveLength(1)
|
|
406
|
+
expect(result['org-2'].agents).toHaveLength(2)
|
|
407
|
+
})
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
describe('edge cases and organization isolation', () => {
|
|
411
|
+
it('maintains organization isolation (resources not shared)', () => {
|
|
412
|
+
const workflow1 = createMockWorkflow('workflow-1')
|
|
413
|
+
const workflow2 = createMockWorkflow('workflow-2')
|
|
414
|
+
|
|
415
|
+
const registry = new ResourceRegistry({
|
|
416
|
+
'org-1': { workflows: [workflow1] },
|
|
417
|
+
'org-2': { workflows: [workflow2] }
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
// org-1 should only see workflow-1
|
|
421
|
+
expect(registry.getResourceDefinition('org-1', 'workflow-1')).toBe(workflow1)
|
|
422
|
+
expect(registry.getResourceDefinition('org-1', 'workflow-2')).toBeNull()
|
|
423
|
+
|
|
424
|
+
// org-2 should only see workflow-2
|
|
425
|
+
expect(registry.getResourceDefinition('org-2', 'workflow-2')).toBe(workflow2)
|
|
426
|
+
expect(registry.getResourceDefinition('org-2', 'workflow-1')).toBeNull()
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
it('handles organization with undefined workflows', () => {
|
|
430
|
+
const registry = new ResourceRegistry({
|
|
431
|
+
'test-org': { agents: [createMockAgent('agent-1')] }
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
435
|
+
|
|
436
|
+
expect(result.workflows).toEqual([])
|
|
437
|
+
expect(result.agents).toHaveLength(1)
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
it('handles organization with undefined agents', () => {
|
|
441
|
+
const registry = new ResourceRegistry({
|
|
442
|
+
'test-org': { workflows: [createMockWorkflow('workflow-1')] }
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
446
|
+
|
|
447
|
+
expect(result.workflows).toHaveLength(1)
|
|
448
|
+
expect(result.agents).toEqual([])
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
it('handles large number of resources', () => {
|
|
452
|
+
const workflows = Array.from({ length: 100 }, (_, i) => createMockWorkflow(`workflow-${i}`))
|
|
453
|
+
const agents = Array.from({ length: 100 }, (_, i) => createMockAgent(`agent-${i}`))
|
|
454
|
+
|
|
455
|
+
const registry = new ResourceRegistry({
|
|
456
|
+
'test-org': { workflows, agents }
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
460
|
+
|
|
461
|
+
expect(result.workflows).toHaveLength(100)
|
|
462
|
+
expect(result.agents).toHaveLength(100)
|
|
463
|
+
expect(result.total).toBe(200)
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
it('handles special characters in organization names', () => {
|
|
467
|
+
const workflow = createMockWorkflow('test-workflow')
|
|
468
|
+
|
|
469
|
+
const registry = new ResourceRegistry({
|
|
470
|
+
'org-with-dashes': { workflows: [workflow] },
|
|
471
|
+
org_with_underscores: { workflows: [workflow] },
|
|
472
|
+
'org.with.dots': { workflows: [workflow] }
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
expect(registry.getResourceDefinition('org-with-dashes', 'test-workflow')).toBe(workflow)
|
|
476
|
+
expect(registry.getResourceDefinition('org_with_underscores', 'test-workflow')).toBe(workflow)
|
|
477
|
+
expect(registry.getResourceDefinition('org.with.dots', 'test-workflow')).toBe(workflow)
|
|
478
|
+
})
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
describe('duplicate resourceId validation', () => {
|
|
482
|
+
it('throws error on duplicate workflow resourceIds', () => {
|
|
483
|
+
expect(() => {
|
|
484
|
+
new ResourceRegistry({
|
|
485
|
+
'test-org': {
|
|
486
|
+
workflows: [createMockWorkflow('duplicate-id'), createMockWorkflow('duplicate-id')]
|
|
487
|
+
}
|
|
488
|
+
})
|
|
489
|
+
}).toThrow('Duplicate resourceId "duplicate-id" in organization "test-org"')
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
it('throws error on duplicate agent resourceIds', () => {
|
|
493
|
+
expect(() => {
|
|
494
|
+
new ResourceRegistry({
|
|
495
|
+
'test-org': {
|
|
496
|
+
agents: [createMockAgent('duplicate-id'), createMockAgent('duplicate-id')]
|
|
497
|
+
}
|
|
498
|
+
})
|
|
499
|
+
}).toThrow('Duplicate resourceId "duplicate-id" in organization "test-org"')
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
it('throws error on workflow/agent resourceId collision', () => {
|
|
503
|
+
expect(() => {
|
|
504
|
+
new ResourceRegistry({
|
|
505
|
+
'test-org': {
|
|
506
|
+
workflows: [createMockWorkflow('collision-id')],
|
|
507
|
+
agents: [createMockAgent('collision-id')]
|
|
508
|
+
}
|
|
509
|
+
})
|
|
510
|
+
}).toThrow('Duplicate resourceId "collision-id" in organization "test-org"')
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
it('allows same resourceId across different organizations', () => {
|
|
514
|
+
expect(() => {
|
|
515
|
+
new ResourceRegistry({
|
|
516
|
+
'org-1': {
|
|
517
|
+
workflows: [createMockWorkflow('same-id')]
|
|
518
|
+
},
|
|
519
|
+
'org-2': {
|
|
520
|
+
workflows: [createMockWorkflow('same-id')]
|
|
521
|
+
}
|
|
522
|
+
})
|
|
523
|
+
}).not.toThrow()
|
|
524
|
+
})
|
|
525
|
+
|
|
526
|
+
it('throws error on multiple duplicate resourceIds', () => {
|
|
527
|
+
expect(() => {
|
|
528
|
+
new ResourceRegistry({
|
|
529
|
+
'test-org': {
|
|
530
|
+
workflows: [
|
|
531
|
+
createMockWorkflow('id-1'),
|
|
532
|
+
createMockWorkflow('id-2'),
|
|
533
|
+
createMockWorkflow('id-1') // Duplicate
|
|
534
|
+
]
|
|
535
|
+
}
|
|
536
|
+
})
|
|
537
|
+
}).toThrow('Duplicate resourceId "id-1" in organization "test-org"')
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
it('validates across multiple organizations independently', () => {
|
|
541
|
+
// org-1 has duplicate, org-2 is valid - should fail on org-1
|
|
542
|
+
expect(() => {
|
|
543
|
+
new ResourceRegistry({
|
|
544
|
+
'org-1': {
|
|
545
|
+
workflows: [createMockWorkflow('duplicate'), createMockWorkflow('duplicate')]
|
|
546
|
+
},
|
|
547
|
+
'org-2': {
|
|
548
|
+
workflows: [createMockWorkflow('valid-id')]
|
|
549
|
+
}
|
|
550
|
+
})
|
|
551
|
+
}).toThrow('Duplicate resourceId "duplicate" in organization "org-1"')
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
it('allows empty workflows and agents arrays', () => {
|
|
555
|
+
expect(() => {
|
|
556
|
+
new ResourceRegistry({
|
|
557
|
+
'test-org': {
|
|
558
|
+
workflows: [],
|
|
559
|
+
agents: []
|
|
560
|
+
}
|
|
561
|
+
})
|
|
562
|
+
}).not.toThrow()
|
|
563
|
+
})
|
|
564
|
+
|
|
565
|
+
it('allows undefined workflows and agents', () => {
|
|
566
|
+
expect(() => {
|
|
567
|
+
new ResourceRegistry({
|
|
568
|
+
'test-org': {}
|
|
569
|
+
})
|
|
570
|
+
}).not.toThrow()
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
it('validates organization with only workflows', () => {
|
|
574
|
+
expect(() => {
|
|
575
|
+
new ResourceRegistry({
|
|
576
|
+
'test-org': {
|
|
577
|
+
workflows: [createMockWorkflow('dup'), createMockWorkflow('dup')]
|
|
578
|
+
}
|
|
579
|
+
})
|
|
580
|
+
}).toThrow('Duplicate resourceId "dup"')
|
|
581
|
+
})
|
|
582
|
+
|
|
583
|
+
it('validates organization with only agents', () => {
|
|
584
|
+
expect(() => {
|
|
585
|
+
new ResourceRegistry({
|
|
586
|
+
'test-org': {
|
|
587
|
+
agents: [createMockAgent('dup'), createMockAgent('dup')]
|
|
588
|
+
}
|
|
589
|
+
})
|
|
590
|
+
}).toThrow('Duplicate resourceId "dup"')
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
it('includes organization name in error message', () => {
|
|
594
|
+
expect(() => {
|
|
595
|
+
new ResourceRegistry({
|
|
596
|
+
'my-special-org': {
|
|
597
|
+
workflows: [createMockWorkflow('id'), createMockWorkflow('id')]
|
|
598
|
+
}
|
|
599
|
+
})
|
|
600
|
+
}).toThrow('organization "my-special-org"')
|
|
601
|
+
})
|
|
602
|
+
|
|
603
|
+
it('includes resourceId in error message', () => {
|
|
604
|
+
expect(() => {
|
|
605
|
+
new ResourceRegistry({
|
|
606
|
+
'test-org': {
|
|
607
|
+
workflows: [createMockWorkflow('my-workflow-id'), createMockWorkflow('my-workflow-id')]
|
|
608
|
+
}
|
|
609
|
+
})
|
|
610
|
+
}).toThrow('resourceId "my-workflow-id"')
|
|
611
|
+
})
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
describe('model config validation', () => {
|
|
615
|
+
it('validates agent model configs on construction', () => {
|
|
616
|
+
const validAgent = createMockAgent('valid-agent')
|
|
617
|
+
validAgent.modelConfig = {
|
|
618
|
+
provider: 'openai',
|
|
619
|
+
model: 'gpt-5',
|
|
620
|
+
apiKey: 'test-key',
|
|
621
|
+
temperature: 1,
|
|
622
|
+
maxOutputTokens: 8000
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
expect(() => {
|
|
626
|
+
new ResourceRegistry({
|
|
627
|
+
'test-org': { agents: [validAgent] }
|
|
628
|
+
})
|
|
629
|
+
}).not.toThrow()
|
|
630
|
+
})
|
|
631
|
+
|
|
632
|
+
it('throws error for invalid agent temperature', () => {
|
|
633
|
+
const invalidAgent = createMockAgent('invalid-agent')
|
|
634
|
+
invalidAgent.modelConfig = {
|
|
635
|
+
provider: 'openai',
|
|
636
|
+
model: 'gpt-5',
|
|
637
|
+
apiKey: 'test-key',
|
|
638
|
+
temperature: 0.7, // Invalid - gpt-5 requires temperature=1
|
|
639
|
+
maxOutputTokens: 8000
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
expect(() => {
|
|
643
|
+
new ResourceRegistry({
|
|
644
|
+
'test-org': { agents: [invalidAgent] }
|
|
645
|
+
})
|
|
646
|
+
}).toThrow('Invalid model config in test-org/invalid-agent')
|
|
647
|
+
expect(() => {
|
|
648
|
+
new ResourceRegistry({
|
|
649
|
+
'test-org': { agents: [invalidAgent] }
|
|
650
|
+
})
|
|
651
|
+
}).toThrow('expected 1 (field: temperature)')
|
|
652
|
+
})
|
|
653
|
+
|
|
654
|
+
it('throws error for invalid agent maxOutputTokens', () => {
|
|
655
|
+
const invalidAgent = createMockAgent('invalid-agent')
|
|
656
|
+
invalidAgent.modelConfig = {
|
|
657
|
+
provider: 'openai',
|
|
658
|
+
model: 'gpt-5',
|
|
659
|
+
apiKey: 'test-key',
|
|
660
|
+
temperature: 1,
|
|
661
|
+
maxOutputTokens: 2000 // Invalid - below minimum of 4000
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
expect(() => {
|
|
665
|
+
new ResourceRegistry({
|
|
666
|
+
'test-org': { agents: [invalidAgent] }
|
|
667
|
+
})
|
|
668
|
+
}).toThrow('Invalid model config in test-org/invalid-agent')
|
|
669
|
+
expect(() => {
|
|
670
|
+
new ResourceRegistry({
|
|
671
|
+
'test-org': { agents: [invalidAgent] }
|
|
672
|
+
})
|
|
673
|
+
}).toThrow('expected number to be >=4000 (field: maxOutputTokens)')
|
|
674
|
+
})
|
|
675
|
+
|
|
676
|
+
it('validates workflow model configs if present', () => {
|
|
677
|
+
const workflowWithModel = createMockWorkflow('workflow-with-model') as unknown as Record<string, unknown>
|
|
678
|
+
;(workflowWithModel as Record<string, unknown>).modelConfig = {
|
|
679
|
+
provider: 'openai',
|
|
680
|
+
model: 'gpt-5',
|
|
681
|
+
apiKey: 'test-key',
|
|
682
|
+
temperature: 0.5, // Invalid
|
|
683
|
+
maxOutputTokens: 8000
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
expect(() => {
|
|
687
|
+
new ResourceRegistry({
|
|
688
|
+
'test-org': { workflows: [workflowWithModel] }
|
|
689
|
+
})
|
|
690
|
+
}).toThrow('Invalid model config in test-org/workflow-with-model')
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
it('allows workflows without model configs', () => {
|
|
694
|
+
const workflowWithoutModel = createMockWorkflow('workflow-no-model')
|
|
695
|
+
|
|
696
|
+
expect(() => {
|
|
697
|
+
new ResourceRegistry({
|
|
698
|
+
'test-org': { workflows: [workflowWithoutModel] }
|
|
699
|
+
})
|
|
700
|
+
}).not.toThrow()
|
|
701
|
+
})
|
|
702
|
+
|
|
703
|
+
it('validates multiple agents in same organization', () => {
|
|
704
|
+
const validAgent1 = createMockAgent('agent-1')
|
|
705
|
+
validAgent1.modelConfig = {
|
|
706
|
+
provider: 'openai',
|
|
707
|
+
model: 'gpt-5',
|
|
708
|
+
apiKey: 'test-key',
|
|
709
|
+
temperature: 1,
|
|
710
|
+
maxOutputTokens: 8000
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const invalidAgent2 = createMockAgent('agent-2')
|
|
714
|
+
invalidAgent2.modelConfig = {
|
|
715
|
+
provider: 'openai',
|
|
716
|
+
model: 'gpt-5-mini',
|
|
717
|
+
apiKey: 'test-key',
|
|
718
|
+
temperature: 0.7, // Invalid
|
|
719
|
+
maxOutputTokens: 8000
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
expect(() => {
|
|
723
|
+
new ResourceRegistry({
|
|
724
|
+
'test-org': { agents: [validAgent1, invalidAgent2] }
|
|
725
|
+
})
|
|
726
|
+
}).toThrow('Invalid model config in test-org/agent-2')
|
|
727
|
+
})
|
|
728
|
+
|
|
729
|
+
it('validates across multiple organizations', () => {
|
|
730
|
+
const invalidAgent = createMockAgent('invalid-agent')
|
|
731
|
+
invalidAgent.modelConfig = {
|
|
732
|
+
provider: 'openai',
|
|
733
|
+
model: 'gpt-5',
|
|
734
|
+
apiKey: 'test-key',
|
|
735
|
+
temperature: 0.7,
|
|
736
|
+
maxOutputTokens: 8000
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
expect(() => {
|
|
740
|
+
new ResourceRegistry({
|
|
741
|
+
'org-1': { agents: [createMockAgent('valid-agent')] },
|
|
742
|
+
'org-2': { agents: [invalidAgent] }
|
|
743
|
+
})
|
|
744
|
+
}).toThrow('Invalid model config in org-2/invalid-agent')
|
|
745
|
+
})
|
|
746
|
+
|
|
747
|
+
it('includes field name in error message', () => {
|
|
748
|
+
const invalidAgent = createMockAgent('invalid-agent')
|
|
749
|
+
invalidAgent.modelConfig = {
|
|
750
|
+
provider: 'openai',
|
|
751
|
+
model: 'gpt-5',
|
|
752
|
+
apiKey: 'test-key',
|
|
753
|
+
temperature: 0.7,
|
|
754
|
+
maxOutputTokens: 8000
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
expect(() => {
|
|
758
|
+
new ResourceRegistry({
|
|
759
|
+
'test-org': { agents: [invalidAgent] }
|
|
760
|
+
})
|
|
761
|
+
}).toThrow('field: temperature')
|
|
762
|
+
})
|
|
763
|
+
|
|
764
|
+
it('allows mock models with any temperature', () => {
|
|
765
|
+
const mockAgent = createMockAgent('mock-agent')
|
|
766
|
+
mockAgent.modelConfig = {
|
|
767
|
+
provider: 'mock',
|
|
768
|
+
model: 'mock',
|
|
769
|
+
apiKey: 'test-key',
|
|
770
|
+
temperature: 0.5, // Any temperature OK for mock
|
|
771
|
+
maxOutputTokens: 1000
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
expect(() => {
|
|
775
|
+
new ResourceRegistry({
|
|
776
|
+
'test-org': { agents: [mockAgent] }
|
|
777
|
+
})
|
|
778
|
+
}).not.toThrow()
|
|
779
|
+
})
|
|
780
|
+
})
|
|
781
|
+
|
|
782
|
+
describe('environment filtering', () => {
|
|
783
|
+
it('shows all resources in development environment', () => {
|
|
784
|
+
// Mock development environment
|
|
785
|
+
const originalEnv = process.env.NODE_ENV
|
|
786
|
+
process.env.NODE_ENV = 'development'
|
|
787
|
+
|
|
788
|
+
const devWorkflow = createMockWorkflow('dev-workflow', 'dev')
|
|
789
|
+
const prodWorkflow = createMockWorkflow('prod-workflow', 'prod')
|
|
790
|
+
const devAgent = createMockAgent('dev-agent', 'dev')
|
|
791
|
+
const prodAgent = createMockAgent('prod-agent', 'prod')
|
|
792
|
+
|
|
793
|
+
const registry = new ResourceRegistry({
|
|
794
|
+
'test-org': {
|
|
795
|
+
workflows: [devWorkflow, prodWorkflow],
|
|
796
|
+
agents: [devAgent, prodAgent]
|
|
797
|
+
}
|
|
798
|
+
})
|
|
799
|
+
|
|
800
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
801
|
+
|
|
802
|
+
expect(result.total).toBe(4) // All resources visible
|
|
803
|
+
expect(result.workflows).toHaveLength(2)
|
|
804
|
+
expect(result.agents).toHaveLength(2)
|
|
805
|
+
expect(result.workflows.some((w) => w.status === 'dev')).toBe(true)
|
|
806
|
+
expect(result.workflows.some((w) => w.status === 'prod')).toBe(true)
|
|
807
|
+
|
|
808
|
+
// Restore environment
|
|
809
|
+
process.env.NODE_ENV = originalEnv
|
|
810
|
+
})
|
|
811
|
+
|
|
812
|
+
it('shows only prod resources when explicit environment filter is passed', () => {
|
|
813
|
+
const devWorkflow = createMockWorkflow('dev-workflow', 'dev')
|
|
814
|
+
const prodWorkflow = createMockWorkflow('prod-workflow', 'prod')
|
|
815
|
+
const devAgent = createMockAgent('dev-agent', 'dev')
|
|
816
|
+
const prodAgent = createMockAgent('prod-agent', 'prod')
|
|
817
|
+
|
|
818
|
+
const registry = new ResourceRegistry({
|
|
819
|
+
'test-org': {
|
|
820
|
+
workflows: [devWorkflow, prodWorkflow],
|
|
821
|
+
agents: [devAgent, prodAgent]
|
|
822
|
+
}
|
|
823
|
+
})
|
|
824
|
+
|
|
825
|
+
const result = registry.listResourcesForOrganization('test-org', 'prod')
|
|
826
|
+
|
|
827
|
+
expect(result.total).toBe(2) // Only prod resources
|
|
828
|
+
expect(result.workflows).toHaveLength(1)
|
|
829
|
+
expect(result.agents).toHaveLength(1)
|
|
830
|
+
expect(result.workflows[0].status).toBe('prod')
|
|
831
|
+
expect(result.agents[0].status).toBe('prod')
|
|
832
|
+
expect(result.workflows.some((w) => w.status === 'dev')).toBe(false)
|
|
833
|
+
expect(result.agents.some((a) => a.status === 'dev')).toBe(false)
|
|
834
|
+
})
|
|
835
|
+
|
|
836
|
+
it('returns all resources when no environment filter is passed', () => {
|
|
837
|
+
const devWorkflow = createMockWorkflow('dev-workflow', 'dev')
|
|
838
|
+
const prodWorkflow = createMockWorkflow('prod-workflow', 'prod')
|
|
839
|
+
|
|
840
|
+
const registry = new ResourceRegistry({
|
|
841
|
+
'test-org': {
|
|
842
|
+
workflows: [devWorkflow, prodWorkflow]
|
|
843
|
+
}
|
|
844
|
+
})
|
|
845
|
+
|
|
846
|
+
// No environment filter -- returns all resources regardless of status
|
|
847
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
848
|
+
|
|
849
|
+
expect(result.total).toBe(2)
|
|
850
|
+
expect(result.workflows).toHaveLength(2)
|
|
851
|
+
})
|
|
852
|
+
|
|
853
|
+
it('returns empty list when filtering for prod but only dev resources exist', () => {
|
|
854
|
+
const devWorkflow = createMockWorkflow('dev-workflow', 'dev')
|
|
855
|
+
const devAgent = createMockAgent('dev-agent', 'dev')
|
|
856
|
+
|
|
857
|
+
const registry = new ResourceRegistry({
|
|
858
|
+
'test-org': {
|
|
859
|
+
workflows: [devWorkflow],
|
|
860
|
+
agents: [devAgent]
|
|
861
|
+
}
|
|
862
|
+
})
|
|
863
|
+
|
|
864
|
+
const result = registry.listResourcesForOrganization('test-org', 'prod')
|
|
865
|
+
|
|
866
|
+
expect(result.total).toBe(0)
|
|
867
|
+
expect(result.workflows).toHaveLength(0)
|
|
868
|
+
expect(result.agents).toHaveLength(0)
|
|
869
|
+
})
|
|
870
|
+
|
|
871
|
+
it('handles mixed status resources correctly with explicit prod filter', () => {
|
|
872
|
+
const registry = new ResourceRegistry({
|
|
873
|
+
'test-org': {
|
|
874
|
+
workflows: [
|
|
875
|
+
createMockWorkflow('w1', 'dev'),
|
|
876
|
+
createMockWorkflow('w2', 'prod'),
|
|
877
|
+
createMockWorkflow('w3', 'dev'),
|
|
878
|
+
createMockWorkflow('w4', 'prod')
|
|
879
|
+
],
|
|
880
|
+
agents: [createMockAgent('a1', 'dev'), createMockAgent('a2', 'prod'), createMockAgent('a3', 'dev')]
|
|
881
|
+
}
|
|
882
|
+
})
|
|
883
|
+
|
|
884
|
+
const result = registry.listResourcesForOrganization('test-org', 'prod')
|
|
885
|
+
|
|
886
|
+
expect(result.total).toBe(3) // 2 prod workflows + 1 prod agent
|
|
887
|
+
expect(result.workflows).toHaveLength(2)
|
|
888
|
+
expect(result.agents).toHaveLength(1)
|
|
889
|
+
})
|
|
890
|
+
|
|
891
|
+
it('includes environment in response when explicit filter is passed', () => {
|
|
892
|
+
const registry = new ResourceRegistry({
|
|
893
|
+
'test-org': {
|
|
894
|
+
workflows: [createMockWorkflow('w1', 'prod')]
|
|
895
|
+
}
|
|
896
|
+
})
|
|
897
|
+
|
|
898
|
+
const result = registry.listResourcesForOrganization('test-org', 'prod')
|
|
899
|
+
|
|
900
|
+
expect(result.environment).toBe('prod')
|
|
901
|
+
})
|
|
902
|
+
|
|
903
|
+
it('does not include environment in response for development', () => {
|
|
904
|
+
const originalEnv = process.env.NODE_ENV
|
|
905
|
+
process.env.NODE_ENV = 'development'
|
|
906
|
+
|
|
907
|
+
const registry = new ResourceRegistry({
|
|
908
|
+
'test-org': {
|
|
909
|
+
workflows: [createMockWorkflow('w1', 'dev')]
|
|
910
|
+
}
|
|
911
|
+
})
|
|
912
|
+
|
|
913
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
914
|
+
|
|
915
|
+
expect(result.environment).toBeUndefined()
|
|
916
|
+
|
|
917
|
+
process.env.NODE_ENV = originalEnv
|
|
918
|
+
})
|
|
919
|
+
})
|
|
920
|
+
|
|
921
|
+
describe('Resource Manifest Accessors', () => {
|
|
922
|
+
// Helper to create mock triggers, integrations, etc.
|
|
923
|
+
const createMockTrigger = (resourceId: string): import('../types').TriggerDefinition => ({
|
|
924
|
+
resourceId,
|
|
925
|
+
type: 'trigger',
|
|
926
|
+
triggerType: 'manual',
|
|
927
|
+
name: `Trigger ${resourceId}`,
|
|
928
|
+
description: `Test trigger ${resourceId}`,
|
|
929
|
+
version: '1.0.0',
|
|
930
|
+
status: 'dev'
|
|
931
|
+
// NOTE: No triggers field - will be added in tests as needed to reference valid resources
|
|
932
|
+
})
|
|
933
|
+
|
|
934
|
+
const createMockIntegration = (resourceId: string): import('../types').IntegrationDefinition => ({
|
|
935
|
+
resourceId,
|
|
936
|
+
type: 'integration',
|
|
937
|
+
name: `Integration ${resourceId}`,
|
|
938
|
+
description: `Test integration ${resourceId}`,
|
|
939
|
+
version: '1.0.0',
|
|
940
|
+
status: 'dev',
|
|
941
|
+
provider: 'webhook',
|
|
942
|
+
credentialName: 'test-cred'
|
|
943
|
+
})
|
|
944
|
+
|
|
945
|
+
const createMockExternalResource = (resourceId: string): import('../types').ExternalResourceDefinition => ({
|
|
946
|
+
resourceId,
|
|
947
|
+
type: 'external',
|
|
948
|
+
name: `External ${resourceId}`,
|
|
949
|
+
description: `Test external ${resourceId}`,
|
|
950
|
+
version: '1.0.0',
|
|
951
|
+
status: 'dev',
|
|
952
|
+
platform: 'n8n'
|
|
953
|
+
// NOTE: No triggeredBy field
|
|
954
|
+
})
|
|
955
|
+
|
|
956
|
+
const createMockHumanCheckpoint = (resourceId: string): import('../types').HumanCheckpointDefinition => ({
|
|
957
|
+
resourceId,
|
|
958
|
+
type: 'human',
|
|
959
|
+
name: `Human ${resourceId}`,
|
|
960
|
+
description: `Test human checkpoint ${resourceId}`,
|
|
961
|
+
version: '1.0.0',
|
|
962
|
+
status: 'dev'
|
|
963
|
+
})
|
|
964
|
+
|
|
965
|
+
describe('getTrigger', () => {
|
|
966
|
+
it('finds trigger by resourceId', () => {
|
|
967
|
+
const agent = createMockAgent('basic-agent')
|
|
968
|
+
const trigger = createMockTrigger('test-trigger')
|
|
969
|
+
trigger.triggers = { agents: ['basic-agent'] }
|
|
970
|
+
|
|
971
|
+
const registry = new ResourceRegistry({
|
|
972
|
+
'test-org': {
|
|
973
|
+
agents: [agent],
|
|
974
|
+
triggers: [trigger]
|
|
975
|
+
}
|
|
976
|
+
})
|
|
977
|
+
|
|
978
|
+
const result = registry.getTrigger('test-org', 'test-trigger')
|
|
979
|
+
|
|
980
|
+
expect(result).toBe(trigger)
|
|
981
|
+
expect(result?.resourceId).toBe('test-trigger')
|
|
982
|
+
expect(result?.type).toBe('trigger')
|
|
983
|
+
})
|
|
984
|
+
|
|
985
|
+
it('returns null for non-existent trigger', () => {
|
|
986
|
+
const agent = createMockAgent('basic-agent')
|
|
987
|
+
const trigger = createMockTrigger('trigger-1')
|
|
988
|
+
trigger.triggers = { agents: ['basic-agent'] }
|
|
989
|
+
|
|
990
|
+
const registry = new ResourceRegistry({
|
|
991
|
+
'test-org': {
|
|
992
|
+
agents: [agent],
|
|
993
|
+
triggers: [trigger]
|
|
994
|
+
}
|
|
995
|
+
})
|
|
996
|
+
|
|
997
|
+
const result = registry.getTrigger('test-org', 'nonexistent-trigger')
|
|
998
|
+
|
|
999
|
+
expect(result).toBeNull()
|
|
1000
|
+
})
|
|
1001
|
+
|
|
1002
|
+
it('returns null for non-existent organization', () => {
|
|
1003
|
+
const registry = new ResourceRegistry({})
|
|
1004
|
+
|
|
1005
|
+
const result = registry.getTrigger('nonexistent-org', 'test-trigger')
|
|
1006
|
+
|
|
1007
|
+
expect(result).toBeNull()
|
|
1008
|
+
})
|
|
1009
|
+
})
|
|
1010
|
+
|
|
1011
|
+
describe('getIntegration', () => {
|
|
1012
|
+
it('finds integration by resourceId', () => {
|
|
1013
|
+
const integration = createMockIntegration('test-integration')
|
|
1014
|
+
const registry = new ResourceRegistry({
|
|
1015
|
+
'test-org': { integrations: [integration] }
|
|
1016
|
+
})
|
|
1017
|
+
|
|
1018
|
+
const result = registry.getIntegration('test-org', 'test-integration')
|
|
1019
|
+
|
|
1020
|
+
expect(result).toBe(integration)
|
|
1021
|
+
expect(result?.resourceId).toBe('test-integration')
|
|
1022
|
+
expect(result?.type).toBe('integration')
|
|
1023
|
+
})
|
|
1024
|
+
|
|
1025
|
+
it('returns null for non-existent integration', () => {
|
|
1026
|
+
const registry = new ResourceRegistry({
|
|
1027
|
+
'test-org': { integrations: [createMockIntegration('integration-1')] }
|
|
1028
|
+
})
|
|
1029
|
+
|
|
1030
|
+
const result = registry.getIntegration('test-org', 'nonexistent-integration')
|
|
1031
|
+
|
|
1032
|
+
expect(result).toBeNull()
|
|
1033
|
+
})
|
|
1034
|
+
|
|
1035
|
+
it('returns null for non-existent organization', () => {
|
|
1036
|
+
const registry = new ResourceRegistry({})
|
|
1037
|
+
|
|
1038
|
+
const result = registry.getIntegration('nonexistent-org', 'test-integration')
|
|
1039
|
+
|
|
1040
|
+
expect(result).toBeNull()
|
|
1041
|
+
})
|
|
1042
|
+
})
|
|
1043
|
+
|
|
1044
|
+
describe('getExternalResource', () => {
|
|
1045
|
+
it('finds external resource by resourceId', () => {
|
|
1046
|
+
const external = createMockExternalResource('test-external')
|
|
1047
|
+
const registry = new ResourceRegistry({
|
|
1048
|
+
'test-org': { externalResources: [external] }
|
|
1049
|
+
})
|
|
1050
|
+
|
|
1051
|
+
const result = registry.getExternalResource('test-org', 'test-external')
|
|
1052
|
+
|
|
1053
|
+
expect(result).toBe(external)
|
|
1054
|
+
expect(result?.resourceId).toBe('test-external')
|
|
1055
|
+
expect(result?.platform).toBe('n8n')
|
|
1056
|
+
})
|
|
1057
|
+
|
|
1058
|
+
it('returns null for non-existent external resource', () => {
|
|
1059
|
+
const registry = new ResourceRegistry({
|
|
1060
|
+
'test-org': { externalResources: [createMockExternalResource('external-1')] }
|
|
1061
|
+
})
|
|
1062
|
+
|
|
1063
|
+
const result = registry.getExternalResource('test-org', 'nonexistent-external')
|
|
1064
|
+
|
|
1065
|
+
expect(result).toBeNull()
|
|
1066
|
+
})
|
|
1067
|
+
|
|
1068
|
+
it('returns null for non-existent organization', () => {
|
|
1069
|
+
const registry = new ResourceRegistry({})
|
|
1070
|
+
|
|
1071
|
+
const result = registry.getExternalResource('nonexistent-org', 'test-external')
|
|
1072
|
+
|
|
1073
|
+
expect(result).toBeNull()
|
|
1074
|
+
})
|
|
1075
|
+
})
|
|
1076
|
+
|
|
1077
|
+
describe('getHumanCheckpoint', () => {
|
|
1078
|
+
it('finds human checkpoint by resourceId', () => {
|
|
1079
|
+
const humanCheckpoint = createMockHumanCheckpoint('test-human')
|
|
1080
|
+
const registry = new ResourceRegistry({
|
|
1081
|
+
'test-org': { humanCheckpoints: [humanCheckpoint] }
|
|
1082
|
+
})
|
|
1083
|
+
|
|
1084
|
+
const result = registry.getHumanCheckpoint('test-org', 'test-human')
|
|
1085
|
+
|
|
1086
|
+
expect(result).toBe(humanCheckpoint)
|
|
1087
|
+
expect(result?.resourceId).toBe('test-human')
|
|
1088
|
+
expect(result?.type).toBe('human')
|
|
1089
|
+
})
|
|
1090
|
+
|
|
1091
|
+
it('returns null for non-existent human checkpoint', () => {
|
|
1092
|
+
const registry = new ResourceRegistry({
|
|
1093
|
+
'test-org': { humanCheckpoints: [createMockHumanCheckpoint('human-1')] }
|
|
1094
|
+
})
|
|
1095
|
+
|
|
1096
|
+
const result = registry.getHumanCheckpoint('test-org', 'nonexistent-human')
|
|
1097
|
+
|
|
1098
|
+
expect(result).toBeNull()
|
|
1099
|
+
})
|
|
1100
|
+
|
|
1101
|
+
it('returns null for non-existent organization', () => {
|
|
1102
|
+
const registry = new ResourceRegistry({})
|
|
1103
|
+
|
|
1104
|
+
const result = registry.getHumanCheckpoint('nonexistent-org', 'test-human')
|
|
1105
|
+
|
|
1106
|
+
expect(result).toBeNull()
|
|
1107
|
+
})
|
|
1108
|
+
})
|
|
1109
|
+
|
|
1110
|
+
describe('getTriggers, getIntegrations, getExternalResources, getHumanCheckpoints', () => {
|
|
1111
|
+
it('returns all triggers for organization', () => {
|
|
1112
|
+
const agent = createMockAgent('basic-agent')
|
|
1113
|
+
const trigger1 = createMockTrigger('trigger-1')
|
|
1114
|
+
trigger1.triggers = { agents: ['basic-agent'] }
|
|
1115
|
+
const trigger2 = createMockTrigger('trigger-2')
|
|
1116
|
+
trigger2.triggers = { agents: ['basic-agent'] }
|
|
1117
|
+
|
|
1118
|
+
const registry = new ResourceRegistry({
|
|
1119
|
+
'test-org': {
|
|
1120
|
+
agents: [agent],
|
|
1121
|
+
triggers: [trigger1, trigger2]
|
|
1122
|
+
}
|
|
1123
|
+
})
|
|
1124
|
+
|
|
1125
|
+
const result = registry.getTriggers('test-org')
|
|
1126
|
+
|
|
1127
|
+
expect(result).toHaveLength(2)
|
|
1128
|
+
expect(result[0]).toBe(trigger1)
|
|
1129
|
+
expect(result[1]).toBe(trigger2)
|
|
1130
|
+
})
|
|
1131
|
+
|
|
1132
|
+
it('returns all integrations for organization', () => {
|
|
1133
|
+
const integration1 = createMockIntegration('integration-1')
|
|
1134
|
+
const integration2 = createMockIntegration('integration-2')
|
|
1135
|
+
const registry = new ResourceRegistry({
|
|
1136
|
+
'test-org': { integrations: [integration1, integration2] }
|
|
1137
|
+
})
|
|
1138
|
+
|
|
1139
|
+
const result = registry.getIntegrations('test-org')
|
|
1140
|
+
|
|
1141
|
+
expect(result).toHaveLength(2)
|
|
1142
|
+
expect(result[0]).toBe(integration1)
|
|
1143
|
+
expect(result[1]).toBe(integration2)
|
|
1144
|
+
})
|
|
1145
|
+
|
|
1146
|
+
it('returns all external resources for organization', () => {
|
|
1147
|
+
const external1 = createMockExternalResource('external-1')
|
|
1148
|
+
const external2 = createMockExternalResource('external-2')
|
|
1149
|
+
const registry = new ResourceRegistry({
|
|
1150
|
+
'test-org': { externalResources: [external1, external2] }
|
|
1151
|
+
})
|
|
1152
|
+
|
|
1153
|
+
const result = registry.getExternalResources('test-org')
|
|
1154
|
+
|
|
1155
|
+
expect(result).toHaveLength(2)
|
|
1156
|
+
expect(result[0]).toBe(external1)
|
|
1157
|
+
expect(result[1]).toBe(external2)
|
|
1158
|
+
})
|
|
1159
|
+
|
|
1160
|
+
it('returns all human checkpoints for organization', () => {
|
|
1161
|
+
const human1 = createMockHumanCheckpoint('human-1')
|
|
1162
|
+
const human2 = createMockHumanCheckpoint('human-2')
|
|
1163
|
+
const registry = new ResourceRegistry({
|
|
1164
|
+
'test-org': { humanCheckpoints: [human1, human2] }
|
|
1165
|
+
})
|
|
1166
|
+
|
|
1167
|
+
const result = registry.getHumanCheckpoints('test-org')
|
|
1168
|
+
|
|
1169
|
+
expect(result).toHaveLength(2)
|
|
1170
|
+
expect(result[0]).toBe(human1)
|
|
1171
|
+
expect(result[1]).toBe(human2)
|
|
1172
|
+
})
|
|
1173
|
+
|
|
1174
|
+
it('returns empty array for organization with no manifest data', () => {
|
|
1175
|
+
const registry = new ResourceRegistry({
|
|
1176
|
+
'test-org': { workflows: [createMockWorkflow('workflow-1')] }
|
|
1177
|
+
})
|
|
1178
|
+
|
|
1179
|
+
expect(registry.getTriggers('test-org')).toEqual([])
|
|
1180
|
+
expect(registry.getIntegrations('test-org')).toEqual([])
|
|
1181
|
+
expect(registry.getExternalResources('test-org')).toEqual([])
|
|
1182
|
+
expect(registry.getHumanCheckpoints('test-org')).toEqual([])
|
|
1183
|
+
})
|
|
1184
|
+
})
|
|
1185
|
+
})
|
|
1186
|
+
|
|
1187
|
+
describe('getCommandViewData', () => {
|
|
1188
|
+
it('returns correct edge types (triggers, uses, approval only)', () => {
|
|
1189
|
+
const trigger = {
|
|
1190
|
+
resourceId: 'trigger-1',
|
|
1191
|
+
type: 'trigger' as const,
|
|
1192
|
+
triggerType: 'manual' as const,
|
|
1193
|
+
name: 'Manual Trigger',
|
|
1194
|
+
description: 'Test trigger',
|
|
1195
|
+
version: '1.0.0',
|
|
1196
|
+
status: 'dev' as const,
|
|
1197
|
+
triggers: { workflows: ['workflow-1'] }
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
const integration = {
|
|
1201
|
+
resourceId: 'integration-1',
|
|
1202
|
+
type: 'integration' as const,
|
|
1203
|
+
name: 'Test Integration',
|
|
1204
|
+
description: 'Test integration',
|
|
1205
|
+
version: '1.0.0',
|
|
1206
|
+
status: 'dev' as const,
|
|
1207
|
+
provider: 'webhook' as const,
|
|
1208
|
+
credentialName: 'test-cred'
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
const humanCheckpoint = {
|
|
1212
|
+
resourceId: 'human-1',
|
|
1213
|
+
type: 'human' as const,
|
|
1214
|
+
name: 'Approval Point',
|
|
1215
|
+
description: 'Test human checkpoint',
|
|
1216
|
+
version: '1.0.0',
|
|
1217
|
+
status: 'dev' as const,
|
|
1218
|
+
requestedBy: { workflows: ['workflow-1'] },
|
|
1219
|
+
routesTo: { agents: ['agent-1'] }
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
const relationships = {
|
|
1223
|
+
'workflow-1': {
|
|
1224
|
+
uses: { integrations: ['integration-1'] }
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
const registry = new ResourceRegistry({
|
|
1229
|
+
'test-org': {
|
|
1230
|
+
workflows: [createMockWorkflow('workflow-1')],
|
|
1231
|
+
agents: [createMockAgent('agent-1')],
|
|
1232
|
+
triggers: [trigger],
|
|
1233
|
+
integrations: [integration],
|
|
1234
|
+
humanCheckpoints: [humanCheckpoint],
|
|
1235
|
+
relationships
|
|
1236
|
+
}
|
|
1237
|
+
})
|
|
1238
|
+
|
|
1239
|
+
const result = registry.getCommandViewData('test-org')
|
|
1240
|
+
|
|
1241
|
+
// Verify all edge types are valid
|
|
1242
|
+
const edgeTypes = new Set(result.edges.map((e) => e.relationship))
|
|
1243
|
+
expect(edgeTypes.size).toBeGreaterThan(0)
|
|
1244
|
+
for (const edgeType of edgeTypes) {
|
|
1245
|
+
expect(['triggers', 'uses', 'approval']).toContain(edgeType)
|
|
1246
|
+
}
|
|
1247
|
+
})
|
|
1248
|
+
|
|
1249
|
+
it('does NOT return edges with type invokes', () => {
|
|
1250
|
+
const trigger = {
|
|
1251
|
+
resourceId: 'trigger-1',
|
|
1252
|
+
type: 'trigger' as const,
|
|
1253
|
+
triggerType: 'manual' as const,
|
|
1254
|
+
name: 'Manual Trigger',
|
|
1255
|
+
description: 'Test trigger',
|
|
1256
|
+
version: '1.0.0',
|
|
1257
|
+
status: 'dev' as const,
|
|
1258
|
+
triggers: { workflows: ['workflow-1'] }
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
const registry = new ResourceRegistry({
|
|
1262
|
+
'test-org': {
|
|
1263
|
+
workflows: [createMockWorkflow('workflow-1')],
|
|
1264
|
+
triggers: [trigger]
|
|
1265
|
+
}
|
|
1266
|
+
})
|
|
1267
|
+
|
|
1268
|
+
const result = registry.getCommandViewData('test-org')
|
|
1269
|
+
|
|
1270
|
+
// Verify no 'invokes' edge type exists
|
|
1271
|
+
const hasInvokes = result.edges.some((e) => e.relationship === 'invokes')
|
|
1272
|
+
expect(hasInvokes).toBe(false)
|
|
1273
|
+
})
|
|
1274
|
+
|
|
1275
|
+
it('includes all resource types in response', () => {
|
|
1276
|
+
const registry = new ResourceRegistry({
|
|
1277
|
+
'test-org': {
|
|
1278
|
+
workflows: [createMockWorkflow('workflow-1')],
|
|
1279
|
+
agents: [createMockAgent('agent-1')],
|
|
1280
|
+
triggers: [
|
|
1281
|
+
{
|
|
1282
|
+
resourceId: 'trigger-1',
|
|
1283
|
+
type: 'trigger' as const,
|
|
1284
|
+
triggerType: 'manual' as const,
|
|
1285
|
+
name: 'Manual Trigger',
|
|
1286
|
+
description: 'Test trigger',
|
|
1287
|
+
version: '1.0.0',
|
|
1288
|
+
status: 'dev' as const,
|
|
1289
|
+
triggers: { workflows: ['workflow-1'] }
|
|
1290
|
+
}
|
|
1291
|
+
],
|
|
1292
|
+
integrations: [
|
|
1293
|
+
{
|
|
1294
|
+
resourceId: 'integration-1',
|
|
1295
|
+
type: 'integration' as const,
|
|
1296
|
+
name: 'Test Integration',
|
|
1297
|
+
description: 'Test integration',
|
|
1298
|
+
version: '1.0.0',
|
|
1299
|
+
status: 'dev' as const,
|
|
1300
|
+
provider: 'webhook' as const,
|
|
1301
|
+
credentialName: 'test-cred'
|
|
1302
|
+
}
|
|
1303
|
+
],
|
|
1304
|
+
externalResources: [
|
|
1305
|
+
{
|
|
1306
|
+
resourceId: 'external-1',
|
|
1307
|
+
type: 'external' as const,
|
|
1308
|
+
name: 'External Resource',
|
|
1309
|
+
description: 'Test external',
|
|
1310
|
+
version: '1.0.0',
|
|
1311
|
+
status: 'dev' as const,
|
|
1312
|
+
platform: 'n8n' as const
|
|
1313
|
+
}
|
|
1314
|
+
],
|
|
1315
|
+
humanCheckpoints: [
|
|
1316
|
+
{
|
|
1317
|
+
resourceId: 'human-1',
|
|
1318
|
+
type: 'human' as const,
|
|
1319
|
+
name: 'Approval Point',
|
|
1320
|
+
description: 'Test human checkpoint',
|
|
1321
|
+
version: '1.0.0',
|
|
1322
|
+
status: 'dev' as const
|
|
1323
|
+
}
|
|
1324
|
+
]
|
|
1325
|
+
}
|
|
1326
|
+
})
|
|
1327
|
+
|
|
1328
|
+
const result = registry.getCommandViewData('test-org')
|
|
1329
|
+
|
|
1330
|
+
expect(result.workflows).toHaveLength(1)
|
|
1331
|
+
expect(result.agents).toHaveLength(1)
|
|
1332
|
+
expect(result.triggers).toHaveLength(1)
|
|
1333
|
+
expect(result.integrations).toHaveLength(1)
|
|
1334
|
+
expect(result.externalResources).toHaveLength(1)
|
|
1335
|
+
expect(result.humanCheckpoints).toHaveLength(1)
|
|
1336
|
+
expect(result.edges).toBeDefined()
|
|
1337
|
+
})
|
|
1338
|
+
|
|
1339
|
+
it('returns empty data for non-existent organization', () => {
|
|
1340
|
+
const registry = new ResourceRegistry({})
|
|
1341
|
+
|
|
1342
|
+
const result = registry.getCommandViewData('nonexistent-org')
|
|
1343
|
+
|
|
1344
|
+
expect(result).toEqual({
|
|
1345
|
+
workflows: [],
|
|
1346
|
+
agents: [],
|
|
1347
|
+
triggers: [],
|
|
1348
|
+
integrations: [],
|
|
1349
|
+
externalResources: [],
|
|
1350
|
+
humanCheckpoints: [],
|
|
1351
|
+
edges: []
|
|
1352
|
+
})
|
|
1353
|
+
})
|
|
1354
|
+
|
|
1355
|
+
it('returns edges referencing valid resourceIds', () => {
|
|
1356
|
+
const trigger = {
|
|
1357
|
+
resourceId: 'trigger-1',
|
|
1358
|
+
type: 'trigger' as const,
|
|
1359
|
+
triggerType: 'manual' as const,
|
|
1360
|
+
name: 'Manual Trigger',
|
|
1361
|
+
description: 'Test trigger',
|
|
1362
|
+
version: '1.0.0',
|
|
1363
|
+
status: 'dev' as const,
|
|
1364
|
+
triggers: { workflows: ['workflow-1'], agents: ['agent-1'] }
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
const integration = {
|
|
1368
|
+
resourceId: 'integration-1',
|
|
1369
|
+
type: 'integration' as const,
|
|
1370
|
+
name: 'Test Integration',
|
|
1371
|
+
description: 'Test integration',
|
|
1372
|
+
version: '1.0.0',
|
|
1373
|
+
status: 'dev' as const,
|
|
1374
|
+
provider: 'webhook' as const,
|
|
1375
|
+
credentialName: 'test-cred'
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
const relationships = {
|
|
1379
|
+
'workflow-1': {
|
|
1380
|
+
uses: { integrations: ['integration-1'] }
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
const registry = new ResourceRegistry({
|
|
1385
|
+
'test-org': {
|
|
1386
|
+
workflows: [createMockWorkflow('workflow-1')],
|
|
1387
|
+
agents: [createMockAgent('agent-1')],
|
|
1388
|
+
triggers: [trigger],
|
|
1389
|
+
integrations: [integration],
|
|
1390
|
+
relationships
|
|
1391
|
+
}
|
|
1392
|
+
})
|
|
1393
|
+
|
|
1394
|
+
const result = registry.getCommandViewData('test-org')
|
|
1395
|
+
|
|
1396
|
+
// Collect all valid resourceIds
|
|
1397
|
+
const validIds = new Set(['workflow-1', 'agent-1', 'trigger-1', 'integration-1'])
|
|
1398
|
+
|
|
1399
|
+
// Verify all edge sources and targets reference valid resourceIds
|
|
1400
|
+
for (const edge of result.edges) {
|
|
1401
|
+
expect(validIds.has(edge.source) || validIds.has(edge.target)).toBe(true)
|
|
1402
|
+
}
|
|
1403
|
+
})
|
|
1404
|
+
})
|
|
1405
|
+
|
|
1406
|
+
describe('New Field Validation on Construction', () => {
|
|
1407
|
+
it('validates triggers use resourceId (not id)', () => {
|
|
1408
|
+
const agent = createMockAgent('basic-agent')
|
|
1409
|
+
const trigger = {
|
|
1410
|
+
resourceId: 'trigger-1',
|
|
1411
|
+
type: 'trigger' as const,
|
|
1412
|
+
triggerType: 'manual' as const,
|
|
1413
|
+
name: 'Manual Trigger',
|
|
1414
|
+
description: 'Test trigger',
|
|
1415
|
+
version: '1.0.0',
|
|
1416
|
+
status: 'dev' as const,
|
|
1417
|
+
triggers: { agents: ['basic-agent'] }
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
expect(() => {
|
|
1421
|
+
new ResourceRegistry({
|
|
1422
|
+
'test-org': {
|
|
1423
|
+
agents: [agent],
|
|
1424
|
+
triggers: [trigger]
|
|
1425
|
+
}
|
|
1426
|
+
})
|
|
1427
|
+
}).not.toThrow()
|
|
1428
|
+
|
|
1429
|
+
// Verify trigger is stored with resourceId
|
|
1430
|
+
const registry = new ResourceRegistry({
|
|
1431
|
+
'test-org': {
|
|
1432
|
+
agents: [agent],
|
|
1433
|
+
triggers: [trigger]
|
|
1434
|
+
}
|
|
1435
|
+
})
|
|
1436
|
+
const result = registry.getTrigger('test-org', 'trigger-1')
|
|
1437
|
+
expect(result?.resourceId).toBe('trigger-1')
|
|
1438
|
+
})
|
|
1439
|
+
|
|
1440
|
+
it('validates triggers have triggerType field', () => {
|
|
1441
|
+
const workflow = createMockWorkflow('test-workflow')
|
|
1442
|
+
const trigger = {
|
|
1443
|
+
resourceId: 'trigger-1',
|
|
1444
|
+
type: 'trigger' as const,
|
|
1445
|
+
triggerType: 'webhook' as const,
|
|
1446
|
+
name: 'Webhook Trigger',
|
|
1447
|
+
description: 'Test webhook trigger',
|
|
1448
|
+
version: '1.0.0',
|
|
1449
|
+
status: 'dev' as const,
|
|
1450
|
+
webhookPath: '/webhooks/test',
|
|
1451
|
+
triggers: { workflows: ['test-workflow'] }
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
expect(() => {
|
|
1455
|
+
new ResourceRegistry({
|
|
1456
|
+
'test-org': {
|
|
1457
|
+
workflows: [workflow],
|
|
1458
|
+
triggers: [trigger]
|
|
1459
|
+
}
|
|
1460
|
+
})
|
|
1461
|
+
}).not.toThrow()
|
|
1462
|
+
|
|
1463
|
+
const registry = new ResourceRegistry({
|
|
1464
|
+
'test-org': {
|
|
1465
|
+
workflows: [workflow],
|
|
1466
|
+
triggers: [trigger]
|
|
1467
|
+
}
|
|
1468
|
+
})
|
|
1469
|
+
const result = registry.getTrigger('test-org', 'trigger-1')
|
|
1470
|
+
expect(result?.triggerType).toBe('webhook')
|
|
1471
|
+
})
|
|
1472
|
+
|
|
1473
|
+
it('validates integrations have status field', () => {
|
|
1474
|
+
const integration = {
|
|
1475
|
+
resourceId: 'integration-1',
|
|
1476
|
+
type: 'integration' as const,
|
|
1477
|
+
name: 'Test Integration',
|
|
1478
|
+
description: 'Test integration',
|
|
1479
|
+
version: '1.0.0',
|
|
1480
|
+
status: 'prod' as const,
|
|
1481
|
+
provider: 'webhook' as const,
|
|
1482
|
+
credentialName: 'test-cred'
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
expect(() => {
|
|
1486
|
+
new ResourceRegistry({
|
|
1487
|
+
'test-org': { integrations: [integration] }
|
|
1488
|
+
})
|
|
1489
|
+
}).not.toThrow()
|
|
1490
|
+
|
|
1491
|
+
const registry = new ResourceRegistry({
|
|
1492
|
+
'test-org': { integrations: [integration] }
|
|
1493
|
+
})
|
|
1494
|
+
const result = registry.getIntegration('test-org', 'integration-1')
|
|
1495
|
+
expect(result?.status).toBe('prod')
|
|
1496
|
+
})
|
|
1497
|
+
|
|
1498
|
+
it('validates integrations have version field', () => {
|
|
1499
|
+
const integration = {
|
|
1500
|
+
resourceId: 'integration-1',
|
|
1501
|
+
type: 'integration' as const,
|
|
1502
|
+
name: 'Test Integration',
|
|
1503
|
+
description: 'Test integration',
|
|
1504
|
+
version: '2.1.0',
|
|
1505
|
+
status: 'dev' as const,
|
|
1506
|
+
provider: 'webhook' as const,
|
|
1507
|
+
credentialName: 'test-cred'
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
expect(() => {
|
|
1511
|
+
new ResourceRegistry({
|
|
1512
|
+
'test-org': { integrations: [integration] }
|
|
1513
|
+
})
|
|
1514
|
+
}).not.toThrow()
|
|
1515
|
+
|
|
1516
|
+
const registry = new ResourceRegistry({
|
|
1517
|
+
'test-org': { integrations: [integration] }
|
|
1518
|
+
})
|
|
1519
|
+
const result = registry.getIntegration('test-org', 'integration-1')
|
|
1520
|
+
expect(result?.version).toBe('2.1.0')
|
|
1521
|
+
})
|
|
1522
|
+
|
|
1523
|
+
it('validates human checkpoints have description (required)', () => {
|
|
1524
|
+
const humanCheckpoint = {
|
|
1525
|
+
resourceId: 'human-1',
|
|
1526
|
+
type: 'human' as const,
|
|
1527
|
+
name: 'Approval Point',
|
|
1528
|
+
description: 'Human decision point for high-value orders',
|
|
1529
|
+
version: '1.0.0',
|
|
1530
|
+
status: 'dev' as const
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
expect(() => {
|
|
1534
|
+
new ResourceRegistry({
|
|
1535
|
+
'test-org': { humanCheckpoints: [humanCheckpoint] }
|
|
1536
|
+
})
|
|
1537
|
+
}).not.toThrow()
|
|
1538
|
+
|
|
1539
|
+
const registry = new ResourceRegistry({
|
|
1540
|
+
'test-org': { humanCheckpoints: [humanCheckpoint] }
|
|
1541
|
+
})
|
|
1542
|
+
const result = registry.getHumanCheckpoint('test-org', 'human-1')
|
|
1543
|
+
expect(result?.description).toBe('Human decision point for high-value orders')
|
|
1544
|
+
})
|
|
1545
|
+
})
|
|
1546
|
+
|
|
1547
|
+
describe('Runtime Registration', () => {
|
|
1548
|
+
const createMockRemoteConfig = (overrides: Partial<RemoteOrgConfig> = {}): RemoteOrgConfig => ({
|
|
1549
|
+
storagePath: 'test-org/deploy-001/bundle.js',
|
|
1550
|
+
deploymentId: 'deploy-001',
|
|
1551
|
+
...overrides
|
|
1552
|
+
})
|
|
1553
|
+
|
|
1554
|
+
describe('registerOrganization -- merge into existing org', () => {
|
|
1555
|
+
it('registers remote workflows alongside existing static workflows', () => {
|
|
1556
|
+
const staticWorkflow = createMockWorkflow('static-wf')
|
|
1557
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1558
|
+
|
|
1559
|
+
const registry = new ResourceRegistry({
|
|
1560
|
+
'test-org': { workflows: [staticWorkflow] }
|
|
1561
|
+
})
|
|
1562
|
+
|
|
1563
|
+
registry.registerOrganization('test-org', { workflows: [remoteWorkflow] }, createMockRemoteConfig())
|
|
1564
|
+
|
|
1565
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1566
|
+
expect(result.workflows).toHaveLength(2)
|
|
1567
|
+
expect(result.workflows.find((w) => w.resourceId === 'static-wf')).toBeDefined()
|
|
1568
|
+
expect(result.workflows.find((w) => w.resourceId === 'remote-wf')).toBeDefined()
|
|
1569
|
+
})
|
|
1570
|
+
|
|
1571
|
+
it('registers remote agents alongside existing static agents', () => {
|
|
1572
|
+
const staticAgent = createMockAgent('static-agent')
|
|
1573
|
+
const remoteAgent = createMockAgent('remote-agent')
|
|
1574
|
+
|
|
1575
|
+
const registry = new ResourceRegistry({
|
|
1576
|
+
'test-org': { agents: [staticAgent] }
|
|
1577
|
+
})
|
|
1578
|
+
|
|
1579
|
+
registry.registerOrganization('test-org', { agents: [remoteAgent] }, createMockRemoteConfig())
|
|
1580
|
+
|
|
1581
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1582
|
+
expect(result.agents).toHaveLength(2)
|
|
1583
|
+
expect(result.agents.find((a) => a.resourceId === 'static-agent')).toBeDefined()
|
|
1584
|
+
expect(result.agents.find((a) => a.resourceId === 'remote-agent')).toBeDefined()
|
|
1585
|
+
})
|
|
1586
|
+
|
|
1587
|
+
it('marks remote resources with origin "remote" and static resources with origin "local"', () => {
|
|
1588
|
+
const staticWorkflow = createMockWorkflow('static-wf')
|
|
1589
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1590
|
+
|
|
1591
|
+
const registry = new ResourceRegistry({
|
|
1592
|
+
'test-org': { workflows: [staticWorkflow] }
|
|
1593
|
+
})
|
|
1594
|
+
|
|
1595
|
+
registry.registerOrganization('test-org', { workflows: [remoteWorkflow] }, createMockRemoteConfig())
|
|
1596
|
+
|
|
1597
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1598
|
+
expect(result.workflows.find((w) => w.resourceId === 'static-wf')?.origin).toBe('local')
|
|
1599
|
+
expect(result.workflows.find((w) => w.resourceId === 'remote-wf')?.origin).toBe('remote')
|
|
1600
|
+
})
|
|
1601
|
+
|
|
1602
|
+
it('getRemoteConfig returns config for remote resource and null for static resource', () => {
|
|
1603
|
+
const staticWorkflow = createMockWorkflow('static-wf')
|
|
1604
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1605
|
+
const remoteConfig = createMockRemoteConfig({ deploymentId: 'deploy-xyz' })
|
|
1606
|
+
|
|
1607
|
+
const registry = new ResourceRegistry({
|
|
1608
|
+
'test-org': { workflows: [staticWorkflow] }
|
|
1609
|
+
})
|
|
1610
|
+
|
|
1611
|
+
registry.registerOrganization('test-org', { workflows: [remoteWorkflow] }, remoteConfig)
|
|
1612
|
+
|
|
1613
|
+
expect(registry.getRemoteConfig('test-org', 'remote-wf')).toMatchObject({
|
|
1614
|
+
storagePath: 'test-org/deploy-001/bundle.js',
|
|
1615
|
+
deploymentId: 'deploy-xyz'
|
|
1616
|
+
})
|
|
1617
|
+
expect(registry.getRemoteConfig('test-org', 'static-wf')).toBeNull()
|
|
1618
|
+
})
|
|
1619
|
+
|
|
1620
|
+
it('isRemote returns true for org with remote resources', () => {
|
|
1621
|
+
const staticWorkflow = createMockWorkflow('static-wf')
|
|
1622
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1623
|
+
|
|
1624
|
+
const registry = new ResourceRegistry({
|
|
1625
|
+
'test-org': { workflows: [staticWorkflow] }
|
|
1626
|
+
})
|
|
1627
|
+
|
|
1628
|
+
expect(registry.isRemote('test-org')).toBe(false)
|
|
1629
|
+
|
|
1630
|
+
registry.registerOrganization('test-org', { workflows: [remoteWorkflow] }, createMockRemoteConfig())
|
|
1631
|
+
|
|
1632
|
+
expect(registry.isRemote('test-org')).toBe(true)
|
|
1633
|
+
})
|
|
1634
|
+
})
|
|
1635
|
+
|
|
1636
|
+
describe('registerOrganization -- conflict detection', () => {
|
|
1637
|
+
it('throws when remote resourceId collides with an existing static resource', () => {
|
|
1638
|
+
const staticWorkflow = createMockWorkflow('shared-id')
|
|
1639
|
+
const remoteWorkflow = createMockWorkflow('shared-id')
|
|
1640
|
+
|
|
1641
|
+
const registry = new ResourceRegistry({
|
|
1642
|
+
'test-org': { workflows: [staticWorkflow] }
|
|
1643
|
+
})
|
|
1644
|
+
|
|
1645
|
+
expect(() => {
|
|
1646
|
+
registry.registerOrganization('test-org', { workflows: [remoteWorkflow] }, createMockRemoteConfig())
|
|
1647
|
+
}).toThrow("Resource 'shared-id' already exists in 'test-org' as an internal resource")
|
|
1648
|
+
})
|
|
1649
|
+
|
|
1650
|
+
it('throws when deployment contains duplicate resourceIds within itself', () => {
|
|
1651
|
+
const workflow1 = createMockWorkflow('dup-id')
|
|
1652
|
+
const workflow2 = createMockWorkflow('dup-id')
|
|
1653
|
+
|
|
1654
|
+
const registry = new ResourceRegistry({})
|
|
1655
|
+
|
|
1656
|
+
expect(() => {
|
|
1657
|
+
registry.registerOrganization('new-org', { workflows: [workflow1, workflow2] }, createMockRemoteConfig())
|
|
1658
|
+
}).toThrow("Duplicate resource ID 'dup-id' in deployment")
|
|
1659
|
+
})
|
|
1660
|
+
|
|
1661
|
+
it('throws a validation error when incoming relationships reference missing resources after merge', () => {
|
|
1662
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1663
|
+
|
|
1664
|
+
const registry = new ResourceRegistry({
|
|
1665
|
+
'test-org': {
|
|
1666
|
+
workflows: [createMockWorkflow('static-wf')]
|
|
1667
|
+
}
|
|
1668
|
+
})
|
|
1669
|
+
|
|
1670
|
+
expect(() => {
|
|
1671
|
+
registry.registerOrganization(
|
|
1672
|
+
'test-org',
|
|
1673
|
+
{
|
|
1674
|
+
workflows: [remoteWorkflow],
|
|
1675
|
+
relationships: {
|
|
1676
|
+
'remote-wf': {
|
|
1677
|
+
triggers: { workflows: ['missing-target'] }
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
},
|
|
1681
|
+
createMockRemoteConfig()
|
|
1682
|
+
)
|
|
1683
|
+
}).toThrow("[test-org] Resource 'remote-wf' triggers non-existent workflow: missing-target")
|
|
1684
|
+
})
|
|
1685
|
+
})
|
|
1686
|
+
|
|
1687
|
+
describe('registerOrganization -- new org (no static resources)', () => {
|
|
1688
|
+
it('registers an org that does not exist in the static registry', () => {
|
|
1689
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1690
|
+
const remoteAgent = createMockAgent('remote-agent')
|
|
1691
|
+
|
|
1692
|
+
const registry = new ResourceRegistry({})
|
|
1693
|
+
|
|
1694
|
+
registry.registerOrganization(
|
|
1695
|
+
'new-org',
|
|
1696
|
+
{
|
|
1697
|
+
workflows: [remoteWorkflow],
|
|
1698
|
+
agents: [remoteAgent]
|
|
1699
|
+
},
|
|
1700
|
+
createMockRemoteConfig()
|
|
1701
|
+
)
|
|
1702
|
+
|
|
1703
|
+
const result = registry.listResourcesForOrganization('new-org')
|
|
1704
|
+
expect(result.workflows).toHaveLength(1)
|
|
1705
|
+
expect(result.workflows[0].resourceId).toBe('remote-wf')
|
|
1706
|
+
expect(result.agents).toHaveLength(1)
|
|
1707
|
+
expect(result.agents[0].resourceId).toBe('remote-agent')
|
|
1708
|
+
expect(result.total).toBe(2)
|
|
1709
|
+
})
|
|
1710
|
+
|
|
1711
|
+
it('getResourceDefinition finds the registered remote resource', () => {
|
|
1712
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1713
|
+
|
|
1714
|
+
const registry = new ResourceRegistry({})
|
|
1715
|
+
|
|
1716
|
+
registry.registerOrganization('new-org', { workflows: [remoteWorkflow] }, createMockRemoteConfig())
|
|
1717
|
+
|
|
1718
|
+
const definition = registry.getResourceDefinition('new-org', 'remote-wf')
|
|
1719
|
+
expect(definition).not.toBeNull()
|
|
1720
|
+
expect(definition?.config.resourceId).toBe('remote-wf')
|
|
1721
|
+
expect(definition?.config.type).toBe('workflow')
|
|
1722
|
+
})
|
|
1723
|
+
})
|
|
1724
|
+
|
|
1725
|
+
describe('unregisterOrganization -- cleanup', () => {
|
|
1726
|
+
it('remote resources no longer appear in listing after unregister', () => {
|
|
1727
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1728
|
+
|
|
1729
|
+
const registry = new ResourceRegistry({})
|
|
1730
|
+
registry.registerOrganization('test-org', { workflows: [remoteWorkflow] }, createMockRemoteConfig())
|
|
1731
|
+
|
|
1732
|
+
registry.unregisterOrganization('test-org')
|
|
1733
|
+
|
|
1734
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1735
|
+
expect(result.workflows).toHaveLength(0)
|
|
1736
|
+
expect(result.total).toBe(0)
|
|
1737
|
+
})
|
|
1738
|
+
|
|
1739
|
+
it('getRemoteConfig returns null after unregister', () => {
|
|
1740
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1741
|
+
|
|
1742
|
+
const registry = new ResourceRegistry({})
|
|
1743
|
+
registry.registerOrganization('test-org', { workflows: [remoteWorkflow] }, createMockRemoteConfig())
|
|
1744
|
+
|
|
1745
|
+
expect(registry.getRemoteConfig('test-org', 'remote-wf')).not.toBeNull()
|
|
1746
|
+
|
|
1747
|
+
registry.unregisterOrganization('test-org')
|
|
1748
|
+
|
|
1749
|
+
expect(registry.getRemoteConfig('test-org', 'remote-wf')).toBeNull()
|
|
1750
|
+
})
|
|
1751
|
+
|
|
1752
|
+
it('isRemote returns false after unregister', () => {
|
|
1753
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1754
|
+
|
|
1755
|
+
const registry = new ResourceRegistry({})
|
|
1756
|
+
registry.registerOrganization('test-org', { workflows: [remoteWorkflow] }, createMockRemoteConfig())
|
|
1757
|
+
|
|
1758
|
+
expect(registry.isRemote('test-org')).toBe(true)
|
|
1759
|
+
|
|
1760
|
+
registry.unregisterOrganization('test-org')
|
|
1761
|
+
|
|
1762
|
+
expect(registry.isRemote('test-org')).toBe(false)
|
|
1763
|
+
})
|
|
1764
|
+
|
|
1765
|
+
it('static resources survive unregister -- only remote resources are removed', () => {
|
|
1766
|
+
const staticWorkflow = createMockWorkflow('static-wf')
|
|
1767
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1768
|
+
|
|
1769
|
+
const registry = new ResourceRegistry({
|
|
1770
|
+
'test-org': { workflows: [staticWorkflow] }
|
|
1771
|
+
})
|
|
1772
|
+
registry.registerOrganization('test-org', { workflows: [remoteWorkflow] }, createMockRemoteConfig())
|
|
1773
|
+
|
|
1774
|
+
// Both should be present before unregister
|
|
1775
|
+
expect(registry.listResourcesForOrganization('test-org').workflows).toHaveLength(2)
|
|
1776
|
+
|
|
1777
|
+
registry.unregisterOrganization('test-org')
|
|
1778
|
+
|
|
1779
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1780
|
+
expect(result.workflows).toHaveLength(1)
|
|
1781
|
+
expect(result.workflows[0].resourceId).toBe('static-wf')
|
|
1782
|
+
expect(result.workflows[0].origin).toBe('local')
|
|
1783
|
+
})
|
|
1784
|
+
|
|
1785
|
+
it('unregistering an org with no remote resources is a no-op', () => {
|
|
1786
|
+
const staticWorkflow = createMockWorkflow('static-wf')
|
|
1787
|
+
|
|
1788
|
+
const registry = new ResourceRegistry({
|
|
1789
|
+
'test-org': { workflows: [staticWorkflow] }
|
|
1790
|
+
})
|
|
1791
|
+
|
|
1792
|
+
// Should not throw or alter existing resources
|
|
1793
|
+
registry.unregisterOrganization('test-org')
|
|
1794
|
+
|
|
1795
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1796
|
+
expect(result.workflows).toHaveLength(1)
|
|
1797
|
+
expect(result.workflows[0].resourceId).toBe('static-wf')
|
|
1798
|
+
})
|
|
1799
|
+
})
|
|
1800
|
+
|
|
1801
|
+
describe('registerOrganization -- redeploy (register twice)', () => {
|
|
1802
|
+
it('second registerOrganization call replaces previous remote resources', () => {
|
|
1803
|
+
const remoteWorkflowV1 = createMockWorkflow('remote-wf')
|
|
1804
|
+
const remoteWorkflowV2 = createMockWorkflow('remote-wf-v2')
|
|
1805
|
+
|
|
1806
|
+
const registry = new ResourceRegistry({})
|
|
1807
|
+
|
|
1808
|
+
registry.registerOrganization(
|
|
1809
|
+
'test-org',
|
|
1810
|
+
{ workflows: [remoteWorkflowV1] },
|
|
1811
|
+
createMockRemoteConfig({ deploymentId: 'deploy-v1' })
|
|
1812
|
+
)
|
|
1813
|
+
expect(registry.listResourcesForOrganization('test-org').workflows).toHaveLength(1)
|
|
1814
|
+
expect(registry.listResourcesForOrganization('test-org').workflows[0].resourceId).toBe('remote-wf')
|
|
1815
|
+
|
|
1816
|
+
registry.registerOrganization(
|
|
1817
|
+
'test-org',
|
|
1818
|
+
{ workflows: [remoteWorkflowV2] },
|
|
1819
|
+
createMockRemoteConfig({ deploymentId: 'deploy-v2' })
|
|
1820
|
+
)
|
|
1821
|
+
|
|
1822
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1823
|
+
expect(result.workflows).toHaveLength(1)
|
|
1824
|
+
expect(result.workflows[0].resourceId).toBe('remote-wf-v2')
|
|
1825
|
+
})
|
|
1826
|
+
|
|
1827
|
+
it('getRemoteConfig returns the new config after redeploy, not the old one', () => {
|
|
1828
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1829
|
+
|
|
1830
|
+
const registry = new ResourceRegistry({})
|
|
1831
|
+
|
|
1832
|
+
registry.registerOrganization(
|
|
1833
|
+
'test-org',
|
|
1834
|
+
{ workflows: [remoteWorkflow] },
|
|
1835
|
+
createMockRemoteConfig({
|
|
1836
|
+
deploymentId: 'deploy-old',
|
|
1837
|
+
storagePath: 'test-org/deploy-old/bundle.js'
|
|
1838
|
+
})
|
|
1839
|
+
)
|
|
1840
|
+
|
|
1841
|
+
expect(registry.getRemoteConfig('test-org', 'remote-wf')?.deploymentId).toBe('deploy-old')
|
|
1842
|
+
|
|
1843
|
+
// Redeploy with same resource but new config
|
|
1844
|
+
registry.registerOrganization(
|
|
1845
|
+
'test-org',
|
|
1846
|
+
{ workflows: [remoteWorkflow] },
|
|
1847
|
+
createMockRemoteConfig({
|
|
1848
|
+
deploymentId: 'deploy-new',
|
|
1849
|
+
storagePath: 'test-org/deploy-new/bundle.js'
|
|
1850
|
+
})
|
|
1851
|
+
)
|
|
1852
|
+
|
|
1853
|
+
const config = registry.getRemoteConfig('test-org', 'remote-wf')
|
|
1854
|
+
expect(config?.deploymentId).toBe('deploy-new')
|
|
1855
|
+
expect(config?.storagePath).toBe('test-org/deploy-new/bundle.js')
|
|
1856
|
+
})
|
|
1857
|
+
|
|
1858
|
+
it('preserves the current remote deployment when a redeploy fails merged validation', () => {
|
|
1859
|
+
const staticWorkflow = createMockWorkflow('static-wf')
|
|
1860
|
+
const remoteWorkflow = createMockWorkflow('remote-wf')
|
|
1861
|
+
const replacementWorkflow = createMockWorkflow('remote-wf-v2')
|
|
1862
|
+
|
|
1863
|
+
const registry = new ResourceRegistry({
|
|
1864
|
+
'test-org': {
|
|
1865
|
+
workflows: [staticWorkflow]
|
|
1866
|
+
}
|
|
1867
|
+
})
|
|
1868
|
+
|
|
1869
|
+
registry.registerOrganization(
|
|
1870
|
+
'test-org',
|
|
1871
|
+
{
|
|
1872
|
+
workflows: [remoteWorkflow],
|
|
1873
|
+
relationships: {
|
|
1874
|
+
'static-wf': {
|
|
1875
|
+
triggers: { workflows: ['remote-wf'] }
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
},
|
|
1879
|
+
createMockRemoteConfig({ deploymentId: 'deploy-old' })
|
|
1880
|
+
)
|
|
1881
|
+
|
|
1882
|
+
expect(() => {
|
|
1883
|
+
registry.registerOrganization(
|
|
1884
|
+
'test-org',
|
|
1885
|
+
{ workflows: [replacementWorkflow] },
|
|
1886
|
+
createMockRemoteConfig({ deploymentId: 'deploy-new' })
|
|
1887
|
+
)
|
|
1888
|
+
}).toThrow("[test-org] Resource 'static-wf' triggers non-existent workflow: remote-wf")
|
|
1889
|
+
|
|
1890
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1891
|
+
expect(result.workflows.find((w) => w.resourceId === 'remote-wf')).toBeDefined()
|
|
1892
|
+
expect(result.workflows.find((w) => w.resourceId === 'remote-wf-v2')).toBeUndefined()
|
|
1893
|
+
expect(registry.getRemoteConfig('test-org', 'remote-wf')?.deploymentId).toBe('deploy-old')
|
|
1894
|
+
})
|
|
1895
|
+
})
|
|
1896
|
+
|
|
1897
|
+
describe('environment filter with remote resources', () => {
|
|
1898
|
+
it('remote dev resources are filtered out when environment is prod', () => {
|
|
1899
|
+
const staticProdWorkflow = createMockWorkflow('static-prod', 'prod')
|
|
1900
|
+
const remoteDevWorkflow = createMockWorkflow('remote-dev', 'dev')
|
|
1901
|
+
|
|
1902
|
+
const registry = new ResourceRegistry({
|
|
1903
|
+
'test-org': { workflows: [staticProdWorkflow] }
|
|
1904
|
+
})
|
|
1905
|
+
registry.registerOrganization('test-org', { workflows: [remoteDevWorkflow] }, createMockRemoteConfig())
|
|
1906
|
+
|
|
1907
|
+
const result = registry.listResourcesForOrganization('test-org', 'prod')
|
|
1908
|
+
|
|
1909
|
+
expect(result.workflows).toHaveLength(1)
|
|
1910
|
+
expect(result.workflows[0].resourceId).toBe('static-prod')
|
|
1911
|
+
expect(result.workflows.find((w) => w.resourceId === 'remote-dev')).toBeUndefined()
|
|
1912
|
+
})
|
|
1913
|
+
|
|
1914
|
+
it('remote prod resources are included when environment is prod', () => {
|
|
1915
|
+
const staticProdWorkflow = createMockWorkflow('static-prod', 'prod')
|
|
1916
|
+
const remoteProdWorkflow = createMockWorkflow('remote-prod', 'prod')
|
|
1917
|
+
|
|
1918
|
+
const registry = new ResourceRegistry({
|
|
1919
|
+
'test-org': { workflows: [staticProdWorkflow] }
|
|
1920
|
+
})
|
|
1921
|
+
registry.registerOrganization('test-org', { workflows: [remoteProdWorkflow] }, createMockRemoteConfig())
|
|
1922
|
+
|
|
1923
|
+
const result = registry.listResourcesForOrganization('test-org', 'prod')
|
|
1924
|
+
|
|
1925
|
+
expect(result.workflows).toHaveLength(2)
|
|
1926
|
+
expect(result.workflows.find((w) => w.resourceId === 'static-prod')).toBeDefined()
|
|
1927
|
+
expect(result.workflows.find((w) => w.resourceId === 'remote-prod')).toBeDefined()
|
|
1928
|
+
expect(result.workflows.find((w) => w.resourceId === 'remote-prod')?.origin).toBe('remote')
|
|
1929
|
+
})
|
|
1930
|
+
})
|
|
1931
|
+
})
|
|
1932
|
+
|
|
1933
|
+
describe('Archived Resource Filtering', () => {
|
|
1934
|
+
it('excludes archived workflows from registerStaticResources', () => {
|
|
1935
|
+
const activeWorkflow = createMockWorkflow('active-wf')
|
|
1936
|
+
const archivedWorkflow: WorkflowDefinition = {
|
|
1937
|
+
...createMockWorkflow('archived-wf'),
|
|
1938
|
+
config: {
|
|
1939
|
+
...createMockWorkflow('archived-wf').config,
|
|
1940
|
+
archived: true
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
const registry = new ResourceRegistry({})
|
|
1945
|
+
registry.registerStaticResources('test-org', {
|
|
1946
|
+
workflows: [activeWorkflow, archivedWorkflow]
|
|
1947
|
+
})
|
|
1948
|
+
|
|
1949
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1950
|
+
|
|
1951
|
+
expect(result.workflows).toHaveLength(1)
|
|
1952
|
+
expect(result.workflows[0].resourceId).toBe('active-wf')
|
|
1953
|
+
expect(result.total).toBe(1)
|
|
1954
|
+
})
|
|
1955
|
+
|
|
1956
|
+
it('excludes archived agents from registerStaticResources', () => {
|
|
1957
|
+
const activeAgent = createMockAgent('active-agent')
|
|
1958
|
+
const archivedAgent: AgentDefinition = {
|
|
1959
|
+
...createMockAgent('archived-agent'),
|
|
1960
|
+
config: {
|
|
1961
|
+
...createMockAgent('archived-agent').config,
|
|
1962
|
+
archived: true
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
const registry = new ResourceRegistry({})
|
|
1967
|
+
registry.registerStaticResources('test-org', {
|
|
1968
|
+
agents: [activeAgent, archivedAgent]
|
|
1969
|
+
})
|
|
1970
|
+
|
|
1971
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1972
|
+
|
|
1973
|
+
expect(result.agents).toHaveLength(1)
|
|
1974
|
+
expect(result.agents[0].resourceId).toBe('active-agent')
|
|
1975
|
+
expect(result.total).toBe(1)
|
|
1976
|
+
})
|
|
1977
|
+
|
|
1978
|
+
it('includes resources without archived field (backwards compatibility)', () => {
|
|
1979
|
+
const workflow = createMockWorkflow('normal-wf')
|
|
1980
|
+
const agent = createMockAgent('normal-agent')
|
|
1981
|
+
|
|
1982
|
+
// Verify neither has archived set
|
|
1983
|
+
expect(workflow.config).not.toHaveProperty('archived')
|
|
1984
|
+
expect(agent.config).not.toHaveProperty('archived')
|
|
1985
|
+
|
|
1986
|
+
const registry = new ResourceRegistry({})
|
|
1987
|
+
registry.registerStaticResources('test-org', {
|
|
1988
|
+
workflows: [workflow],
|
|
1989
|
+
agents: [agent]
|
|
1990
|
+
})
|
|
1991
|
+
|
|
1992
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
1993
|
+
|
|
1994
|
+
expect(result.workflows).toHaveLength(1)
|
|
1995
|
+
expect(result.agents).toHaveLength(1)
|
|
1996
|
+
expect(result.total).toBe(2)
|
|
1997
|
+
})
|
|
1998
|
+
|
|
1999
|
+
it('excludes archived workflows from registerOrganization', () => {
|
|
2000
|
+
const createMockRemoteConfig = (): RemoteOrgConfig => ({
|
|
2001
|
+
storagePath: 'test-org/deploy-001/bundle.js',
|
|
2002
|
+
deploymentId: 'deploy-001'
|
|
2003
|
+
})
|
|
2004
|
+
|
|
2005
|
+
const activeWorkflow = createMockWorkflow('remote-active-wf')
|
|
2006
|
+
const archivedWorkflow: WorkflowDefinition = {
|
|
2007
|
+
...createMockWorkflow('remote-archived-wf'),
|
|
2008
|
+
config: {
|
|
2009
|
+
...createMockWorkflow('remote-archived-wf').config,
|
|
2010
|
+
archived: true
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
const registry = new ResourceRegistry({})
|
|
2015
|
+
registry.registerOrganization(
|
|
2016
|
+
'test-org',
|
|
2017
|
+
{ workflows: [activeWorkflow, archivedWorkflow] },
|
|
2018
|
+
createMockRemoteConfig()
|
|
2019
|
+
)
|
|
2020
|
+
|
|
2021
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
2022
|
+
|
|
2023
|
+
expect(result.workflows).toHaveLength(1)
|
|
2024
|
+
expect(result.workflows[0].resourceId).toBe('remote-active-wf')
|
|
2025
|
+
})
|
|
2026
|
+
|
|
2027
|
+
it('excludes archived agents from registerOrganization', () => {
|
|
2028
|
+
const createMockRemoteConfig = (): RemoteOrgConfig => ({
|
|
2029
|
+
storagePath: 'test-org/deploy-001/bundle.js',
|
|
2030
|
+
deploymentId: 'deploy-001'
|
|
2031
|
+
})
|
|
2032
|
+
|
|
2033
|
+
const activeAgent = createMockAgent('remote-active-agent')
|
|
2034
|
+
const archivedAgent: AgentDefinition = {
|
|
2035
|
+
...createMockAgent('remote-archived-agent'),
|
|
2036
|
+
config: {
|
|
2037
|
+
...createMockAgent('remote-archived-agent').config,
|
|
2038
|
+
archived: true
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
const registry = new ResourceRegistry({})
|
|
2043
|
+
registry.registerOrganization('test-org', { agents: [activeAgent, archivedAgent] }, createMockRemoteConfig())
|
|
2044
|
+
|
|
2045
|
+
const result = registry.listResourcesForOrganization('test-org')
|
|
2046
|
+
|
|
2047
|
+
expect(result.agents).toHaveLength(1)
|
|
2048
|
+
expect(result.agents[0].resourceId).toBe('remote-active-agent')
|
|
2049
|
+
})
|
|
2050
|
+
})
|
|
2051
|
+
})
|