@bryan-thompson/inspector-assessment-cli 1.30.0 → 1.31.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/build/lib/__tests__/cli-parserSchemas.test.js +553 -0
- package/build/lib/__tests__/zodErrorFormatter.test.js +282 -0
- package/build/lib/assessment-runner/__tests__/server-configSchemas.test.js +394 -0
- package/build/lib/assessment-runner/assessment-executor.js +4 -0
- package/build/lib/assessment-runner/config-builder.js +9 -0
- package/build/lib/assessment-runner/server-configSchemas.js +122 -0
- package/build/lib/cli-parser.js +23 -25
- package/build/lib/cli-parserSchemas.js +231 -0
- package/build/lib/zodErrorFormatter.js +91 -0
- package/package.json +1 -1
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for CLI Parser Zod Schemas
|
|
3
|
+
*
|
|
4
|
+
* Validates the schema definitions used for CLI argument parsing.
|
|
5
|
+
*
|
|
6
|
+
* @module cli/lib/__tests__/cli-parserSchemas
|
|
7
|
+
*/
|
|
8
|
+
// Uses Jest globals (describe, test, expect)
|
|
9
|
+
import { ZodError } from "zod";
|
|
10
|
+
import { AssessmentProfileNameSchema, AssessmentModuleNameSchema, ServerConfigSchema, AssessmentOptionsSchema, ValidationResultSchema, validateAssessmentOptions, validateServerConfig, parseAssessmentOptions, safeParseAssessmentOptions, parseModuleNames, safeParseModuleNames, LogLevelSchema, ReportFormatSchema, TransportTypeSchema, ZOD_SCHEMA_VERSION, } from "../cli-parserSchemas.js";
|
|
11
|
+
describe("cli-parserSchemas", () => {
|
|
12
|
+
describe("Re-exported schemas", () => {
|
|
13
|
+
test("exports ZOD_SCHEMA_VERSION", () => {
|
|
14
|
+
expect(ZOD_SCHEMA_VERSION).toBe(1);
|
|
15
|
+
});
|
|
16
|
+
test("exports LogLevelSchema", () => {
|
|
17
|
+
const validLevels = ["silent", "error", "warn", "info", "debug"];
|
|
18
|
+
for (const level of validLevels) {
|
|
19
|
+
expect(LogLevelSchema.safeParse(level).success).toBe(true);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
test("exports ReportFormatSchema", () => {
|
|
23
|
+
expect(ReportFormatSchema.safeParse("json").success).toBe(true);
|
|
24
|
+
expect(ReportFormatSchema.safeParse("markdown").success).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
test("exports TransportTypeSchema", () => {
|
|
27
|
+
const validTypes = ["stdio", "http", "sse"];
|
|
28
|
+
for (const type of validTypes) {
|
|
29
|
+
expect(TransportTypeSchema.safeParse(type).success).toBe(true);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe("AssessmentProfileNameSchema", () => {
|
|
34
|
+
describe("valid profiles", () => {
|
|
35
|
+
test('accepts "quick"', () => {
|
|
36
|
+
const result = AssessmentProfileNameSchema.safeParse("quick");
|
|
37
|
+
expect(result.success).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
test('accepts "security"', () => {
|
|
40
|
+
const result = AssessmentProfileNameSchema.safeParse("security");
|
|
41
|
+
expect(result.success).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
test('accepts "compliance"', () => {
|
|
44
|
+
const result = AssessmentProfileNameSchema.safeParse("compliance");
|
|
45
|
+
expect(result.success).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
test('accepts "full"', () => {
|
|
48
|
+
const result = AssessmentProfileNameSchema.safeParse("full");
|
|
49
|
+
expect(result.success).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe("invalid profiles", () => {
|
|
53
|
+
test('rejects uppercase "QUICK"', () => {
|
|
54
|
+
const result = AssessmentProfileNameSchema.safeParse("QUICK");
|
|
55
|
+
expect(result.success).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
test("rejects unknown profile", () => {
|
|
58
|
+
const result = AssessmentProfileNameSchema.safeParse("unknown");
|
|
59
|
+
expect(result.success).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
test("rejects empty string", () => {
|
|
62
|
+
const result = AssessmentProfileNameSchema.safeParse("");
|
|
63
|
+
expect(result.success).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
test("rejects null", () => {
|
|
66
|
+
const result = AssessmentProfileNameSchema.safeParse(null);
|
|
67
|
+
expect(result.success).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe("AssessmentModuleNameSchema", () => {
|
|
72
|
+
const validModules = [
|
|
73
|
+
"functionality",
|
|
74
|
+
"security",
|
|
75
|
+
"documentation",
|
|
76
|
+
"errorHandling",
|
|
77
|
+
"usability",
|
|
78
|
+
"mcpSpecCompliance",
|
|
79
|
+
"aupCompliance",
|
|
80
|
+
"toolAnnotations",
|
|
81
|
+
"prohibitedLibraries",
|
|
82
|
+
"manifestValidation",
|
|
83
|
+
"portability",
|
|
84
|
+
"externalAPIScanner",
|
|
85
|
+
"authentication",
|
|
86
|
+
"temporal",
|
|
87
|
+
"resources",
|
|
88
|
+
"prompts",
|
|
89
|
+
"crossCapability",
|
|
90
|
+
"protocolConformance",
|
|
91
|
+
];
|
|
92
|
+
describe("valid modules", () => {
|
|
93
|
+
test("accepts all 18 valid module names", () => {
|
|
94
|
+
for (const module of validModules) {
|
|
95
|
+
const result = AssessmentModuleNameSchema.safeParse(module);
|
|
96
|
+
expect(result.success).toBe(true);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
test("count of valid modules is 18", () => {
|
|
100
|
+
expect(validModules.length).toBe(18);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe("invalid modules", () => {
|
|
104
|
+
test("rejects unknown module name", () => {
|
|
105
|
+
const result = AssessmentModuleNameSchema.safeParse("unknownModule");
|
|
106
|
+
expect(result.success).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
test("rejects case-sensitive mismatch", () => {
|
|
109
|
+
const result = AssessmentModuleNameSchema.safeParse("Security");
|
|
110
|
+
expect(result.success).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
test("rejects empty string", () => {
|
|
113
|
+
const result = AssessmentModuleNameSchema.safeParse("");
|
|
114
|
+
expect(result.success).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
test("rejects typo", () => {
|
|
117
|
+
const result = AssessmentModuleNameSchema.safeParse("functinoality");
|
|
118
|
+
expect(result.success).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe("ServerConfigSchema", () => {
|
|
123
|
+
describe("http/sse transport", () => {
|
|
124
|
+
test("accepts http transport with url", () => {
|
|
125
|
+
const result = ServerConfigSchema.safeParse({
|
|
126
|
+
transport: "http",
|
|
127
|
+
url: "http://localhost:3000/mcp",
|
|
128
|
+
});
|
|
129
|
+
expect(result.success).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
test("accepts sse transport with url", () => {
|
|
132
|
+
const result = ServerConfigSchema.safeParse({
|
|
133
|
+
transport: "sse",
|
|
134
|
+
url: "http://localhost:3000/sse",
|
|
135
|
+
});
|
|
136
|
+
expect(result.success).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
test("rejects http transport without url", () => {
|
|
139
|
+
const result = ServerConfigSchema.safeParse({
|
|
140
|
+
transport: "http",
|
|
141
|
+
});
|
|
142
|
+
expect(result.success).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
test("rejects sse transport without url", () => {
|
|
145
|
+
const result = ServerConfigSchema.safeParse({
|
|
146
|
+
transport: "sse",
|
|
147
|
+
});
|
|
148
|
+
expect(result.success).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
test("rejects http transport with empty url", () => {
|
|
151
|
+
const result = ServerConfigSchema.safeParse({
|
|
152
|
+
transport: "http",
|
|
153
|
+
url: "",
|
|
154
|
+
});
|
|
155
|
+
expect(result.success).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe("stdio transport", () => {
|
|
159
|
+
test("accepts stdio transport with command", () => {
|
|
160
|
+
const result = ServerConfigSchema.safeParse({
|
|
161
|
+
transport: "stdio",
|
|
162
|
+
command: "python3",
|
|
163
|
+
});
|
|
164
|
+
expect(result.success).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
test("accepts stdio transport with command, args, and env", () => {
|
|
167
|
+
const result = ServerConfigSchema.safeParse({
|
|
168
|
+
transport: "stdio",
|
|
169
|
+
command: "python3",
|
|
170
|
+
args: ["server.py", "--port", "8080"],
|
|
171
|
+
env: { DEBUG: "true" },
|
|
172
|
+
cwd: "/home/user/server",
|
|
173
|
+
});
|
|
174
|
+
expect(result.success).toBe(true);
|
|
175
|
+
});
|
|
176
|
+
test("rejects stdio transport without command", () => {
|
|
177
|
+
const result = ServerConfigSchema.safeParse({
|
|
178
|
+
transport: "stdio",
|
|
179
|
+
});
|
|
180
|
+
expect(result.success).toBe(false);
|
|
181
|
+
});
|
|
182
|
+
test("rejects stdio transport with empty command", () => {
|
|
183
|
+
const result = ServerConfigSchema.safeParse({
|
|
184
|
+
transport: "stdio",
|
|
185
|
+
command: "",
|
|
186
|
+
});
|
|
187
|
+
expect(result.success).toBe(false);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
describe("no transport specified", () => {
|
|
191
|
+
test("accepts config with url only (infers http/sse)", () => {
|
|
192
|
+
const result = ServerConfigSchema.safeParse({
|
|
193
|
+
url: "http://localhost:3000/mcp",
|
|
194
|
+
});
|
|
195
|
+
expect(result.success).toBe(true);
|
|
196
|
+
});
|
|
197
|
+
test("accepts config with command only (infers stdio)", () => {
|
|
198
|
+
const result = ServerConfigSchema.safeParse({
|
|
199
|
+
command: "python3",
|
|
200
|
+
});
|
|
201
|
+
expect(result.success).toBe(true);
|
|
202
|
+
});
|
|
203
|
+
test("rejects config with neither url nor command", () => {
|
|
204
|
+
const result = ServerConfigSchema.safeParse({});
|
|
205
|
+
expect(result.success).toBe(false);
|
|
206
|
+
});
|
|
207
|
+
test("accepts config with both url and command", () => {
|
|
208
|
+
// When both are present and no transport specified, validation passes
|
|
209
|
+
// because either url or command satisfies the refinement
|
|
210
|
+
const result = ServerConfigSchema.safeParse({
|
|
211
|
+
url: "http://localhost:3000/mcp",
|
|
212
|
+
command: "python3",
|
|
213
|
+
});
|
|
214
|
+
expect(result.success).toBe(true);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
describe("optional fields", () => {
|
|
218
|
+
test("accepts args as string array", () => {
|
|
219
|
+
const result = ServerConfigSchema.safeParse({
|
|
220
|
+
command: "python3",
|
|
221
|
+
args: ["--verbose", "-m", "module"],
|
|
222
|
+
});
|
|
223
|
+
expect(result.success).toBe(true);
|
|
224
|
+
});
|
|
225
|
+
test("accepts env as record", () => {
|
|
226
|
+
const result = ServerConfigSchema.safeParse({
|
|
227
|
+
command: "python3",
|
|
228
|
+
env: { PATH: "/usr/bin", DEBUG: "1" },
|
|
229
|
+
});
|
|
230
|
+
expect(result.success).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
test("accepts cwd", () => {
|
|
233
|
+
const result = ServerConfigSchema.safeParse({
|
|
234
|
+
command: "python3",
|
|
235
|
+
cwd: "/home/user/project",
|
|
236
|
+
});
|
|
237
|
+
expect(result.success).toBe(true);
|
|
238
|
+
});
|
|
239
|
+
test("rejects non-string array for args", () => {
|
|
240
|
+
const result = ServerConfigSchema.safeParse({
|
|
241
|
+
command: "python3",
|
|
242
|
+
args: [1, 2, 3],
|
|
243
|
+
});
|
|
244
|
+
expect(result.success).toBe(false);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
describe("AssessmentOptionsSchema", () => {
|
|
249
|
+
describe("required fields", () => {
|
|
250
|
+
test("requires serverName", () => {
|
|
251
|
+
const result = AssessmentOptionsSchema.safeParse({});
|
|
252
|
+
expect(result.success).toBe(false);
|
|
253
|
+
});
|
|
254
|
+
test("rejects empty serverName", () => {
|
|
255
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
256
|
+
serverName: "",
|
|
257
|
+
});
|
|
258
|
+
expect(result.success).toBe(false);
|
|
259
|
+
});
|
|
260
|
+
test("accepts minimal valid options", () => {
|
|
261
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
262
|
+
serverName: "test-server",
|
|
263
|
+
});
|
|
264
|
+
expect(result.success).toBe(true);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
describe("optional fields", () => {
|
|
268
|
+
test("accepts all optional fields", () => {
|
|
269
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
270
|
+
serverName: "test-server",
|
|
271
|
+
serverConfigPath: "/path/to/config.json",
|
|
272
|
+
outputPath: "/path/to/output.json",
|
|
273
|
+
sourceCodePath: "/path/to/source",
|
|
274
|
+
verbose: true,
|
|
275
|
+
jsonOnly: false,
|
|
276
|
+
format: "json",
|
|
277
|
+
includePolicy: true,
|
|
278
|
+
preflightOnly: false,
|
|
279
|
+
logLevel: "debug",
|
|
280
|
+
});
|
|
281
|
+
expect(result.success).toBe(true);
|
|
282
|
+
});
|
|
283
|
+
test("validates mcpAuditorUrl as URL", () => {
|
|
284
|
+
const validResult = AssessmentOptionsSchema.safeParse({
|
|
285
|
+
serverName: "test-server",
|
|
286
|
+
mcpAuditorUrl: "http://localhost:8085",
|
|
287
|
+
});
|
|
288
|
+
expect(validResult.success).toBe(true);
|
|
289
|
+
const invalidResult = AssessmentOptionsSchema.safeParse({
|
|
290
|
+
serverName: "test-server",
|
|
291
|
+
mcpAuditorUrl: "not-a-url",
|
|
292
|
+
});
|
|
293
|
+
expect(invalidResult.success).toBe(false);
|
|
294
|
+
});
|
|
295
|
+
test("validates temporalInvocations as positive integer", () => {
|
|
296
|
+
const validResult = AssessmentOptionsSchema.safeParse({
|
|
297
|
+
serverName: "test-server",
|
|
298
|
+
temporalInvocations: 5,
|
|
299
|
+
});
|
|
300
|
+
expect(validResult.success).toBe(true);
|
|
301
|
+
const zeroResult = AssessmentOptionsSchema.safeParse({
|
|
302
|
+
serverName: "test-server",
|
|
303
|
+
temporalInvocations: 0,
|
|
304
|
+
});
|
|
305
|
+
expect(zeroResult.success).toBe(false);
|
|
306
|
+
const negativeResult = AssessmentOptionsSchema.safeParse({
|
|
307
|
+
serverName: "test-server",
|
|
308
|
+
temporalInvocations: -1,
|
|
309
|
+
});
|
|
310
|
+
expect(negativeResult.success).toBe(false);
|
|
311
|
+
const floatResult = AssessmentOptionsSchema.safeParse({
|
|
312
|
+
serverName: "test-server",
|
|
313
|
+
temporalInvocations: 3.5,
|
|
314
|
+
});
|
|
315
|
+
expect(floatResult.success).toBe(false);
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
describe("refinement: profile + modules", () => {
|
|
319
|
+
test("accepts profile alone", () => {
|
|
320
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
321
|
+
serverName: "test-server",
|
|
322
|
+
profile: "quick",
|
|
323
|
+
});
|
|
324
|
+
expect(result.success).toBe(true);
|
|
325
|
+
});
|
|
326
|
+
test("accepts skipModules alone", () => {
|
|
327
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
328
|
+
serverName: "test-server",
|
|
329
|
+
skipModules: ["security", "temporal"],
|
|
330
|
+
});
|
|
331
|
+
expect(result.success).toBe(true);
|
|
332
|
+
});
|
|
333
|
+
test("accepts onlyModules alone", () => {
|
|
334
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
335
|
+
serverName: "test-server",
|
|
336
|
+
onlyModules: ["functionality", "documentation"],
|
|
337
|
+
});
|
|
338
|
+
expect(result.success).toBe(true);
|
|
339
|
+
});
|
|
340
|
+
test("rejects profile + skipModules", () => {
|
|
341
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
342
|
+
serverName: "test-server",
|
|
343
|
+
profile: "quick",
|
|
344
|
+
skipModules: ["security"],
|
|
345
|
+
});
|
|
346
|
+
expect(result.success).toBe(false);
|
|
347
|
+
if (!result.success) {
|
|
348
|
+
expect(result.error.errors[0].message).toContain("--profile cannot be used with --skip-modules or --only-modules");
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
test("rejects profile + onlyModules", () => {
|
|
352
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
353
|
+
serverName: "test-server",
|
|
354
|
+
profile: "security",
|
|
355
|
+
onlyModules: ["functionality"],
|
|
356
|
+
});
|
|
357
|
+
expect(result.success).toBe(false);
|
|
358
|
+
if (!result.success) {
|
|
359
|
+
expect(result.error.errors[0].message).toContain("--profile cannot be used with --skip-modules or --only-modules");
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
test("accepts profile with empty module arrays", () => {
|
|
363
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
364
|
+
serverName: "test-server",
|
|
365
|
+
profile: "quick",
|
|
366
|
+
skipModules: [],
|
|
367
|
+
onlyModules: [],
|
|
368
|
+
});
|
|
369
|
+
expect(result.success).toBe(true);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
describe("refinement: skip + only modules", () => {
|
|
373
|
+
test("rejects skipModules + onlyModules together", () => {
|
|
374
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
375
|
+
serverName: "test-server",
|
|
376
|
+
skipModules: ["security"],
|
|
377
|
+
onlyModules: ["functionality"],
|
|
378
|
+
});
|
|
379
|
+
expect(result.success).toBe(false);
|
|
380
|
+
if (!result.success) {
|
|
381
|
+
expect(result.error.errors[0].message).toContain("--skip-modules and --only-modules are mutually exclusive");
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
test("accepts both as empty arrays", () => {
|
|
385
|
+
const result = AssessmentOptionsSchema.safeParse({
|
|
386
|
+
serverName: "test-server",
|
|
387
|
+
skipModules: [],
|
|
388
|
+
onlyModules: [],
|
|
389
|
+
});
|
|
390
|
+
expect(result.success).toBe(true);
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
describe("ValidationResultSchema", () => {
|
|
395
|
+
test("accepts valid result with no errors", () => {
|
|
396
|
+
const result = ValidationResultSchema.safeParse({
|
|
397
|
+
valid: true,
|
|
398
|
+
errors: [],
|
|
399
|
+
});
|
|
400
|
+
expect(result.success).toBe(true);
|
|
401
|
+
});
|
|
402
|
+
test("accepts valid result with errors", () => {
|
|
403
|
+
const result = ValidationResultSchema.safeParse({
|
|
404
|
+
valid: false,
|
|
405
|
+
errors: ["Error 1", "Error 2"],
|
|
406
|
+
});
|
|
407
|
+
expect(result.success).toBe(true);
|
|
408
|
+
});
|
|
409
|
+
test("rejects missing valid field", () => {
|
|
410
|
+
const result = ValidationResultSchema.safeParse({
|
|
411
|
+
errors: [],
|
|
412
|
+
});
|
|
413
|
+
expect(result.success).toBe(false);
|
|
414
|
+
});
|
|
415
|
+
test("rejects missing errors field", () => {
|
|
416
|
+
const result = ValidationResultSchema.safeParse({
|
|
417
|
+
valid: true,
|
|
418
|
+
});
|
|
419
|
+
expect(result.success).toBe(false);
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
describe("validateAssessmentOptions", () => {
|
|
423
|
+
test("returns empty array for valid options", () => {
|
|
424
|
+
const errors = validateAssessmentOptions({
|
|
425
|
+
serverName: "test-server",
|
|
426
|
+
});
|
|
427
|
+
expect(errors).toEqual([]);
|
|
428
|
+
});
|
|
429
|
+
test("returns error array for invalid options", () => {
|
|
430
|
+
const errors = validateAssessmentOptions({});
|
|
431
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
432
|
+
});
|
|
433
|
+
test("includes path in error messages", () => {
|
|
434
|
+
const errors = validateAssessmentOptions({
|
|
435
|
+
serverName: "",
|
|
436
|
+
});
|
|
437
|
+
expect(errors[0]).toContain("serverName");
|
|
438
|
+
});
|
|
439
|
+
test("returns multiple errors for multiple issues", () => {
|
|
440
|
+
const errors = validateAssessmentOptions({
|
|
441
|
+
serverName: "test",
|
|
442
|
+
profile: "quick",
|
|
443
|
+
skipModules: ["security"],
|
|
444
|
+
});
|
|
445
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
describe("validateServerConfig", () => {
|
|
449
|
+
test("returns empty array for valid config", () => {
|
|
450
|
+
const errors = validateServerConfig({
|
|
451
|
+
url: "http://localhost:3000/mcp",
|
|
452
|
+
});
|
|
453
|
+
expect(errors).toEqual([]);
|
|
454
|
+
});
|
|
455
|
+
test("returns error array for invalid config", () => {
|
|
456
|
+
const errors = validateServerConfig({});
|
|
457
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
458
|
+
});
|
|
459
|
+
test("returns error for http without url", () => {
|
|
460
|
+
const errors = validateServerConfig({
|
|
461
|
+
transport: "http",
|
|
462
|
+
});
|
|
463
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
describe("parseAssessmentOptions", () => {
|
|
467
|
+
test("returns parsed options for valid input", () => {
|
|
468
|
+
const options = parseAssessmentOptions({
|
|
469
|
+
serverName: "test-server",
|
|
470
|
+
verbose: true,
|
|
471
|
+
});
|
|
472
|
+
expect(options.serverName).toBe("test-server");
|
|
473
|
+
expect(options.verbose).toBe(true);
|
|
474
|
+
});
|
|
475
|
+
test("throws ZodError for invalid input", () => {
|
|
476
|
+
expect(() => parseAssessmentOptions({})).toThrow(ZodError);
|
|
477
|
+
});
|
|
478
|
+
test("throws ZodError for empty serverName", () => {
|
|
479
|
+
expect(() => parseAssessmentOptions({ serverName: "" })).toThrow(ZodError);
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
describe("safeParseAssessmentOptions", () => {
|
|
483
|
+
test("returns success: true with data for valid input", () => {
|
|
484
|
+
const result = safeParseAssessmentOptions({
|
|
485
|
+
serverName: "test-server",
|
|
486
|
+
});
|
|
487
|
+
expect(result.success).toBe(true);
|
|
488
|
+
if (result.success) {
|
|
489
|
+
expect(result.data.serverName).toBe("test-server");
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
test("returns success: false with error for invalid input", () => {
|
|
493
|
+
const result = safeParseAssessmentOptions({});
|
|
494
|
+
expect(result.success).toBe(false);
|
|
495
|
+
if (!result.success) {
|
|
496
|
+
expect(result.error).toBeInstanceOf(ZodError);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
describe("parseModuleNames", () => {
|
|
501
|
+
test("parses single module name", () => {
|
|
502
|
+
const modules = parseModuleNames("functionality");
|
|
503
|
+
expect(modules).toEqual(["functionality"]);
|
|
504
|
+
});
|
|
505
|
+
test("parses comma-separated modules", () => {
|
|
506
|
+
const modules = parseModuleNames("functionality,security,documentation");
|
|
507
|
+
expect(modules).toEqual(["functionality", "security", "documentation"]);
|
|
508
|
+
});
|
|
509
|
+
test("trims whitespace", () => {
|
|
510
|
+
const modules = parseModuleNames(" functionality , security , temporal ");
|
|
511
|
+
expect(modules).toEqual(["functionality", "security", "temporal"]);
|
|
512
|
+
});
|
|
513
|
+
test("filters empty strings", () => {
|
|
514
|
+
const modules = parseModuleNames("functionality,,security");
|
|
515
|
+
expect(modules).toEqual(["functionality", "security"]);
|
|
516
|
+
});
|
|
517
|
+
test("throws for invalid module name", () => {
|
|
518
|
+
expect(() => parseModuleNames("functionality,invalid,security")).toThrow(ZodError);
|
|
519
|
+
});
|
|
520
|
+
test("returns empty array for empty string", () => {
|
|
521
|
+
// Empty string splits to [""], trim to [""], filter(Boolean) removes falsy values
|
|
522
|
+
// Result is empty array [] which is valid
|
|
523
|
+
const modules = parseModuleNames("");
|
|
524
|
+
expect(modules).toEqual([]);
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
describe("safeParseModuleNames", () => {
|
|
528
|
+
test("returns valid array for all valid names", () => {
|
|
529
|
+
const result = safeParseModuleNames("functionality,security");
|
|
530
|
+
expect(result.valid).toEqual(["functionality", "security"]);
|
|
531
|
+
expect(result.invalid).toEqual([]);
|
|
532
|
+
});
|
|
533
|
+
test("returns invalid array for unknown names", () => {
|
|
534
|
+
const result = safeParseModuleNames("invalid1,invalid2");
|
|
535
|
+
expect(result.valid).toEqual([]);
|
|
536
|
+
expect(result.invalid).toEqual(["invalid1", "invalid2"]);
|
|
537
|
+
});
|
|
538
|
+
test("handles mixed valid/invalid input", () => {
|
|
539
|
+
const result = safeParseModuleNames("functionality,invalid,security");
|
|
540
|
+
expect(result.valid).toEqual(["functionality", "security"]);
|
|
541
|
+
expect(result.invalid).toEqual(["invalid"]);
|
|
542
|
+
});
|
|
543
|
+
test("trims whitespace in names", () => {
|
|
544
|
+
const result = safeParseModuleNames(" functionality , security ");
|
|
545
|
+
expect(result.valid).toEqual(["functionality", "security"]);
|
|
546
|
+
});
|
|
547
|
+
test("handles empty string", () => {
|
|
548
|
+
const result = safeParseModuleNames("");
|
|
549
|
+
expect(result.valid).toEqual([]);
|
|
550
|
+
expect(result.invalid).toEqual([]);
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
});
|