Skip to content

Make targeted mutation tests faster and fail safely - #1854

Merged
stefan-burke merged 4 commits into
mainfrom
speed-up-mutation-runner
Jul 18, 2026
Merged

Make targeted mutation tests faster and fail safely#1854
stefan-burke merged 4 commits into
mainfrom
speed-up-mutation-runner

Conversation

@stefan-burke

@stefan-burke stefan-burke commented Jul 17, 2026

Copy link
Copy Markdown
Member

Summary

  • Run mirror-located direct tests before the broader integration and end-to-end suites.
  • Build one shared database state only for mutants that survive direct tests.
  • Reuse browser build contexts and dependency data across mutants.
  • Fail when a test is misplaced instead of treating it as an integration fallback.
  • Apply one timeout to the full life of each mutant.
  • Recheck failed state and browser builds with the original source so broken tooling cannot improve the score.
  • Attempt every cleanup and report task and cleanup failures together.

Test layout

A mutable source must have a direct test at the matching path under test/. Only files under test/integration/ and test/e2e/ may be unmatched. This makes the mutation run point out tests that need to move while their assertions are strengthened.

Performance

Benchmark Before After Change
Phone mutations with an unrelated database test 36.7s 7.3s 80% faster
Pure phone mutations 11.2s 5.2s 53% faster

Incremental browser rebuilds averaged about 39ms. Mutant database state builds averaged about 159ms.

Structure

The 919-line mutation runner is now split into focused files for orchestration, execution, mutant evaluation, and per-file progress. The largest is under 400 lines. Static asset sessions and cleanup are also shared production mechanisms with direct tests.

Verification

  • The phone test detects all 16 phone mutants.
  • New mutation-runner modules have complete line and branch coverage.
  • deno task test:quality-audit was reviewed; it found no new weak-assertion warning in these tests.
  • deno task precommit passes, including type checking, lint, zero duplication, full coverage, all tests, copy checks, and the edge build.

Summary by CodeRabbit

  • New Features
    • Mutation testing now runs mirrored direct tests first, then integration tests only for surviving mutants.
    • Mutation reports now include per-phase execution timings in terminal output and GitHub step summaries.
  • Bug Fixes
    • Improved cleanup/rollback for mutation evaluation, including safer restoration of generated outputs and assets.
  • Documentation
    • Refined mutation-testing precommit workflow docs, including changed-file selection and test-state handling.
  • Tests
    • Added/expanded coverage for mutation phases, test mapping, summaries, evaluation/execution, planning, static-asset sessions, cleanup utilities, and environment overrides.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a2c77779-6be6-4e4d-adba-9508ed108430

📥 Commits

Reviewing files that changed from the base of the PR and between 82c32a7 and 51966e0.

📒 Files selected for processing (4)
  • scripts/mutation/test-map.ts
  • scripts/static-assets/output-rollback.ts
  • test/scripts/mutation-test-map.test.ts
  • test/scripts/static-asset-build.test.ts

📝 Walkthrough

Walkthrough

The mutation workflow now reuses disposable static-asset builds, maps direct tests to source files, rebuilds shared mutant test state for integration tests, records phase timings, and updates reporting, tests, cleanup, and documentation.

Changes

Mutation workflow overhaul

Layer / File(s) Summary
Disposable static-asset build session
scripts/build-static-assets.ts, scripts/static-assets/*, scripts/test-harness.ts, scripts/edge-bundle-lib.ts, test/scripts/static-asset-build.test.ts
Static asset builds now expose incremental rebuild, dependency lookup, baseline restoration, rollback, and disposal through StaticAssetBuild; harness and edge-bundle paths explicitly dispose the build session.
Shared cleanup and environment guarantees
scripts/cleanup.ts, scripts/test-environment.ts, scripts/test-harness.ts, test/scripts/cleanup.test.ts, test/scripts/test-environment.test.ts
Cleanup tasks run in order, missing paths are tolerated, task and cleanup failures are composed, and temporary environment overrides are restored.
Mutation target mapping and fresh state
scripts/mutation/test-map.ts, scripts/mutation/test-state.ts, scripts/mutation/build-test-state.ts, scripts/mutation/runner.ts, mutation mapping/state tests, test/test-utils/*
Sources are paired with mirror direct tests, integration tests are retained separately, and mutant state is built in a fresh child process with cleanup and failure handling.
Timed staged mutant evaluation
scripts/mutation/execution.ts, scripts/mutation/phases.ts, scripts/mutation/evaluate.ts, scripts/mutation/run-file.ts, scripts/mutation/runner.ts, related mutation tests
Static gates, asset builds, direct tests, and integration tests produce phase timings; direct-test survivors can run integration tests using shared fresh state, with timeout and abort outcomes propagated.
Timing reporting and workflow documentation
scripts/mutation/summary.ts, AGENTS.md, scripts/mutation/state-graph.ts, scripts/precommit-mutation.ts, scripts/precommit/mutation-step.ts, mutation summary fixtures and tests
Mutation results include detection phases and aggregated timings, while reports, fixtures, and workflow documentation reflect the updated execution model.

Estimated code review effort: 4 (Complex) | ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: faster targeted mutation tests with safer failure handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch speed-up-mutation-runner

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/mutation/runner.ts`:
- Around line 412-455: Update the mutant execution flow around runTestStages to
establish a single deadline before any direct tests, createMutantTestState, or
integration tests run. Derive each stage’s AbortSignal timeout from the
remaining time until that deadline, and pass those remaining budgets to runTests
and the state-creation path so the combined stages cannot exceed
perMutantTimeout.

In `@scripts/mutation/summary.ts`:
- Around line 149-160: Update the successful summary paths that return early
when effective === 0 in both formatters to include the collected timing output:
append timingLines(s) in the text formatter and timingTable in the table
formatter. Preserve the existing all-ignored summary content while ensuring
phase timings are shown.

In `@scripts/mutation/test-map.ts`:
- Around line 14-24: Normalize path separators consistently in testPrefix and
ownsTest before comparing ownership paths, converting both slash types to “/” so
mixed-separator Windows paths match correctly. Preserve the existing prefix and
descendant checks, and add a regression test covering mixed separators such as
“test\shared/a” versus “test/shared/a.test.ts”.

In `@scripts/test-harness.ts`:
- Around line 72-76: Update the cleanup function so stripe-mock, test-state,
staticAssets.cleanup(), and generated-file removal run independently, ensuring
one failure does not skip later cleanup. In the generated loop, suppress only
Deno not-found errors and propagate permission or other I/O failures. Preserve
and report unexpected failures after all cleanup steps have been attempted,
without empty catches or catch-and-continue handling.

In `@test/scripts/mutation-summary.test.ts`:
- Around line 67-93: Split the combined test around summarize,
formatSummaryLines, and writeStepSummary into three independent test cases, each
asserting only one production function’s behavior. Preserve the shared timing
fixtures and expected aggregation data, while keeping console-format assertions
in the formatSummaryLines test and Markdown-output assertions in the
writeStepSummary test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: af22b566-2aeb-4f16-a2c2-32dd44e7f14f

📥 Commits

Reviewing files that changed from the base of the PR and between 38c25f7 and 1902013.

📒 Files selected for processing (19)
  • AGENTS.md
  • scripts/build-static-assets.ts
  • scripts/edge-bundle-lib.ts
  • scripts/mutation/assets.ts
  • scripts/mutation/build-test-state.ts
  • scripts/mutation/phases.ts
  • scripts/mutation/runner.ts
  • scripts/mutation/state-graph.ts
  • scripts/mutation/summary.ts
  • scripts/mutation/test-map.ts
  • scripts/mutation/test-state.ts
  • scripts/precommit-mutation.ts
  • scripts/precommit/mutation-step.ts
  • scripts/test-harness.ts
  • test/scripts/mutation-ignore.test.ts
  • test/scripts/mutation-phases.test.ts
  • test/scripts/mutation-summary.test.ts
  • test/scripts/mutation-test-map.test.ts
  • test/scripts/mutation-test-state.test.ts
💤 Files with no reviewable changes (1)
  • scripts/mutation/assets.ts

Comment thread scripts/mutation/runner.ts Outdated
Comment thread scripts/mutation/summary.ts Outdated
Comment thread scripts/mutation/test-map.ts Outdated
Comment thread scripts/test-harness.ts Outdated
Comment thread test/scripts/mutation-summary.test.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1902013226

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/build-static-assets.ts Outdated
Comment thread scripts/mutation/test-map.ts Outdated
Comment thread scripts/build-static-assets.ts Outdated
@stefan-burke stefan-burke changed the title Speed up mutation testing Make targeted mutation tests faster and fail safely Jul 17, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
scripts/test-harness.ts (1)

68-75: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Rollback generated assets when static-asset setup fails.

CSS and successful bundles may already be written when another build rejects. Because setupStaticAssets never returns, its cleanup is not registered and those generated files remain.

Wrap the build call and invoke failAfterCleanups with generated.map(removeIfPresent) on failure. Add a regression test for partial build failure.

As per coding guidelines, every bug fix requires a regression test reproducing the original failure.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test-harness.ts` around lines 68 - 75, The setupStaticAssets build
flow must clean up generated files when buildStaticAssets fails. Wrap the build
call in failure handling and invoke failAfterCleanups with
generated.map(removeIfPresent), while preserving normal success behavior; add a
regression test covering partial asset generation followed by a rejected build.

Source: Coding guidelines

test/scripts/mutation-phases.test.ts (1)

68-77: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert the complete integration-only contract.

The callback ignores files, and the timing assertion checks only the first phase. Capture both callback arguments and compare the complete result so incorrect file forwarding, extra timings, or a lost duration cannot survive.

As per coding guidelines, tests must use strong mutation-resistant assertions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/scripts/mutation-phases.test.ts` around lines 68 - 77, Strengthen the
test “runs integration tests directly when there are no direct tests” by
capturing both callback arguments from runTestStages and asserting the files
argument is the expected integration file and the phase is “integration-tests”.
Compare the complete result, including status, detectedBy, and the exact single
timing entry with its durationMs and phase, so extra timings or lost values are
detected.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/build-static-assets.ts`:
- Around line 16-19: Remove the StaticAssetBuild and StaticBundle re-export from
the build-static-assets module, then update all internal callers to import those
types directly from ./static-assets/session.ts. Do not add an alias,
compatibility wrapper, or replacement re-export.

In `@scripts/mutation/phases.ts`:
- Around line 31-34: Update TestStageResult and the state-creation handling in
evaluate.ts so the stage result preserves and propagates the actual detectedBy
source, including test-state, instead of relabeling it as integration-tests.
Ensure timeout outcomes report no detection rather than a detector. Add
regression tests covering both failed and timed-out state creation.

In `@scripts/mutation/test-map.ts`:
- Around line 29-32: Update isIntegrationTest and the surrounding test-map
pipeline to use the shared `#fp` curried combinators instead of native some, map,
and filter calls. Replace the mutating sort with the project’s non-mutating
ordering helper, preserving the existing filtering, mapping, and ordering
behavior.

In `@scripts/static-assets/session.ts`:
- Around line 79-94: Update the rebuild and restore handlers around builtBundle
so all selected operations settle before either handler completes: use
Promise.allSettled for context.rebuild calls and aggregate restore writes with
runCleanups or an equivalent mechanism that waits for every write while
preserving failure reporting. Add deterministic regression coverage where one
selected context fails and another completes later, verifying restoration does
not race with the late operation.

In `@scripts/test-harness.ts`:
- Around line 226-239: Update the test harness setup around STRIPE_MOCK_HOST and
STRIPE_MOCK_PORT to capture each variable’s pre-existing value before
overwriting it. Add a teardown task alongside the existing cleanup functions
that restores those values when present and deletes them when previously unset,
preserving isolation across harness runs.

In `@test/scripts/mutation-execution.test.ts`:
- Around line 45-53: Update the STRIPE_MOCK_PORT restoration logic in the test’s
finally block to distinguish an unset variable from an existing empty value.
Restore previousPort whenever it is not undefined, and delete the environment
variable only when it was originally absent.

In `@test/scripts/mutation-file-plan.test.ts`:
- Around line 18-40: The test around createFilePlan must verify that
result.assets.restore forwards the affected bundle to the rebuilder. Track
restore calls and their arguments in the rebuilder mock, invoke the restoration
hook, and add a mutation-resistant assertion confirming exactly one call with
bundle.

---

Outside diff comments:
In `@scripts/test-harness.ts`:
- Around line 68-75: The setupStaticAssets build flow must clean up generated
files when buildStaticAssets fails. Wrap the build call in failure handling and
invoke failAfterCleanups with generated.map(removeIfPresent), while preserving
normal success behavior; add a regression test covering partial asset generation
followed by a rejected build.

In `@test/scripts/mutation-phases.test.ts`:
- Around line 68-77: Strengthen the test “runs integration tests directly when
there are no direct tests” by capturing both callback arguments from
runTestStages and asserting the files argument is the expected integration file
and the phase is “integration-tests”. Compare the complete result, including
status, detectedBy, and the exact single timing entry with its durationMs and
phase, so extra timings or lost values are detected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f8be2d02-889f-4c6d-9178-d73baee47ca8

📥 Commits

Reviewing files that changed from the base of the PR and between 1902013 and 4fb1d1d.

📒 Files selected for processing (25)
  • AGENTS.md
  • scripts/build-static-assets.ts
  • scripts/cleanup.ts
  • scripts/mutation/evaluate.ts
  • scripts/mutation/execution.ts
  • scripts/mutation/phases.ts
  • scripts/mutation/run-file.ts
  • scripts/mutation/runner.ts
  • scripts/mutation/summary.ts
  • scripts/mutation/test-map.ts
  • scripts/mutation/test-state.ts
  • scripts/precommit-mutation.ts
  • scripts/precommit/mutation-step.ts
  • scripts/static-assets/session.ts
  • scripts/test-harness.ts
  • test/scripts/cleanup.test.ts
  • test/scripts/mutation-evaluate.test.ts
  • test/scripts/mutation-execution.test.ts
  • test/scripts/mutation-file-plan.test.ts
  • test/scripts/mutation-phases.test.ts
  • test/scripts/mutation-test-map.test.ts
  • test/scripts/mutation-test-state.test.ts
  • test/scripts/static-asset-build.test.ts
  • test/test-utils/command-capture.ts
  • test/test-utils/test-state.ts

Comment thread scripts/build-static-assets.ts Outdated
Comment thread scripts/mutation/phases.ts
Comment thread scripts/mutation/test-map.ts
Comment thread scripts/static-assets/session.ts Outdated
Comment thread scripts/test-harness.ts Outdated
Comment thread test/scripts/mutation-execution.test.ts Outdated
Comment thread test/scripts/mutation-file-plan.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/mutation/test-map.ts`:
- Around line 59-62: Update the ownership classification flow in the test-map
generation logic so explicit integration/e2e test paths are recognized and
placed in integrationTestFiles before the owner calculation assigns direct
ownership, including when a mirrored source path such as src/integration/app.ts
is selected. Add a regression test covering src/integration/app.ts with
test/integration/app.test.ts and verify the test is classified as integration
rather than receiving an owner.

In `@scripts/static-assets/output-rollback.ts`:
- Around line 13-23: Update the rollback flow around outputFiles and
failAfterCleanups to snapshot the contents of every pre-existing output before
task() runs, then restore those contents during failure cleanup while retaining
removal of newly created files. Add or update the regression test in the
existing static-asset build failure case to overwrite the existing asset and
verify its original contents are restored.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bdb70d1b-e2cc-41a8-b4f5-1387da23f464

📥 Commits

Reviewing files that changed from the base of the PR and between 4fb1d1d and 82c32a7.

📒 Files selected for processing (18)
  • scripts/build-static-assets.ts
  • scripts/mutation/evaluate.ts
  • scripts/mutation/phases.ts
  • scripts/mutation/runner.ts
  • scripts/mutation/summary.ts
  • scripts/mutation/test-map.ts
  • scripts/static-assets/output-rollback.ts
  • scripts/static-assets/session.ts
  • scripts/test-environment.ts
  • scripts/test-harness.ts
  • test/scripts/mutation-evaluate.test.ts
  • test/scripts/mutation-execution.test.ts
  • test/scripts/mutation-file-plan.test.ts
  • test/scripts/mutation-phases.test.ts
  • test/scripts/mutation-summary.test.ts
  • test/scripts/mutation-test-map.test.ts
  • test/scripts/static-asset-build.test.ts
  • test/scripts/test-environment.test.ts

Comment thread scripts/mutation/test-map.ts Outdated
Comment thread scripts/static-assets/output-rollback.ts Outdated
@stefan-burke

Copy link
Copy Markdown
Member Author

Review-only comment: the integration-only phase test is strengthened in 82c32a7. It captures both callback arguments, verifies the exact integration file and phase, and compares the complete result including status, detector, and the single timing entry.

@stefan-burke

Copy link
Copy Markdown
Member Author

Review-only comment: static asset setup rollback is fixed in 82c32a7 and strengthened in 51966e0. buildStaticAssets now runs through withGeneratedOutputRollback; a failed build disposes contexts, stops esbuild, restores overwritten outputs byte-for-byte, and removes newly created outputs. The direct regression overwrites one existing file and creates another before rejecting, then verifies both rollback outcomes.

@stefan-burke
stefan-burke added this pull request to the merge queue Jul 18, 2026
Merged via the queue into main with commit b9e4541 Jul 18, 2026
3 checks passed
@stefan-burke
stefan-burke deleted the speed-up-mutation-runner branch July 18, 2026 06:55
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