@kirrosh/zond 0.26.1 → 0.27.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/CHANGELOG.md +47 -0
- package/README.md +22 -21
- package/bin/zond.mjs +23 -0
- package/package.json +13 -16
- package/scripts/npm/postinstall.mjs +76 -0
- package/src/CLAUDE.md +0 -112
- package/src/bun-types.d.ts +0 -5
- package/src/cli/argv.ts +0 -122
- package/src/cli/commands/add-api.ts +0 -146
- package/src/cli/commands/api/annotate/idempotency.ts +0 -59
- package/src/cli/commands/api/annotate/index.ts +0 -880
- package/src/cli/commands/api/annotate/lifecycle.ts +0 -74
- package/src/cli/commands/api/annotate/overlay.ts +0 -206
- package/src/cli/commands/api/annotate/pagination.ts +0 -64
- package/src/cli/commands/api/annotate/prompts.ts +0 -220
- package/src/cli/commands/api/annotate/readback.ts +0 -58
- package/src/cli/commands/api/annotate/resources.ts +0 -91
- package/src/cli/commands/api/annotate/seed-bodies.ts +0 -61
- package/src/cli/commands/audit.ts +0 -786
- package/src/cli/commands/catalog.ts +0 -97
- package/src/cli/commands/check.ts +0 -361
- package/src/cli/commands/checks.ts +0 -1072
- package/src/cli/commands/ci-init.ts +0 -223
- package/src/cli/commands/clean.ts +0 -212
- package/src/cli/commands/cleanup.ts +0 -236
- package/src/cli/commands/completions.ts +0 -192
- package/src/cli/commands/coverage.ts +0 -872
- package/src/cli/commands/db.ts +0 -611
- package/src/cli/commands/describe.ts +0 -120
- package/src/cli/commands/discover.ts +0 -1356
- package/src/cli/commands/doctor.ts +0 -661
- package/src/cli/commands/fixtures.ts +0 -402
- package/src/cli/commands/generate.ts +0 -577
- package/src/cli/commands/init/agents-md.ts +0 -61
- package/src/cli/commands/init/bootstrap.ts +0 -111
- package/src/cli/commands/init/index.ts +0 -244
- package/src/cli/commands/init/skills.ts +0 -141
- package/src/cli/commands/init/templates/agents.md +0 -86
- package/src/cli/commands/init/templates/markdown.d.ts +0 -4
- package/src/cli/commands/init/templates/skills/warm-up-target.md +0 -122
- package/src/cli/commands/init/templates/skills/zond-checks.md +0 -621
- package/src/cli/commands/init/templates/skills/zond-seed.md +0 -114
- package/src/cli/commands/init/templates/skills/zond-triage.md +0 -272
- package/src/cli/commands/init/templates/skills/zond.md +0 -861
- package/src/cli/commands/init/templates/zond-config.yml +0 -14
- package/src/cli/commands/prepare-fixtures.ts +0 -97
- package/src/cli/commands/probe/_seed-bodies.ts +0 -52
- package/src/cli/commands/probe/mass-assignment.ts +0 -594
- package/src/cli/commands/probe/security.ts +0 -537
- package/src/cli/commands/probe/static.ts +0 -255
- package/src/cli/commands/probe/webhooks.ts +0 -163
- package/src/cli/commands/probe.ts +0 -535
- package/src/cli/commands/reference.ts +0 -87
- package/src/cli/commands/refresh-api.ts +0 -227
- package/src/cli/commands/remove-api.ts +0 -150
- package/src/cli/commands/report-bundle.ts +0 -310
- package/src/cli/commands/report.ts +0 -241
- package/src/cli/commands/request.ts +0 -548
- package/src/cli/commands/run.ts +0 -1162
- package/src/cli/commands/schema-from-runs.ts +0 -128
- package/src/cli/commands/secrets.ts +0 -133
- package/src/cli/commands/session.ts +0 -244
- package/src/cli/commands/use.ts +0 -74
- package/src/cli/index.ts +0 -55
- package/src/cli/json-envelope.ts +0 -108
- package/src/cli/json-schemas.ts +0 -314
- package/src/cli/output.ts +0 -40
- package/src/cli/program.ts +0 -219
- package/src/cli/resolve.ts +0 -105
- package/src/cli/runtime.ts +0 -7
- package/src/cli/safe-live.ts +0 -24
- package/src/cli/status-filter.ts +0 -114
- package/src/cli/util/api-context.ts +0 -85
- package/src/cli/version.ts +0 -8
- package/src/core/audit/persist.ts +0 -183
- package/src/core/checks/budget.ts +0 -59
- package/src/core/checks/checks/_crud-helpers.ts +0 -133
- package/src/core/checks/checks/_negative_mutator.ts +0 -133
- package/src/core/checks/checks/_readback-helpers.ts +0 -133
- package/src/core/checks/checks/content_type_conformance.ts +0 -39
- package/src/core/checks/checks/cross_call_references.ts +0 -147
- package/src/core/checks/checks/cursor_boundary_fuzzing.ts +0 -219
- package/src/core/checks/checks/ensure_resource_availability.ts +0 -62
- package/src/core/checks/checks/idempotency_replay.ts +0 -242
- package/src/core/checks/checks/ignored_auth.ts +0 -254
- package/src/core/checks/checks/index.ts +0 -68
- package/src/core/checks/checks/lifecycle_transitions.ts +0 -416
- package/src/core/checks/checks/missing_required_header.ts +0 -40
- package/src/core/checks/checks/negative_data_rejection.ts +0 -148
- package/src/core/checks/checks/not_a_server_error.ts +0 -35
- package/src/core/checks/checks/open_cors_on_sensitive.ts +0 -160
- package/src/core/checks/checks/pagination_invariants.ts +0 -419
- package/src/core/checks/checks/positive_data_acceptance.ts +0 -33
- package/src/core/checks/checks/rate_limit_headers_absent.ts +0 -77
- package/src/core/checks/checks/response_headers_conformance.ts +0 -74
- package/src/core/checks/checks/response_schema_conformance.ts +0 -30
- package/src/core/checks/checks/status_code_conformance.ts +0 -132
- package/src/core/checks/checks/unsupported_method.ts +0 -63
- package/src/core/checks/checks/use_after_free.ts +0 -78
- package/src/core/checks/index.ts +0 -30
- package/src/core/checks/mode.ts +0 -82
- package/src/core/checks/recommended-action.ts +0 -68
- package/src/core/checks/registry.ts +0 -78
- package/src/core/checks/runner.ts +0 -1461
- package/src/core/checks/sarif.ts +0 -230
- package/src/core/checks/spec-findings.ts +0 -308
- package/src/core/checks/stateful.ts +0 -121
- package/src/core/checks/types.ts +0 -305
- package/src/core/checks/zond-extensions.ts +0 -73
- package/src/core/classifier/recommended-action.ts +0 -251
- package/src/core/context/current.ts +0 -51
- package/src/core/context/session.ts +0 -78
- package/src/core/coverage/loader.ts +0 -216
- package/src/core/coverage/reasons.ts +0 -300
- package/src/core/diagnostics/db-analysis.ts +0 -666
- package/src/core/diagnostics/failure-class.ts +0 -140
- package/src/core/diagnostics/failure-hints.ts +0 -112
- package/src/core/diagnostics/spec-pointer.ts +0 -99
- package/src/core/diagnostics/suggested-fixes.ts +0 -155
- package/src/core/exporter/case-study/index.ts +0 -270
- package/src/core/exporter/curl.ts +0 -40
- package/src/core/exporter/exporter.ts +0 -48
- package/src/core/exporter/html-report/escape.ts +0 -24
- package/src/core/exporter/html-report/index.ts +0 -479
- package/src/core/exporter/html-report/script.ts +0 -100
- package/src/core/exporter/html-report/styles.ts +0 -408
- package/src/core/generator/catalog-builder.ts +0 -179
- package/src/core/generator/chunker.ts +0 -78
- package/src/core/generator/coverage-phase.ts +0 -0
- package/src/core/generator/coverage-scanner.ts +0 -87
- package/src/core/generator/data-factory.ts +0 -759
- package/src/core/generator/describe.ts +0 -302
- package/src/core/generator/endpoint-warnings.ts +0 -52
- package/src/core/generator/fixtures-builder.ts +0 -332
- package/src/core/generator/index.ts +0 -14
- package/src/core/generator/openapi-reader.ts +0 -297
- package/src/core/generator/path-param-disambig.ts +0 -140
- package/src/core/generator/resources-builder.ts +0 -898
- package/src/core/generator/schema-utils.ts +0 -112
- package/src/core/generator/serializer.ts +0 -343
- package/src/core/generator/suite-generator.ts +0 -1301
- package/src/core/generator/types.ts +0 -63
- package/src/core/identity/identity-file.ts +0 -0
- package/src/core/lint/affects.ts +0 -28
- package/src/core/lint/config.ts +0 -96
- package/src/core/lint/format.ts +0 -42
- package/src/core/lint/index.ts +0 -94
- package/src/core/lint/reporter.ts +0 -128
- package/src/core/lint/rules/consistency.ts +0 -158
- package/src/core/lint/rules/heuristics.ts +0 -97
- package/src/core/lint/rules/strictness.ts +0 -109
- package/src/core/lint/types.ts +0 -96
- package/src/core/lint/walker.ts +0 -248
- package/src/core/meta/meta-store.ts +0 -11
- package/src/core/output/README.md +0 -73
- package/src/core/output/index.ts +0 -13
- package/src/core/output/run.ts +0 -91
- package/src/core/output/types.ts +0 -122
- package/src/core/parser/dynamic-values.ts +0 -160
- package/src/core/parser/env-interpolation.ts +0 -104
- package/src/core/parser/filter.ts +0 -97
- package/src/core/parser/schema.ts +0 -359
- package/src/core/parser/types.ts +0 -117
- package/src/core/parser/variables.ts +0 -0
- package/src/core/parser/yaml-parser.ts +0 -181
- package/src/core/probe/bootstrap.ts +0 -34
- package/src/core/probe/dry-run-envelope.ts +0 -61
- package/src/core/probe/mass-assignment/classify.ts +0 -175
- package/src/core/probe/mass-assignment/cleanup.ts +0 -52
- package/src/core/probe/mass-assignment/digest.ts +0 -114
- package/src/core/probe/mass-assignment/orchestrator.ts +0 -459
- package/src/core/probe/mass-assignment/regression.ts +0 -141
- package/src/core/probe/mass-assignment/suspects.ts +0 -92
- package/src/core/probe/mass-assignment/types.ts +0 -135
- package/src/core/probe/mass-assignment-probe-class.ts +0 -198
- package/src/core/probe/mass-assignment-probe.ts +0 -27
- package/src/core/probe/mass-assignment-template.ts +0 -240
- package/src/core/probe/method-probe.ts +0 -164
- package/src/core/probe/method-shared.ts +0 -69
- package/src/core/probe/negative-probe.ts +0 -691
- package/src/core/probe/orphan-tracker.ts +0 -188
- package/src/core/probe/path-discovery.ts +0 -439
- package/src/core/probe/probe-harness.ts +0 -119
- package/src/core/probe/registry.ts +0 -89
- package/src/core/probe/runner.ts +0 -136
- package/src/core/probe/security/baseline.ts +0 -174
- package/src/core/probe/security/classify.ts +0 -341
- package/src/core/probe/security/cleanup.ts +0 -125
- package/src/core/probe/security/detectors.ts +0 -71
- package/src/core/probe/security/digest.ts +0 -104
- package/src/core/probe/security/orchestrator.ts +0 -398
- package/src/core/probe/security/regression.ts +0 -103
- package/src/core/probe/security/types.ts +0 -151
- package/src/core/probe/security-probe-class.ts +0 -207
- package/src/core/probe/security-probe.ts +0 -32
- package/src/core/probe/shared.ts +0 -531
- package/src/core/probe/static-probe-class.ts +0 -125
- package/src/core/probe/types.ts +0 -165
- package/src/core/probe/verdict-aggregator.ts +0 -33
- package/src/core/probe/webhooks-probe.ts +0 -282
- package/src/core/reporter/console.ts +0 -240
- package/src/core/reporter/index.ts +0 -22
- package/src/core/reporter/json.ts +0 -22
- package/src/core/reporter/junit.ts +0 -93
- package/src/core/reporter/ndjson.ts +0 -37
- package/src/core/reporter/types.ts +0 -15
- package/src/core/runner/assertions.ts +0 -383
- package/src/core/runner/async-pool.ts +0 -108
- package/src/core/runner/auth-path.ts +0 -8
- package/src/core/runner/ci-context.ts +0 -72
- package/src/core/runner/executor.ts +0 -652
- package/src/core/runner/expr-eval.ts +0 -41
- package/src/core/runner/form-encode.ts +0 -41
- package/src/core/runner/http-client.ts +0 -224
- package/src/core/runner/learn-drift.ts +0 -293
- package/src/core/runner/preflight-vars.ts +0 -153
- package/src/core/runner/progress-tracker.ts +0 -73
- package/src/core/runner/rate-limiter.ts +0 -185
- package/src/core/runner/run-kind.ts +0 -45
- package/src/core/runner/schema-validator.ts +0 -308
- package/src/core/runner/send-request.ts +0 -232
- package/src/core/runner/transforms.ts +0 -65
- package/src/core/runner/types.ts +0 -94
- package/src/core/secrets/registry.ts +0 -164
- package/src/core/secrets/secrets-file.ts +0 -115
- package/src/core/selectors/operation-filter.ts +0 -144
- package/src/core/setup-api.ts +0 -590
- package/src/core/severity/category.ts +0 -94
- package/src/core/severity/index.ts +0 -58
- package/src/core/spec/infer-schema.ts +0 -102
- package/src/core/spec/layers.ts +0 -154
- package/src/core/spec/merge-specs.ts +0 -156
- package/src/core/spec/schema-from-runs.ts +0 -117
- package/src/core/spec/schema-overlay.ts +0 -130
- package/src/core/util/ajv.ts +0 -13
- package/src/core/util/format-eta.ts +0 -21
- package/src/core/util/headers.ts +0 -9
- package/src/core/util/url.ts +0 -24
- package/src/core/utils.ts +0 -13
- package/src/core/workspace/config.ts +0 -129
- package/src/core/workspace/fixture-gap-report.ts +0 -84
- package/src/core/workspace/fixture-gaps.ts +0 -71
- package/src/core/workspace/manifest.ts +0 -283
- package/src/core/workspace/output-rotation.ts +0 -62
- package/src/core/workspace/root.ts +0 -96
- package/src/core/workspace/triage-path.ts +0 -87
- package/src/db/lint-runs.ts +0 -47
- package/src/db/migrate.ts +0 -128
- package/src/db/migrations/0001_run_kind.sql +0 -25
- package/src/db/migrations/0002_run_kind_request.sql +0 -59
- package/src/db/migrations/sql.d.ts +0 -4
- package/src/db/queries/collections.ts +0 -133
- package/src/db/queries/coverage.ts +0 -9
- package/src/db/queries/dashboard.ts +0 -59
- package/src/db/queries/results.ts +0 -216
- package/src/db/queries/runs.ts +0 -289
- package/src/db/queries/sessions.ts +0 -42
- package/src/db/queries/settings.ts +0 -28
- package/src/db/queries/types.ts +0 -172
- package/src/db/queries.ts +0 -75
- package/src/db/schema.ts +0 -298
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import type { TestRunResult, StepResult, AssertionResult } from "../runner/types.ts";
|
|
2
|
-
import type { Reporter, ReporterOptions } from "./types.ts";
|
|
3
|
-
|
|
4
|
-
// ANSI escape codes
|
|
5
|
-
const RESET = "\x1b[0m";
|
|
6
|
-
const BOLD = "\x1b[1m";
|
|
7
|
-
const DIM = "\x1b[2m";
|
|
8
|
-
const GREEN = "\x1b[32m";
|
|
9
|
-
const RED = "\x1b[31m";
|
|
10
|
-
const GRAY = "\x1b[90m";
|
|
11
|
-
const YELLOW = "\x1b[33m";
|
|
12
|
-
|
|
13
|
-
const PASS_ICON = "\u2713"; // ✓
|
|
14
|
-
const FAIL_ICON = "\u2717"; // ✗
|
|
15
|
-
const SKIP_ICON = "\u25CB"; // ○
|
|
16
|
-
|
|
17
|
-
export function is5xx(step: StepResult): boolean {
|
|
18
|
-
const status = step.response?.status;
|
|
19
|
-
return typeof status === "number" && status >= 500 && status < 600;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function count5xx(steps: StepResult[]): number {
|
|
23
|
-
let n = 0;
|
|
24
|
-
for (const s of steps) {
|
|
25
|
-
if ((s.status === "fail" || s.status === "error") && is5xx(s)) n++;
|
|
26
|
-
}
|
|
27
|
-
return n;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function formatDuration(ms: number): string {
|
|
31
|
-
if (ms < 1000) return `${Math.round(ms)}ms`;
|
|
32
|
-
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
33
|
-
const mins = Math.floor(ms / 60000);
|
|
34
|
-
const secs = Math.round((ms % 60000) / 1000);
|
|
35
|
-
return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function formatStep(step: StepResult, color: boolean): string {
|
|
39
|
-
const duration = formatDuration(step.duration_ms);
|
|
40
|
-
|
|
41
|
-
switch (step.status) {
|
|
42
|
-
case "pass": {
|
|
43
|
-
const icon = color ? `${GREEN}${PASS_ICON}${RESET}` : PASS_ICON;
|
|
44
|
-
const dim = color ? `${DIM}(${duration})${RESET}` : `(${duration})`;
|
|
45
|
-
return ` ${icon} ${step.name} ${dim}`;
|
|
46
|
-
}
|
|
47
|
-
case "fail": {
|
|
48
|
-
const icon = color ? `${RED}${FAIL_ICON}${RESET}` : FAIL_ICON;
|
|
49
|
-
const dim = color ? `${DIM}(${duration})${RESET}` : `(${duration})`;
|
|
50
|
-
const tag = is5xx(step) ? (color ? ` ${BOLD}${YELLOW}[5xx ${step.response?.status}]${RESET}` : ` [5xx ${step.response?.status}]`) : "";
|
|
51
|
-
return ` ${icon} ${step.name}${tag} ${dim}`;
|
|
52
|
-
}
|
|
53
|
-
case "skip": {
|
|
54
|
-
const icon = color ? `${GRAY}${SKIP_ICON}${RESET}` : SKIP_ICON;
|
|
55
|
-
const reason = step.error ? `skipped: ${step.error}` : "skipped";
|
|
56
|
-
const label = color ? `${GRAY}(${reason})${RESET}` : `(${reason})`;
|
|
57
|
-
return ` ${icon} ${step.name} ${label}`;
|
|
58
|
-
}
|
|
59
|
-
case "error": {
|
|
60
|
-
const icon = color ? `${RED}${FAIL_ICON}${RESET}` : FAIL_ICON;
|
|
61
|
-
const label = color ? `${DIM}(error)${RESET}` : "(error)";
|
|
62
|
-
return ` ${icon} ${step.name} ${label}`;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function formatFailures(step: StepResult, color: boolean): string {
|
|
68
|
-
const lines: string[] = [];
|
|
69
|
-
|
|
70
|
-
if (step.status === "error" && step.error) {
|
|
71
|
-
const msg = color ? `${RED}Error: ${step.error}${RESET}` : `Error: ${step.error}`;
|
|
72
|
-
lines.push(` ${msg}`);
|
|
73
|
-
return lines.join("\n");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const failed = step.assertions.filter((a) => !a.passed);
|
|
77
|
-
for (const a of failed) {
|
|
78
|
-
const msg = formatAssertion(a);
|
|
79
|
-
lines.push(color ? ` ${RED}${msg}${RESET}` : ` ${msg}`);
|
|
80
|
-
}
|
|
81
|
-
return lines.join("\n");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function formatAssertion(a: { field: string; rule: string; actual: unknown; expected: unknown; kind?: string }): string {
|
|
85
|
-
// Schema assertions already carry a humanised `expected` string ("missing
|
|
86
|
-
// required field …", "type integer", …). Use it directly — interpolating
|
|
87
|
-
// the actual subtree via String() turns into "[object Object]" and buries
|
|
88
|
-
// the actionable detail (TASK-277).
|
|
89
|
-
//
|
|
90
|
-
// ARV-27: when `actual` is a primitive (string/number/bool/null), append it
|
|
91
|
-
// to the message so format/type/enum/const failures show the offending value
|
|
92
|
-
// — same shape as runtime asserts ("expected equals 200 but got 422").
|
|
93
|
-
// Skipped for objects/arrays to avoid burying the message under JSON dumps
|
|
94
|
-
// (the original TASK-277 concern).
|
|
95
|
-
if (a.kind === "schema" && typeof a.expected === "string") {
|
|
96
|
-
if (isPrimitive(a.actual)) return `${a.field}: ${a.expected} (got ${formatValue(a.actual)})`;
|
|
97
|
-
return `${a.field}: ${a.expected}`;
|
|
98
|
-
}
|
|
99
|
-
return `${a.field}: expected ${a.rule} but got ${formatValue(a.actual)}`;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function isPrimitive(v: unknown): boolean {
|
|
103
|
-
return v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean";
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function formatValue(value: unknown): string {
|
|
107
|
-
if (value === undefined) return "undefined";
|
|
108
|
-
if (value === null) return "null";
|
|
109
|
-
if (typeof value === "string") return `"${value}"`;
|
|
110
|
-
if (typeof value === "object") return JSON.stringify(value);
|
|
111
|
-
return String(value);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function formatSuiteResult(result: TestRunResult, color: boolean): string {
|
|
115
|
-
const lines: string[] = [];
|
|
116
|
-
|
|
117
|
-
// Suite header
|
|
118
|
-
const header = color ? ` ${BOLD}${result.suite_name}${RESET}` : ` ${result.suite_name}`;
|
|
119
|
-
lines.push(header);
|
|
120
|
-
|
|
121
|
-
// Tags
|
|
122
|
-
if (result.suite_tags?.length) {
|
|
123
|
-
const tagsStr = result.suite_tags.map(t => `[${t}]`).join(" ");
|
|
124
|
-
lines.push(color ? ` ${DIM}${tagsStr}${RESET}` : ` ${tagsStr}`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Steps
|
|
128
|
-
for (const step of result.steps) {
|
|
129
|
-
lines.push(formatStep(step, color));
|
|
130
|
-
if (step.status === "fail" || step.status === "error") {
|
|
131
|
-
const details = formatFailures(step, color);
|
|
132
|
-
if (details) lines.push(details);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Summary
|
|
137
|
-
const totalMs = Date.parse(result.finished_at) - Date.parse(result.started_at);
|
|
138
|
-
const duration = formatDuration(totalMs > 0 ? totalMs : 0);
|
|
139
|
-
const parts: string[] = [];
|
|
140
|
-
|
|
141
|
-
if (result.passed > 0) {
|
|
142
|
-
parts.push(color ? `${GREEN}${result.passed} passed${RESET}` : `${result.passed} passed`);
|
|
143
|
-
}
|
|
144
|
-
if (result.failed > 0) {
|
|
145
|
-
parts.push(color ? `${RED}${result.failed} failed${RESET}` : `${result.failed} failed`);
|
|
146
|
-
}
|
|
147
|
-
const fiveXx = count5xx(result.steps);
|
|
148
|
-
if (fiveXx > 0) {
|
|
149
|
-
const label = `${fiveXx} 5xx`;
|
|
150
|
-
parts.push(color ? `${BOLD}${YELLOW}${label}${RESET}` : label);
|
|
151
|
-
}
|
|
152
|
-
const errored = result.steps.filter(s => s.status === "error").length;
|
|
153
|
-
if (errored > 0) {
|
|
154
|
-
parts.push(color ? `${RED}${errored} errored${RESET}` : `${errored} errored`);
|
|
155
|
-
}
|
|
156
|
-
if (result.skipped > 0) {
|
|
157
|
-
parts.push(color ? `${GRAY}${result.skipped} skipped${RESET}` : `${result.skipped} skipped`);
|
|
158
|
-
}
|
|
159
|
-
if (parts.length === 0) {
|
|
160
|
-
parts.push("0 tests");
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
lines.push("");
|
|
164
|
-
lines.push(`Results: ${parts.join(", ")} (${duration})`);
|
|
165
|
-
|
|
166
|
-
return lines.join("\n");
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export function formatGrandTotal(results: TestRunResult[], color: boolean): string {
|
|
170
|
-
const totals = { passed: 0, failed: 0, skipped: 0, errored: 0, total: 0, fiveXx: 0 };
|
|
171
|
-
let minStart = Infinity;
|
|
172
|
-
let maxEnd = -Infinity;
|
|
173
|
-
|
|
174
|
-
for (const r of results) {
|
|
175
|
-
totals.passed += r.passed;
|
|
176
|
-
totals.failed += r.failed;
|
|
177
|
-
totals.skipped += r.skipped;
|
|
178
|
-
totals.errored += r.steps.filter(s => s.status === "error").length;
|
|
179
|
-
totals.total += r.total;
|
|
180
|
-
totals.fiveXx += count5xx(r.steps);
|
|
181
|
-
const start = Date.parse(r.started_at);
|
|
182
|
-
const end = Date.parse(r.finished_at);
|
|
183
|
-
if (start < minStart) minStart = start;
|
|
184
|
-
if (end > maxEnd) maxEnd = end;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const totalMs = maxEnd - minStart;
|
|
188
|
-
const duration = formatDuration(totalMs > 0 ? totalMs : 0);
|
|
189
|
-
const parts: string[] = [];
|
|
190
|
-
|
|
191
|
-
if (totals.passed > 0) {
|
|
192
|
-
parts.push(color ? `${GREEN}${totals.passed} passed${RESET}` : `${totals.passed} passed`);
|
|
193
|
-
}
|
|
194
|
-
if (totals.failed > 0) {
|
|
195
|
-
parts.push(color ? `${RED}${totals.failed} failed${RESET}` : `${totals.failed} failed`);
|
|
196
|
-
}
|
|
197
|
-
if (totals.fiveXx > 0) {
|
|
198
|
-
const label = `${totals.fiveXx} 5xx`;
|
|
199
|
-
parts.push(color ? `${BOLD}${YELLOW}${label}${RESET}` : label);
|
|
200
|
-
}
|
|
201
|
-
if (totals.errored > 0) {
|
|
202
|
-
parts.push(color ? `${RED}${totals.errored} errored${RESET}` : `${totals.errored} errored`);
|
|
203
|
-
}
|
|
204
|
-
if (totals.skipped > 0) {
|
|
205
|
-
parts.push(color ? `${GRAY}${totals.skipped} skipped${RESET}` : `${totals.skipped} skipped`);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const header = color ? `${BOLD}Total:${RESET}` : "Total:";
|
|
209
|
-
return `${header} ${parts.join(", ")} (${duration})`;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export const consoleReporter: Reporter = {
|
|
213
|
-
report(results: TestRunResult[], options?: ReporterOptions): void {
|
|
214
|
-
const color = options?.color ?? (process.stdout.isTTY ?? false);
|
|
215
|
-
|
|
216
|
-
if (results.length === 0) {
|
|
217
|
-
console.log("No test suites found.");
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// TASK-265: --quiet collapses output to one summary line. Exit code
|
|
222
|
-
// still differentiates pass/fail; this is for CI logs and `run --watch`
|
|
223
|
-
// where per-test detail is noise between iterations.
|
|
224
|
-
if (options?.quiet) {
|
|
225
|
-
console.log(formatGrandTotal(results, color));
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const blocks: string[] = [];
|
|
230
|
-
for (const result of results) {
|
|
231
|
-
blocks.push(formatSuiteResult(result, color));
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
console.log(blocks.join("\n\n"));
|
|
235
|
-
|
|
236
|
-
if (results.length > 1) {
|
|
237
|
-
console.log("\n" + formatGrandTotal(results, color));
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export type { Reporter, ReporterOptions, ReporterName } from "./types.ts";
|
|
2
|
-
export { generateJsonReport } from "./json.ts";
|
|
3
|
-
export { generateJunitXml } from "./junit.ts";
|
|
4
|
-
|
|
5
|
-
import type { Reporter, ReporterName } from "./types.ts";
|
|
6
|
-
import { consoleReporter } from "./console.ts";
|
|
7
|
-
import { jsonReporter } from "./json.ts";
|
|
8
|
-
import { junitReporter } from "./junit.ts";
|
|
9
|
-
|
|
10
|
-
const reporters: Record<ReporterName, Reporter> = {
|
|
11
|
-
console: consoleReporter,
|
|
12
|
-
json: jsonReporter,
|
|
13
|
-
junit: junitReporter,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export function getReporter(name: ReporterName): Reporter {
|
|
17
|
-
const reporter = reporters[name];
|
|
18
|
-
if (!reporter) {
|
|
19
|
-
throw new Error(`Unknown reporter: ${name}. Available: ${Object.keys(reporters).join(", ")}`);
|
|
20
|
-
}
|
|
21
|
-
return reporter;
|
|
22
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { TestRunResult } from "../runner/types.ts";
|
|
2
|
-
import type { Reporter, ReporterOptions } from "./types.ts";
|
|
3
|
-
import { type Exporter, runExporter } from "../exporter/exporter.ts";
|
|
4
|
-
|
|
5
|
-
const jsonExporter: Exporter<TestRunResult[]> = {
|
|
6
|
-
name: "json",
|
|
7
|
-
mime: "application/json",
|
|
8
|
-
render(results: TestRunResult[]): string {
|
|
9
|
-
return JSON.stringify(results, null, 2);
|
|
10
|
-
},
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/** TASK-186: pure render → sanitizer pipeline; redaction lives in runExporter. */
|
|
14
|
-
export function generateJsonReport(results: TestRunResult[]): string {
|
|
15
|
-
return runExporter(jsonExporter, results);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const jsonReporter: Reporter = {
|
|
19
|
-
report(results: TestRunResult[], _options?: ReporterOptions): void {
|
|
20
|
-
console.log(generateJsonReport(results));
|
|
21
|
-
},
|
|
22
|
-
};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import type { TestRunResult, StepResult } from "../runner/types.ts";
|
|
2
|
-
import { type Exporter, runExporter } from "../exporter/exporter.ts";
|
|
3
|
-
import type { Reporter, ReporterOptions } from "./types.ts";
|
|
4
|
-
|
|
5
|
-
function escapeXml(str: string): string {
|
|
6
|
-
return str
|
|
7
|
-
.replace(/&/g, "&")
|
|
8
|
-
.replace(/</g, "<")
|
|
9
|
-
.replace(/>/g, ">")
|
|
10
|
-
.replace(/"/g, """)
|
|
11
|
-
.replace(/'/g, "'");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function formatTime(ms: number): string {
|
|
15
|
-
return (ms / 1000).toFixed(3);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function renderTestcase(step: StepResult): string {
|
|
19
|
-
const time = formatTime(step.duration_ms);
|
|
20
|
-
const name = escapeXml(step.name);
|
|
21
|
-
|
|
22
|
-
if (step.status === "pass") {
|
|
23
|
-
return ` <testcase name="${name}" time="${time}"/>`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (step.status === "skip") {
|
|
27
|
-
return ` <testcase name="${name}" time="${time}">\n <skipped/>\n </testcase>`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (step.status === "fail") {
|
|
31
|
-
const failedAssertions = step.assertions.filter((a) => !a.passed);
|
|
32
|
-
const message = failedAssertions.length > 0
|
|
33
|
-
? escapeXml(`${failedAssertions[0]!.rule}: expected ${JSON.stringify(failedAssertions[0]!.expected)}, got ${JSON.stringify(failedAssertions[0]!.actual)}`)
|
|
34
|
-
: escapeXml(step.error ?? "Assertion failed");
|
|
35
|
-
const body = failedAssertions
|
|
36
|
-
.map((a) => escapeXml(`${a.rule}: expected ${JSON.stringify(a.expected)}, got ${JSON.stringify(a.actual)}`))
|
|
37
|
-
.join("\n");
|
|
38
|
-
return ` <testcase name="${name}" time="${time}">\n <failure message="${message}">${body}</failure>\n </testcase>`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// error
|
|
42
|
-
const message = escapeXml(step.error ?? "Unknown error");
|
|
43
|
-
return ` <testcase name="${name}" time="${time}">\n <error message="${message}">${message}</error>\n </testcase>`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function renderTestsuite(result: TestRunResult): string {
|
|
47
|
-
const name = escapeXml(result.suite_name);
|
|
48
|
-
const failures = result.failed;
|
|
49
|
-
const tests = result.total;
|
|
50
|
-
const errors = result.steps.filter((s) => s.status === "error").length;
|
|
51
|
-
const skipped = result.skipped;
|
|
52
|
-
const time = formatTime(result.steps.reduce((sum, s) => sum + s.duration_ms, 0));
|
|
53
|
-
|
|
54
|
-
const testcases = result.steps.map(renderTestcase).join("\n");
|
|
55
|
-
|
|
56
|
-
return ` <testsuite name="${name}" tests="${tests}" failures="${failures}" errors="${errors}" skipped="${skipped}" time="${time}">\n${testcases}\n </testsuite>`;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const junitExporter: Exporter<TestRunResult[]> = {
|
|
60
|
-
name: "junit",
|
|
61
|
-
mime: "application/xml",
|
|
62
|
-
render(results: TestRunResult[]): string {
|
|
63
|
-
const totalTests = results.reduce((s, r) => s + r.total, 0);
|
|
64
|
-
const totalFailures = results.reduce((s, r) => s + r.failed, 0);
|
|
65
|
-
const totalErrors = results.reduce(
|
|
66
|
-
(s, r) => s + r.steps.filter((s) => s.status === "error").length,
|
|
67
|
-
0,
|
|
68
|
-
);
|
|
69
|
-
const totalTime = formatTime(
|
|
70
|
-
results.reduce((s, r) => s + r.steps.reduce((ss, step) => ss + step.duration_ms, 0), 0),
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
const suites = results.map(renderTestsuite).join("\n");
|
|
74
|
-
|
|
75
|
-
return [
|
|
76
|
-
`<?xml version="1.0" encoding="UTF-8"?>`,
|
|
77
|
-
`<testsuites tests="${totalTests}" failures="${totalFailures}" errors="${totalErrors}" time="${totalTime}">`,
|
|
78
|
-
suites,
|
|
79
|
-
`</testsuites>`,
|
|
80
|
-
].join("\n");
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/** TASK-186: pure render → sanitizer pipeline; redaction lives in runExporter. */
|
|
85
|
-
export function generateJunitXml(results: TestRunResult[]): string {
|
|
86
|
-
return runExporter(junitExporter, results);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export const junitReporter: Reporter = {
|
|
90
|
-
report(results: TestRunResult[], _options?: ReporterOptions): void {
|
|
91
|
-
console.log(generateJunitXml(results));
|
|
92
|
-
},
|
|
93
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ARV-10 (m-15): NDJSON streaming reporter for `zond checks run`.
|
|
3
|
-
*
|
|
4
|
-
* Each event is a single JSON line on stdout — agents pipe the stream
|
|
5
|
-
* into `jq` / ajv / their own consumer and act on findings *as they
|
|
6
|
-
* happen* instead of waiting for the run to wrap up. The discriminated
|
|
7
|
-
* union of event shapes lives in `src/cli/json-schemas.ts` (zod source
|
|
8
|
-
* of truth) and ships as `docs/json-schema/ndjson-events.schema.json`.
|
|
9
|
-
*
|
|
10
|
-
* The CLI passes `emitToStdout` as the `onEvent` callback to runChecks;
|
|
11
|
-
* tests can pass an in-memory accumulator instead.
|
|
12
|
-
*/
|
|
13
|
-
import type { z } from "zod";
|
|
14
|
-
import type {
|
|
15
|
-
NdjsonCheckStartEventSchema,
|
|
16
|
-
NdjsonCheckResultEventSchema,
|
|
17
|
-
NdjsonFindingEventSchema,
|
|
18
|
-
NdjsonSummaryEventSchema,
|
|
19
|
-
NdjsonEventSchema,
|
|
20
|
-
} from "../../cli/json-schemas.ts";
|
|
21
|
-
|
|
22
|
-
export type NdjsonCheckStartEvent = z.infer<typeof NdjsonCheckStartEventSchema>;
|
|
23
|
-
export type NdjsonCheckResultEvent = z.infer<typeof NdjsonCheckResultEventSchema>;
|
|
24
|
-
export type NdjsonFindingEvent = z.infer<typeof NdjsonFindingEventSchema>;
|
|
25
|
-
export type NdjsonSummaryEvent = z.infer<typeof NdjsonSummaryEventSchema>;
|
|
26
|
-
export type NdjsonEvent = z.infer<typeof NdjsonEventSchema>;
|
|
27
|
-
|
|
28
|
-
/** Write one event to stdout as a single line. AC #5 — when the CLI is
|
|
29
|
-
* in `--ndjson` mode, *every* user-readable message goes to stderr, so
|
|
30
|
-
* stdout stays a clean NDJSON stream that pipes into `jq` / ajv. */
|
|
31
|
-
export function emitToStdout(ev: NdjsonEvent): void {
|
|
32
|
-
process.stdout.write(`${JSON.stringify(ev)}\n`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function nowIso(): string {
|
|
36
|
-
return new Date().toISOString();
|
|
37
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { TestRunResult } from "../runner/types.ts";
|
|
2
|
-
|
|
3
|
-
export interface ReporterOptions {
|
|
4
|
-
/** Whether to use ANSI colors. Default: auto-detect via isTTY. */
|
|
5
|
-
color?: boolean;
|
|
6
|
-
/** TASK-265: suppress per-suite/per-test detail and emit only the
|
|
7
|
-
* grand-total summary line. Exit code still carries pass/fail. */
|
|
8
|
-
quiet?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type ReporterName = "console" | "json" | "junit";
|
|
12
|
-
|
|
13
|
-
export interface Reporter {
|
|
14
|
-
report(results: TestRunResult[], options?: ReporterOptions): void;
|
|
15
|
-
}
|