@bryan-thompson/inspector-assessment-cli 1.39.0 ā 1.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/__tests__/flag-parsing.test.js +613 -0
- package/build/assess-full.js +19 -2
- package/build/lib/__tests__/cli-parserSchemas.test.js +9 -3
- package/build/lib/assessment-runner/assessment-executor.js +28 -3
- package/build/lib/assessment-runner/index.js +2 -0
- package/build/lib/assessment-runner/single-module-runner.js +315 -0
- package/build/lib/cli-parser.js +129 -1
- package/build/lib/cli-parserSchemas.js +6 -0
- package/build/lib/result-output.js +130 -0
- package/package.json +1 -1
|
@@ -388,7 +388,10 @@ describe("Module Name Validation", () => {
|
|
|
388
388
|
const VALID_MODULE_NAMES = [
|
|
389
389
|
"functionality",
|
|
390
390
|
"security",
|
|
391
|
+
"documentation",
|
|
391
392
|
"errorHandling",
|
|
393
|
+
"usability",
|
|
394
|
+
"mcpSpecCompliance", // deprecated, use protocolCompliance
|
|
392
395
|
"protocolCompliance",
|
|
393
396
|
"aupCompliance",
|
|
394
397
|
"toolAnnotations",
|
|
@@ -399,9 +402,12 @@ describe("Module Name Validation", () => {
|
|
|
399
402
|
"resources",
|
|
400
403
|
"prompts",
|
|
401
404
|
"crossCapability",
|
|
405
|
+
"protocolConformance", // deprecated, use protocolCompliance
|
|
402
406
|
"developerExperience",
|
|
403
407
|
"portability",
|
|
404
408
|
"externalAPIScanner",
|
|
409
|
+
"fileModularization",
|
|
410
|
+
"conformance",
|
|
405
411
|
];
|
|
406
412
|
function validateModuleNames(input) {
|
|
407
413
|
const names = input
|
|
@@ -840,3 +846,610 @@ describe("parseArgs Zod Schema Integration", () => {
|
|
|
840
846
|
});
|
|
841
847
|
});
|
|
842
848
|
});
|
|
849
|
+
/**
|
|
850
|
+
* Transport Flag Tests (--http, --sse)
|
|
851
|
+
*
|
|
852
|
+
* Tests for the convenience transport flags that allow quick testing
|
|
853
|
+
* without creating a config file.
|
|
854
|
+
*/
|
|
855
|
+
describe("Transport Flags (--http, --sse)", () => {
|
|
856
|
+
let processExitSpy;
|
|
857
|
+
let consoleErrorSpy;
|
|
858
|
+
beforeEach(() => {
|
|
859
|
+
jest.useFakeTimers();
|
|
860
|
+
processExitSpy = jest
|
|
861
|
+
.spyOn(process, "exit")
|
|
862
|
+
.mockImplementation((() => { }));
|
|
863
|
+
consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
|
|
864
|
+
});
|
|
865
|
+
afterEach(() => {
|
|
866
|
+
jest.runAllTimers();
|
|
867
|
+
jest.useRealTimers();
|
|
868
|
+
processExitSpy?.mockRestore();
|
|
869
|
+
consoleErrorSpy?.mockRestore();
|
|
870
|
+
});
|
|
871
|
+
describe("--http flag", () => {
|
|
872
|
+
it("should accept valid HTTP URL", () => {
|
|
873
|
+
const result = parseArgs([
|
|
874
|
+
"test-server",
|
|
875
|
+
"--http",
|
|
876
|
+
"http://localhost:10900/mcp",
|
|
877
|
+
]);
|
|
878
|
+
expect(result.httpUrl).toBe("http://localhost:10900/mcp");
|
|
879
|
+
expect(result.helpRequested).toBeFalsy();
|
|
880
|
+
});
|
|
881
|
+
it("should accept valid HTTPS URL", () => {
|
|
882
|
+
const result = parseArgs([
|
|
883
|
+
"test-server",
|
|
884
|
+
"--http",
|
|
885
|
+
"https://api.example.com/mcp",
|
|
886
|
+
]);
|
|
887
|
+
expect(result.httpUrl).toBe("https://api.example.com/mcp");
|
|
888
|
+
expect(result.helpRequested).toBeFalsy();
|
|
889
|
+
});
|
|
890
|
+
it("should reject invalid URL", () => {
|
|
891
|
+
const result = parseArgs(["test-server", "--http", "not-a-valid-url"]);
|
|
892
|
+
expect(result.helpRequested).toBe(true);
|
|
893
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Invalid URL for --http"));
|
|
894
|
+
});
|
|
895
|
+
it("should reject missing URL argument", () => {
|
|
896
|
+
const result = parseArgs(["test-server", "--http"]);
|
|
897
|
+
expect(result.helpRequested).toBe(true);
|
|
898
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--http requires a URL argument"));
|
|
899
|
+
});
|
|
900
|
+
it("should reject when next argument is another flag", () => {
|
|
901
|
+
const result = parseArgs(["test-server", "--http", "--verbose"]);
|
|
902
|
+
expect(result.helpRequested).toBe(true);
|
|
903
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--http requires a URL argument"));
|
|
904
|
+
});
|
|
905
|
+
it("should reject non-HTTP protocol (file://)", () => {
|
|
906
|
+
const result = parseArgs(["test-server", "--http", "file:///etc/passwd"]);
|
|
907
|
+
expect(result.helpRequested).toBe(true);
|
|
908
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--http requires HTTP or HTTPS URL, got: file:"));
|
|
909
|
+
});
|
|
910
|
+
it("should reject non-HTTP protocol (ftp://)", () => {
|
|
911
|
+
const result = parseArgs([
|
|
912
|
+
"test-server",
|
|
913
|
+
"--http",
|
|
914
|
+
"ftp://example.com/file",
|
|
915
|
+
]);
|
|
916
|
+
expect(result.helpRequested).toBe(true);
|
|
917
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--http requires HTTP or HTTPS URL, got: ftp:"));
|
|
918
|
+
});
|
|
919
|
+
});
|
|
920
|
+
describe("--sse flag", () => {
|
|
921
|
+
it("should accept valid SSE URL", () => {
|
|
922
|
+
const result = parseArgs([
|
|
923
|
+
"test-server",
|
|
924
|
+
"--sse",
|
|
925
|
+
"http://localhost:9002/sse",
|
|
926
|
+
]);
|
|
927
|
+
expect(result.sseUrl).toBe("http://localhost:9002/sse");
|
|
928
|
+
expect(result.helpRequested).toBeFalsy();
|
|
929
|
+
});
|
|
930
|
+
it("should accept valid HTTPS SSE URL", () => {
|
|
931
|
+
const result = parseArgs([
|
|
932
|
+
"test-server",
|
|
933
|
+
"--sse",
|
|
934
|
+
"https://api.example.com/sse",
|
|
935
|
+
]);
|
|
936
|
+
expect(result.sseUrl).toBe("https://api.example.com/sse");
|
|
937
|
+
expect(result.helpRequested).toBeFalsy();
|
|
938
|
+
});
|
|
939
|
+
it("should reject invalid URL", () => {
|
|
940
|
+
const result = parseArgs(["test-server", "--sse", "invalid-url"]);
|
|
941
|
+
expect(result.helpRequested).toBe(true);
|
|
942
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Invalid URL for --sse"));
|
|
943
|
+
});
|
|
944
|
+
it("should reject missing URL argument", () => {
|
|
945
|
+
const result = parseArgs(["test-server", "--sse"]);
|
|
946
|
+
expect(result.helpRequested).toBe(true);
|
|
947
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--sse requires a URL argument"));
|
|
948
|
+
});
|
|
949
|
+
it("should reject non-HTTP protocol (file://)", () => {
|
|
950
|
+
const result = parseArgs(["test-server", "--sse", "file:///etc/passwd"]);
|
|
951
|
+
expect(result.helpRequested).toBe(true);
|
|
952
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--sse requires HTTP or HTTPS URL, got: file:"));
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
describe("mutual exclusivity", () => {
|
|
956
|
+
it("should reject --http with --config", () => {
|
|
957
|
+
const result = parseArgs([
|
|
958
|
+
"test-server",
|
|
959
|
+
"--http",
|
|
960
|
+
"http://localhost:10900/mcp",
|
|
961
|
+
"--config",
|
|
962
|
+
"config.json",
|
|
963
|
+
]);
|
|
964
|
+
expect(result.helpRequested).toBe(true);
|
|
965
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--http/--sse cannot be used with --config"));
|
|
966
|
+
});
|
|
967
|
+
it("should reject --sse with --config", () => {
|
|
968
|
+
const result = parseArgs([
|
|
969
|
+
"test-server",
|
|
970
|
+
"--sse",
|
|
971
|
+
"http://localhost:9002/sse",
|
|
972
|
+
"--config",
|
|
973
|
+
"config.json",
|
|
974
|
+
]);
|
|
975
|
+
expect(result.helpRequested).toBe(true);
|
|
976
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--http/--sse cannot be used with --config"));
|
|
977
|
+
});
|
|
978
|
+
it("should reject --http with --sse", () => {
|
|
979
|
+
const result = parseArgs([
|
|
980
|
+
"test-server",
|
|
981
|
+
"--http",
|
|
982
|
+
"http://localhost:10900/mcp",
|
|
983
|
+
"--sse",
|
|
984
|
+
"http://localhost:9002/sse",
|
|
985
|
+
]);
|
|
986
|
+
expect(result.helpRequested).toBe(true);
|
|
987
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--http and --sse are mutually exclusive"));
|
|
988
|
+
});
|
|
989
|
+
it("should allow --http without --config or --sse", () => {
|
|
990
|
+
const result = parseArgs([
|
|
991
|
+
"test-server",
|
|
992
|
+
"--http",
|
|
993
|
+
"http://localhost:10900/mcp",
|
|
994
|
+
]);
|
|
995
|
+
expect(result.httpUrl).toBe("http://localhost:10900/mcp");
|
|
996
|
+
expect(result.sseUrl).toBeUndefined();
|
|
997
|
+
expect(result.serverConfigPath).toBeUndefined();
|
|
998
|
+
expect(result.helpRequested).toBeFalsy();
|
|
999
|
+
});
|
|
1000
|
+
it("should allow --sse without --config or --http", () => {
|
|
1001
|
+
const result = parseArgs([
|
|
1002
|
+
"test-server",
|
|
1003
|
+
"--sse",
|
|
1004
|
+
"http://localhost:9002/sse",
|
|
1005
|
+
]);
|
|
1006
|
+
expect(result.sseUrl).toBe("http://localhost:9002/sse");
|
|
1007
|
+
expect(result.httpUrl).toBeUndefined();
|
|
1008
|
+
expect(result.serverConfigPath).toBeUndefined();
|
|
1009
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1010
|
+
});
|
|
1011
|
+
});
|
|
1012
|
+
describe("combined with other options", () => {
|
|
1013
|
+
it("should work with --http and --temporal-invocations", () => {
|
|
1014
|
+
const result = parseArgs([
|
|
1015
|
+
"test-server",
|
|
1016
|
+
"--http",
|
|
1017
|
+
"http://localhost:10900/mcp",
|
|
1018
|
+
"--temporal-invocations",
|
|
1019
|
+
"5",
|
|
1020
|
+
]);
|
|
1021
|
+
expect(result.httpUrl).toBe("http://localhost:10900/mcp");
|
|
1022
|
+
expect(result.temporalInvocations).toBe(5);
|
|
1023
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1024
|
+
});
|
|
1025
|
+
it("should work with --sse and --profile", () => {
|
|
1026
|
+
const result = parseArgs([
|
|
1027
|
+
"test-server",
|
|
1028
|
+
"--sse",
|
|
1029
|
+
"http://localhost:9002/sse",
|
|
1030
|
+
"--profile",
|
|
1031
|
+
"quick",
|
|
1032
|
+
]);
|
|
1033
|
+
expect(result.sseUrl).toBe("http://localhost:9002/sse");
|
|
1034
|
+
expect(result.profile).toBe("quick");
|
|
1035
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1036
|
+
});
|
|
1037
|
+
it("should work with --http and --output", () => {
|
|
1038
|
+
const result = parseArgs([
|
|
1039
|
+
"test-server",
|
|
1040
|
+
"--http",
|
|
1041
|
+
"http://localhost:10900/mcp",
|
|
1042
|
+
"--output",
|
|
1043
|
+
"/tmp/results.json",
|
|
1044
|
+
]);
|
|
1045
|
+
expect(result.httpUrl).toBe("http://localhost:10900/mcp");
|
|
1046
|
+
expect(result.outputPath).toBe("/tmp/results.json");
|
|
1047
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1048
|
+
});
|
|
1049
|
+
it("should work with --http and --conformance", () => {
|
|
1050
|
+
const result = parseArgs([
|
|
1051
|
+
"test-server",
|
|
1052
|
+
"--http",
|
|
1053
|
+
"http://localhost:10900/mcp",
|
|
1054
|
+
"--conformance",
|
|
1055
|
+
]);
|
|
1056
|
+
expect(result.httpUrl).toBe("http://localhost:10900/mcp");
|
|
1057
|
+
expect(result.conformanceEnabled).toBe(true);
|
|
1058
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1059
|
+
});
|
|
1060
|
+
it("should work with --sse and --conformance", () => {
|
|
1061
|
+
const result = parseArgs([
|
|
1062
|
+
"test-server",
|
|
1063
|
+
"--sse",
|
|
1064
|
+
"http://localhost:9002/sse",
|
|
1065
|
+
"--conformance",
|
|
1066
|
+
]);
|
|
1067
|
+
expect(result.sseUrl).toBe("http://localhost:9002/sse");
|
|
1068
|
+
expect(result.conformanceEnabled).toBe(true);
|
|
1069
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
1072
|
+
});
|
|
1073
|
+
/**
|
|
1074
|
+
* Module Flag Tests (--module, -m)
|
|
1075
|
+
*
|
|
1076
|
+
* Tests for the single module execution flag that bypasses orchestrator.
|
|
1077
|
+
* Issue #184: Single module runner for focused testing without orchestration overhead.
|
|
1078
|
+
*/
|
|
1079
|
+
describe("Module Flag (--module, -m)", () => {
|
|
1080
|
+
let processExitSpy;
|
|
1081
|
+
let consoleErrorSpy;
|
|
1082
|
+
beforeEach(() => {
|
|
1083
|
+
jest.useFakeTimers();
|
|
1084
|
+
processExitSpy = jest
|
|
1085
|
+
.spyOn(process, "exit")
|
|
1086
|
+
.mockImplementation((() => { }));
|
|
1087
|
+
consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
|
|
1088
|
+
});
|
|
1089
|
+
afterEach(() => {
|
|
1090
|
+
jest.runAllTimers();
|
|
1091
|
+
jest.useRealTimers();
|
|
1092
|
+
processExitSpy?.mockRestore();
|
|
1093
|
+
consoleErrorSpy?.mockRestore();
|
|
1094
|
+
});
|
|
1095
|
+
describe("valid module names", () => {
|
|
1096
|
+
it("should accept valid module name with long flag", () => {
|
|
1097
|
+
const result = parseArgs([
|
|
1098
|
+
"test-server",
|
|
1099
|
+
"--config",
|
|
1100
|
+
"config.json",
|
|
1101
|
+
"--module",
|
|
1102
|
+
"toolAnnotations",
|
|
1103
|
+
]);
|
|
1104
|
+
expect(result.singleModule).toBe("toolAnnotations");
|
|
1105
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1106
|
+
});
|
|
1107
|
+
it("should accept valid module name with short flag", () => {
|
|
1108
|
+
const result = parseArgs([
|
|
1109
|
+
"test-server",
|
|
1110
|
+
"--config",
|
|
1111
|
+
"config.json",
|
|
1112
|
+
"-m",
|
|
1113
|
+
"security",
|
|
1114
|
+
]);
|
|
1115
|
+
expect(result.singleModule).toBe("security");
|
|
1116
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1117
|
+
});
|
|
1118
|
+
it("should accept all valid core module names", () => {
|
|
1119
|
+
const coreModules = [
|
|
1120
|
+
"functionality",
|
|
1121
|
+
"security",
|
|
1122
|
+
"documentation",
|
|
1123
|
+
"errorHandling",
|
|
1124
|
+
"usability",
|
|
1125
|
+
"mcpSpecCompliance",
|
|
1126
|
+
"aupCompliance",
|
|
1127
|
+
"toolAnnotations",
|
|
1128
|
+
"prohibitedLibraries",
|
|
1129
|
+
"externalAPIScanner",
|
|
1130
|
+
"authentication",
|
|
1131
|
+
"temporal",
|
|
1132
|
+
"resources",
|
|
1133
|
+
"prompts",
|
|
1134
|
+
"crossCapability",
|
|
1135
|
+
"protocolConformance",
|
|
1136
|
+
];
|
|
1137
|
+
for (const moduleName of coreModules) {
|
|
1138
|
+
consoleErrorSpy.mockClear();
|
|
1139
|
+
const result = parseArgs([
|
|
1140
|
+
"test-server",
|
|
1141
|
+
"--config",
|
|
1142
|
+
"config.json",
|
|
1143
|
+
"--module",
|
|
1144
|
+
moduleName,
|
|
1145
|
+
]);
|
|
1146
|
+
expect(result.singleModule).toBe(moduleName);
|
|
1147
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
it("should accept optional module names", () => {
|
|
1151
|
+
const optionalModules = ["manifestValidation", "portability"];
|
|
1152
|
+
for (const moduleName of optionalModules) {
|
|
1153
|
+
consoleErrorSpy.mockClear();
|
|
1154
|
+
const result = parseArgs([
|
|
1155
|
+
"test-server",
|
|
1156
|
+
"--config",
|
|
1157
|
+
"config.json",
|
|
1158
|
+
"--module",
|
|
1159
|
+
moduleName,
|
|
1160
|
+
]);
|
|
1161
|
+
expect(result.singleModule).toBe(moduleName);
|
|
1162
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
});
|
|
1166
|
+
describe("invalid module names", () => {
|
|
1167
|
+
it("should reject invalid module name", () => {
|
|
1168
|
+
const result = parseArgs([
|
|
1169
|
+
"test-server",
|
|
1170
|
+
"--config",
|
|
1171
|
+
"config.json",
|
|
1172
|
+
"--module",
|
|
1173
|
+
"invalidModule",
|
|
1174
|
+
]);
|
|
1175
|
+
expect(result.helpRequested).toBe(true);
|
|
1176
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Invalid module name"));
|
|
1177
|
+
});
|
|
1178
|
+
it("should reject missing module argument", () => {
|
|
1179
|
+
const result = parseArgs([
|
|
1180
|
+
"test-server",
|
|
1181
|
+
"--config",
|
|
1182
|
+
"config.json",
|
|
1183
|
+
"--module",
|
|
1184
|
+
]);
|
|
1185
|
+
expect(result.helpRequested).toBe(true);
|
|
1186
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--module requires a module name"));
|
|
1187
|
+
});
|
|
1188
|
+
it("should reject when next argument is another flag", () => {
|
|
1189
|
+
const result = parseArgs([
|
|
1190
|
+
"test-server",
|
|
1191
|
+
"--config",
|
|
1192
|
+
"config.json",
|
|
1193
|
+
"--module",
|
|
1194
|
+
"--verbose",
|
|
1195
|
+
]);
|
|
1196
|
+
expect(result.helpRequested).toBe(true);
|
|
1197
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--module requires a module name"));
|
|
1198
|
+
});
|
|
1199
|
+
it("should reject case-sensitive mismatch", () => {
|
|
1200
|
+
const result = parseArgs([
|
|
1201
|
+
"test-server",
|
|
1202
|
+
"--config",
|
|
1203
|
+
"config.json",
|
|
1204
|
+
"--module",
|
|
1205
|
+
"SECURITY",
|
|
1206
|
+
]);
|
|
1207
|
+
expect(result.helpRequested).toBe(true);
|
|
1208
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Invalid module name"));
|
|
1209
|
+
});
|
|
1210
|
+
it("should reject module name with typo", () => {
|
|
1211
|
+
const result = parseArgs([
|
|
1212
|
+
"test-server",
|
|
1213
|
+
"--config",
|
|
1214
|
+
"config.json",
|
|
1215
|
+
"--module",
|
|
1216
|
+
"functionalaty",
|
|
1217
|
+
]);
|
|
1218
|
+
expect(result.helpRequested).toBe(true);
|
|
1219
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Invalid module name"));
|
|
1220
|
+
});
|
|
1221
|
+
});
|
|
1222
|
+
describe("mutual exclusivity with orchestrator flags", () => {
|
|
1223
|
+
it("should reject --module with --profile", () => {
|
|
1224
|
+
const result = parseArgs([
|
|
1225
|
+
"test-server",
|
|
1226
|
+
"--config",
|
|
1227
|
+
"config.json",
|
|
1228
|
+
"--module",
|
|
1229
|
+
"security",
|
|
1230
|
+
"--profile",
|
|
1231
|
+
"quick",
|
|
1232
|
+
]);
|
|
1233
|
+
expect(result.helpRequested).toBe(true);
|
|
1234
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--module cannot be used with --skip-modules, --only-modules, or --profile"));
|
|
1235
|
+
});
|
|
1236
|
+
it("should reject --module with --skip-modules", () => {
|
|
1237
|
+
const result = parseArgs([
|
|
1238
|
+
"test-server",
|
|
1239
|
+
"--config",
|
|
1240
|
+
"config.json",
|
|
1241
|
+
"--module",
|
|
1242
|
+
"security",
|
|
1243
|
+
"--skip-modules",
|
|
1244
|
+
"temporal",
|
|
1245
|
+
]);
|
|
1246
|
+
expect(result.helpRequested).toBe(true);
|
|
1247
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--module cannot be used with"));
|
|
1248
|
+
});
|
|
1249
|
+
it("should reject --module with --only-modules", () => {
|
|
1250
|
+
const result = parseArgs([
|
|
1251
|
+
"test-server",
|
|
1252
|
+
"--config",
|
|
1253
|
+
"config.json",
|
|
1254
|
+
"--module",
|
|
1255
|
+
"security",
|
|
1256
|
+
"--only-modules",
|
|
1257
|
+
"functionality",
|
|
1258
|
+
]);
|
|
1259
|
+
expect(result.helpRequested).toBe(true);
|
|
1260
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--module cannot be used with"));
|
|
1261
|
+
});
|
|
1262
|
+
it("should reject --profile with --module (order reversed)", () => {
|
|
1263
|
+
const result = parseArgs([
|
|
1264
|
+
"test-server",
|
|
1265
|
+
"--config",
|
|
1266
|
+
"config.json",
|
|
1267
|
+
"--profile",
|
|
1268
|
+
"quick",
|
|
1269
|
+
"--module",
|
|
1270
|
+
"security",
|
|
1271
|
+
]);
|
|
1272
|
+
expect(result.helpRequested).toBe(true);
|
|
1273
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--module cannot be used with"));
|
|
1274
|
+
});
|
|
1275
|
+
});
|
|
1276
|
+
describe("combined with transport flags", () => {
|
|
1277
|
+
it("should work with --http", () => {
|
|
1278
|
+
const result = parseArgs([
|
|
1279
|
+
"test-server",
|
|
1280
|
+
"--http",
|
|
1281
|
+
"http://localhost:10900/mcp",
|
|
1282
|
+
"--module",
|
|
1283
|
+
"security",
|
|
1284
|
+
]);
|
|
1285
|
+
expect(result.httpUrl).toBe("http://localhost:10900/mcp");
|
|
1286
|
+
expect(result.singleModule).toBe("security");
|
|
1287
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1288
|
+
});
|
|
1289
|
+
it("should work with --sse", () => {
|
|
1290
|
+
const result = parseArgs([
|
|
1291
|
+
"test-server",
|
|
1292
|
+
"--sse",
|
|
1293
|
+
"http://localhost:9002/sse",
|
|
1294
|
+
"--module",
|
|
1295
|
+
"functionality",
|
|
1296
|
+
]);
|
|
1297
|
+
expect(result.sseUrl).toBe("http://localhost:9002/sse");
|
|
1298
|
+
expect(result.singleModule).toBe("functionality");
|
|
1299
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1300
|
+
});
|
|
1301
|
+
it("should work with --config", () => {
|
|
1302
|
+
const result = parseArgs([
|
|
1303
|
+
"test-server",
|
|
1304
|
+
"--config",
|
|
1305
|
+
"config.json",
|
|
1306
|
+
"--module",
|
|
1307
|
+
"temporal",
|
|
1308
|
+
]);
|
|
1309
|
+
expect(result.serverConfigPath).toBe("config.json");
|
|
1310
|
+
expect(result.singleModule).toBe("temporal");
|
|
1311
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1312
|
+
});
|
|
1313
|
+
});
|
|
1314
|
+
describe("combined with other compatible flags", () => {
|
|
1315
|
+
it("should work with --output", () => {
|
|
1316
|
+
const result = parseArgs([
|
|
1317
|
+
"test-server",
|
|
1318
|
+
"--config",
|
|
1319
|
+
"config.json",
|
|
1320
|
+
"--module",
|
|
1321
|
+
"security",
|
|
1322
|
+
"--output",
|
|
1323
|
+
"/tmp/results.json",
|
|
1324
|
+
]);
|
|
1325
|
+
expect(result.singleModule).toBe("security");
|
|
1326
|
+
expect(result.outputPath).toBe("/tmp/results.json");
|
|
1327
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1328
|
+
});
|
|
1329
|
+
it("should work with --verbose", () => {
|
|
1330
|
+
const result = parseArgs([
|
|
1331
|
+
"test-server",
|
|
1332
|
+
"--config",
|
|
1333
|
+
"config.json",
|
|
1334
|
+
"--module",
|
|
1335
|
+
"toolAnnotations",
|
|
1336
|
+
"--verbose",
|
|
1337
|
+
]);
|
|
1338
|
+
expect(result.singleModule).toBe("toolAnnotations");
|
|
1339
|
+
expect(result.verbose).toBe(true);
|
|
1340
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1341
|
+
});
|
|
1342
|
+
it("should work with --log-level", () => {
|
|
1343
|
+
const result = parseArgs([
|
|
1344
|
+
"test-server",
|
|
1345
|
+
"--config",
|
|
1346
|
+
"config.json",
|
|
1347
|
+
"--module",
|
|
1348
|
+
"errorHandling",
|
|
1349
|
+
"--log-level",
|
|
1350
|
+
"debug",
|
|
1351
|
+
]);
|
|
1352
|
+
expect(result.singleModule).toBe("errorHandling");
|
|
1353
|
+
expect(result.logLevel).toBe("debug");
|
|
1354
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1355
|
+
});
|
|
1356
|
+
it("should work with --temporal-invocations", () => {
|
|
1357
|
+
const result = parseArgs([
|
|
1358
|
+
"test-server",
|
|
1359
|
+
"--config",
|
|
1360
|
+
"config.json",
|
|
1361
|
+
"--module",
|
|
1362
|
+
"temporal",
|
|
1363
|
+
"--temporal-invocations",
|
|
1364
|
+
"10",
|
|
1365
|
+
]);
|
|
1366
|
+
expect(result.singleModule).toBe("temporal");
|
|
1367
|
+
expect(result.temporalInvocations).toBe(10);
|
|
1368
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1369
|
+
});
|
|
1370
|
+
it("should work with --conformance", () => {
|
|
1371
|
+
const result = parseArgs([
|
|
1372
|
+
"test-server",
|
|
1373
|
+
"--http",
|
|
1374
|
+
"http://localhost:10900/mcp",
|
|
1375
|
+
"--module",
|
|
1376
|
+
"protocolConformance",
|
|
1377
|
+
"--conformance",
|
|
1378
|
+
]);
|
|
1379
|
+
expect(result.singleModule).toBe("protocolConformance");
|
|
1380
|
+
expect(result.conformanceEnabled).toBe(true);
|
|
1381
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1382
|
+
});
|
|
1383
|
+
it("should work with --format", () => {
|
|
1384
|
+
const result = parseArgs([
|
|
1385
|
+
"test-server",
|
|
1386
|
+
"--config",
|
|
1387
|
+
"config.json",
|
|
1388
|
+
"--module",
|
|
1389
|
+
"security",
|
|
1390
|
+
"--format",
|
|
1391
|
+
"markdown",
|
|
1392
|
+
]);
|
|
1393
|
+
expect(result.singleModule).toBe("security");
|
|
1394
|
+
expect(result.format).toBe("markdown");
|
|
1395
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1396
|
+
});
|
|
1397
|
+
});
|
|
1398
|
+
describe("short flag behavior", () => {
|
|
1399
|
+
it("should accept -m with all transport types", () => {
|
|
1400
|
+
// Test with --http
|
|
1401
|
+
let result = parseArgs([
|
|
1402
|
+
"test-server",
|
|
1403
|
+
"--http",
|
|
1404
|
+
"http://localhost:10900/mcp",
|
|
1405
|
+
"-m",
|
|
1406
|
+
"security",
|
|
1407
|
+
]);
|
|
1408
|
+
expect(result.singleModule).toBe("security");
|
|
1409
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1410
|
+
// Test with --sse
|
|
1411
|
+
consoleErrorSpy.mockClear();
|
|
1412
|
+
result = parseArgs([
|
|
1413
|
+
"test-server",
|
|
1414
|
+
"--sse",
|
|
1415
|
+
"http://localhost:9002/sse",
|
|
1416
|
+
"-m",
|
|
1417
|
+
"functionality",
|
|
1418
|
+
]);
|
|
1419
|
+
expect(result.singleModule).toBe("functionality");
|
|
1420
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1421
|
+
// Test with --config
|
|
1422
|
+
consoleErrorSpy.mockClear();
|
|
1423
|
+
result = parseArgs([
|
|
1424
|
+
"test-server",
|
|
1425
|
+
"--config",
|
|
1426
|
+
"config.json",
|
|
1427
|
+
"-m",
|
|
1428
|
+
"temporal",
|
|
1429
|
+
]);
|
|
1430
|
+
expect(result.singleModule).toBe("temporal");
|
|
1431
|
+
expect(result.helpRequested).toBeFalsy();
|
|
1432
|
+
});
|
|
1433
|
+
it("should reject -m with missing argument", () => {
|
|
1434
|
+
const result = parseArgs([
|
|
1435
|
+
"test-server",
|
|
1436
|
+
"--config",
|
|
1437
|
+
"config.json",
|
|
1438
|
+
"-m",
|
|
1439
|
+
]);
|
|
1440
|
+
expect(result.helpRequested).toBe(true);
|
|
1441
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("--module requires a module name"));
|
|
1442
|
+
});
|
|
1443
|
+
it("should reject -m with invalid module", () => {
|
|
1444
|
+
const result = parseArgs([
|
|
1445
|
+
"test-server",
|
|
1446
|
+
"--config",
|
|
1447
|
+
"config.json",
|
|
1448
|
+
"-m",
|
|
1449
|
+
"notAModule",
|
|
1450
|
+
]);
|
|
1451
|
+
expect(result.helpRequested).toBe(true);
|
|
1452
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Invalid module name"));
|
|
1453
|
+
});
|
|
1454
|
+
});
|
|
1455
|
+
});
|
package/build/assess-full.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
import { ScopedListenerConfig } from "./lib/event-config.js";
|
|
13
13
|
// Import from extracted modules
|
|
14
14
|
import { parseArgs } from "./lib/cli-parser.js";
|
|
15
|
-
import { runFullAssessment } from "./lib/assessment-runner.js";
|
|
16
|
-
import { saveResults, saveTieredResults, saveSummaryOnly, displaySummary, } from "./lib/result-output.js";
|
|
15
|
+
import { runFullAssessment, runSingleModule } from "./lib/assessment-runner.js";
|
|
16
|
+
import { saveResults, saveTieredResults, saveSummaryOnly, displaySummary, saveSingleModuleResults, displaySingleModuleSummary, } from "./lib/result-output.js";
|
|
17
17
|
import { handleComparison, displayComparisonSummary, } from "./lib/comparison-handler.js";
|
|
18
18
|
import { shouldAutoTier, formatTokenEstimate, } from "../../client/lib/lib/assessment/summarizer/index.js";
|
|
19
19
|
// ============================================================================
|
|
@@ -35,6 +35,23 @@ async function main() {
|
|
|
35
35
|
}
|
|
36
36
|
// Apply scoped listener configuration for assessment
|
|
37
37
|
listenerConfig.apply();
|
|
38
|
+
// Single module mode - bypass orchestrator for lightweight execution (Issue #184)
|
|
39
|
+
if (options.singleModule) {
|
|
40
|
+
const result = await runSingleModule(options.singleModule, options);
|
|
41
|
+
if (!options.jsonOnly) {
|
|
42
|
+
displaySingleModuleSummary(result);
|
|
43
|
+
}
|
|
44
|
+
const outputPath = saveSingleModuleResults(options.serverName, options.singleModule, result, options);
|
|
45
|
+
if (options.jsonOnly) {
|
|
46
|
+
console.log(outputPath);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
console.log(`\nš Results saved to: ${outputPath}\n`);
|
|
50
|
+
}
|
|
51
|
+
const exitCode = result.status === "FAIL" ? 1 : 0;
|
|
52
|
+
setTimeout(() => process.exit(exitCode), 10);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
38
55
|
const results = await runFullAssessment(options);
|
|
39
56
|
// Pre-flight mode handles its own output and exit
|
|
40
57
|
if (options.preflightOnly) {
|