@rog0x/mcp-testing-tools 1.0.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/README.md +109 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +250 -0
- package/dist/tools/api-mock.d.ts +30 -0
- package/dist/tools/api-mock.js +193 -0
- package/dist/tools/assertion-helper.d.ts +25 -0
- package/dist/tools/assertion-helper.js +223 -0
- package/dist/tools/mock-data.d.ts +14 -0
- package/dist/tools/mock-data.js +191 -0
- package/dist/tools/test-coverage-analyzer.d.ts +30 -0
- package/dist/tools/test-coverage-analyzer.js +247 -0
- package/dist/tools/test-generator.d.ts +7 -0
- package/dist/tools/test-generator.js +278 -0
- package/package.json +26 -0
- package/src/index.ts +311 -0
- package/src/tools/api-mock.ts +221 -0
- package/src/tools/assertion-helper.ts +273 -0
- package/src/tools/mock-data.ts +241 -0
- package/src/tools/test-coverage-analyzer.ts +283 -0
- package/src/tools/test-generator.ts +300 -0
- package/tsconfig.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# @rog0x/mcp-testing-tools
|
|
2
|
+
|
|
3
|
+
Testing and quality assurance tools for AI agents, exposed via the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/).
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
### generate_tests
|
|
8
|
+
|
|
9
|
+
Generate test cases from a function signature. Produces four categories of tests as ready-to-run Jest or Vitest code:
|
|
10
|
+
|
|
11
|
+
- **Happy path** -- valid inputs, expected outputs
|
|
12
|
+
- **Edge cases** -- empty strings, zero values, null parameters
|
|
13
|
+
- **Error cases** -- missing arguments, wrong types
|
|
14
|
+
- **Boundary values** -- extreme numbers, large arrays, NaN
|
|
15
|
+
|
|
16
|
+
**Parameters:**
|
|
17
|
+
|
|
18
|
+
| Name | Type | Required | Description |
|
|
19
|
+
|------|------|----------|-------------|
|
|
20
|
+
| `signature` | string | Yes | Function signature, e.g. `async function fetchUser(id: number): Promise<User>` |
|
|
21
|
+
| `framework` | string | No | `jest` or `vitest` (default: `vitest`) |
|
|
22
|
+
| `module_path` | string | No | Import path for the module under test (default: `./module`) |
|
|
23
|
+
|
|
24
|
+
### generate_mock_data
|
|
25
|
+
|
|
26
|
+
Generate realistic mock data for testing. Supported types:
|
|
27
|
+
|
|
28
|
+
`name`, `email`, `address`, `date`, `uuid`, `phone`, `company`, `credit_card`, `ip`
|
|
29
|
+
|
|
30
|
+
**Parameters:**
|
|
31
|
+
|
|
32
|
+
| Name | Type | Required | Description |
|
|
33
|
+
|------|------|----------|-------------|
|
|
34
|
+
| `type` | string | Yes | Data type to generate |
|
|
35
|
+
| `count` | number | No | Number of items (default: 10, max: 1000) |
|
|
36
|
+
| `locale` | string | No | `en` or `es` (default: `en`) |
|
|
37
|
+
| `types` | string[] | No | Generate mixed records with multiple field types |
|
|
38
|
+
|
|
39
|
+
### generate_api_mock
|
|
40
|
+
|
|
41
|
+
Generate mock API responses from a schema definition. Creates realistic JSON payloads for REST endpoints by inferring values from field names and types.
|
|
42
|
+
|
|
43
|
+
**Parameters:**
|
|
44
|
+
|
|
45
|
+
| Name | Type | Required | Description |
|
|
46
|
+
|------|------|----------|-------------|
|
|
47
|
+
| `endpoint` | string | Yes | API endpoint path |
|
|
48
|
+
| `method` | string | No | HTTP method (default: `GET`) |
|
|
49
|
+
| `fields` | object[] | Yes | Field schemas with `name`, `type`, optional `items`, `fields`, `nullable`, `enum` |
|
|
50
|
+
| `count` | number | No | Number of records (default: 1, max: 100) |
|
|
51
|
+
| `status_code` | number | No | HTTP status code (default: 200) |
|
|
52
|
+
| `wrap_in_envelope` | boolean | No | Wrap in `{ success, data, meta }` (default: true) |
|
|
53
|
+
|
|
54
|
+
### analyze_test_coverage
|
|
55
|
+
|
|
56
|
+
Analyze source code and test code to find untested functions. Prioritizes suggestions by:
|
|
57
|
+
|
|
58
|
+
- Export status (public API surface)
|
|
59
|
+
- Cyclomatic complexity estimate
|
|
60
|
+
- Parameter count
|
|
61
|
+
- Async functions (more error paths)
|
|
62
|
+
|
|
63
|
+
**Parameters:**
|
|
64
|
+
|
|
65
|
+
| Name | Type | Required | Description |
|
|
66
|
+
|------|------|----------|-------------|
|
|
67
|
+
| `source_code` | string | Yes | Source code to analyze |
|
|
68
|
+
| `test_code` | string | Yes | Existing test code |
|
|
69
|
+
| `source_file_name` | string | No | Filename label (default: `source.ts`) |
|
|
70
|
+
|
|
71
|
+
### generate_assertions
|
|
72
|
+
|
|
73
|
+
Generate detailed assertion code by comparing expected and actual values. Performs deep diff and produces per-field assertions with descriptive comments.
|
|
74
|
+
|
|
75
|
+
**Parameters:**
|
|
76
|
+
|
|
77
|
+
| Name | Type | Required | Description |
|
|
78
|
+
|------|------|----------|-------------|
|
|
79
|
+
| `expected` | string | Yes | Expected value as JSON string |
|
|
80
|
+
| `actual` | string | Yes | Actual value as JSON string |
|
|
81
|
+
| `label` | string | No | Description for the comparison |
|
|
82
|
+
| `framework` | string | No | `jest`, `vitest`, or `chai` (default: `jest`) |
|
|
83
|
+
| `deep` | boolean | No | Deep equality for objects/arrays (default: true) |
|
|
84
|
+
|
|
85
|
+
## Setup
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm install
|
|
89
|
+
npm run build
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Usage with Claude Desktop
|
|
93
|
+
|
|
94
|
+
Add to your `claude_desktop_config.json`:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"mcpServers": {
|
|
99
|
+
"testing-tools": {
|
|
100
|
+
"command": "node",
|
|
101
|
+
"args": ["path/to/mcp-testing-tools/dist/index.js"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { generateTests } from "./tools/test-generator.js";
|
|
6
|
+
import { generateMockData, generateMixedMockData } from "./tools/mock-data.js";
|
|
7
|
+
import { generateApiMockResponse } from "./tools/api-mock.js";
|
|
8
|
+
import { analyzeCoverage } from "./tools/test-coverage-analyzer.js";
|
|
9
|
+
import { generateAssertions } from "./tools/assertion-helper.js";
|
|
10
|
+
const server = new Server({
|
|
11
|
+
name: "mcp-testing-tools",
|
|
12
|
+
version: "1.0.0",
|
|
13
|
+
}, {
|
|
14
|
+
capabilities: {
|
|
15
|
+
tools: {},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
// List available tools
|
|
19
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
20
|
+
tools: [
|
|
21
|
+
{
|
|
22
|
+
name: "generate_tests",
|
|
23
|
+
description: "Generate test cases from a function signature. Produces happy path, edge cases, error cases, and boundary value tests as Jest/Vitest test code.",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: {
|
|
27
|
+
signature: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: 'The function signature to generate tests for, e.g. "export async function fetchUser(id: number): Promise<User>"',
|
|
30
|
+
},
|
|
31
|
+
framework: {
|
|
32
|
+
type: "string",
|
|
33
|
+
enum: ["jest", "vitest"],
|
|
34
|
+
description: "Test framework to generate for (default: vitest)",
|
|
35
|
+
},
|
|
36
|
+
module_path: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: 'Import path for the module under test (default: "./module")',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
required: ["signature"],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "generate_mock_data",
|
|
46
|
+
description: "Generate realistic mock data: names, emails, addresses, dates, UUIDs, phone numbers, company names, credit cards (fake), IP addresses. Configurable count and locale.",
|
|
47
|
+
inputSchema: {
|
|
48
|
+
type: "object",
|
|
49
|
+
properties: {
|
|
50
|
+
type: {
|
|
51
|
+
type: "string",
|
|
52
|
+
description: "Type of data to generate: name, email, address, date, uuid, phone, company, credit_card, ip",
|
|
53
|
+
},
|
|
54
|
+
count: {
|
|
55
|
+
type: "number",
|
|
56
|
+
description: "Number of items to generate (default: 10, max: 1000)",
|
|
57
|
+
},
|
|
58
|
+
locale: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: 'Locale for generated data: "en" or "es" (default: "en")',
|
|
61
|
+
},
|
|
62
|
+
types: {
|
|
63
|
+
type: "array",
|
|
64
|
+
items: { type: "string" },
|
|
65
|
+
description: "Generate mixed records with multiple field types (alternative to single type). Each record will have all specified fields.",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
required: ["type"],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "generate_api_mock",
|
|
73
|
+
description: "Generate mock API responses from a schema. Creates realistic JSON responses for REST endpoints based on field names and types.",
|
|
74
|
+
inputSchema: {
|
|
75
|
+
type: "object",
|
|
76
|
+
properties: {
|
|
77
|
+
endpoint: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: 'API endpoint path, e.g. "/api/v1/users"',
|
|
80
|
+
},
|
|
81
|
+
method: {
|
|
82
|
+
type: "string",
|
|
83
|
+
enum: ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
84
|
+
description: "HTTP method (default: GET)",
|
|
85
|
+
},
|
|
86
|
+
fields: {
|
|
87
|
+
type: "array",
|
|
88
|
+
description: "Array of field schemas. Each field has: name (string), type (string|number|boolean|date|array|object), optional items (for arrays), optional fields (for nested objects), optional nullable, optional enum.",
|
|
89
|
+
items: {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
name: { type: "string" },
|
|
93
|
+
type: { type: "string" },
|
|
94
|
+
items: { type: "array" },
|
|
95
|
+
fields: { type: "array" },
|
|
96
|
+
nullable: { type: "boolean" },
|
|
97
|
+
enum: { type: "array", items: { type: "string" } },
|
|
98
|
+
},
|
|
99
|
+
required: ["name", "type"],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
count: {
|
|
103
|
+
type: "number",
|
|
104
|
+
description: "Number of records to generate (default: 1, max: 100)",
|
|
105
|
+
},
|
|
106
|
+
status_code: {
|
|
107
|
+
type: "number",
|
|
108
|
+
description: "HTTP status code for the response (default: 200)",
|
|
109
|
+
},
|
|
110
|
+
wrap_in_envelope: {
|
|
111
|
+
type: "boolean",
|
|
112
|
+
description: "Wrap response in { success, data, meta } envelope (default: true)",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
required: ["endpoint", "fields"],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "analyze_test_coverage",
|
|
120
|
+
description: "Parse source code and test code to identify untested functions. Suggests which functions need tests most based on complexity, export status, and parameter count.",
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {
|
|
124
|
+
source_code: {
|
|
125
|
+
type: "string",
|
|
126
|
+
description: "The source code to analyze for functions",
|
|
127
|
+
},
|
|
128
|
+
test_code: {
|
|
129
|
+
type: "string",
|
|
130
|
+
description: "The test code to check which functions are already tested",
|
|
131
|
+
},
|
|
132
|
+
source_file_name: {
|
|
133
|
+
type: "string",
|
|
134
|
+
description: 'Filename for the source (used in output, default: "source.ts")',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
required: ["source_code", "test_code"],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "generate_assertions",
|
|
142
|
+
description: "Given expected and actual values (as JSON strings), generate detailed assertion code with descriptive messages. Supports deep object comparison, array comparison, and type checking.",
|
|
143
|
+
inputSchema: {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: {
|
|
146
|
+
expected: {
|
|
147
|
+
type: "string",
|
|
148
|
+
description: "The expected value as a JSON string (or plain string for primitives)",
|
|
149
|
+
},
|
|
150
|
+
actual: {
|
|
151
|
+
type: "string",
|
|
152
|
+
description: "The actual value as a JSON string (or plain string for primitives)",
|
|
153
|
+
},
|
|
154
|
+
label: {
|
|
155
|
+
type: "string",
|
|
156
|
+
description: 'Descriptive label for the comparison (default: "value comparison")',
|
|
157
|
+
},
|
|
158
|
+
framework: {
|
|
159
|
+
type: "string",
|
|
160
|
+
enum: ["jest", "vitest", "chai"],
|
|
161
|
+
description: "Assertion framework to generate for (default: jest)",
|
|
162
|
+
},
|
|
163
|
+
deep: {
|
|
164
|
+
type: "boolean",
|
|
165
|
+
description: "Use deep equality for objects/arrays (default: true)",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
required: ["expected", "actual"],
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
}));
|
|
173
|
+
// Handle tool calls
|
|
174
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
175
|
+
const { name, arguments: args } = request.params;
|
|
176
|
+
try {
|
|
177
|
+
switch (name) {
|
|
178
|
+
case "generate_tests": {
|
|
179
|
+
const result = generateTests(args?.signature, args?.framework || "vitest", args?.module_path || "./module");
|
|
180
|
+
return {
|
|
181
|
+
content: [{ type: "text", text: result }],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
case "generate_mock_data": {
|
|
185
|
+
if (args?.types && Array.isArray(args.types)) {
|
|
186
|
+
const result = generateMixedMockData(args.types, args?.count || 10, args?.locale || "en");
|
|
187
|
+
return {
|
|
188
|
+
content: [
|
|
189
|
+
{ type: "text", text: JSON.stringify(result, null, 2) },
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const result = generateMockData(args?.type, args?.count || 10, args?.locale || "en");
|
|
194
|
+
return {
|
|
195
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
case "generate_api_mock": {
|
|
199
|
+
const result = generateApiMockResponse({
|
|
200
|
+
endpoint: args?.endpoint,
|
|
201
|
+
method: args?.method || "GET",
|
|
202
|
+
fields: args?.fields,
|
|
203
|
+
count: args?.count || 1,
|
|
204
|
+
statusCode: args?.status_code || 200,
|
|
205
|
+
wrapInEnvelope: args?.wrap_in_envelope !== undefined
|
|
206
|
+
? args.wrap_in_envelope
|
|
207
|
+
: true,
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
case "analyze_test_coverage": {
|
|
214
|
+
const result = analyzeCoverage(args?.source_code, args?.test_code, args?.source_file_name || "source.ts");
|
|
215
|
+
return {
|
|
216
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
case "generate_assertions": {
|
|
220
|
+
const result = generateAssertions({
|
|
221
|
+
expected: args?.expected,
|
|
222
|
+
actual: args?.actual,
|
|
223
|
+
label: args?.label || "value comparison",
|
|
224
|
+
framework: args?.framework || "jest",
|
|
225
|
+
deep: args?.deep !== undefined ? args.deep : true,
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
default:
|
|
232
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
237
|
+
return {
|
|
238
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
239
|
+
isError: true,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
// Start server
|
|
244
|
+
async function main() {
|
|
245
|
+
const transport = new StdioServerTransport();
|
|
246
|
+
await server.connect(transport);
|
|
247
|
+
console.error("MCP Testing Tools server running on stdio");
|
|
248
|
+
}
|
|
249
|
+
main().catch(console.error);
|
|
250
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate mock API responses from a schema definition.
|
|
3
|
+
* Creates realistic JSON responses for REST endpoints
|
|
4
|
+
* based on field names and types.
|
|
5
|
+
*/
|
|
6
|
+
interface FieldSchema {
|
|
7
|
+
name: string;
|
|
8
|
+
type: string;
|
|
9
|
+
items?: FieldSchema[];
|
|
10
|
+
fields?: FieldSchema[];
|
|
11
|
+
nullable?: boolean;
|
|
12
|
+
enum?: string[];
|
|
13
|
+
}
|
|
14
|
+
interface ApiMockOptions {
|
|
15
|
+
endpoint: string;
|
|
16
|
+
method: string;
|
|
17
|
+
fields: FieldSchema[];
|
|
18
|
+
count?: number;
|
|
19
|
+
statusCode?: number;
|
|
20
|
+
wrapInEnvelope?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare function generateApiMockResponse(options: ApiMockOptions): {
|
|
23
|
+
endpoint: string;
|
|
24
|
+
method: string;
|
|
25
|
+
statusCode: number;
|
|
26
|
+
headers: Record<string, string>;
|
|
27
|
+
body: unknown;
|
|
28
|
+
};
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=api-mock.d.ts.map
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate mock API responses from a schema definition.
|
|
3
|
+
* Creates realistic JSON responses for REST endpoints
|
|
4
|
+
* based on field names and types.
|
|
5
|
+
*/
|
|
6
|
+
class MockRng {
|
|
7
|
+
s;
|
|
8
|
+
constructor(seed) {
|
|
9
|
+
this.s = seed;
|
|
10
|
+
}
|
|
11
|
+
next() {
|
|
12
|
+
this.s = (this.s * 1664525 + 1013904223) & 0x7fffffff;
|
|
13
|
+
return this.s / 0x7fffffff;
|
|
14
|
+
}
|
|
15
|
+
int(min, max) {
|
|
16
|
+
return Math.floor(this.next() * (max - min + 1)) + min;
|
|
17
|
+
}
|
|
18
|
+
pick(arr) {
|
|
19
|
+
return arr[this.int(0, arr.length - 1)];
|
|
20
|
+
}
|
|
21
|
+
float(min, max, decimals = 2) {
|
|
22
|
+
return parseFloat((this.next() * (max - min) + min).toFixed(decimals));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const SAMPLE_WORDS = [
|
|
26
|
+
"alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf",
|
|
27
|
+
"hotel", "india", "juliet", "kilo", "lima", "mike", "november",
|
|
28
|
+
];
|
|
29
|
+
const SAMPLE_NAMES = [
|
|
30
|
+
"Alice Morgan", "Bob Chen", "Carol Evans", "Dan Walsh", "Eve Porter",
|
|
31
|
+
"Frank Santos", "Grace Kim", "Henry Liu", "Irene Cruz", "Jack Reed",
|
|
32
|
+
];
|
|
33
|
+
const SAMPLE_TITLES = [
|
|
34
|
+
"Getting Started Guide", "Advanced Configuration", "Performance Tuning",
|
|
35
|
+
"Security Best Practices", "API Reference Manual", "Migration Notes",
|
|
36
|
+
"Release Highlights", "Troubleshooting Tips", "Architecture Overview",
|
|
37
|
+
];
|
|
38
|
+
const SAMPLE_DESCRIPTIONS = [
|
|
39
|
+
"A comprehensive solution for modern workflows.",
|
|
40
|
+
"Streamlined process with enterprise-grade reliability.",
|
|
41
|
+
"Built for scalability and high availability.",
|
|
42
|
+
"Designed with developer experience in mind.",
|
|
43
|
+
"Optimized for performance and low latency.",
|
|
44
|
+
];
|
|
45
|
+
const SAMPLE_URLS = [
|
|
46
|
+
"https://api.example.com/v1/resource",
|
|
47
|
+
"https://cdn.example.com/assets/image.png",
|
|
48
|
+
"https://docs.example.com/guides/setup",
|
|
49
|
+
];
|
|
50
|
+
const SAMPLE_EMAILS = [
|
|
51
|
+
"user@example.com", "admin@testdomain.org", "support@mockapi.net",
|
|
52
|
+
];
|
|
53
|
+
function inferValueFromName(name, rng) {
|
|
54
|
+
const lower = name.toLowerCase();
|
|
55
|
+
if (lower === "id" || lower.endsWith("_id") || lower.endsWith("Id")) {
|
|
56
|
+
return rng.int(1, 99999);
|
|
57
|
+
}
|
|
58
|
+
if (lower === "uuid" || lower.endsWith("_uuid")) {
|
|
59
|
+
const hex = () => rng.int(0, 15).toString(16);
|
|
60
|
+
const seg = (n) => Array.from({ length: n }, hex).join("");
|
|
61
|
+
return `${seg(8)}-${seg(4)}-4${seg(3)}-${rng.pick(["8", "9", "a", "b"])}${seg(3)}-${seg(12)}`;
|
|
62
|
+
}
|
|
63
|
+
if (lower.includes("email"))
|
|
64
|
+
return rng.pick(SAMPLE_EMAILS);
|
|
65
|
+
if (lower.includes("name") || lower === "author" || lower === "user")
|
|
66
|
+
return rng.pick(SAMPLE_NAMES);
|
|
67
|
+
if (lower.includes("title") || lower === "subject")
|
|
68
|
+
return rng.pick(SAMPLE_TITLES);
|
|
69
|
+
if (lower.includes("description") || lower.includes("summary") || lower === "body" || lower === "content")
|
|
70
|
+
return rng.pick(SAMPLE_DESCRIPTIONS);
|
|
71
|
+
if (lower.includes("url") || lower.includes("link") || lower.includes("href"))
|
|
72
|
+
return rng.pick(SAMPLE_URLS);
|
|
73
|
+
if (lower.includes("image") || lower.includes("avatar") || lower.includes("photo"))
|
|
74
|
+
return `https://picsum.photos/seed/${rng.int(1, 999)}/200/200`;
|
|
75
|
+
if (lower.includes("price") || lower.includes("amount") || lower.includes("cost") || lower.includes("total"))
|
|
76
|
+
return rng.float(1, 999, 2);
|
|
77
|
+
if (lower.includes("count") || lower.includes("quantity") || lower === "total" || lower === "size")
|
|
78
|
+
return rng.int(0, 500);
|
|
79
|
+
if (lower.includes("rating") || lower.includes("score"))
|
|
80
|
+
return rng.float(1, 5, 1);
|
|
81
|
+
if (lower.includes("active") || lower.includes("enabled") || lower.includes("verified") || lower.startsWith("is_") || lower.startsWith("has_"))
|
|
82
|
+
return rng.next() > 0.3;
|
|
83
|
+
if (lower.includes("date") || lower.includes("created") || lower.includes("updated") || lower.includes("timestamp")) {
|
|
84
|
+
const y = rng.int(2020, 2026);
|
|
85
|
+
const m = String(rng.int(1, 12)).padStart(2, "0");
|
|
86
|
+
const d = String(rng.int(1, 28)).padStart(2, "0");
|
|
87
|
+
return `${y}-${m}-${d}T${String(rng.int(0, 23)).padStart(2, "0")}:${String(rng.int(0, 59)).padStart(2, "0")}:00Z`;
|
|
88
|
+
}
|
|
89
|
+
if (lower.includes("phone"))
|
|
90
|
+
return `+1 (${rng.int(200, 999)}) ${rng.int(200, 999)}-${rng.int(1000, 9999)}`;
|
|
91
|
+
if (lower.includes("status"))
|
|
92
|
+
return rng.pick(["active", "inactive", "pending", "completed"]);
|
|
93
|
+
if (lower.includes("type") || lower.includes("category"))
|
|
94
|
+
return rng.pick(["standard", "premium", "basic", "enterprise"]);
|
|
95
|
+
if (lower.includes("tag"))
|
|
96
|
+
return rng.pick(SAMPLE_WORDS);
|
|
97
|
+
if (lower.includes("color"))
|
|
98
|
+
return `#${rng.int(0, 0xffffff).toString(16).padStart(6, "0")}`;
|
|
99
|
+
if (lower.includes("ip"))
|
|
100
|
+
return `${rng.int(1, 254)}.${rng.int(0, 255)}.${rng.int(0, 255)}.${rng.int(1, 254)}`;
|
|
101
|
+
if (lower.includes("latitude") || lower === "lat")
|
|
102
|
+
return rng.float(-90, 90, 6);
|
|
103
|
+
if (lower.includes("longitude") || lower === "lng" || lower === "lon")
|
|
104
|
+
return rng.float(-180, 180, 6);
|
|
105
|
+
return rng.pick(SAMPLE_WORDS);
|
|
106
|
+
}
|
|
107
|
+
function generateFieldValue(field, rng) {
|
|
108
|
+
if (field.nullable && rng.next() < 0.15)
|
|
109
|
+
return null;
|
|
110
|
+
if (field.enum && field.enum.length > 0)
|
|
111
|
+
return rng.pick(field.enum);
|
|
112
|
+
const type = field.type.toLowerCase();
|
|
113
|
+
if (type === "string")
|
|
114
|
+
return String(inferValueFromName(field.name, rng));
|
|
115
|
+
if (type === "number" || type === "integer" || type === "int") {
|
|
116
|
+
const inferred = inferValueFromName(field.name, rng);
|
|
117
|
+
return typeof inferred === "number" ? inferred : rng.int(1, 1000);
|
|
118
|
+
}
|
|
119
|
+
if (type === "float" || type === "decimal" || type === "double") {
|
|
120
|
+
const inferred = inferValueFromName(field.name, rng);
|
|
121
|
+
return typeof inferred === "number" ? inferred : rng.float(0, 1000, 2);
|
|
122
|
+
}
|
|
123
|
+
if (type === "boolean" || type === "bool") {
|
|
124
|
+
const inferred = inferValueFromName(field.name, rng);
|
|
125
|
+
return typeof inferred === "boolean" ? inferred : rng.next() > 0.5;
|
|
126
|
+
}
|
|
127
|
+
if (type === "date" || type === "datetime" || type === "timestamp") {
|
|
128
|
+
return inferValueFromName(field.name.includes("date") ? field.name : "created_at", rng);
|
|
129
|
+
}
|
|
130
|
+
if (type === "array") {
|
|
131
|
+
const arrLen = rng.int(1, 5);
|
|
132
|
+
if (field.items && field.items.length > 0) {
|
|
133
|
+
if (field.items.length === 1 && !field.items[0].fields) {
|
|
134
|
+
return Array.from({ length: arrLen }, () => generateFieldValue(field.items[0], rng));
|
|
135
|
+
}
|
|
136
|
+
return Array.from({ length: arrLen }, () => {
|
|
137
|
+
const obj = {};
|
|
138
|
+
for (const sub of field.items) {
|
|
139
|
+
obj[sub.name] = generateFieldValue(sub, rng);
|
|
140
|
+
}
|
|
141
|
+
return obj;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return Array.from({ length: arrLen }, () => rng.pick(SAMPLE_WORDS));
|
|
145
|
+
}
|
|
146
|
+
if (type === "object") {
|
|
147
|
+
if (field.fields && field.fields.length > 0) {
|
|
148
|
+
const obj = {};
|
|
149
|
+
for (const sub of field.fields) {
|
|
150
|
+
obj[sub.name] = generateFieldValue(sub, rng);
|
|
151
|
+
}
|
|
152
|
+
return obj;
|
|
153
|
+
}
|
|
154
|
+
return {};
|
|
155
|
+
}
|
|
156
|
+
return inferValueFromName(field.name, rng);
|
|
157
|
+
}
|
|
158
|
+
function generateSingleRecord(fields, rng) {
|
|
159
|
+
const record = {};
|
|
160
|
+
for (const field of fields) {
|
|
161
|
+
record[field.name] = generateFieldValue(field, rng);
|
|
162
|
+
}
|
|
163
|
+
return record;
|
|
164
|
+
}
|
|
165
|
+
export function generateApiMockResponse(options) {
|
|
166
|
+
const { endpoint, method = "GET", fields, count = 1, statusCode = 200, wrapInEnvelope = true, } = options;
|
|
167
|
+
const rng = new MockRng(Date.now() % 100000);
|
|
168
|
+
const safeCount = Math.min(Math.max(1, count), 100);
|
|
169
|
+
const isList = safeCount > 1 || method.toUpperCase() === "GET" && endpoint.match(/\/\w+\/?$/);
|
|
170
|
+
let body;
|
|
171
|
+
if (safeCount === 1 && !isList) {
|
|
172
|
+
const record = generateSingleRecord(fields, rng);
|
|
173
|
+
body = wrapInEnvelope ? { success: true, data: record } : record;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
const records = Array.from({ length: safeCount }, () => generateSingleRecord(fields, rng));
|
|
177
|
+
body = wrapInEnvelope
|
|
178
|
+
? { success: true, data: records, meta: { total: rng.int(safeCount, safeCount * 10), page: 1, perPage: safeCount } }
|
|
179
|
+
: records;
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
endpoint,
|
|
183
|
+
method: method.toUpperCase(),
|
|
184
|
+
statusCode,
|
|
185
|
+
headers: {
|
|
186
|
+
"Content-Type": "application/json",
|
|
187
|
+
"X-Request-Id": String(rng.int(100000, 999999)),
|
|
188
|
+
"X-RateLimit-Remaining": String(rng.int(50, 1000)),
|
|
189
|
+
},
|
|
190
|
+
body,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=api-mock.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Given expected vs actual values, generate detailed assertion code
|
|
3
|
+
* with descriptive messages. Supports deep object comparison,
|
|
4
|
+
* array comparison, and type checking.
|
|
5
|
+
*/
|
|
6
|
+
interface AssertionOptions {
|
|
7
|
+
expected: string;
|
|
8
|
+
actual: string;
|
|
9
|
+
label?: string;
|
|
10
|
+
framework?: "jest" | "vitest" | "chai";
|
|
11
|
+
deep?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface DiffEntry {
|
|
14
|
+
path: string;
|
|
15
|
+
expected: unknown;
|
|
16
|
+
actual: unknown;
|
|
17
|
+
type: "missing" | "extra" | "type_mismatch" | "value_mismatch";
|
|
18
|
+
}
|
|
19
|
+
export declare function generateAssertions(options: AssertionOptions): {
|
|
20
|
+
code: string;
|
|
21
|
+
diffs: DiffEntry[];
|
|
22
|
+
summary: string;
|
|
23
|
+
};
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=assertion-helper.d.ts.map
|