@kirrosh/zond 0.8.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.
|
|
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",
|
|
@@ -26,11 +26,13 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"zond": "bun run src/cli/index.ts",
|
|
28
28
|
"test": "bun run test:unit && bun run test:mocked",
|
|
29
|
-
"test:unit": "bun test tests/db/ tests/parser/ tests/runner/ tests/generator/ tests/core/ tests/cli/args.test.ts tests/cli/chat.test.ts tests/cli/ci-init.test.ts tests/cli/commands.test.ts tests/cli/doctor.test.ts tests/cli/init.test.ts tests/cli/runs.test.ts tests/cli/safe-run.test.ts tests/cli/update.test.ts tests/integration/ tests/web/ tests/mcp/tools.test.ts tests/mcp/save-test-suite.test.ts tests/agent/agent-loop.test.ts tests/agent/context-manager.test.ts tests/agent/system-prompt.test.ts tests/reporter/",
|
|
29
|
+
"test:unit": "bun test tests/db/ tests/parser/ tests/runner/ tests/generator/ tests/core/ tests/cli/args.test.ts tests/cli/chat.test.ts tests/cli/ci-init.test.ts tests/cli/commands.test.ts tests/cli/doctor.test.ts tests/cli/init.test.ts tests/cli/runs.test.ts tests/cli/safe-run.test.ts tests/cli/update.test.ts tests/integration/ tests/web/ tests/mcp/tools.test.ts tests/mcp/save-test-suite.test.ts tests/agent/agent-loop.test.ts tests/agent/context-manager.test.ts tests/agent/system-prompt.test.ts tests/reporter/ tests/version-sync.test.ts",
|
|
30
30
|
"test:mocked": "bun run scripts/run-mocked-tests.ts",
|
|
31
31
|
"test:ai": "bun test tests/ai/",
|
|
32
32
|
"check": "tsc --noEmit --project tsconfig.json",
|
|
33
|
-
"build": "bun build --compile src/cli/index.ts --outfile zond"
|
|
33
|
+
"build": "bun build --compile src/cli/index.ts --outfile zond",
|
|
34
|
+
"version:sync": "bun run scripts/sync-version.ts",
|
|
35
|
+
"postversion": "bun run scripts/sync-version.ts && git add .claude-plugin/plugin.json .claude-plugin/marketplace.json"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
36
38
|
"@types/bun": "latest"
|
|
@@ -44,6 +44,16 @@ export function envCategory(hint: string | undefined): string | null {
|
|
|
44
44
|
return null;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
export function schemaHint(
|
|
48
|
+
failureType: string,
|
|
49
|
+
responseStatus: number | null | undefined,
|
|
50
|
+
): string | null {
|
|
51
|
+
if (failureType === "assertion_failed" || responseStatus === 400 || responseStatus === 422) {
|
|
52
|
+
return "Use describe_endpoint(specPath, method, path) to verify expected request/response schema";
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
47
57
|
export function computeSharedEnvIssue(
|
|
48
58
|
failures: Array<{ hint?: string }>,
|
|
49
59
|
envFilePath?: string,
|
|
@@ -72,269 +72,15 @@ export interface GuideOptions {
|
|
|
72
72
|
export function buildGenerationGuide(opts: GuideOptions): string {
|
|
73
73
|
const hasAuth = opts.securitySchemes.length > 0;
|
|
74
74
|
|
|
75
|
+
const securitySummary = hasAuth
|
|
76
|
+
? `Security: ${opts.securitySchemes.map(s => `${s.name} (${s.type}${s.scheme ? `/${s.scheme}` : ""})`).join(", ")}`
|
|
77
|
+
: "Security: none";
|
|
78
|
+
|
|
75
79
|
return `# Test Generation Guide for ${opts.title}
|
|
76
80
|
${opts.coverageHeader ? `\n${opts.coverageHeader}\n` : ""}
|
|
77
81
|
## API Specification (${opts.endpointCount} endpoints)
|
|
78
82
|
${opts.baseUrl ? `Base URL: ${opts.baseUrl}` : "Base URL: use {{base_url}} environment variable"}
|
|
83
|
+
${securitySummary}
|
|
79
84
|
|
|
80
|
-
${opts.apiContext}
|
|
81
|
-
|
|
82
|
-
${hasAuth ? `---
|
|
83
|
-
|
|
84
|
-
## Environment Setup (Required for Authentication)
|
|
85
|
-
|
|
86
|
-
This API uses authentication. Before running tests, set up your credentials:
|
|
87
|
-
|
|
88
|
-
### Edit the env file
|
|
89
|
-
After \`setup_api\`, the collection directory contains \`.env.yaml\`. Edit it to add your credentials:
|
|
90
|
-
\`\`\`yaml
|
|
91
|
-
base_url: "https://api.example.com"
|
|
92
|
-
api_key: "your-actual-api-key-here"
|
|
93
|
-
auth_token: "your-token-here"
|
|
94
|
-
\`\`\`
|
|
95
|
-
|
|
96
|
-
### How it works
|
|
97
|
-
- Tests **automatically** load the \`"default"\` environment — no need to pass \`envName\` to \`run_tests\`
|
|
98
|
-
- If the env file is in the collection root and tests are in a \`tests/\` subdirectory, the file is still found automatically
|
|
99
|
-
- Use \`{{api_key}}\`, \`{{auth_token}}\`, \`{{base_url}}\` etc. in test headers/bodies
|
|
100
|
-
- **Never hardcode credentials** in YAML files — always use \`{{variable}}\` references
|
|
101
|
-
|
|
102
|
-
` : ""}---
|
|
103
|
-
|
|
104
|
-
## YAML Test Suite Format Reference
|
|
105
|
-
|
|
106
|
-
\`\`\`yaml
|
|
107
|
-
name: "Suite Name"
|
|
108
|
-
description: "What this suite tests" # optional
|
|
109
|
-
tags: [smoke, crud] # optional — used for filtering with --tag
|
|
110
|
-
base_url: "{{base_url}}"
|
|
111
|
-
headers: # optional suite-level headers
|
|
112
|
-
Authorization: "Bearer {{auth_token}}"
|
|
113
|
-
Content-Type: "application/json"
|
|
114
|
-
config: # optional
|
|
115
|
-
timeout: 30000
|
|
116
|
-
retries: 0
|
|
117
|
-
follow_redirects: true
|
|
118
|
-
tests:
|
|
119
|
-
- name: "Test step name"
|
|
120
|
-
POST: "/path/{{variable}}" # exactly ONE method key: GET, POST, PUT, PATCH, DELETE
|
|
121
|
-
json: # request body (object)
|
|
122
|
-
field: "value"
|
|
123
|
-
query: # query parameters
|
|
124
|
-
limit: "10"
|
|
125
|
-
headers: # step-level headers (override suite)
|
|
126
|
-
X-Custom: "value"
|
|
127
|
-
expect:
|
|
128
|
-
status: 200 # expected HTTP status: integer OR array [200, 204]
|
|
129
|
-
body: # field-level assertions
|
|
130
|
-
id: { type: "integer", capture: "item_id" }
|
|
131
|
-
name: { equals: "expected" }
|
|
132
|
-
email: { contains: "@", type: "string" }
|
|
133
|
-
count: { gt: 0, lt: 100 }
|
|
134
|
-
items: { exists: true } # exists must be boolean, NEVER string
|
|
135
|
-
pattern: { matches: "^[A-Z]+" }
|
|
136
|
-
headers:
|
|
137
|
-
Content-Type: "application/json"
|
|
138
|
-
duration: 5000 # max response time in ms
|
|
139
|
-
\`\`\`
|
|
140
|
-
|
|
141
|
-
### Assertion Rules
|
|
142
|
-
- \`capture: "var_name"\` — SAVES the value into a variable (use in later steps as {{var_name}})
|
|
143
|
-
- \`equals: value\` — exact match COMPARISON (NEVER use equals to save a value!)
|
|
144
|
-
- \`type: "string"|"number"|"integer"|"boolean"|"array"|"object"\`
|
|
145
|
-
- \`contains: "substring"\` — string substring match
|
|
146
|
-
- \`matches: "regex"\` — regex pattern match
|
|
147
|
-
- \`gt: N\` / \`lt: N\` — numeric comparison
|
|
148
|
-
- \`exists: true|false\` — field presence check (MUST be boolean, not string)
|
|
149
|
-
|
|
150
|
-
### Nested Body Assertions
|
|
151
|
-
Both forms are equivalent and supported:
|
|
152
|
-
|
|
153
|
-
**Dot-notation (flat):**
|
|
154
|
-
\`\`\`yaml
|
|
155
|
-
body:
|
|
156
|
-
"category.name": { equals: "Dogs" }
|
|
157
|
-
"address.city": { type: "string" }
|
|
158
|
-
\`\`\`
|
|
159
|
-
|
|
160
|
-
**Nested YAML (auto-flattened):**
|
|
161
|
-
\`\`\`yaml
|
|
162
|
-
body:
|
|
163
|
-
category:
|
|
164
|
-
name: { equals: "Dogs" }
|
|
165
|
-
address:
|
|
166
|
-
city: { type: "string" }
|
|
167
|
-
\`\`\`
|
|
168
|
-
|
|
169
|
-
### Root Body Assertions (\`_body\`)
|
|
170
|
-
Use \`_body\` to assert on the response body itself (not a field inside it):
|
|
171
|
-
|
|
172
|
-
\`\`\`yaml
|
|
173
|
-
body:
|
|
174
|
-
_body: { type: "array" } # check that response body IS an array
|
|
175
|
-
_body: { type: "object" } # check that response body IS an object
|
|
176
|
-
_body: { exists: true } # check that body is not null/undefined
|
|
177
|
-
\`\`\`
|
|
178
|
-
|
|
179
|
-
### Built-in Generators
|
|
180
|
-
Use in string values: \`{{$randomInt}}\`, \`{{$uuid}}\`, \`{{$timestamp}}\`, \`{{$randomEmail}}\`, \`{{$randomString}}\`, \`{{$randomName}}\`
|
|
181
|
-
These are the ONLY generators — do NOT invent others.
|
|
182
|
-
|
|
183
|
-
### Variable Interpolation
|
|
184
|
-
- \`{{variable}}\` in paths, bodies, headers, query params
|
|
185
|
-
- Captured values from previous steps are available in subsequent steps
|
|
186
|
-
- Environment variables from .env.yaml files: \`{{base_url}}\`, \`{{auth_username}}\`, etc.
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
## Step-by-Step Generation Algorithm
|
|
191
|
-
|
|
192
|
-
### Step 0: Register the API (REQUIRED FIRST)
|
|
193
|
-
**Always call \`setup_api\` before generating any tests.** This registers the collection in the database so WebUI, coverage tracking, and env loading all work.
|
|
194
|
-
\`\`\`
|
|
195
|
-
setup_api(name: "myapi", specPath: "/path/to/openapi.json", dir: "/path/to/project/apis/myapi")
|
|
196
|
-
\`\`\`
|
|
197
|
-
If you skip this step, WebUI will show "No API collections registered yet" and env variables won't auto-load.
|
|
198
|
-
|
|
199
|
-
${hasAuth ? `**Then set credentials immediately after setup_api** — edit the \`.env.yaml\` file in the API directory to add your credentials:
|
|
200
|
-
\`\`\`yaml
|
|
201
|
-
api_key: "<actual-key>"
|
|
202
|
-
base_url: "https://..."
|
|
203
|
-
\`\`\`
|
|
204
|
-
Never put actual key values directly in YAML test files.
|
|
205
|
-
|
|
206
|
-
` : ""}\
|
|
207
|
-
### Step 1: Analyze the API
|
|
208
|
-
- Identify authentication method (${hasAuth ? opts.securitySchemes.map(s => `${s.name}: ${s.type}${s.scheme ? `/${s.scheme}` : ""}`).join(", ") : "none detected"})
|
|
209
|
-
- Group endpoints by resource (e.g., /users/*, /pets/*, /orders/*)
|
|
210
|
-
- Identify CRUD patterns: POST (create) → GET (read) → PUT/PATCH (update) → DELETE
|
|
211
|
-
- Note required fields in request bodies
|
|
212
|
-
|
|
213
|
-
### Step 2: Plan Test Suites
|
|
214
|
-
Before generating, check coverage with \`coverage_analysis\` to avoid duplicating existing tests. Use \`generate_and_save(testsDir=...)\` for incremental generation of uncovered endpoints only.
|
|
215
|
-
|
|
216
|
-
> **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.
|
|
217
|
-
|
|
218
|
-
Create separate files for each concern:
|
|
219
|
-
${hasAuth ? `- \`${opts.outputDir}auth.yaml\` — Authentication flow\n` : ""}\
|
|
220
|
-
- \`${opts.outputDir}{resource}-crud.yaml\` — CRUD lifecycle per resource
|
|
221
|
-
- \`${opts.outputDir}{resource}-validation.yaml\` — Error cases per resource
|
|
222
|
-
|
|
223
|
-
### Step 3: Generate Each Suite
|
|
224
|
-
|
|
225
|
-
${hasAuth ? `**Auth suite** (\`auth.yaml\`):
|
|
226
|
-
1. Login with valid credentials → capture token
|
|
227
|
-
2. Access protected endpoint with token → 200
|
|
228
|
-
3. Login with invalid credentials → 401/403
|
|
229
|
-
4. Access protected endpoint without token → 401
|
|
230
|
-
|
|
231
|
-
` : ""}\
|
|
232
|
-
**CRUD lifecycle** (\`{resource}-crud.yaml\`):
|
|
233
|
-
1. Create resource (POST) → 201, **always verify key fields in response body** (at minimum: id, name/title)
|
|
234
|
-
2. Read created resource (GET /resource/{{id}}) → 200, verify fields match what was sent
|
|
235
|
-
3. List resources (GET /resource) → 200, verify \`_body: { type: "array" }\` AND \`_body.length: { gt: 0 }\`
|
|
236
|
-
4. Update resource (PUT/PATCH /resource/{{id}}) → 200
|
|
237
|
-
5. Read updated resource → verify changes applied
|
|
238
|
-
6. Delete resource (DELETE /resource/{{id}}) → 200/204
|
|
239
|
-
7. Verify deleted (GET /resource/{{id}}) → 404
|
|
240
|
-
8. For bulk create endpoints (createWithArray/List): create → then GET each to verify they exist
|
|
241
|
-
|
|
242
|
-
**Validation suite** (\`{resource}-validation.yaml\`):
|
|
243
|
-
1. Create with missing required fields → 400/422, verify \`message: { exists: true }\` in error body
|
|
244
|
-
2. Create with invalid field types → 400/422
|
|
245
|
-
3. Get non-existent resource (e.g. id=999999) → 404
|
|
246
|
-
4. Delete non-existent resource → 404
|
|
247
|
-
5. For error responses: always assert error body has meaningful content, not just status code
|
|
248
|
-
|
|
249
|
-
### Step 4: Save, Run, Debug
|
|
250
|
-
1. Use \`save_test_suite\` to save each file — it validates YAML before writing
|
|
251
|
-
2. Use \`run_tests\` to execute — review pass/fail summary
|
|
252
|
-
3. If failures: use \`query_db\` with \`action: "diagnose_failure"\` and the runId to see full request/response details
|
|
253
|
-
4. Fix issues and re-save with \`overwrite: true\`
|
|
254
|
-
|
|
255
|
-
---
|
|
256
|
-
|
|
257
|
-
## Tag Conventions
|
|
258
|
-
|
|
259
|
-
Use standard tags to enable safe filtering:
|
|
260
|
-
|
|
261
|
-
| Tag | HTTP Methods | Safe for |
|
|
262
|
-
|-----|-------------|---------|
|
|
263
|
-
| \`smoke\` | GET only | Production (read-only, zero risk) |
|
|
264
|
-
| \`crud\` | POST/PUT/PATCH | Staging only (state-changing) |
|
|
265
|
-
| \`destructive\` | DELETE | Explicit opt-in, run last |
|
|
266
|
-
| \`auth\` | Any (auth flows) | Run first to capture tokens |
|
|
267
|
-
|
|
268
|
-
Example: \`zond run --tag smoke --safe\` → reads-only, safe against production.
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## Practical Tips
|
|
273
|
-
|
|
274
|
-
- **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.
|
|
275
|
-
- **Nested assertions**: Use dot-notation or nested YAML — both work identically.
|
|
276
|
-
- **Root body type**: Use \`_body: { type: "array" }\` to verify the response body type itself.
|
|
277
|
-
- **List endpoints**: Always check both type AND non-emptiness: \`_body: { type: "array" }\` + \`_body.length: { gt: 0 }\`
|
|
278
|
-
- **Create responses**: Always verify at least the key identifying fields (id, name) in the response body — don't just check status.
|
|
279
|
-
- **Error responses**: Assert that error bodies contain useful info (\`message: { exists: true }\`), not just status codes.
|
|
280
|
-
- **Bulk operations**: After bulk create (createWithArray, createWithList), add GET steps to verify resources were actually created.
|
|
281
|
-
- **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.
|
|
282
|
-
- **Cleanup pattern**: Always delete test data in the same suite. Use a create → read → delete lifecycle so tests are idempotent:
|
|
283
|
-
\`\`\`yaml
|
|
284
|
-
tests:
|
|
285
|
-
- name: Create test resource
|
|
286
|
-
POST: /users
|
|
287
|
-
json: { name: "zond-test-{{$randomString}}" }
|
|
288
|
-
expect:
|
|
289
|
-
status: 201
|
|
290
|
-
body:
|
|
291
|
-
id: { capture: user_id }
|
|
292
|
-
- name: Read created resource
|
|
293
|
-
GET: /users/{{user_id}}
|
|
294
|
-
expect:
|
|
295
|
-
status: 200
|
|
296
|
-
- name: Cleanup - delete test resource
|
|
297
|
-
DELETE: /users/{{user_id}}
|
|
298
|
-
expect:
|
|
299
|
-
status: 204
|
|
300
|
-
\`\`\`
|
|
301
|
-
- **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.
|
|
302
|
-
|
|
303
|
-
---
|
|
304
|
-
|
|
305
|
-
## Common Mistakes to Avoid
|
|
306
|
-
|
|
307
|
-
1. **equals vs capture**: \`capture\` SAVES a value, \`equals\` COMPARES. To extract a token: \`{ capture: "token" }\` NOT \`{ equals: "{{token}}" }\`
|
|
308
|
-
2. **exists must be boolean**: \`exists: true\` NOT \`exists: "true"\`
|
|
309
|
-
3. **Status must be integer or array**: \`status: 200\` or \`status: [200, 204]\` NOT \`status: "200"\`
|
|
310
|
-
4. **One method per step**: Each test step has exactly ONE of GET/POST/PUT/PATCH/DELETE
|
|
311
|
-
5. **Don't hardcode base URL**: Use \`{{base_url}}\` — set it in environment or suite base_url
|
|
312
|
-
6. **Auth credentials**: Use environment variables \`{{auth_username}}\`, \`{{auth_password}}\` — NOT generators
|
|
313
|
-
7. **String query params**: Query parameter values must be strings: \`limit: "10"\` not \`limit: 10\`
|
|
314
|
-
8. **Hardcoded credentials**: NEVER put actual API keys/tokens in YAML — use \`{{api_key}}\` from env instead
|
|
315
|
-
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.
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
## Tools to Use
|
|
320
|
-
|
|
321
|
-
| Tool | When |
|
|
322
|
-
|------|------|
|
|
323
|
-
| \`setup_api\` | Register a new API (creates dirs, reads spec, sets up env) |
|
|
324
|
-
| \`generate_and_save\` | Get test generation guide (with auto-chunking for large APIs) |
|
|
325
|
-
| \`save_test_suite\` | Save generated YAML (validates before writing) |
|
|
326
|
-
| \`save_test_suites\` | Save multiple YAML files in one call |
|
|
327
|
-
| \`run_tests\` | Execute saved test suites |
|
|
328
|
-
| \`query_db\` | Query runs, collections, results, diagnose failures |
|
|
329
|
-
| \`coverage_analysis\` | Find untested endpoints for incremental generation |
|
|
330
|
-
| \`describe_endpoint\` | Get full details for one endpoint when debugging |
|
|
331
|
-
| \`ci_init\` | Generate CI/CD workflow (GitHub Actions / GitLab CI) to run tests on push |
|
|
332
|
-
|
|
333
|
-
## Workflow After Tests Pass
|
|
334
|
-
|
|
335
|
-
After tests are saved and running successfully, ask the user if they want to set up CI/CD:
|
|
336
|
-
1. Use \`ci_init\` to generate a CI workflow (auto-detects platform or use platform param)
|
|
337
|
-
2. Help them commit and push to their repository
|
|
338
|
-
3. Tests will run automatically on push, PR, and on schedule
|
|
339
|
-
`;
|
|
85
|
+
${opts.apiContext}`;
|
|
340
86
|
}
|
|
@@ -4,7 +4,7 @@ import { getDb } from "../../db/schema.ts";
|
|
|
4
4
|
import { listCollections, listRuns, getRunById, getResultsByRunId, getCollectionById } from "../../db/queries.ts";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { TOOL_DESCRIPTIONS } from "../descriptions.js";
|
|
7
|
-
import { statusHint, classifyFailure, envHint, envCategory } from "../../core/diagnostics/failure-hints.ts";
|
|
7
|
+
import { statusHint, classifyFailure, envHint, envCategory, schemaHint } from "../../core/diagnostics/failure-hints.ts";
|
|
8
8
|
|
|
9
9
|
function parseBodySafe(raw: string | null | undefined): unknown {
|
|
10
10
|
if (!raw) return undefined;
|
|
@@ -124,6 +124,7 @@ export function registerQueryDbTool(server: McpServer, dbPath?: string) {
|
|
|
124
124
|
// env issues take priority over generic status hints
|
|
125
125
|
const hint = envHint(r.request_url, r.error_message, envFilePath) ?? statusHint(r.response_status);
|
|
126
126
|
const failure_type = classifyFailure(r.status, r.response_status);
|
|
127
|
+
const sHint = schemaHint(failure_type, r.response_status);
|
|
127
128
|
return {
|
|
128
129
|
suite_name: r.suite_name,
|
|
129
130
|
test_name: r.test_name,
|
|
@@ -134,6 +135,7 @@ export function registerQueryDbTool(server: McpServer, dbPath?: string) {
|
|
|
134
135
|
request_url: r.request_url,
|
|
135
136
|
response_status: r.response_status,
|
|
136
137
|
...(hint ? { hint } : {}),
|
|
138
|
+
...(sHint ? { schema_hint: sHint } : {}),
|
|
137
139
|
response_body: parseBodySafe(r.response_body),
|
|
138
140
|
response_headers: r.response_headers
|
|
139
141
|
? JSON.parse(r.response_headers)
|
|
@@ -48,6 +48,12 @@ export function registerRunTestsTool(server: McpServer, dbPath?: string) {
|
|
|
48
48
|
const hints: string[] = [];
|
|
49
49
|
if (failedSteps.length > 0) {
|
|
50
50
|
hints.push("Use query_db(action: 'diagnose_failure', runId: " + runId + ") for detailed failure analysis");
|
|
51
|
+
const hasAssertionFailures = failedSteps.some(s => s.assertions.length > 0);
|
|
52
|
+
if (hasAssertionFailures) {
|
|
53
|
+
hints.push(
|
|
54
|
+
"Some tests have assertion failures — use describe_endpoint(specPath, method, path) to verify expected schemas"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
51
57
|
}
|
|
52
58
|
hints.push("Use manage_server(action: 'start') to launch the Web UI and view results visually in a browser at http://localhost:8080");
|
|
53
59
|
hints.push("Ask the user if they want to set up CI/CD to run these tests automatically on push. If yes, use ci_init to generate a workflow and help them push to GitHub/GitLab.");
|