@kirrosh/zond 0.9.1 → 0.9.2
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/package.json
CHANGED
|
@@ -59,6 +59,66 @@ export function compressEndpointsWithSchemas(
|
|
|
59
59
|
return lines.join("\n");
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
const YAML_FORMAT_CHEATSHEET = `
|
|
63
|
+
## YAML Test Format Reference
|
|
64
|
+
|
|
65
|
+
### Suite structure
|
|
66
|
+
\`\`\`yaml
|
|
67
|
+
name: Suite Name # required
|
|
68
|
+
base_url: "{{base_url}}" # or hardcoded URL
|
|
69
|
+
tags: [smoke] # optional: smoke | crud | destructive | auth
|
|
70
|
+
tests:
|
|
71
|
+
- GET /endpoint: # method + path as YAML key
|
|
72
|
+
query: { limit: 10 }
|
|
73
|
+
expect:
|
|
74
|
+
status: 200
|
|
75
|
+
_body: { type: array }
|
|
76
|
+
\`\`\`
|
|
77
|
+
|
|
78
|
+
### Assertion operators
|
|
79
|
+
| Operator | Example |
|
|
80
|
+
|----------|---------|
|
|
81
|
+
| equals (default) | \`status: 200\` |
|
|
82
|
+
| not_equals | \`status: { not_equals: 500 }\` |
|
|
83
|
+
| contains | \`name: { contains: "john" }\` |
|
|
84
|
+
| not_contains | \`body: { not_contains: "error" }\` |
|
|
85
|
+
| exists / not_exists | \`id: { exists: true }\` |
|
|
86
|
+
| gt / gte / lt / lte | \`count: { gte: 1 }\` |
|
|
87
|
+
| matches (regex) | \`email: { matches: "^.+@.+$" }\` |
|
|
88
|
+
| type | \`items: { type: array }\` |
|
|
89
|
+
| length | \`items: { length: 5 }\` |
|
|
90
|
+
| length_gt/gte/lt/lte | \`items: { length_gt: 0 }\` |
|
|
91
|
+
|
|
92
|
+
### Body assertions
|
|
93
|
+
- \`_body\` — assert on entire response body: \`_body: { type: array }\`
|
|
94
|
+
- Dot-notation for nested: \`data.user.id: { exists: true }\`
|
|
95
|
+
- Array item access: \`items.0.name: { exists: true }\`
|
|
96
|
+
|
|
97
|
+
### Request body (JSON)
|
|
98
|
+
\`\`\`yaml
|
|
99
|
+
- POST /resource:
|
|
100
|
+
json: { name: "test", email: "a@b.com" }
|
|
101
|
+
expect:
|
|
102
|
+
status: 201
|
|
103
|
+
\`\`\`
|
|
104
|
+
|
|
105
|
+
### Built-in generators
|
|
106
|
+
\`{{$uuid}}\`, \`{{$randomInt}}\`, \`{{$timestamp}}\`, \`{{$isoTimestamp}}\`, \`{{$randomEmail}}\`, \`{{$randomString}}\`
|
|
107
|
+
|
|
108
|
+
### Variable capture & interpolation
|
|
109
|
+
\`\`\`yaml
|
|
110
|
+
- POST /items:
|
|
111
|
+
json: { name: "test-{{$uuid}}" }
|
|
112
|
+
capture:
|
|
113
|
+
created_id: id # saves response.id
|
|
114
|
+
expect:
|
|
115
|
+
status: 201
|
|
116
|
+
- GET /items/{{created_id}}:
|
|
117
|
+
expect:
|
|
118
|
+
status: 200
|
|
119
|
+
\`\`\`
|
|
120
|
+
`;
|
|
121
|
+
|
|
62
122
|
export interface GuideOptions {
|
|
63
123
|
title: string;
|
|
64
124
|
baseUrl?: string;
|
|
@@ -67,6 +127,7 @@ export interface GuideOptions {
|
|
|
67
127
|
securitySchemes: SecuritySchemeInfo[];
|
|
68
128
|
endpointCount: number;
|
|
69
129
|
coverageHeader?: string;
|
|
130
|
+
includeFormat?: boolean;
|
|
70
131
|
}
|
|
71
132
|
|
|
72
133
|
export function buildGenerationGuide(opts: GuideOptions): string {
|
|
@@ -76,11 +137,13 @@ export function buildGenerationGuide(opts: GuideOptions): string {
|
|
|
76
137
|
? `Security: ${opts.securitySchemes.map(s => `${s.name} (${s.type}${s.scheme ? `/${s.scheme}` : ""})`).join(", ")}`
|
|
77
138
|
: "Security: none";
|
|
78
139
|
|
|
140
|
+
const formatSection = opts.includeFormat !== false ? YAML_FORMAT_CHEATSHEET : "";
|
|
141
|
+
|
|
79
142
|
return `# Test Generation Guide for ${opts.title}
|
|
80
143
|
${opts.coverageHeader ? `\n${opts.coverageHeader}\n` : ""}
|
|
81
144
|
## API Specification (${opts.endpointCount} endpoints)
|
|
82
145
|
${opts.baseUrl ? `Base URL: ${opts.baseUrl}` : "Base URL: use {{base_url}} environment variable"}
|
|
83
146
|
${securitySummary}
|
|
84
147
|
|
|
85
|
-
${opts.apiContext}`;
|
|
148
|
+
${opts.apiContext}${formatSection}`;
|
|
86
149
|
}
|
package/src/mcp/descriptions.ts
CHANGED
|
@@ -56,7 +56,8 @@ export const TOOL_DESCRIPTIONS = {
|
|
|
56
56
|
"Read an OpenAPI spec, auto-chunk by tags if large (>30 endpoints), " +
|
|
57
57
|
"and return a focused test generation guide. For large APIs returns a chunking plan — " +
|
|
58
58
|
"call again with tag parameter for each chunk. Use testsDir param to only generate for uncovered endpoints. " +
|
|
59
|
-
"After generating YAML, use save_test_suites to save files, then run_tests to verify."
|
|
59
|
+
"After generating YAML, use save_test_suites to save files, then run_tests to verify. " +
|
|
60
|
+
"Includes YAML format cheatsheet by default; pass includeFormat: false for subsequent tag chunks to save tokens.",
|
|
60
61
|
|
|
61
62
|
ci_init:
|
|
62
63
|
"Generate a CI/CD workflow file for running API tests automatically on push, PR, and schedule. " +
|
|
@@ -21,8 +21,9 @@ export function registerGenerateAndSaveTool(server: McpServer) {
|
|
|
21
21
|
methodFilter: z.optional(z.array(z.string())).describe("Only include endpoints with these HTTP methods (e.g. [\"GET\"] for smoke tests)"),
|
|
22
22
|
testsDir: z.optional(z.string()).describe("Path to existing tests directory — filters to uncovered endpoints only"),
|
|
23
23
|
overwrite: z.optional(z.boolean()).describe("Hint for save_test_suites overwrite behavior (default: false)"),
|
|
24
|
+
includeFormat: z.optional(z.boolean()).describe("Include YAML format reference (default: true, set false for subsequent tag chunks)"),
|
|
24
25
|
},
|
|
25
|
-
}, async ({ specPath, outputDir, tag, methodFilter, testsDir, overwrite }) => {
|
|
26
|
+
}, async ({ specPath, outputDir, tag, methodFilter, testsDir, overwrite, includeFormat }) => {
|
|
26
27
|
try {
|
|
27
28
|
const doc = await readOpenApiSpec(specPath);
|
|
28
29
|
let endpoints = extractEndpoints(doc);
|
|
@@ -82,6 +83,7 @@ export function registerGenerateAndSaveTool(server: McpServer) {
|
|
|
82
83
|
instruction:
|
|
83
84
|
`This API has ${plan.totalEndpoints} endpoints across ${plan.chunks.length} tags. ` +
|
|
84
85
|
`Call generate_and_save with tag parameter for each chunk sequentially. ` +
|
|
86
|
+
`Pass includeFormat: false for subsequent chunks to save tokens. ` +
|
|
85
87
|
`Example: generate_and_save(specPath: '${specPath}', tag: '${plan.chunks[0].tag}')`,
|
|
86
88
|
};
|
|
87
89
|
if (coverageInfo) {
|
|
@@ -106,6 +108,7 @@ export function registerGenerateAndSaveTool(server: McpServer) {
|
|
|
106
108
|
securitySchemes,
|
|
107
109
|
endpointCount: endpoints.length,
|
|
108
110
|
coverageHeader,
|
|
111
|
+
includeFormat: includeFormat ?? true,
|
|
109
112
|
});
|
|
110
113
|
|
|
111
114
|
const saveInstructions = `
|