@kirrosh/zond 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kirrosh/zond",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "API testing platform — define tests in YAML, run from CLI or WebUI, generate from OpenAPI specs",
5
5
  "license": "MIT",
6
6
  "module": "index.ts",
@@ -67,293 +67,20 @@ export interface GuideOptions {
67
67
  securitySchemes: SecuritySchemeInfo[];
68
68
  endpointCount: number;
69
69
  coverageHeader?: string;
70
- compact?: boolean;
71
70
  }
72
71
 
73
72
  export function buildGenerationGuide(opts: GuideOptions): string {
74
73
  const hasAuth = opts.securitySchemes.length > 0;
75
74
 
76
- if (opts.compact) {
77
- const securitySummary = hasAuth
78
- ? `Security: ${opts.securitySchemes.map(s => `${s.name} (${s.type}${s.scheme ? `/${s.scheme}` : ""})`).join(", ")}`
79
- : "Security: none";
80
-
81
- return `# Test Generation Guide for ${opts.title}
82
- ${opts.coverageHeader ? `\n${opts.coverageHeader}\n` : ""}
83
- ## API Specification (${opts.endpointCount} endpoints)
84
- ${opts.baseUrl ? `Base URL: ${opts.baseUrl}` : "Base URL: use {{base_url}} environment variable"}
85
- ${securitySummary}
86
-
87
- ${opts.apiContext}
88
-
89
- ---
90
-
91
- > **Note:** Refer to the full YAML format reference, generation algorithm, tag conventions, and practical tips from the initial \`generate_and_save\` call (without \`tag\`). Only the API endpoints above are unique to this chunk.`;
92
- }
75
+ const securitySummary = hasAuth
76
+ ? `Security: ${opts.securitySchemes.map(s => `${s.name} (${s.type}${s.scheme ? `/${s.scheme}` : ""})`).join(", ")}`
77
+ : "Security: none";
93
78
 
94
79
  return `# Test Generation Guide for ${opts.title}
95
80
  ${opts.coverageHeader ? `\n${opts.coverageHeader}\n` : ""}
96
81
  ## API Specification (${opts.endpointCount} endpoints)
97
82
  ${opts.baseUrl ? `Base URL: ${opts.baseUrl}` : "Base URL: use {{base_url}} environment variable"}
83
+ ${securitySummary}
98
84
 
99
- ${opts.apiContext}
100
-
101
- ${hasAuth ? `---
102
-
103
- ## Environment Setup (Required for Authentication)
104
-
105
- This API uses authentication. Before running tests, set up your credentials:
106
-
107
- ### Edit the env file
108
- After \`setup_api\`, the collection directory contains \`.env.yaml\`. Edit it to add your credentials:
109
- \`\`\`yaml
110
- base_url: "https://api.example.com"
111
- api_key: "your-actual-api-key-here"
112
- auth_token: "your-token-here"
113
- \`\`\`
114
-
115
- ### How it works
116
- - Tests **automatically** load the \`"default"\` environment — no need to pass \`envName\` to \`run_tests\`
117
- - If the env file is in the collection root and tests are in a \`tests/\` subdirectory, the file is still found automatically
118
- - Use \`{{api_key}}\`, \`{{auth_token}}\`, \`{{base_url}}\` etc. in test headers/bodies
119
- - **Never hardcode credentials** in YAML files — always use \`{{variable}}\` references
120
-
121
- ` : ""}---
122
-
123
- ## YAML Test Suite Format Reference
124
-
125
- \`\`\`yaml
126
- name: "Suite Name"
127
- description: "What this suite tests" # optional
128
- tags: [smoke, crud] # optional — used for filtering with --tag
129
- base_url: "{{base_url}}"
130
- headers: # optional suite-level headers
131
- Authorization: "Bearer {{auth_token}}"
132
- Content-Type: "application/json"
133
- config: # optional
134
- timeout: 30000
135
- retries: 0
136
- follow_redirects: true
137
- tests:
138
- - name: "Test step name"
139
- POST: "/path/{{variable}}" # exactly ONE method key: GET, POST, PUT, PATCH, DELETE
140
- json: # request body (object)
141
- field: "value"
142
- query: # query parameters
143
- limit: "10"
144
- headers: # step-level headers (override suite)
145
- X-Custom: "value"
146
- expect:
147
- status: 200 # expected HTTP status: integer OR array [200, 204]
148
- body: # field-level assertions
149
- id: { type: "integer", capture: "item_id" }
150
- name: { equals: "expected" }
151
- email: { contains: "@", type: "string" }
152
- count: { gt: 0, lt: 100 }
153
- items: { exists: true } # exists must be boolean, NEVER string
154
- pattern: { matches: "^[A-Z]+" }
155
- headers:
156
- Content-Type: "application/json"
157
- duration: 5000 # max response time in ms
158
- \`\`\`
159
-
160
- ### Assertion Rules
161
- - \`capture: "var_name"\` — SAVES the value into a variable (use in later steps as {{var_name}})
162
- - \`equals: value\` — exact match COMPARISON (NEVER use equals to save a value!)
163
- - \`type: "string"|"number"|"integer"|"boolean"|"array"|"object"\`
164
- - \`contains: "substring"\` — string substring match
165
- - \`matches: "regex"\` — regex pattern match
166
- - \`gt: N\` / \`lt: N\` — numeric comparison
167
- - \`exists: true|false\` — field presence check (MUST be boolean, not string)
168
-
169
- ### Nested Body Assertions
170
- Both forms are equivalent and supported:
171
-
172
- **Dot-notation (flat):**
173
- \`\`\`yaml
174
- body:
175
- "category.name": { equals: "Dogs" }
176
- "address.city": { type: "string" }
177
- \`\`\`
178
-
179
- **Nested YAML (auto-flattened):**
180
- \`\`\`yaml
181
- body:
182
- category:
183
- name: { equals: "Dogs" }
184
- address:
185
- city: { type: "string" }
186
- \`\`\`
187
-
188
- ### Root Body Assertions (\`_body\`)
189
- Use \`_body\` to assert on the response body itself (not a field inside it):
190
-
191
- \`\`\`yaml
192
- body:
193
- _body: { type: "array" } # check that response body IS an array
194
- _body: { type: "object" } # check that response body IS an object
195
- _body: { exists: true } # check that body is not null/undefined
196
- \`\`\`
197
-
198
- ### Built-in Generators
199
- Use in string values: \`{{$randomInt}}\`, \`{{$uuid}}\`, \`{{$timestamp}}\`, \`{{$randomEmail}}\`, \`{{$randomString}}\`, \`{{$randomName}}\`
200
- These are the ONLY generators — do NOT invent others.
201
-
202
- ### Variable Interpolation
203
- - \`{{variable}}\` in paths, bodies, headers, query params
204
- - Captured values from previous steps are available in subsequent steps
205
- - Environment variables from .env.yaml files: \`{{base_url}}\`, \`{{auth_username}}\`, etc.
206
-
207
- ---
208
-
209
- ## Step-by-Step Generation Algorithm
210
-
211
- ### Step 0: Register the API (REQUIRED FIRST)
212
- **Always call \`setup_api\` before generating any tests.** This registers the collection in the database so WebUI, coverage tracking, and env loading all work.
213
- \`\`\`
214
- setup_api(name: "myapi", specPath: "/path/to/openapi.json", dir: "/path/to/project/apis/myapi")
215
- \`\`\`
216
- If you skip this step, WebUI will show "No API collections registered yet" and env variables won't auto-load.
217
-
218
- ${hasAuth ? `**Then set credentials immediately after setup_api** — edit the \`.env.yaml\` file in the API directory to add your credentials:
219
- \`\`\`yaml
220
- api_key: "<actual-key>"
221
- base_url: "https://..."
222
- \`\`\`
223
- Never put actual key values directly in YAML test files.
224
-
225
- ` : ""}\
226
- ### Step 1: Analyze the API
227
- - Identify authentication method (${hasAuth ? opts.securitySchemes.map(s => `${s.name}: ${s.type}${s.scheme ? `/${s.scheme}` : ""}`).join(", ") : "none detected"})
228
- - Group endpoints by resource (e.g., /users/*, /pets/*, /orders/*)
229
- - Identify CRUD patterns: POST (create) → GET (read) → PUT/PATCH (update) → DELETE
230
- - Note required fields in request bodies
231
-
232
- ### Step 2: Plan Test Suites
233
- Before generating, check coverage with \`coverage_analysis\` to avoid duplicating existing tests. Use \`generate_and_save(testsDir=...)\` for incremental generation of uncovered endpoints only.
234
-
235
- > **Coverage note**: coverage is a static scan of YAML files — an endpoint is "covered" if a test file contains a matching METHOD + path line, regardless of whether tests pass or actually run.
236
-
237
- Create separate files for each concern:
238
- ${hasAuth ? `- \`${opts.outputDir}auth.yaml\` — Authentication flow\n` : ""}\
239
- - \`${opts.outputDir}{resource}-crud.yaml\` — CRUD lifecycle per resource
240
- - \`${opts.outputDir}{resource}-validation.yaml\` — Error cases per resource
241
-
242
- ### Step 3: Generate Each Suite
243
-
244
- ${hasAuth ? `**Auth suite** (\`auth.yaml\`):
245
- 1. Login with valid credentials → capture token
246
- 2. Access protected endpoint with token → 200
247
- 3. Login with invalid credentials → 401/403
248
- 4. Access protected endpoint without token → 401
249
-
250
- ` : ""}\
251
- **CRUD lifecycle** (\`{resource}-crud.yaml\`):
252
- 1. Create resource (POST) → 201, **always verify key fields in response body** (at minimum: id, name/title)
253
- 2. Read created resource (GET /resource/{{id}}) → 200, verify fields match what was sent
254
- 3. List resources (GET /resource) → 200, verify \`_body: { type: "array" }\` AND \`_body.length: { gt: 0 }\`
255
- 4. Update resource (PUT/PATCH /resource/{{id}}) → 200
256
- 5. Read updated resource → verify changes applied
257
- 6. Delete resource (DELETE /resource/{{id}}) → 200/204
258
- 7. Verify deleted (GET /resource/{{id}}) → 404
259
- 8. For bulk create endpoints (createWithArray/List): create → then GET each to verify they exist
260
-
261
- **Validation suite** (\`{resource}-validation.yaml\`):
262
- 1. Create with missing required fields → 400/422, verify \`message: { exists: true }\` in error body
263
- 2. Create with invalid field types → 400/422
264
- 3. Get non-existent resource (e.g. id=999999) → 404
265
- 4. Delete non-existent resource → 404
266
- 5. For error responses: always assert error body has meaningful content, not just status code
267
-
268
- ### Step 4: Save, Run, Debug
269
- 1. Use \`save_test_suite\` to save each file — it validates YAML before writing
270
- 2. Use \`run_tests\` to execute — review pass/fail summary
271
- 3. If failures: use \`query_db\` with \`action: "diagnose_failure"\` and the runId to see full request/response details
272
- 4. Fix issues and re-save with \`overwrite: true\`
273
-
274
- ---
275
-
276
- ## Tag Conventions
277
-
278
- Use standard tags to enable safe filtering:
279
-
280
- | Tag | HTTP Methods | Safe for |
281
- |-----|-------------|---------|
282
- | \`smoke\` | GET only | Production (read-only, zero risk) |
283
- | \`crud\` | POST/PUT/PATCH | Staging only (state-changing) |
284
- | \`destructive\` | DELETE | Explicit opt-in, run last |
285
- | \`auth\` | Any (auth flows) | Run first to capture tokens |
286
-
287
- Example: \`zond run --tag smoke --safe\` → reads-only, safe against production.
288
-
289
- ---
290
-
291
- ## Practical Tips
292
-
293
- - **int64 IDs**: For APIs returning large auto-generated IDs (int64), prefer setting fixed IDs in request bodies rather than capturing auto-generated ones, as JSON number precision may cause mismatches.
294
- - **Nested assertions**: Use dot-notation or nested YAML — both work identically.
295
- - **Root body type**: Use \`_body: { type: "array" }\` to verify the response body type itself.
296
- - **List endpoints**: Always check both type AND non-emptiness: \`_body: { type: "array" }\` + \`_body.length: { gt: 0 }\`
297
- - **Create responses**: Always verify at least the key identifying fields (id, name) in the response body — don't just check status.
298
- - **Error responses**: Assert that error bodies contain useful info (\`message: { exists: true }\`), not just status codes.
299
- - **Bulk operations**: After bulk create (createWithArray, createWithList), add GET steps to verify resources were actually created.
300
- - **204 No Content**: When an endpoint returns 204, omit \`body:\` assertions entirely — an empty response IS the correct behavior. Adding body assertions on 204 will always fail.
301
- - **Cleanup pattern**: Always delete test data in the same suite. Use a create → read → delete lifecycle so tests are idempotent:
302
- \`\`\`yaml
303
- tests:
304
- - name: Create test resource
305
- POST: /users
306
- json: { name: "zond-test-{{$randomString}}" }
307
- expect:
308
- status: 201
309
- body:
310
- id: { capture: user_id }
311
- - name: Read created resource
312
- GET: /users/{{user_id}}
313
- expect:
314
- status: 200
315
- - name: Cleanup - delete test resource
316
- DELETE: /users/{{user_id}}
317
- expect:
318
- status: 204
319
- \`\`\`
320
- - **Identifiable test data**: Prefix test data with \`zond-test-\` or use \`{{$uuid}}\` / \`zond-test-{{$randomString}}\` so you can identify and clean up leftover test data if needed.
321
-
322
- ---
323
-
324
- ## Common Mistakes to Avoid
325
-
326
- 1. **equals vs capture**: \`capture\` SAVES a value, \`equals\` COMPARES. To extract a token: \`{ capture: "token" }\` NOT \`{ equals: "{{token}}" }\`
327
- 2. **exists must be boolean**: \`exists: true\` NOT \`exists: "true"\`
328
- 3. **Status must be integer or array**: \`status: 200\` or \`status: [200, 204]\` NOT \`status: "200"\`
329
- 4. **One method per step**: Each test step has exactly ONE of GET/POST/PUT/PATCH/DELETE
330
- 5. **Don't hardcode base URL**: Use \`{{base_url}}\` — set it in environment or suite base_url
331
- 6. **Auth credentials**: Use environment variables \`{{auth_username}}\`, \`{{auth_password}}\` — NOT generators
332
- 7. **String query params**: Query parameter values must be strings: \`limit: "10"\` not \`limit: 10\`
333
- 8. **Hardcoded credentials**: NEVER put actual API keys/tokens in YAML — use \`{{api_key}}\` from env instead
334
- 9. **Body assertions on 204**: Don't add \`body:\` checks for DELETE or other endpoints that return 204 No Content — the body is empty by design.
335
-
336
- ---
337
-
338
- ## Tools to Use
339
-
340
- | Tool | When |
341
- |------|------|
342
- | \`setup_api\` | Register a new API (creates dirs, reads spec, sets up env) |
343
- | \`generate_and_save\` | Get test generation guide (with auto-chunking for large APIs) |
344
- | \`save_test_suite\` | Save generated YAML (validates before writing) |
345
- | \`save_test_suites\` | Save multiple YAML files in one call |
346
- | \`run_tests\` | Execute saved test suites |
347
- | \`query_db\` | Query runs, collections, results, diagnose failures |
348
- | \`coverage_analysis\` | Find untested endpoints for incremental generation |
349
- | \`describe_endpoint\` | Get full details for one endpoint when debugging |
350
- | \`ci_init\` | Generate CI/CD workflow (GitHub Actions / GitLab CI) to run tests on push |
351
-
352
- ## Workflow After Tests Pass
353
-
354
- After tests are saved and running successfully, ask the user if they want to set up CI/CD:
355
- 1. Use \`ci_init\` to generate a CI workflow (auto-detects platform or use platform param)
356
- 2. Help them commit and push to their repository
357
- 3. Tests will run automatically on push, PR, and on schedule
358
- `;
85
+ ${opts.apiContext}`;
359
86
  }
@@ -106,7 +106,6 @@ export function registerGenerateAndSaveTool(server: McpServer) {
106
106
  securitySchemes,
107
107
  endpointCount: endpoints.length,
108
108
  coverageHeader,
109
- compact: !!tag,
110
109
  });
111
110
 
112
111
  const saveInstructions = `