postgresai 0.14.0 → 0.15.0-dev.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/bin/postgres-ai.ts +712 -108
- package/bun.lock +4 -4
- package/dist/bin/postgres-ai.js +2755 -572
- package/instances.demo.yml +14 -0
- package/lib/checkup-api.ts +25 -6
- package/lib/checkup.ts +465 -8
- package/lib/config.ts +7 -0
- package/lib/init.ts +196 -4
- package/lib/issues.ts +72 -72
- package/lib/mcp-server.ts +90 -0
- package/lib/metrics-loader.ts +6 -1
- package/lib/reports.ts +373 -0
- package/lib/storage.ts +291 -0
- package/lib/supabase.ts +8 -1
- package/lib/util.ts +7 -1
- package/package.json +4 -4
- package/scripts/embed-checkup-dictionary.ts +9 -0
- package/scripts/embed-metrics.ts +5 -0
- package/scripts/generate-release-notes.ts +283 -48
- package/test/PERMISSION_CHECK_TEST_SUMMARY.md +139 -0
- package/test/checkup.test.ts +1316 -2
- package/test/compose-cmd.test.ts +120 -0
- package/test/config-consistency.test.ts +321 -5
- package/test/init.integration.test.ts +27 -28
- package/test/init.test.ts +534 -6
- package/test/issues.cli.test.ts +230 -1
- package/test/mcp-server.test.ts +516 -0
- package/test/monitoring.test.ts +339 -0
- package/test/permission-check-sql.test.ts +116 -0
- package/test/reports.cli.test.ts +793 -0
- package/test/reports.test.ts +977 -0
- package/test/schema-validation.test.ts +81 -0
- package/test/storage.test.ts +761 -0
- package/test/test-utils.ts +51 -2
- package/test/upgrade.test.ts +422 -0
package/test/mcp-server.test.ts
CHANGED
|
@@ -75,8 +75,10 @@ describe("MCP Server", () => {
|
|
|
75
75
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
76
76
|
apiKey: null,
|
|
77
77
|
baseUrl: null,
|
|
78
|
+
storageBaseUrl: null,
|
|
78
79
|
orgId: null,
|
|
79
80
|
defaultProject: null,
|
|
81
|
+
projectName: null,
|
|
80
82
|
});
|
|
81
83
|
|
|
82
84
|
const response = await handleToolCall(createRequest("list_issues"));
|
|
@@ -91,8 +93,10 @@ describe("MCP Server", () => {
|
|
|
91
93
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
92
94
|
apiKey: null,
|
|
93
95
|
baseUrl: null,
|
|
96
|
+
storageBaseUrl: null,
|
|
94
97
|
orgId: null,
|
|
95
98
|
defaultProject: null,
|
|
99
|
+
projectName: null,
|
|
96
100
|
});
|
|
97
101
|
|
|
98
102
|
// Mock fetch to verify API key is used
|
|
@@ -119,8 +123,10 @@ describe("MCP Server", () => {
|
|
|
119
123
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
120
124
|
apiKey: "config-api-key",
|
|
121
125
|
baseUrl: null,
|
|
126
|
+
storageBaseUrl: null,
|
|
122
127
|
orgId: null,
|
|
123
128
|
defaultProject: null,
|
|
129
|
+
projectName: null,
|
|
124
130
|
});
|
|
125
131
|
|
|
126
132
|
let capturedHeaders: Record<string, string> | undefined;
|
|
@@ -148,8 +154,10 @@ describe("MCP Server", () => {
|
|
|
148
154
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
149
155
|
apiKey: null,
|
|
150
156
|
baseUrl: null,
|
|
157
|
+
storageBaseUrl: null,
|
|
151
158
|
orgId: null,
|
|
152
159
|
defaultProject: null,
|
|
160
|
+
projectName: null,
|
|
153
161
|
});
|
|
154
162
|
|
|
155
163
|
let capturedHeaders: Record<string, string> | undefined;
|
|
@@ -182,8 +190,10 @@ describe("MCP Server", () => {
|
|
|
182
190
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
183
191
|
apiKey: "test-key",
|
|
184
192
|
baseUrl: null,
|
|
193
|
+
storageBaseUrl: null,
|
|
185
194
|
orgId: null,
|
|
186
195
|
defaultProject: null,
|
|
196
|
+
projectName: null,
|
|
187
197
|
});
|
|
188
198
|
|
|
189
199
|
globalThis.fetch = mock(() =>
|
|
@@ -209,8 +219,10 @@ describe("MCP Server", () => {
|
|
|
209
219
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
210
220
|
apiKey: "test-key",
|
|
211
221
|
baseUrl: null,
|
|
222
|
+
storageBaseUrl: null,
|
|
212
223
|
orgId: null,
|
|
213
224
|
defaultProject: null,
|
|
225
|
+
projectName: null,
|
|
214
226
|
});
|
|
215
227
|
|
|
216
228
|
globalThis.fetch = mock(() =>
|
|
@@ -236,8 +248,10 @@ describe("MCP Server", () => {
|
|
|
236
248
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
237
249
|
apiKey: "test-key",
|
|
238
250
|
baseUrl: null,
|
|
251
|
+
storageBaseUrl: null,
|
|
239
252
|
orgId: null,
|
|
240
253
|
defaultProject: null,
|
|
254
|
+
projectName: null,
|
|
241
255
|
});
|
|
242
256
|
|
|
243
257
|
const response = await handleToolCall(createRequest("view_issue", { issue_id: "" }));
|
|
@@ -252,8 +266,10 @@ describe("MCP Server", () => {
|
|
|
252
266
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
253
267
|
apiKey: "test-key",
|
|
254
268
|
baseUrl: null,
|
|
269
|
+
storageBaseUrl: null,
|
|
255
270
|
orgId: null,
|
|
256
271
|
defaultProject: null,
|
|
272
|
+
projectName: null,
|
|
257
273
|
});
|
|
258
274
|
|
|
259
275
|
const response = await handleToolCall(createRequest("view_issue", { issue_id: " " }));
|
|
@@ -268,8 +284,10 @@ describe("MCP Server", () => {
|
|
|
268
284
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
269
285
|
apiKey: "test-key",
|
|
270
286
|
baseUrl: null,
|
|
287
|
+
storageBaseUrl: null,
|
|
271
288
|
orgId: null,
|
|
272
289
|
defaultProject: null,
|
|
290
|
+
projectName: null,
|
|
273
291
|
});
|
|
274
292
|
|
|
275
293
|
// Return null for issue (not found)
|
|
@@ -297,8 +315,10 @@ describe("MCP Server", () => {
|
|
|
297
315
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
298
316
|
apiKey: "test-key",
|
|
299
317
|
baseUrl: null,
|
|
318
|
+
storageBaseUrl: null,
|
|
300
319
|
orgId: null,
|
|
301
320
|
defaultProject: null,
|
|
321
|
+
projectName: null,
|
|
302
322
|
});
|
|
303
323
|
|
|
304
324
|
let callCount = 0;
|
|
@@ -337,8 +357,10 @@ describe("MCP Server", () => {
|
|
|
337
357
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
338
358
|
apiKey: "test-key",
|
|
339
359
|
baseUrl: null,
|
|
360
|
+
storageBaseUrl: null,
|
|
340
361
|
orgId: null,
|
|
341
362
|
defaultProject: null,
|
|
363
|
+
projectName: null,
|
|
342
364
|
});
|
|
343
365
|
|
|
344
366
|
const response = await handleToolCall(
|
|
@@ -355,8 +377,10 @@ describe("MCP Server", () => {
|
|
|
355
377
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
356
378
|
apiKey: "test-key",
|
|
357
379
|
baseUrl: null,
|
|
380
|
+
storageBaseUrl: null,
|
|
358
381
|
orgId: null,
|
|
359
382
|
defaultProject: null,
|
|
383
|
+
projectName: null,
|
|
360
384
|
});
|
|
361
385
|
|
|
362
386
|
const response = await handleToolCall(
|
|
@@ -373,8 +397,10 @@ describe("MCP Server", () => {
|
|
|
373
397
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
374
398
|
apiKey: "test-key",
|
|
375
399
|
baseUrl: null,
|
|
400
|
+
storageBaseUrl: null,
|
|
376
401
|
orgId: null,
|
|
377
402
|
defaultProject: null,
|
|
403
|
+
projectName: null,
|
|
378
404
|
});
|
|
379
405
|
|
|
380
406
|
let capturedBody: string | undefined;
|
|
@@ -406,8 +432,10 @@ describe("MCP Server", () => {
|
|
|
406
432
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
407
433
|
apiKey: "test-key",
|
|
408
434
|
baseUrl: null,
|
|
435
|
+
storageBaseUrl: null,
|
|
409
436
|
orgId: null,
|
|
410
437
|
defaultProject: null,
|
|
438
|
+
projectName: null,
|
|
411
439
|
});
|
|
412
440
|
|
|
413
441
|
let capturedBody: string | undefined;
|
|
@@ -443,8 +471,10 @@ describe("MCP Server", () => {
|
|
|
443
471
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
444
472
|
apiKey: "test-key",
|
|
445
473
|
baseUrl: null,
|
|
474
|
+
storageBaseUrl: null,
|
|
446
475
|
orgId: 1,
|
|
447
476
|
defaultProject: null,
|
|
477
|
+
projectName: null,
|
|
448
478
|
});
|
|
449
479
|
|
|
450
480
|
const response = await handleToolCall(createRequest("create_issue", { title: "" }));
|
|
@@ -459,8 +489,10 @@ describe("MCP Server", () => {
|
|
|
459
489
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
460
490
|
apiKey: "test-key",
|
|
461
491
|
baseUrl: null,
|
|
492
|
+
storageBaseUrl: null,
|
|
462
493
|
orgId: 1,
|
|
463
494
|
defaultProject: null,
|
|
495
|
+
projectName: null,
|
|
464
496
|
});
|
|
465
497
|
|
|
466
498
|
const response = await handleToolCall(createRequest("create_issue", { title: " " }));
|
|
@@ -475,8 +507,10 @@ describe("MCP Server", () => {
|
|
|
475
507
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
476
508
|
apiKey: "test-key",
|
|
477
509
|
baseUrl: null,
|
|
510
|
+
storageBaseUrl: null,
|
|
478
511
|
orgId: null,
|
|
479
512
|
defaultProject: null,
|
|
513
|
+
projectName: null,
|
|
480
514
|
});
|
|
481
515
|
|
|
482
516
|
const response = await handleToolCall(createRequest("create_issue", { title: "Test Issue" }));
|
|
@@ -491,8 +525,10 @@ describe("MCP Server", () => {
|
|
|
491
525
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
492
526
|
apiKey: "test-key",
|
|
493
527
|
baseUrl: null,
|
|
528
|
+
storageBaseUrl: null,
|
|
494
529
|
orgId: 42,
|
|
495
530
|
defaultProject: null,
|
|
531
|
+
projectName: null,
|
|
496
532
|
});
|
|
497
533
|
|
|
498
534
|
let capturedBody: string | undefined;
|
|
@@ -519,8 +555,10 @@ describe("MCP Server", () => {
|
|
|
519
555
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
520
556
|
apiKey: "test-key",
|
|
521
557
|
baseUrl: null,
|
|
558
|
+
storageBaseUrl: null,
|
|
522
559
|
orgId: 1,
|
|
523
560
|
defaultProject: null,
|
|
561
|
+
projectName: null,
|
|
524
562
|
});
|
|
525
563
|
|
|
526
564
|
let capturedBody: string | undefined;
|
|
@@ -553,8 +591,10 @@ describe("MCP Server", () => {
|
|
|
553
591
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
554
592
|
apiKey: "test-key",
|
|
555
593
|
baseUrl: null,
|
|
594
|
+
storageBaseUrl: null,
|
|
556
595
|
orgId: null,
|
|
557
596
|
defaultProject: null,
|
|
597
|
+
projectName: null,
|
|
558
598
|
});
|
|
559
599
|
|
|
560
600
|
let capturedBody: string | undefined;
|
|
@@ -596,8 +636,10 @@ describe("MCP Server", () => {
|
|
|
596
636
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
597
637
|
apiKey: "test-key",
|
|
598
638
|
baseUrl: null,
|
|
639
|
+
storageBaseUrl: null,
|
|
599
640
|
orgId: null,
|
|
600
641
|
defaultProject: null,
|
|
642
|
+
projectName: null,
|
|
601
643
|
});
|
|
602
644
|
|
|
603
645
|
const response = await handleToolCall(
|
|
@@ -614,8 +656,10 @@ describe("MCP Server", () => {
|
|
|
614
656
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
615
657
|
apiKey: "test-key",
|
|
616
658
|
baseUrl: null,
|
|
659
|
+
storageBaseUrl: null,
|
|
617
660
|
orgId: null,
|
|
618
661
|
defaultProject: null,
|
|
662
|
+
projectName: null,
|
|
619
663
|
});
|
|
620
664
|
|
|
621
665
|
const response = await handleToolCall(createRequest("update_issue", { issue_id: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" }));
|
|
@@ -630,8 +674,10 @@ describe("MCP Server", () => {
|
|
|
630
674
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
631
675
|
apiKey: "test-key",
|
|
632
676
|
baseUrl: null,
|
|
677
|
+
storageBaseUrl: null,
|
|
633
678
|
orgId: null,
|
|
634
679
|
defaultProject: null,
|
|
680
|
+
projectName: null,
|
|
635
681
|
});
|
|
636
682
|
|
|
637
683
|
const response = await handleToolCall(
|
|
@@ -648,8 +694,10 @@ describe("MCP Server", () => {
|
|
|
648
694
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
649
695
|
apiKey: "test-key",
|
|
650
696
|
baseUrl: null,
|
|
697
|
+
storageBaseUrl: null,
|
|
651
698
|
orgId: null,
|
|
652
699
|
defaultProject: null,
|
|
700
|
+
projectName: null,
|
|
653
701
|
});
|
|
654
702
|
|
|
655
703
|
const response = await handleToolCall(
|
|
@@ -666,8 +714,10 @@ describe("MCP Server", () => {
|
|
|
666
714
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
667
715
|
apiKey: "test-key",
|
|
668
716
|
baseUrl: null,
|
|
717
|
+
storageBaseUrl: null,
|
|
669
718
|
orgId: null,
|
|
670
719
|
defaultProject: null,
|
|
720
|
+
projectName: null,
|
|
671
721
|
});
|
|
672
722
|
|
|
673
723
|
let capturedBody: string | undefined;
|
|
@@ -701,8 +751,10 @@ describe("MCP Server", () => {
|
|
|
701
751
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
702
752
|
apiKey: "test-key",
|
|
703
753
|
baseUrl: null,
|
|
754
|
+
storageBaseUrl: null,
|
|
704
755
|
orgId: null,
|
|
705
756
|
defaultProject: null,
|
|
757
|
+
projectName: null,
|
|
706
758
|
});
|
|
707
759
|
|
|
708
760
|
globalThis.fetch = mock(() =>
|
|
@@ -727,8 +779,10 @@ describe("MCP Server", () => {
|
|
|
727
779
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
728
780
|
apiKey: "test-key",
|
|
729
781
|
baseUrl: null,
|
|
782
|
+
storageBaseUrl: null,
|
|
730
783
|
orgId: null,
|
|
731
784
|
defaultProject: null,
|
|
785
|
+
projectName: null,
|
|
732
786
|
});
|
|
733
787
|
|
|
734
788
|
let capturedBody: string | undefined;
|
|
@@ -758,8 +812,10 @@ describe("MCP Server", () => {
|
|
|
758
812
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
759
813
|
apiKey: "test-key",
|
|
760
814
|
baseUrl: null,
|
|
815
|
+
storageBaseUrl: null,
|
|
761
816
|
orgId: null,
|
|
762
817
|
defaultProject: null,
|
|
818
|
+
projectName: null,
|
|
763
819
|
});
|
|
764
820
|
|
|
765
821
|
let capturedBody: string | undefined;
|
|
@@ -789,8 +845,10 @@ describe("MCP Server", () => {
|
|
|
789
845
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
790
846
|
apiKey: "test-key",
|
|
791
847
|
baseUrl: null,
|
|
848
|
+
storageBaseUrl: null,
|
|
792
849
|
orgId: null,
|
|
793
850
|
defaultProject: null,
|
|
851
|
+
projectName: null,
|
|
794
852
|
});
|
|
795
853
|
|
|
796
854
|
let capturedBody: string | undefined;
|
|
@@ -822,8 +880,10 @@ describe("MCP Server", () => {
|
|
|
822
880
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
823
881
|
apiKey: "test-key",
|
|
824
882
|
baseUrl: null,
|
|
883
|
+
storageBaseUrl: null,
|
|
825
884
|
orgId: null,
|
|
826
885
|
defaultProject: null,
|
|
886
|
+
projectName: null,
|
|
827
887
|
});
|
|
828
888
|
|
|
829
889
|
const response = await handleToolCall(
|
|
@@ -840,8 +900,10 @@ describe("MCP Server", () => {
|
|
|
840
900
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
841
901
|
apiKey: "test-key",
|
|
842
902
|
baseUrl: null,
|
|
903
|
+
storageBaseUrl: null,
|
|
843
904
|
orgId: null,
|
|
844
905
|
defaultProject: null,
|
|
906
|
+
projectName: null,
|
|
845
907
|
});
|
|
846
908
|
|
|
847
909
|
const response = await handleToolCall(
|
|
@@ -858,8 +920,10 @@ describe("MCP Server", () => {
|
|
|
858
920
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
859
921
|
apiKey: "test-key",
|
|
860
922
|
baseUrl: null,
|
|
923
|
+
storageBaseUrl: null,
|
|
861
924
|
orgId: null,
|
|
862
925
|
defaultProject: null,
|
|
926
|
+
projectName: null,
|
|
863
927
|
});
|
|
864
928
|
|
|
865
929
|
let capturedBody: string | undefined;
|
|
@@ -891,8 +955,10 @@ describe("MCP Server", () => {
|
|
|
891
955
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
892
956
|
apiKey: "test-key",
|
|
893
957
|
baseUrl: null,
|
|
958
|
+
storageBaseUrl: null,
|
|
894
959
|
orgId: null,
|
|
895
960
|
defaultProject: null,
|
|
961
|
+
projectName: null,
|
|
896
962
|
});
|
|
897
963
|
|
|
898
964
|
globalThis.fetch = mock(() =>
|
|
@@ -924,8 +990,10 @@ describe("MCP Server", () => {
|
|
|
924
990
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
925
991
|
apiKey: "test-key",
|
|
926
992
|
baseUrl: null,
|
|
993
|
+
storageBaseUrl: null,
|
|
927
994
|
orgId: null,
|
|
928
995
|
defaultProject: null,
|
|
996
|
+
projectName: null,
|
|
929
997
|
});
|
|
930
998
|
|
|
931
999
|
const response = await handleToolCall(createRequest("view_action_item", {}));
|
|
@@ -940,8 +1008,10 @@ describe("MCP Server", () => {
|
|
|
940
1008
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
941
1009
|
apiKey: "test-key",
|
|
942
1010
|
baseUrl: null,
|
|
1011
|
+
storageBaseUrl: null,
|
|
943
1012
|
orgId: null,
|
|
944
1013
|
defaultProject: null,
|
|
1014
|
+
projectName: null,
|
|
945
1015
|
});
|
|
946
1016
|
|
|
947
1017
|
const response = await handleToolCall(createRequest("view_action_item", { action_item_id: "" }));
|
|
@@ -956,8 +1026,10 @@ describe("MCP Server", () => {
|
|
|
956
1026
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
957
1027
|
apiKey: "test-key",
|
|
958
1028
|
baseUrl: null,
|
|
1029
|
+
storageBaseUrl: null,
|
|
959
1030
|
orgId: null,
|
|
960
1031
|
defaultProject: null,
|
|
1032
|
+
projectName: null,
|
|
961
1033
|
});
|
|
962
1034
|
|
|
963
1035
|
const response = await handleToolCall(createRequest("view_action_item", { action_item_ids: [] }));
|
|
@@ -972,8 +1044,10 @@ describe("MCP Server", () => {
|
|
|
972
1044
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
973
1045
|
apiKey: "test-key",
|
|
974
1046
|
baseUrl: null,
|
|
1047
|
+
storageBaseUrl: null,
|
|
975
1048
|
orgId: null,
|
|
976
1049
|
defaultProject: null,
|
|
1050
|
+
projectName: null,
|
|
977
1051
|
});
|
|
978
1052
|
|
|
979
1053
|
const response = await handleToolCall(createRequest("view_action_item", { action_item_id: "invalid-id-format" }));
|
|
@@ -988,8 +1062,10 @@ describe("MCP Server", () => {
|
|
|
988
1062
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
989
1063
|
apiKey: "test-key",
|
|
990
1064
|
baseUrl: null,
|
|
1065
|
+
storageBaseUrl: null,
|
|
991
1066
|
orgId: null,
|
|
992
1067
|
defaultProject: null,
|
|
1068
|
+
projectName: null,
|
|
993
1069
|
});
|
|
994
1070
|
|
|
995
1071
|
globalThis.fetch = mock(() =>
|
|
@@ -1025,8 +1101,10 @@ describe("MCP Server", () => {
|
|
|
1025
1101
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1026
1102
|
apiKey: "test-key",
|
|
1027
1103
|
baseUrl: null,
|
|
1104
|
+
storageBaseUrl: null,
|
|
1028
1105
|
orgId: null,
|
|
1029
1106
|
defaultProject: null,
|
|
1107
|
+
projectName: null,
|
|
1030
1108
|
});
|
|
1031
1109
|
|
|
1032
1110
|
globalThis.fetch = mock(() =>
|
|
@@ -1059,8 +1137,10 @@ describe("MCP Server", () => {
|
|
|
1059
1137
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1060
1138
|
apiKey: "test-key",
|
|
1061
1139
|
baseUrl: null,
|
|
1140
|
+
storageBaseUrl: null,
|
|
1062
1141
|
orgId: null,
|
|
1063
1142
|
defaultProject: null,
|
|
1143
|
+
projectName: null,
|
|
1064
1144
|
});
|
|
1065
1145
|
|
|
1066
1146
|
let capturedUrl: string | undefined;
|
|
@@ -1093,8 +1173,10 @@ describe("MCP Server", () => {
|
|
|
1093
1173
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1094
1174
|
apiKey: "test-key",
|
|
1095
1175
|
baseUrl: null,
|
|
1176
|
+
storageBaseUrl: null,
|
|
1096
1177
|
orgId: null,
|
|
1097
1178
|
defaultProject: null,
|
|
1179
|
+
projectName: null,
|
|
1098
1180
|
});
|
|
1099
1181
|
|
|
1100
1182
|
const response = await handleToolCall(createRequest("list_action_items", { issue_id: "" }));
|
|
@@ -1109,8 +1191,10 @@ describe("MCP Server", () => {
|
|
|
1109
1191
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1110
1192
|
apiKey: "test-key",
|
|
1111
1193
|
baseUrl: null,
|
|
1194
|
+
storageBaseUrl: null,
|
|
1112
1195
|
orgId: null,
|
|
1113
1196
|
defaultProject: null,
|
|
1197
|
+
projectName: null,
|
|
1114
1198
|
});
|
|
1115
1199
|
|
|
1116
1200
|
const response = await handleToolCall(createRequest("list_action_items", { issue_id: " " }));
|
|
@@ -1130,8 +1214,10 @@ describe("MCP Server", () => {
|
|
|
1130
1214
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1131
1215
|
apiKey: "test-key",
|
|
1132
1216
|
baseUrl: null,
|
|
1217
|
+
storageBaseUrl: null,
|
|
1133
1218
|
orgId: null,
|
|
1134
1219
|
defaultProject: null,
|
|
1220
|
+
projectName: null,
|
|
1135
1221
|
});
|
|
1136
1222
|
|
|
1137
1223
|
globalThis.fetch = mock(() =>
|
|
@@ -1159,8 +1245,10 @@ describe("MCP Server", () => {
|
|
|
1159
1245
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1160
1246
|
apiKey: "test-key",
|
|
1161
1247
|
baseUrl: null,
|
|
1248
|
+
storageBaseUrl: null,
|
|
1162
1249
|
orgId: null,
|
|
1163
1250
|
defaultProject: null,
|
|
1251
|
+
projectName: null,
|
|
1164
1252
|
});
|
|
1165
1253
|
|
|
1166
1254
|
const response = await handleToolCall(
|
|
@@ -1177,8 +1265,10 @@ describe("MCP Server", () => {
|
|
|
1177
1265
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1178
1266
|
apiKey: "test-key",
|
|
1179
1267
|
baseUrl: null,
|
|
1268
|
+
storageBaseUrl: null,
|
|
1180
1269
|
orgId: null,
|
|
1181
1270
|
defaultProject: null,
|
|
1271
|
+
projectName: null,
|
|
1182
1272
|
});
|
|
1183
1273
|
|
|
1184
1274
|
const response = await handleToolCall(
|
|
@@ -1195,8 +1285,10 @@ describe("MCP Server", () => {
|
|
|
1195
1285
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1196
1286
|
apiKey: "test-key",
|
|
1197
1287
|
baseUrl: null,
|
|
1288
|
+
storageBaseUrl: null,
|
|
1198
1289
|
orgId: null,
|
|
1199
1290
|
defaultProject: null,
|
|
1291
|
+
projectName: null,
|
|
1200
1292
|
});
|
|
1201
1293
|
|
|
1202
1294
|
let capturedBody: string | undefined;
|
|
@@ -1230,8 +1322,10 @@ describe("MCP Server", () => {
|
|
|
1230
1322
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1231
1323
|
apiKey: "test-key",
|
|
1232
1324
|
baseUrl: null,
|
|
1325
|
+
storageBaseUrl: null,
|
|
1233
1326
|
orgId: null,
|
|
1234
1327
|
defaultProject: null,
|
|
1328
|
+
projectName: null,
|
|
1235
1329
|
});
|
|
1236
1330
|
|
|
1237
1331
|
let capturedBody: string | undefined;
|
|
@@ -1271,8 +1365,10 @@ describe("MCP Server", () => {
|
|
|
1271
1365
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1272
1366
|
apiKey: "test-key",
|
|
1273
1367
|
baseUrl: null,
|
|
1368
|
+
storageBaseUrl: null,
|
|
1274
1369
|
orgId: null,
|
|
1275
1370
|
defaultProject: null,
|
|
1371
|
+
projectName: null,
|
|
1276
1372
|
});
|
|
1277
1373
|
|
|
1278
1374
|
let capturedBody: string | undefined;
|
|
@@ -1308,8 +1404,10 @@ describe("MCP Server", () => {
|
|
|
1308
1404
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1309
1405
|
apiKey: "test-key",
|
|
1310
1406
|
baseUrl: null,
|
|
1407
|
+
storageBaseUrl: null,
|
|
1311
1408
|
orgId: null,
|
|
1312
1409
|
defaultProject: null,
|
|
1410
|
+
projectName: null,
|
|
1313
1411
|
});
|
|
1314
1412
|
|
|
1315
1413
|
const response = await handleToolCall(
|
|
@@ -1326,8 +1424,10 @@ describe("MCP Server", () => {
|
|
|
1326
1424
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1327
1425
|
apiKey: "test-key",
|
|
1328
1426
|
baseUrl: null,
|
|
1427
|
+
storageBaseUrl: null,
|
|
1329
1428
|
orgId: null,
|
|
1330
1429
|
defaultProject: null,
|
|
1430
|
+
projectName: null,
|
|
1331
1431
|
});
|
|
1332
1432
|
|
|
1333
1433
|
const response = await handleToolCall(
|
|
@@ -1344,8 +1444,10 @@ describe("MCP Server", () => {
|
|
|
1344
1444
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1345
1445
|
apiKey: "test-key",
|
|
1346
1446
|
baseUrl: null,
|
|
1447
|
+
storageBaseUrl: null,
|
|
1347
1448
|
orgId: null,
|
|
1348
1449
|
defaultProject: null,
|
|
1450
|
+
projectName: null,
|
|
1349
1451
|
});
|
|
1350
1452
|
|
|
1351
1453
|
const response = await handleToolCall(
|
|
@@ -1362,8 +1464,10 @@ describe("MCP Server", () => {
|
|
|
1362
1464
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1363
1465
|
apiKey: "test-key",
|
|
1364
1466
|
baseUrl: null,
|
|
1467
|
+
storageBaseUrl: null,
|
|
1365
1468
|
orgId: null,
|
|
1366
1469
|
defaultProject: null,
|
|
1470
|
+
projectName: null,
|
|
1367
1471
|
});
|
|
1368
1472
|
|
|
1369
1473
|
let capturedBody: string | undefined;
|
|
@@ -1394,8 +1498,10 @@ describe("MCP Server", () => {
|
|
|
1394
1498
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1395
1499
|
apiKey: "test-key",
|
|
1396
1500
|
baseUrl: null,
|
|
1501
|
+
storageBaseUrl: null,
|
|
1397
1502
|
orgId: null,
|
|
1398
1503
|
defaultProject: null,
|
|
1504
|
+
projectName: null,
|
|
1399
1505
|
});
|
|
1400
1506
|
|
|
1401
1507
|
let capturedBody: string | undefined;
|
|
@@ -1425,8 +1531,10 @@ describe("MCP Server", () => {
|
|
|
1425
1531
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1426
1532
|
apiKey: "test-key",
|
|
1427
1533
|
baseUrl: null,
|
|
1534
|
+
storageBaseUrl: null,
|
|
1428
1535
|
orgId: null,
|
|
1429
1536
|
defaultProject: null,
|
|
1537
|
+
projectName: null,
|
|
1430
1538
|
});
|
|
1431
1539
|
|
|
1432
1540
|
let capturedBody: string | undefined;
|
|
@@ -1463,8 +1571,10 @@ describe("MCP Server", () => {
|
|
|
1463
1571
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1464
1572
|
apiKey: "test-key",
|
|
1465
1573
|
baseUrl: null,
|
|
1574
|
+
storageBaseUrl: null,
|
|
1466
1575
|
orgId: null,
|
|
1467
1576
|
defaultProject: null,
|
|
1577
|
+
projectName: null,
|
|
1468
1578
|
});
|
|
1469
1579
|
|
|
1470
1580
|
const response = await handleToolCall(createRequest("nonexistent_tool"));
|
|
@@ -1476,13 +1586,417 @@ describe("MCP Server", () => {
|
|
|
1476
1586
|
});
|
|
1477
1587
|
});
|
|
1478
1588
|
|
|
1589
|
+
describe("list_reports tool", () => {
|
|
1590
|
+
test("successfully returns reports list as JSON", async () => {
|
|
1591
|
+
const mockReports = [
|
|
1592
|
+
{ id: 1, org_id: 1, org_name: "TestOrg", project_id: 10, project_name: "prod-db", status: "completed" },
|
|
1593
|
+
];
|
|
1594
|
+
|
|
1595
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1596
|
+
apiKey: "test-key",
|
|
1597
|
+
baseUrl: null,
|
|
1598
|
+
storageBaseUrl: null,
|
|
1599
|
+
orgId: null,
|
|
1600
|
+
defaultProject: null,
|
|
1601
|
+
projectName: null,
|
|
1602
|
+
});
|
|
1603
|
+
|
|
1604
|
+
globalThis.fetch = mock(() =>
|
|
1605
|
+
Promise.resolve(
|
|
1606
|
+
new Response(JSON.stringify(mockReports), {
|
|
1607
|
+
status: 200,
|
|
1608
|
+
headers: { "Content-Type": "application/json" },
|
|
1609
|
+
})
|
|
1610
|
+
)
|
|
1611
|
+
) as unknown as typeof fetch;
|
|
1612
|
+
|
|
1613
|
+
const response = await handleToolCall(createRequest("list_reports"));
|
|
1614
|
+
|
|
1615
|
+
expect(response.isError).toBeUndefined();
|
|
1616
|
+
const parsed = JSON.parse(getResponseText(response));
|
|
1617
|
+
expect(parsed).toHaveLength(1);
|
|
1618
|
+
expect(parsed[0].status).toBe("completed");
|
|
1619
|
+
|
|
1620
|
+
readConfigSpy.mockRestore();
|
|
1621
|
+
});
|
|
1622
|
+
|
|
1623
|
+
test("passes filters to API", async () => {
|
|
1624
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1625
|
+
apiKey: "test-key",
|
|
1626
|
+
baseUrl: null,
|
|
1627
|
+
storageBaseUrl: null,
|
|
1628
|
+
orgId: null,
|
|
1629
|
+
defaultProject: null,
|
|
1630
|
+
projectName: null,
|
|
1631
|
+
});
|
|
1632
|
+
|
|
1633
|
+
let capturedUrl: string | undefined;
|
|
1634
|
+
globalThis.fetch = mock((url: string) => {
|
|
1635
|
+
capturedUrl = url;
|
|
1636
|
+
return Promise.resolve(
|
|
1637
|
+
new Response(JSON.stringify([]), {
|
|
1638
|
+
status: 200,
|
|
1639
|
+
headers: { "Content-Type": "application/json" },
|
|
1640
|
+
})
|
|
1641
|
+
);
|
|
1642
|
+
}) as unknown as typeof fetch;
|
|
1643
|
+
|
|
1644
|
+
await handleToolCall(createRequest("list_reports", {
|
|
1645
|
+
project_id: 5,
|
|
1646
|
+
status: "completed",
|
|
1647
|
+
limit: 10,
|
|
1648
|
+
}));
|
|
1649
|
+
|
|
1650
|
+
expect(capturedUrl).toContain("project_id=eq.5");
|
|
1651
|
+
expect(capturedUrl).toContain("status=eq.completed");
|
|
1652
|
+
expect(capturedUrl).toContain("limit=10");
|
|
1653
|
+
|
|
1654
|
+
readConfigSpy.mockRestore();
|
|
1655
|
+
});
|
|
1656
|
+
|
|
1657
|
+
test("handles API errors gracefully", async () => {
|
|
1658
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1659
|
+
apiKey: "test-key",
|
|
1660
|
+
baseUrl: null,
|
|
1661
|
+
storageBaseUrl: null,
|
|
1662
|
+
orgId: null,
|
|
1663
|
+
defaultProject: null,
|
|
1664
|
+
projectName: null,
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1667
|
+
globalThis.fetch = mock(() =>
|
|
1668
|
+
Promise.resolve(
|
|
1669
|
+
new Response('{"message": "Unauthorized"}', {
|
|
1670
|
+
status: 401,
|
|
1671
|
+
headers: { "Content-Type": "application/json" },
|
|
1672
|
+
})
|
|
1673
|
+
)
|
|
1674
|
+
) as unknown as typeof fetch;
|
|
1675
|
+
|
|
1676
|
+
const response = await handleToolCall(createRequest("list_reports"));
|
|
1677
|
+
|
|
1678
|
+
expect(response.isError).toBe(true);
|
|
1679
|
+
expect(getResponseText(response)).toContain("401");
|
|
1680
|
+
|
|
1681
|
+
readConfigSpy.mockRestore();
|
|
1682
|
+
});
|
|
1683
|
+
|
|
1684
|
+
test("passes before_date to API as created_at filter", async () => {
|
|
1685
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1686
|
+
apiKey: "test-key",
|
|
1687
|
+
baseUrl: null,
|
|
1688
|
+
storageBaseUrl: null,
|
|
1689
|
+
orgId: null,
|
|
1690
|
+
defaultProject: null,
|
|
1691
|
+
projectName: null,
|
|
1692
|
+
});
|
|
1693
|
+
|
|
1694
|
+
let capturedUrl: string | undefined;
|
|
1695
|
+
globalThis.fetch = mock((url: string) => {
|
|
1696
|
+
capturedUrl = url;
|
|
1697
|
+
return Promise.resolve(
|
|
1698
|
+
new Response(JSON.stringify([]), {
|
|
1699
|
+
status: 200,
|
|
1700
|
+
headers: { "Content-Type": "application/json" },
|
|
1701
|
+
})
|
|
1702
|
+
);
|
|
1703
|
+
}) as unknown as typeof fetch;
|
|
1704
|
+
|
|
1705
|
+
await handleToolCall(createRequest("list_reports", {
|
|
1706
|
+
before_date: "2025-01-15",
|
|
1707
|
+
}));
|
|
1708
|
+
|
|
1709
|
+
expect(capturedUrl).toContain("created_at=lt.2025-01-15");
|
|
1710
|
+
|
|
1711
|
+
readConfigSpy.mockRestore();
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1714
|
+
test("fetches all reports when all=true", async () => {
|
|
1715
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1716
|
+
apiKey: "test-key",
|
|
1717
|
+
baseUrl: null,
|
|
1718
|
+
storageBaseUrl: null,
|
|
1719
|
+
orgId: null,
|
|
1720
|
+
defaultProject: null,
|
|
1721
|
+
projectName: null,
|
|
1722
|
+
});
|
|
1723
|
+
|
|
1724
|
+
const mockReports = [
|
|
1725
|
+
{ id: 10, org_id: 1, org_name: "O", project_id: 1, project_name: "P", status: "completed" },
|
|
1726
|
+
];
|
|
1727
|
+
|
|
1728
|
+
globalThis.fetch = mock(() =>
|
|
1729
|
+
Promise.resolve(
|
|
1730
|
+
new Response(JSON.stringify(mockReports), {
|
|
1731
|
+
status: 200,
|
|
1732
|
+
headers: { "Content-Type": "application/json" },
|
|
1733
|
+
})
|
|
1734
|
+
)
|
|
1735
|
+
) as unknown as typeof fetch;
|
|
1736
|
+
|
|
1737
|
+
const response = await handleToolCall(createRequest("list_reports", {
|
|
1738
|
+
all: true,
|
|
1739
|
+
}));
|
|
1740
|
+
|
|
1741
|
+
expect(response.isError).toBeUndefined();
|
|
1742
|
+
const parsed = JSON.parse(getResponseText(response));
|
|
1743
|
+
expect(parsed).toHaveLength(1);
|
|
1744
|
+
expect(parsed[0].id).toBe(10);
|
|
1745
|
+
|
|
1746
|
+
readConfigSpy.mockRestore();
|
|
1747
|
+
});
|
|
1748
|
+
});
|
|
1749
|
+
|
|
1750
|
+
describe("list_report_files tool", () => {
|
|
1751
|
+
test("returns error when neither report_id nor check_id is provided", async () => {
|
|
1752
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1753
|
+
apiKey: "test-key",
|
|
1754
|
+
baseUrl: null,
|
|
1755
|
+
storageBaseUrl: null,
|
|
1756
|
+
orgId: null,
|
|
1757
|
+
defaultProject: null,
|
|
1758
|
+
projectName: null,
|
|
1759
|
+
});
|
|
1760
|
+
|
|
1761
|
+
const response = await handleToolCall(createRequest("list_report_files", {}));
|
|
1762
|
+
|
|
1763
|
+
expect(response.isError).toBe(true);
|
|
1764
|
+
expect(getResponseText(response)).toContain("Either report_id or check_id is required");
|
|
1765
|
+
|
|
1766
|
+
readConfigSpy.mockRestore();
|
|
1767
|
+
});
|
|
1768
|
+
|
|
1769
|
+
test("works with only check_id (no report_id)", async () => {
|
|
1770
|
+
const mockFiles = [
|
|
1771
|
+
{ id: 100, checkup_report_id: 1, filename: "H002.md", check_id: "H002", type: "md" },
|
|
1772
|
+
];
|
|
1773
|
+
|
|
1774
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1775
|
+
apiKey: "test-key",
|
|
1776
|
+
baseUrl: null,
|
|
1777
|
+
storageBaseUrl: null,
|
|
1778
|
+
orgId: null,
|
|
1779
|
+
defaultProject: null,
|
|
1780
|
+
projectName: null,
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1783
|
+
let capturedUrl: string | undefined;
|
|
1784
|
+
globalThis.fetch = mock((url: string) => {
|
|
1785
|
+
capturedUrl = url;
|
|
1786
|
+
return Promise.resolve(
|
|
1787
|
+
new Response(JSON.stringify(mockFiles), {
|
|
1788
|
+
status: 200,
|
|
1789
|
+
headers: { "Content-Type": "application/json" },
|
|
1790
|
+
})
|
|
1791
|
+
);
|
|
1792
|
+
}) as unknown as typeof fetch;
|
|
1793
|
+
|
|
1794
|
+
const response = await handleToolCall(createRequest("list_report_files", {
|
|
1795
|
+
check_id: "H002",
|
|
1796
|
+
}));
|
|
1797
|
+
|
|
1798
|
+
expect(response.isError).toBeUndefined();
|
|
1799
|
+
const parsed = JSON.parse(getResponseText(response));
|
|
1800
|
+
expect(parsed[0].filename).toBe("H002.md");
|
|
1801
|
+
expect(capturedUrl).toContain("check_id=eq.H002");
|
|
1802
|
+
expect(capturedUrl).not.toContain("checkup_report_id");
|
|
1803
|
+
|
|
1804
|
+
readConfigSpy.mockRestore();
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1807
|
+
test("successfully returns report files", async () => {
|
|
1808
|
+
const mockFiles = [
|
|
1809
|
+
{ id: 100, checkup_report_id: 1, filename: "H002.md", check_id: "H002", type: "md" },
|
|
1810
|
+
];
|
|
1811
|
+
|
|
1812
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1813
|
+
apiKey: "test-key",
|
|
1814
|
+
baseUrl: null,
|
|
1815
|
+
storageBaseUrl: null,
|
|
1816
|
+
orgId: null,
|
|
1817
|
+
defaultProject: null,
|
|
1818
|
+
projectName: null,
|
|
1819
|
+
});
|
|
1820
|
+
|
|
1821
|
+
let capturedUrl: string | undefined;
|
|
1822
|
+
globalThis.fetch = mock((url: string) => {
|
|
1823
|
+
capturedUrl = url;
|
|
1824
|
+
return Promise.resolve(
|
|
1825
|
+
new Response(JSON.stringify(mockFiles), {
|
|
1826
|
+
status: 200,
|
|
1827
|
+
headers: { "Content-Type": "application/json" },
|
|
1828
|
+
})
|
|
1829
|
+
);
|
|
1830
|
+
}) as unknown as typeof fetch;
|
|
1831
|
+
|
|
1832
|
+
const response = await handleToolCall(createRequest("list_report_files", {
|
|
1833
|
+
report_id: 1,
|
|
1834
|
+
type: "md",
|
|
1835
|
+
check_id: "H002",
|
|
1836
|
+
}));
|
|
1837
|
+
|
|
1838
|
+
expect(response.isError).toBeUndefined();
|
|
1839
|
+
const parsed = JSON.parse(getResponseText(response));
|
|
1840
|
+
expect(parsed[0].filename).toBe("H002.md");
|
|
1841
|
+
expect(capturedUrl).toContain("checkup_report_id=eq.1");
|
|
1842
|
+
expect(capturedUrl).toContain("type=eq.md");
|
|
1843
|
+
expect(capturedUrl).toContain("check_id=eq.H002");
|
|
1844
|
+
|
|
1845
|
+
readConfigSpy.mockRestore();
|
|
1846
|
+
});
|
|
1847
|
+
});
|
|
1848
|
+
|
|
1849
|
+
describe("get_report_data tool", () => {
|
|
1850
|
+
test("returns error when neither report_id nor check_id is provided", async () => {
|
|
1851
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1852
|
+
apiKey: "test-key",
|
|
1853
|
+
baseUrl: null,
|
|
1854
|
+
storageBaseUrl: null,
|
|
1855
|
+
orgId: null,
|
|
1856
|
+
defaultProject: null,
|
|
1857
|
+
projectName: null,
|
|
1858
|
+
});
|
|
1859
|
+
|
|
1860
|
+
const response = await handleToolCall(createRequest("get_report_data", {}));
|
|
1861
|
+
|
|
1862
|
+
expect(response.isError).toBe(true);
|
|
1863
|
+
expect(getResponseText(response)).toContain("Either report_id or check_id is required");
|
|
1864
|
+
|
|
1865
|
+
readConfigSpy.mockRestore();
|
|
1866
|
+
});
|
|
1867
|
+
|
|
1868
|
+
test("works with only check_id (no report_id)", async () => {
|
|
1869
|
+
const mockData = [
|
|
1870
|
+
{
|
|
1871
|
+
id: 100,
|
|
1872
|
+
checkup_report_id: 1,
|
|
1873
|
+
filename: "H002.md",
|
|
1874
|
+
check_id: "H002",
|
|
1875
|
+
type: "md",
|
|
1876
|
+
data: "# H002\n\nUnused indexes found.",
|
|
1877
|
+
},
|
|
1878
|
+
];
|
|
1879
|
+
|
|
1880
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1881
|
+
apiKey: "test-key",
|
|
1882
|
+
baseUrl: null,
|
|
1883
|
+
storageBaseUrl: null,
|
|
1884
|
+
orgId: null,
|
|
1885
|
+
defaultProject: null,
|
|
1886
|
+
projectName: null,
|
|
1887
|
+
});
|
|
1888
|
+
|
|
1889
|
+
let capturedUrl: string | undefined;
|
|
1890
|
+
globalThis.fetch = mock((url: string) => {
|
|
1891
|
+
capturedUrl = url;
|
|
1892
|
+
return Promise.resolve(
|
|
1893
|
+
new Response(JSON.stringify(mockData), {
|
|
1894
|
+
status: 200,
|
|
1895
|
+
headers: { "Content-Type": "application/json" },
|
|
1896
|
+
})
|
|
1897
|
+
);
|
|
1898
|
+
}) as unknown as typeof fetch;
|
|
1899
|
+
|
|
1900
|
+
const response = await handleToolCall(createRequest("get_report_data", {
|
|
1901
|
+
check_id: "H002",
|
|
1902
|
+
}));
|
|
1903
|
+
|
|
1904
|
+
expect(response.isError).toBeUndefined();
|
|
1905
|
+
const parsed = JSON.parse(getResponseText(response));
|
|
1906
|
+
expect(parsed[0].data).toContain("# H002");
|
|
1907
|
+
expect(capturedUrl).toContain("check_id=eq.H002");
|
|
1908
|
+
expect(capturedUrl).not.toContain("checkup_report_id");
|
|
1909
|
+
|
|
1910
|
+
readConfigSpy.mockRestore();
|
|
1911
|
+
});
|
|
1912
|
+
|
|
1913
|
+
test("successfully returns report data with content", async () => {
|
|
1914
|
+
const mockData = [
|
|
1915
|
+
{
|
|
1916
|
+
id: 100,
|
|
1917
|
+
checkup_report_id: 1,
|
|
1918
|
+
filename: "H002.md",
|
|
1919
|
+
check_id: "H002",
|
|
1920
|
+
type: "md",
|
|
1921
|
+
data: "# H002\n\nUnused indexes found.",
|
|
1922
|
+
},
|
|
1923
|
+
];
|
|
1924
|
+
|
|
1925
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1926
|
+
apiKey: "test-key",
|
|
1927
|
+
baseUrl: null,
|
|
1928
|
+
storageBaseUrl: null,
|
|
1929
|
+
orgId: null,
|
|
1930
|
+
defaultProject: null,
|
|
1931
|
+
projectName: null,
|
|
1932
|
+
});
|
|
1933
|
+
|
|
1934
|
+
globalThis.fetch = mock(() =>
|
|
1935
|
+
Promise.resolve(
|
|
1936
|
+
new Response(JSON.stringify(mockData), {
|
|
1937
|
+
status: 200,
|
|
1938
|
+
headers: { "Content-Type": "application/json" },
|
|
1939
|
+
})
|
|
1940
|
+
)
|
|
1941
|
+
) as unknown as typeof fetch;
|
|
1942
|
+
|
|
1943
|
+
const response = await handleToolCall(createRequest("get_report_data", {
|
|
1944
|
+
report_id: 1,
|
|
1945
|
+
type: "md",
|
|
1946
|
+
check_id: "H002",
|
|
1947
|
+
}));
|
|
1948
|
+
|
|
1949
|
+
expect(response.isError).toBeUndefined();
|
|
1950
|
+
const parsed = JSON.parse(getResponseText(response));
|
|
1951
|
+
expect(parsed[0].data).toContain("# H002");
|
|
1952
|
+
|
|
1953
|
+
readConfigSpy.mockRestore();
|
|
1954
|
+
});
|
|
1955
|
+
|
|
1956
|
+
test("passes filters to API", async () => {
|
|
1957
|
+
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1958
|
+
apiKey: "test-key",
|
|
1959
|
+
baseUrl: null,
|
|
1960
|
+
storageBaseUrl: null,
|
|
1961
|
+
orgId: null,
|
|
1962
|
+
defaultProject: null,
|
|
1963
|
+
projectName: null,
|
|
1964
|
+
});
|
|
1965
|
+
|
|
1966
|
+
let capturedUrl: string | undefined;
|
|
1967
|
+
globalThis.fetch = mock((url: string) => {
|
|
1968
|
+
capturedUrl = url;
|
|
1969
|
+
return Promise.resolve(
|
|
1970
|
+
new Response(JSON.stringify([]), {
|
|
1971
|
+
status: 200,
|
|
1972
|
+
headers: { "Content-Type": "application/json" },
|
|
1973
|
+
})
|
|
1974
|
+
);
|
|
1975
|
+
}) as unknown as typeof fetch;
|
|
1976
|
+
|
|
1977
|
+
await handleToolCall(createRequest("get_report_data", {
|
|
1978
|
+
report_id: 42,
|
|
1979
|
+
type: "json",
|
|
1980
|
+
check_id: "F004",
|
|
1981
|
+
}));
|
|
1982
|
+
|
|
1983
|
+
expect(capturedUrl).toContain("checkup_report_id=eq.42");
|
|
1984
|
+
expect(capturedUrl).toContain("type=eq.json");
|
|
1985
|
+
expect(capturedUrl).toContain("check_id=eq.F004");
|
|
1986
|
+
|
|
1987
|
+
readConfigSpy.mockRestore();
|
|
1988
|
+
});
|
|
1989
|
+
});
|
|
1990
|
+
|
|
1479
1991
|
describe("error propagation", () => {
|
|
1480
1992
|
test("propagates API errors through MCP layer", async () => {
|
|
1481
1993
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1482
1994
|
apiKey: "test-key",
|
|
1483
1995
|
baseUrl: null,
|
|
1996
|
+
storageBaseUrl: null,
|
|
1484
1997
|
orgId: 1,
|
|
1485
1998
|
defaultProject: null,
|
|
1999
|
+
projectName: null,
|
|
1486
2000
|
});
|
|
1487
2001
|
|
|
1488
2002
|
globalThis.fetch = mock(() =>
|
|
@@ -1508,8 +2022,10 @@ describe("MCP Server", () => {
|
|
|
1508
2022
|
const readConfigSpy = spyOn(config, "readConfig").mockReturnValue({
|
|
1509
2023
|
apiKey: "test-key",
|
|
1510
2024
|
baseUrl: null,
|
|
2025
|
+
storageBaseUrl: null,
|
|
1511
2026
|
orgId: 1,
|
|
1512
2027
|
defaultProject: null,
|
|
2028
|
+
projectName: null,
|
|
1513
2029
|
});
|
|
1514
2030
|
|
|
1515
2031
|
globalThis.fetch = mock(() => Promise.reject(new Error("Network error"))) as unknown as typeof fetch;
|