@bryan-thompson/inspector-assessment-client 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{OAuthCallback-DGVqLct6.js → OAuthCallback-Xo9zS7pv.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-DHflRQgp.js → OAuthDebugCallback-CaIey8K_.js} +1 -1
- package/dist/assets/{index-Btl7vuTl.js → index-nCPw6E-c.js} +4 -4
- package/dist/index.html +1 -1
- package/lib/lib/assessmentTypes.d.ts +670 -0
- package/lib/lib/assessmentTypes.d.ts.map +1 -0
- package/lib/lib/assessmentTypes.js +220 -0
- package/lib/lib/aupPatterns.d.ts +63 -0
- package/lib/lib/aupPatterns.d.ts.map +1 -0
- package/lib/lib/aupPatterns.js +344 -0
- package/lib/lib/prohibitedLibraries.d.ts +76 -0
- package/lib/lib/prohibitedLibraries.d.ts.map +1 -0
- package/lib/lib/prohibitedLibraries.js +364 -0
- package/lib/lib/securityPatterns.d.ts +64 -0
- package/lib/lib/securityPatterns.d.ts.map +1 -0
- package/lib/lib/securityPatterns.js +453 -0
- package/lib/services/assessment/AssessmentOrchestrator.d.ts +88 -0
- package/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -0
- package/lib/services/assessment/AssessmentOrchestrator.js +418 -0
- package/lib/services/assessment/ResponseValidator.d.ts +69 -0
- package/lib/services/assessment/ResponseValidator.d.ts.map +1 -0
- package/lib/services/assessment/ResponseValidator.js +1038 -0
- package/lib/services/assessment/TestDataGenerator.d.ts +86 -0
- package/lib/services/assessment/TestDataGenerator.d.ts.map +1 -0
- package/lib/services/assessment/TestDataGenerator.js +669 -0
- package/lib/services/assessment/TestScenarioEngine.d.ts +91 -0
- package/lib/services/assessment/TestScenarioEngine.d.ts.map +1 -0
- package/lib/services/assessment/TestScenarioEngine.js +505 -0
- package/lib/services/assessment/ToolClassifier.d.ts +61 -0
- package/lib/services/assessment/ToolClassifier.d.ts.map +1 -0
- package/lib/services/assessment/ToolClassifier.js +349 -0
- package/lib/services/assessment/lib/claudeCodeBridge.d.ts +160 -0
- package/lib/services/assessment/lib/claudeCodeBridge.d.ts.map +1 -0
- package/lib/services/assessment/lib/claudeCodeBridge.js +357 -0
- package/lib/services/assessment/modules/AUPComplianceAssessor.d.ts +100 -0
- package/lib/services/assessment/modules/AUPComplianceAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/AUPComplianceAssessor.js +474 -0
- package/lib/services/assessment/modules/BaseAssessor.d.ts +71 -0
- package/lib/services/assessment/modules/BaseAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/BaseAssessor.js +171 -0
- package/lib/services/assessment/modules/DocumentationAssessor.d.ts +45 -0
- package/lib/services/assessment/modules/DocumentationAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/DocumentationAssessor.js +355 -0
- package/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +25 -0
- package/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/ErrorHandlingAssessor.js +564 -0
- package/lib/services/assessment/modules/FunctionalityAssessor.d.ts +20 -0
- package/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/FunctionalityAssessor.js +253 -0
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts +70 -0
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.js +508 -0
- package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts +70 -0
- package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/ManifestValidationAssessor.js +430 -0
- package/lib/services/assessment/modules/PortabilityAssessor.d.ts +43 -0
- package/lib/services/assessment/modules/PortabilityAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/PortabilityAssessor.js +347 -0
- package/lib/services/assessment/modules/ProhibitedLibrariesAssessor.d.ts +41 -0
- package/lib/services/assessment/modules/ProhibitedLibrariesAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/ProhibitedLibrariesAssessor.js +256 -0
- package/lib/services/assessment/modules/SecurityAssessor.d.ts +176 -0
- package/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/SecurityAssessor.js +1333 -0
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts +96 -0
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/ToolAnnotationAssessor.js +593 -0
- package/lib/services/assessment/modules/UsabilityAssessor.d.ts +21 -0
- package/lib/services/assessment/modules/UsabilityAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/UsabilityAssessor.js +241 -0
- package/lib/services/assessment/modules/index.d.ts +33 -0
- package/lib/services/assessment/modules/index.d.ts.map +1 -0
- package/lib/services/assessment/modules/index.js +35 -0
- package/package.json +15 -3
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Test Data Generator for MCP Tool Testing
|
|
3
|
+
* Generates realistic, context-aware test data based on parameter schemas
|
|
4
|
+
*
|
|
5
|
+
* Supports optional Claude Code integration for intelligent test generation
|
|
6
|
+
* when ClaudeCodeBridge is provided.
|
|
7
|
+
*/
|
|
8
|
+
export class TestDataGenerator {
|
|
9
|
+
// Optional Claude Code bridge for intelligent test generation
|
|
10
|
+
static claudeBridge = null;
|
|
11
|
+
/**
|
|
12
|
+
* Set the Claude Code bridge for intelligent test generation
|
|
13
|
+
* Call this once during initialization if Claude integration is enabled
|
|
14
|
+
*/
|
|
15
|
+
static setClaudeBridge(bridge) {
|
|
16
|
+
this.claudeBridge = bridge;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Check if Claude Code integration is available and enabled
|
|
20
|
+
*/
|
|
21
|
+
static isClaudeEnabled() {
|
|
22
|
+
return (this.claudeBridge !== null &&
|
|
23
|
+
this.claudeBridge.isFeatureEnabled("intelligentTestGeneration"));
|
|
24
|
+
}
|
|
25
|
+
// Realistic data pools for different types - using values that are more likely to exist
|
|
26
|
+
static REALISTIC_DATA = {
|
|
27
|
+
urls: [
|
|
28
|
+
"https://www.google.com", // Public, always accessible
|
|
29
|
+
"https://api.github.com/users/octocat", // Public API endpoint that exists
|
|
30
|
+
"https://jsonplaceholder.typicode.com/posts/1", // Test API that always works
|
|
31
|
+
"https://httpbin.org/get", // HTTP testing service
|
|
32
|
+
"https://example.com", // RFC 2606 reserved domain for examples
|
|
33
|
+
"https://www.wikipedia.org", // Public, stable site
|
|
34
|
+
"https://api.openweathermap.org/data/2.5/weather?q=London", // Public API
|
|
35
|
+
],
|
|
36
|
+
emails: [
|
|
37
|
+
"admin@example.com", // Common admin email
|
|
38
|
+
"support@example.com", // Common support email
|
|
39
|
+
"info@example.com", // Common info email
|
|
40
|
+
"test@test.com", // Generic test email
|
|
41
|
+
"user@domain.com", // Generic user email
|
|
42
|
+
"noreply@example.com", // Common no-reply format
|
|
43
|
+
"hello@world.com", // Simple, memorable
|
|
44
|
+
],
|
|
45
|
+
names: [
|
|
46
|
+
"Default", // Common default name
|
|
47
|
+
"Admin", // Common admin user
|
|
48
|
+
"Test User", // Clear test user
|
|
49
|
+
"Sample Item", // Generic sample
|
|
50
|
+
"Example Project", // Clear example
|
|
51
|
+
"Demo Application", // Common demo name
|
|
52
|
+
"Main", // Common main/primary name
|
|
53
|
+
],
|
|
54
|
+
ids: [
|
|
55
|
+
"1", // Simple numeric ID that often exists
|
|
56
|
+
"123", // Common test ID
|
|
57
|
+
"550e8400-e29b-41d4-a716-446655440000", // Valid UUID v4 (replaces "test")
|
|
58
|
+
"default", // Common default ID
|
|
59
|
+
"main", // Common main ID
|
|
60
|
+
"264051cd-48ab-80ff-864e-d1aa9bc41429", // Valid UUID from realistic data
|
|
61
|
+
"00000000-0000-0000-0000-000000000000", // Nil UUID (often used as placeholder)
|
|
62
|
+
"admin", // Common admin ID
|
|
63
|
+
"user1", // Common user ID pattern
|
|
64
|
+
],
|
|
65
|
+
paths: [
|
|
66
|
+
"/tmp/test.txt", // Common temp file path (usually writable)
|
|
67
|
+
"/home", // Common home directory
|
|
68
|
+
"./README.md", // Often exists in projects
|
|
69
|
+
"./package.json", // Common in Node projects
|
|
70
|
+
"./src", // Common source directory
|
|
71
|
+
"./test", // Common test directory
|
|
72
|
+
"./config", // Common config directory
|
|
73
|
+
"/var/log", // Common log directory (readable)
|
|
74
|
+
"/etc", // Common config directory (readable)
|
|
75
|
+
],
|
|
76
|
+
queries: [
|
|
77
|
+
"test", // Simple search term
|
|
78
|
+
"hello", // Common greeting
|
|
79
|
+
"*", // Wildcard that matches everything
|
|
80
|
+
"name", // Common field name
|
|
81
|
+
"id:1", // Common ID search
|
|
82
|
+
"status:active", // Common status filter
|
|
83
|
+
"type:user", // Common type filter
|
|
84
|
+
"limit:10", // Common pagination
|
|
85
|
+
'{"match_all": {}}', // Elasticsearch match all
|
|
86
|
+
],
|
|
87
|
+
numbers: [0, 1, 10, 100, 1000, 5, 50, 200, 404, 500],
|
|
88
|
+
booleans: [true, false],
|
|
89
|
+
jsonObjects: [
|
|
90
|
+
{ message: "Hello World" }, // Simple message object
|
|
91
|
+
{ status: "ok", code: 200 }, // Common status response
|
|
92
|
+
{ data: [], total: 0 }, // Empty result set
|
|
93
|
+
{ id: 1, name: "Test" }, // Simple entity
|
|
94
|
+
{ success: true }, // Common success response
|
|
95
|
+
{ error: false }, // Common no-error response
|
|
96
|
+
{ results: [] }, // Common empty results
|
|
97
|
+
{}, // Empty object (often valid)
|
|
98
|
+
],
|
|
99
|
+
arrays: [
|
|
100
|
+
[], // Empty array (often valid)
|
|
101
|
+
[1], // Single item
|
|
102
|
+
["a", "b", "c"], // Simple string array
|
|
103
|
+
[1, 2, 3], // Simple number array
|
|
104
|
+
[{ id: 1 }, { id: 2 }], // Simple object array
|
|
105
|
+
["test"], // Single test item
|
|
106
|
+
[true, false], // Boolean array
|
|
107
|
+
],
|
|
108
|
+
timestamps: [
|
|
109
|
+
new Date().toISOString(), // Current time (always valid)
|
|
110
|
+
new Date(Date.now() - 86400000).toISOString(), // Yesterday
|
|
111
|
+
new Date(Date.now() + 86400000).toISOString(), // Tomorrow
|
|
112
|
+
"2024-01-01T00:00:00Z", // New Year 2024
|
|
113
|
+
"2023-12-31T23:59:59Z", // End of 2023
|
|
114
|
+
new Date(0).toISOString(), // Unix epoch
|
|
115
|
+
"2024-06-15T12:00:00Z", // Midday mid-year
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Generate multiple test scenarios for a tool
|
|
120
|
+
* Uses Claude Code if available for intelligent generation,
|
|
121
|
+
* otherwise falls back to schema-based generation.
|
|
122
|
+
*/
|
|
123
|
+
static generateTestScenarios(tool) {
|
|
124
|
+
const scenarios = [];
|
|
125
|
+
// Always include at least one happy path scenario
|
|
126
|
+
scenarios.push(this.generateHappyPathScenario(tool));
|
|
127
|
+
// Add edge cases based on tool complexity
|
|
128
|
+
const edgeCases = this.generateEdgeCaseScenarios(tool);
|
|
129
|
+
scenarios.push(...edgeCases);
|
|
130
|
+
// Add boundary value scenarios for numeric inputs
|
|
131
|
+
const boundaryScenarios = this.generateBoundaryScenarios(tool);
|
|
132
|
+
scenarios.push(...boundaryScenarios);
|
|
133
|
+
// Add one error scenario to test error handling
|
|
134
|
+
scenarios.push(this.generateErrorScenario(tool));
|
|
135
|
+
return scenarios;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Generate test scenarios with optional Claude enhancement
|
|
139
|
+
* This async version tries Claude first if enabled, then falls back to schema-based.
|
|
140
|
+
*/
|
|
141
|
+
static async generateTestScenariosAsync(tool) {
|
|
142
|
+
// Try Claude-enhanced generation first
|
|
143
|
+
if (this.isClaudeEnabled() && this.claudeBridge) {
|
|
144
|
+
try {
|
|
145
|
+
const claudeParams = await this.claudeBridge.generateTestParameters(tool);
|
|
146
|
+
if (claudeParams && claudeParams.length > 0) {
|
|
147
|
+
console.log(`[TestDataGenerator] Using Claude-generated params for ${tool.name}`);
|
|
148
|
+
// Convert Claude params to TestScenario format
|
|
149
|
+
const claudeScenarios = claudeParams.map((params, index) => ({
|
|
150
|
+
name: this.getClaudeScenarioName(index),
|
|
151
|
+
description: `Claude-generated test case ${index + 1} for ${tool.name}`,
|
|
152
|
+
params,
|
|
153
|
+
expectedBehavior: "Should execute successfully with valid response",
|
|
154
|
+
category: this.getClaudeScenarioCategory(index),
|
|
155
|
+
source: "claude-generated",
|
|
156
|
+
}));
|
|
157
|
+
// Add one error scenario (Claude focuses on valid inputs)
|
|
158
|
+
claudeScenarios.push({
|
|
159
|
+
...this.generateErrorScenario(tool),
|
|
160
|
+
source: "schema-based",
|
|
161
|
+
});
|
|
162
|
+
return claudeScenarios;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
console.warn(`[TestDataGenerator] Claude generation failed for ${tool.name}, falling back to schema-based:`, error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Fall back to schema-based generation
|
|
170
|
+
return this.generateTestScenarios(tool).map((scenario) => ({
|
|
171
|
+
...scenario,
|
|
172
|
+
source: "schema-based",
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get scenario name based on index for Claude-generated scenarios
|
|
177
|
+
*/
|
|
178
|
+
static getClaudeScenarioName(index) {
|
|
179
|
+
const names = [
|
|
180
|
+
"Happy Path - Typical Usage",
|
|
181
|
+
"Edge Case - Boundary Values",
|
|
182
|
+
"Minimal Input - Required Fields Only",
|
|
183
|
+
"Comprehensive - All Fields Populated",
|
|
184
|
+
"Variant - Alternative Valid Input",
|
|
185
|
+
];
|
|
186
|
+
return names[index] || `Test Case ${index + 1}`;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get scenario category based on index for Claude-generated scenarios
|
|
190
|
+
*/
|
|
191
|
+
static getClaudeScenarioCategory(index) {
|
|
192
|
+
const categories = [
|
|
193
|
+
"happy_path",
|
|
194
|
+
"edge_case",
|
|
195
|
+
"boundary",
|
|
196
|
+
"happy_path",
|
|
197
|
+
"edge_case",
|
|
198
|
+
];
|
|
199
|
+
return categories[index] || "happy_path";
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Generate a happy path scenario with realistic data
|
|
203
|
+
*/
|
|
204
|
+
static generateHappyPathScenario(tool) {
|
|
205
|
+
const params = this.generateRealisticParams(tool, "typical");
|
|
206
|
+
return {
|
|
207
|
+
name: "Happy Path - Typical Usage",
|
|
208
|
+
description: `Test ${tool.name} with typical, valid inputs`,
|
|
209
|
+
params,
|
|
210
|
+
expectedBehavior: "Should execute successfully and return valid response",
|
|
211
|
+
category: "happy_path",
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Generate edge case scenarios
|
|
216
|
+
*/
|
|
217
|
+
static generateEdgeCaseScenarios(tool) {
|
|
218
|
+
const scenarios = [];
|
|
219
|
+
// Empty values scenario (where applicable)
|
|
220
|
+
const emptyParams = this.generateRealisticParams(tool, "empty");
|
|
221
|
+
if (Object.keys(emptyParams).length > 0) {
|
|
222
|
+
scenarios.push({
|
|
223
|
+
name: "Edge Case - Empty Values",
|
|
224
|
+
description: "Test with empty but valid values",
|
|
225
|
+
params: emptyParams,
|
|
226
|
+
expectedBehavior: "Should handle empty values gracefully",
|
|
227
|
+
category: "edge_case",
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
// Maximum values scenario
|
|
231
|
+
const maxParams = this.generateRealisticParams(tool, "maximum");
|
|
232
|
+
scenarios.push({
|
|
233
|
+
name: "Edge Case - Maximum Values",
|
|
234
|
+
description: "Test with maximum/large values",
|
|
235
|
+
params: maxParams,
|
|
236
|
+
expectedBehavior: "Should handle large inputs without issues",
|
|
237
|
+
category: "edge_case",
|
|
238
|
+
});
|
|
239
|
+
// Special characters scenario (for string inputs)
|
|
240
|
+
if (this.hasStringInputs(tool)) {
|
|
241
|
+
const specialParams = this.generateRealisticParams(tool, "special");
|
|
242
|
+
scenarios.push({
|
|
243
|
+
name: "Edge Case - Special Characters",
|
|
244
|
+
description: "Test with special characters and unicode",
|
|
245
|
+
params: specialParams,
|
|
246
|
+
expectedBehavior: "Should properly handle special characters",
|
|
247
|
+
category: "edge_case",
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
return scenarios;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Generate boundary value scenarios
|
|
254
|
+
*/
|
|
255
|
+
static generateBoundaryScenarios(tool) {
|
|
256
|
+
const scenarios = [];
|
|
257
|
+
if (!tool.inputSchema || tool.inputSchema.type !== "object") {
|
|
258
|
+
return scenarios;
|
|
259
|
+
}
|
|
260
|
+
const properties = tool.inputSchema.properties || {};
|
|
261
|
+
// OPTIMIZATION: Check if any fields have boundary constraints before generating tests
|
|
262
|
+
// This prevents running boundary tests on tools that don't define min/max constraints
|
|
263
|
+
let hasBoundaries = false;
|
|
264
|
+
for (const [_key, schema] of Object.entries(properties)) {
|
|
265
|
+
const schemaObj = schema;
|
|
266
|
+
if (schemaObj.minimum !== undefined ||
|
|
267
|
+
schemaObj.maximum !== undefined ||
|
|
268
|
+
schemaObj.minLength !== undefined ||
|
|
269
|
+
schemaObj.maxLength !== undefined) {
|
|
270
|
+
hasBoundaries = true;
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// Early return if no boundaries defined - saves 0-4 test scenarios per tool
|
|
275
|
+
if (!hasBoundaries) {
|
|
276
|
+
return scenarios;
|
|
277
|
+
}
|
|
278
|
+
for (const [key, schema] of Object.entries(properties)) {
|
|
279
|
+
const schemaObj = schema;
|
|
280
|
+
// Test numeric boundaries
|
|
281
|
+
if (schemaObj.type === "number" || schemaObj.type === "integer") {
|
|
282
|
+
if (schemaObj.minimum !== undefined) {
|
|
283
|
+
const params = this.generateRealisticParams(tool, "typical");
|
|
284
|
+
params[key] = schemaObj.minimum;
|
|
285
|
+
scenarios.push({
|
|
286
|
+
name: `Boundary - ${key} at minimum`,
|
|
287
|
+
description: `Test ${key} at its minimum value`,
|
|
288
|
+
params,
|
|
289
|
+
expectedBehavior: "Should accept minimum value",
|
|
290
|
+
category: "boundary",
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
if (schemaObj.maximum !== undefined) {
|
|
294
|
+
const params = this.generateRealisticParams(tool, "typical");
|
|
295
|
+
params[key] = schemaObj.maximum;
|
|
296
|
+
scenarios.push({
|
|
297
|
+
name: `Boundary - ${key} at maximum`,
|
|
298
|
+
description: `Test ${key} at its maximum value`,
|
|
299
|
+
params,
|
|
300
|
+
expectedBehavior: "Should accept maximum value",
|
|
301
|
+
category: "boundary",
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// Test string length boundaries
|
|
306
|
+
if (schemaObj.type === "string") {
|
|
307
|
+
if (schemaObj.minLength !== undefined) {
|
|
308
|
+
const params = this.generateRealisticParams(tool, "typical");
|
|
309
|
+
params[key] = "a".repeat(schemaObj.minLength);
|
|
310
|
+
scenarios.push({
|
|
311
|
+
name: `Boundary - ${key} at min length`,
|
|
312
|
+
description: `Test ${key} at minimum length`,
|
|
313
|
+
params,
|
|
314
|
+
expectedBehavior: "Should accept minimum length string",
|
|
315
|
+
category: "boundary",
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
if (schemaObj.maxLength !== undefined) {
|
|
319
|
+
const params = this.generateRealisticParams(tool, "typical");
|
|
320
|
+
params[key] = "a".repeat(schemaObj.maxLength);
|
|
321
|
+
scenarios.push({
|
|
322
|
+
name: `Boundary - ${key} at max length`,
|
|
323
|
+
description: `Test ${key} at maximum length`,
|
|
324
|
+
params,
|
|
325
|
+
expectedBehavior: "Should accept maximum length string",
|
|
326
|
+
category: "boundary",
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return scenarios;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Generate an error scenario
|
|
335
|
+
*/
|
|
336
|
+
static generateErrorScenario(tool) {
|
|
337
|
+
const params = {};
|
|
338
|
+
if (tool.inputSchema &&
|
|
339
|
+
tool.inputSchema.type === "object" &&
|
|
340
|
+
tool.inputSchema.properties) {
|
|
341
|
+
// Intentionally provide wrong types
|
|
342
|
+
for (const [key, schema] of Object.entries(tool.inputSchema.properties)) {
|
|
343
|
+
const schemaObj = schema;
|
|
344
|
+
switch (schemaObj.type) {
|
|
345
|
+
case "string":
|
|
346
|
+
params[key] = 123; // Wrong type
|
|
347
|
+
break;
|
|
348
|
+
case "number":
|
|
349
|
+
case "integer":
|
|
350
|
+
params[key] = "not_a_number"; // Wrong type
|
|
351
|
+
break;
|
|
352
|
+
case "boolean":
|
|
353
|
+
params[key] = "not_a_boolean"; // Wrong type
|
|
354
|
+
break;
|
|
355
|
+
case "array":
|
|
356
|
+
params[key] = "not_an_array"; // Wrong type
|
|
357
|
+
break;
|
|
358
|
+
case "object":
|
|
359
|
+
params[key] = "not_an_object"; // Wrong type
|
|
360
|
+
break;
|
|
361
|
+
default:
|
|
362
|
+
params[key] = null;
|
|
363
|
+
}
|
|
364
|
+
// Only set one wrong parameter to make the error clear
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
name: "Error Case - Invalid Type",
|
|
370
|
+
description: "Test error handling with invalid parameter types",
|
|
371
|
+
params,
|
|
372
|
+
expectedBehavior: "Should return clear error about invalid parameter type",
|
|
373
|
+
category: "error_case",
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Generate realistic parameters based on schema and variant
|
|
378
|
+
*/
|
|
379
|
+
static generateRealisticParams(tool, variant) {
|
|
380
|
+
const params = {};
|
|
381
|
+
if (!tool.inputSchema || tool.inputSchema.type !== "object") {
|
|
382
|
+
return params;
|
|
383
|
+
}
|
|
384
|
+
const properties = tool.inputSchema.properties || {};
|
|
385
|
+
for (const [key, schema] of Object.entries(properties)) {
|
|
386
|
+
params[key] = this.generateRealisticValue(key, schema, variant);
|
|
387
|
+
}
|
|
388
|
+
return params;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Generate a realistic value based on field name and schema
|
|
392
|
+
*/
|
|
393
|
+
static generateRealisticValue(fieldName, schema, variant) {
|
|
394
|
+
const lowerFieldName = fieldName.toLowerCase();
|
|
395
|
+
switch (schema.type) {
|
|
396
|
+
case "string":
|
|
397
|
+
// Check for enums first
|
|
398
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
399
|
+
return variant === "typical"
|
|
400
|
+
? schema.enum[0]
|
|
401
|
+
: schema.enum[schema.enum.length - 1];
|
|
402
|
+
}
|
|
403
|
+
// Context-aware string generation
|
|
404
|
+
if (lowerFieldName.includes("url") ||
|
|
405
|
+
lowerFieldName.includes("link") ||
|
|
406
|
+
lowerFieldName.includes("endpoint")) {
|
|
407
|
+
return variant === "empty"
|
|
408
|
+
? ""
|
|
409
|
+
: variant === "maximum"
|
|
410
|
+
? "https://very-long-domain-name-for-testing-maximum-length.example.com/path/to/resource?param1=value1¶m2=value2"
|
|
411
|
+
: variant === "special"
|
|
412
|
+
? "https://example.com/path?special=!@#$%^&*()"
|
|
413
|
+
: this.REALISTIC_DATA.urls[Math.floor(Math.random() * this.REALISTIC_DATA.urls.length)];
|
|
414
|
+
}
|
|
415
|
+
if (lowerFieldName.includes("email") ||
|
|
416
|
+
lowerFieldName.includes("mail")) {
|
|
417
|
+
return variant === "empty"
|
|
418
|
+
? ""
|
|
419
|
+
: variant === "maximum"
|
|
420
|
+
? "very.long.email.address.for.testing@subdomain.example-company.co.uk"
|
|
421
|
+
: variant === "special"
|
|
422
|
+
? "user+tag@example.com"
|
|
423
|
+
: this.REALISTIC_DATA.emails[Math.floor(Math.random() * this.REALISTIC_DATA.emails.length)];
|
|
424
|
+
}
|
|
425
|
+
if (lowerFieldName.includes("path") ||
|
|
426
|
+
lowerFieldName.includes("file") ||
|
|
427
|
+
lowerFieldName.includes("directory") ||
|
|
428
|
+
lowerFieldName.includes("folder")) {
|
|
429
|
+
return variant === "empty"
|
|
430
|
+
? ""
|
|
431
|
+
: variant === "maximum"
|
|
432
|
+
? "/very/long/path/to/deeply/nested/directory/structure/for/testing/file.txt"
|
|
433
|
+
: variant === "special"
|
|
434
|
+
? "./path/with spaces/and-special#chars.txt"
|
|
435
|
+
: this.REALISTIC_DATA.paths[Math.floor(Math.random() * this.REALISTIC_DATA.paths.length)];
|
|
436
|
+
}
|
|
437
|
+
if (lowerFieldName.includes("query") ||
|
|
438
|
+
lowerFieldName.includes("search") ||
|
|
439
|
+
lowerFieldName.includes("filter")) {
|
|
440
|
+
return variant === "empty"
|
|
441
|
+
? "test" // Use "test" instead of "" to ensure search tools have valid input
|
|
442
|
+
: variant === "maximum"
|
|
443
|
+
? "very long search query with many terms for testing maximum input length handling"
|
|
444
|
+
: variant === "special"
|
|
445
|
+
? 'search with "quotes" and special: characters!'
|
|
446
|
+
: this.REALISTIC_DATA.queries[Math.floor(Math.random() * this.REALISTIC_DATA.queries.length)];
|
|
447
|
+
}
|
|
448
|
+
if (lowerFieldName.includes("id") ||
|
|
449
|
+
lowerFieldName.includes("key") ||
|
|
450
|
+
lowerFieldName.includes("identifier")) {
|
|
451
|
+
// Check if this field requires UUID format based on common patterns
|
|
452
|
+
const requiresUuid = lowerFieldName.includes("uuid") ||
|
|
453
|
+
lowerFieldName.includes("page_id") ||
|
|
454
|
+
lowerFieldName.includes("database_id") ||
|
|
455
|
+
lowerFieldName.includes("user_id") ||
|
|
456
|
+
lowerFieldName.includes("block_id") ||
|
|
457
|
+
lowerFieldName.includes("comment_id") ||
|
|
458
|
+
lowerFieldName.includes("workspace_id") ||
|
|
459
|
+
lowerFieldName.includes("notion") ||
|
|
460
|
+
// Check schema description for UUID hints
|
|
461
|
+
(schema.description &&
|
|
462
|
+
(schema.description.toLowerCase().includes("uuid") ||
|
|
463
|
+
schema.description
|
|
464
|
+
.toLowerCase()
|
|
465
|
+
.includes("universally unique")));
|
|
466
|
+
if (requiresUuid) {
|
|
467
|
+
// Always return a valid UUID for UUID-required fields
|
|
468
|
+
return variant === "empty"
|
|
469
|
+
? "00000000-0000-0000-0000-000000000000" // Nil UUID
|
|
470
|
+
: "550e8400-e29b-41d4-a716-446655440000"; // Valid UUID v4
|
|
471
|
+
}
|
|
472
|
+
return variant === "empty"
|
|
473
|
+
? "1" // Minimal non-empty ID to avoid creating invalid entities
|
|
474
|
+
: variant === "maximum"
|
|
475
|
+
? "very_long_identifier_string_for_testing_maximum_length_handling_in_system"
|
|
476
|
+
: this.REALISTIC_DATA.ids[Math.floor(Math.random() * this.REALISTIC_DATA.ids.length)];
|
|
477
|
+
}
|
|
478
|
+
if (lowerFieldName.includes("name") ||
|
|
479
|
+
lowerFieldName.includes("title") ||
|
|
480
|
+
lowerFieldName.includes("label")) {
|
|
481
|
+
return variant === "empty"
|
|
482
|
+
? "a" // Minimal non-empty value to avoid breaking search functionality
|
|
483
|
+
: variant === "maximum"
|
|
484
|
+
? "Very Long Name For Testing Maximum String Length Handling In The System"
|
|
485
|
+
: variant === "special"
|
|
486
|
+
? "Name with Special™ Characters® and Émojis 🎉"
|
|
487
|
+
: this.REALISTIC_DATA.names[Math.floor(Math.random() * this.REALISTIC_DATA.names.length)];
|
|
488
|
+
}
|
|
489
|
+
if (lowerFieldName.includes("date") ||
|
|
490
|
+
lowerFieldName.includes("time")) {
|
|
491
|
+
return variant === "empty" ? "" : this.REALISTIC_DATA.timestamps[0];
|
|
492
|
+
}
|
|
493
|
+
// Default string value - try to be contextual
|
|
494
|
+
return variant === "empty"
|
|
495
|
+
? ""
|
|
496
|
+
: variant === "maximum"
|
|
497
|
+
? "x".repeat(100)
|
|
498
|
+
: variant === "special"
|
|
499
|
+
? 'Special chars: !@#$%^&*()_+-=[]{}|;:",.<>?/~`'
|
|
500
|
+
: "test"; // Simple, generic test value that often works
|
|
501
|
+
case "number":
|
|
502
|
+
case "integer":
|
|
503
|
+
if (variant === "maximum") {
|
|
504
|
+
return schema.maximum || 999999;
|
|
505
|
+
}
|
|
506
|
+
if (variant === "empty") {
|
|
507
|
+
return schema.minimum || 0;
|
|
508
|
+
}
|
|
509
|
+
// Context-aware number generation
|
|
510
|
+
if (lowerFieldName.includes("port")) {
|
|
511
|
+
return 8080;
|
|
512
|
+
}
|
|
513
|
+
if (lowerFieldName.includes("timeout") ||
|
|
514
|
+
lowerFieldName.includes("delay")) {
|
|
515
|
+
return 5000; // milliseconds
|
|
516
|
+
}
|
|
517
|
+
if (lowerFieldName.includes("count") ||
|
|
518
|
+
lowerFieldName.includes("limit")) {
|
|
519
|
+
return 10;
|
|
520
|
+
}
|
|
521
|
+
if (lowerFieldName.includes("page") ||
|
|
522
|
+
lowerFieldName.includes("offset")) {
|
|
523
|
+
return 0;
|
|
524
|
+
}
|
|
525
|
+
if (lowerFieldName.includes("size") ||
|
|
526
|
+
lowerFieldName.includes("length")) {
|
|
527
|
+
return 100;
|
|
528
|
+
}
|
|
529
|
+
return schema.minimum || 1;
|
|
530
|
+
case "boolean":
|
|
531
|
+
return variant === "empty" ? false : true;
|
|
532
|
+
case "array":
|
|
533
|
+
if (variant === "empty") {
|
|
534
|
+
// For mutation tools with array inputs, empty arrays are valid but useless for testing
|
|
535
|
+
// Generate one minimal item instead to make the test meaningful
|
|
536
|
+
const isMutationField = lowerFieldName.includes("entities") ||
|
|
537
|
+
lowerFieldName.includes("relations") ||
|
|
538
|
+
lowerFieldName.includes("observations") ||
|
|
539
|
+
lowerFieldName.includes("documents");
|
|
540
|
+
if (isMutationField && schema.items) {
|
|
541
|
+
// Generate one minimal item even for "empty" variant
|
|
542
|
+
const item = this.generateValueFromSchema(schema.items, "empty");
|
|
543
|
+
return [item];
|
|
544
|
+
}
|
|
545
|
+
return [];
|
|
546
|
+
}
|
|
547
|
+
if (variant === "maximum") {
|
|
548
|
+
// Generate multiple items
|
|
549
|
+
const count = 10;
|
|
550
|
+
if (schema.items) {
|
|
551
|
+
return Array(count)
|
|
552
|
+
.fill(0)
|
|
553
|
+
.map(() => this.generateValueFromSchema(schema.items, variant));
|
|
554
|
+
}
|
|
555
|
+
return Array(count)
|
|
556
|
+
.fill(0)
|
|
557
|
+
.map((_, i) => `item_${i}`);
|
|
558
|
+
}
|
|
559
|
+
// Typical variant - generate realistic array
|
|
560
|
+
if (schema.items) {
|
|
561
|
+
// Generate 1-2 items based on schema.items
|
|
562
|
+
const item = this.generateValueFromSchema(schema.items, variant);
|
|
563
|
+
return [item];
|
|
564
|
+
}
|
|
565
|
+
// Context-aware array generation (fallback for simple arrays without schema.items)
|
|
566
|
+
if (lowerFieldName.includes("tag") ||
|
|
567
|
+
lowerFieldName.includes("label")) {
|
|
568
|
+
return ["tag1", "tag2", "tag3"];
|
|
569
|
+
}
|
|
570
|
+
if (lowerFieldName.includes("id")) {
|
|
571
|
+
return ["id_1", "id_2", "id_3"];
|
|
572
|
+
}
|
|
573
|
+
return this.REALISTIC_DATA.arrays[1];
|
|
574
|
+
case "object":
|
|
575
|
+
// Don't return empty object for "empty" variant
|
|
576
|
+
// Let it fall through to generate minimal object properties
|
|
577
|
+
// This avoids creating objects with no required fields
|
|
578
|
+
if (variant === "maximum") {
|
|
579
|
+
return this.REALISTIC_DATA.jsonObjects[4]; // deeply nested
|
|
580
|
+
}
|
|
581
|
+
// Context-aware object generation
|
|
582
|
+
if (lowerFieldName.includes("config") ||
|
|
583
|
+
lowerFieldName.includes("settings")) {
|
|
584
|
+
return variant === "empty"
|
|
585
|
+
? { enabled: false }
|
|
586
|
+
: { enabled: true, timeout: 5000, retries: 3 };
|
|
587
|
+
}
|
|
588
|
+
if (lowerFieldName.includes("metadata") ||
|
|
589
|
+
lowerFieldName.includes("meta")) {
|
|
590
|
+
return variant === "empty"
|
|
591
|
+
? { version: "1.0.0" }
|
|
592
|
+
: {
|
|
593
|
+
created: new Date().toISOString(),
|
|
594
|
+
version: "1.0.0",
|
|
595
|
+
author: "test",
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
if (lowerFieldName.includes("filter") ||
|
|
599
|
+
lowerFieldName.includes("query")) {
|
|
600
|
+
return variant === "empty"
|
|
601
|
+
? { limit: 1 }
|
|
602
|
+
: { status: "active", type: "user", limit: 10 };
|
|
603
|
+
}
|
|
604
|
+
return variant === "empty"
|
|
605
|
+
? { id: 1 }
|
|
606
|
+
: this.REALISTIC_DATA.jsonObjects[0];
|
|
607
|
+
default:
|
|
608
|
+
// Return safe default instead of null to prevent tool crashes
|
|
609
|
+
return "test";
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Check if tool has string inputs
|
|
614
|
+
*/
|
|
615
|
+
static hasStringInputs(tool) {
|
|
616
|
+
if (!tool.inputSchema || tool.inputSchema.type !== "object") {
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
const properties = tool.inputSchema.properties || {};
|
|
620
|
+
for (const schema of Object.values(properties)) {
|
|
621
|
+
if (schema.type === "string") {
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Generate a single realistic value for backward compatibility
|
|
629
|
+
*/
|
|
630
|
+
static generateSingleValue(fieldName, schema) {
|
|
631
|
+
return this.generateRealisticValue(fieldName, schema, "typical");
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Generate value from JSON schema definition
|
|
635
|
+
*/
|
|
636
|
+
static generateValueFromSchema(schema, variant) {
|
|
637
|
+
if (!schema || !schema.type) {
|
|
638
|
+
// Return safe default instead of null to prevent tool crashes
|
|
639
|
+
return "test";
|
|
640
|
+
}
|
|
641
|
+
switch (schema.type) {
|
|
642
|
+
case "object": {
|
|
643
|
+
const obj = {};
|
|
644
|
+
if (schema.properties) {
|
|
645
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
646
|
+
obj[key] = this.generateRealisticValue(key, propSchema, variant);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return obj;
|
|
650
|
+
}
|
|
651
|
+
case "array":
|
|
652
|
+
if (schema.items) {
|
|
653
|
+
const item = this.generateValueFromSchema(schema.items, variant);
|
|
654
|
+
return [item];
|
|
655
|
+
}
|
|
656
|
+
return [];
|
|
657
|
+
case "string":
|
|
658
|
+
return variant === "empty" ? "" : "test";
|
|
659
|
+
case "number":
|
|
660
|
+
case "integer":
|
|
661
|
+
return variant === "empty" ? 0 : 1;
|
|
662
|
+
case "boolean":
|
|
663
|
+
return variant === "empty" ? false : true;
|
|
664
|
+
default:
|
|
665
|
+
// Return safe default instead of null to prevent tool crashes
|
|
666
|
+
return "test";
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|