Skip to content

feat(mutation): caseforge mutate command — HTTP boundary mutation testing (Phase 1 + Phase 2)#74

Merged
yuchou87 merged 14 commits into
mainfrom
feat/mutation-loop
May 10, 2026
Merged

feat(mutation): caseforge mutate command — HTTP boundary mutation testing (Phase 1 + Phase 2)#74
yuchou87 merged 14 commits into
mainfrom
feat/mutation-loop

Conversation

@yuchou87

Copy link
Copy Markdown
Contributor

Summary

  • Adds caseforge mutate command that runs HTTP boundary mutations via a reverse proxy between hurl and a live API, identifies assertions that fail to catch mutations ("survivors"), and optionally uses LLM OC-prompting to suggest stronger assertions and patch index.json
  • Fixes a pre-existing bug in internal/runner/hurl.go where JSON report parsing used {"entries":[…]} but hurl emits a top-level array — silently zeroing Passed/Failed counts

What's included

Phase 1 — Engine + 12 operators + CLI

  • internal/mutation/proxy.goMutationProxy wrapping httputil.ReverseProxy on a random local port; Registry() returning all 12 operators
  • internal/mutation/operator_body.go — 8 JSON body operators: field_drop, field_type_swap, array_to_null, null_to_array, pagination_off_by_one, empty_result_injection, date_format_swap, numeric_precision_loss (all deterministic via sorted key iteration)
  • internal/mutation/operator_status.go — 4 status/header operators: status_swap_2xx, error_inflation, content_type_swap (uses strings.HasPrefix for robustness), header_drop
  • internal/mutation/engine.goRun() orchestrates operators serially × cases concurrently (errgroup + channel semaphore); runOnce copies a single .hurl to a temp dir and runs HurlRunner
  • internal/mutation/report.goTextSummary, WriteReport, Persist, ClusterSurvivors (groups survivors by case, computes RiskScore, sorts descending)
  • cmd/mutate.go — CLI with --cases (required), --target (required), --output, --operator, --concurrency (default 4), --spec, --feedback, --auto-fix, --yes; exit code 6 when survivors > 0

Phase 2 — LLM feedback + auto-fix

  • internal/mutation/feedback.goAnalyze() (OC-prompting per survivor cluster via llm.Retry), BuildObservePrompt(), PatchIndex() (patches index.json + re-renders .hurl files via existing render.HurlRenderer)
  • --feedback: calls LLM per survivor cluster, prints diagnosis and suggested assertions; gracefully degrades when no provider configured
  • --auto-fix: patches index.json with suggested assertions and re-renders .hurl files; requires --feedback; prompts for confirmation unless --yes

Acceptance tests

  • AT-401: mutate --help contains "mutation"
  • AT-402: mutate without required flags exits non-zero
  • AT-403: full end-to-end run against sandbox writes mutation-report.json (skipped if hurl not installed)

Usage

# Phase 1 — run all 12 operators
caseforge mutate --cases ./cases --target http://localhost:8080

# Phase 1 — specific operator, with JSON report
caseforge mutate --cases ./cases --target http://localhost:8080 \
  --operator field_drop --output ./reports

# Phase 2 — LLM feedback on survivors
caseforge mutate --cases ./cases --target http://localhost:8080 --feedback

# Phase 2 — auto-fix index.json with suggested assertions
caseforge mutate --cases ./cases --target http://localhost:8080 \
  --feedback --auto-fix --yes

Test plan

  • go test -race ./... — all packages green
  • ./scripts/acceptance.sh — 259/259 passed including AT-401, AT-402, AT-403
  • Three independent code reviews — final verdict: Ready to merge

Acknowledgements

Mutation testing operator taxonomy inspired by established mutation testing literature (PIT, StrykerJS).

yuchou87 added 14 commits May 10, 2026 08:19
Also fixes hurl.go JSON report parsing to match actual array-format
output produced by hurl --report-json, and updates hurl_test.go to use
the correct format.
Add Analyze() and BuildObservePrompt() to internal/mutation/feedback.go
for OC-prompting survivor clusters via LLM. Expose --feedback, --auto-fix,
and --yes flags on the mutate command; include runAutoFix stub for Task 9.
@yuchou87 yuchou87 merged commit a7df364 into main May 10, 2026
1 check passed
@yuchou87 yuchou87 deleted the feat/mutation-loop branch May 10, 2026 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant