Skip to content

test: 136 surviving mutants in the four path-guard modules (pre-existing, outside the changed lines) #74

Description

@cdeust

Wiring Python mutation testing (scripts/mutation_check.sh, mutmut 3.x) while fixing the py/path-injection alerts surfaced a pre-existing backlog. only_mutate takes file globs, not line ranges, so a scoped run on a pre-existing file mutates the whole file — separating what the change introduced from what was already there.

The blocking tier is clean. Every mutant on a line the path-injection fix touched is killed: 0 survivors across all six files (classified by matching each mutant's removed source text against the diff's changed lines).

The wide sweep is not, in the three pre-existing files the fix touched:

Module Test selection Survivors (none on changed lines)
cortex_viz/infrastructure/wiki_read.py tests/test_wiki_read.py 67
cortex_viz/server/http_standalone_static.py tests/test_static_path_traversal.py 53
cortex_viz/server/http_file_diff.py tests/test_git_diff_engine.py, tests/test_file_diff.py 16

The two new modules are at 0 survivors (shared/path_containment.py 21/21 killed, infrastructure/file_sandbox.py 11/11), and server/git_diff_engine.py is at 0 survivors for the whole file.

This is coding-standards §12.5's non-blocking critical-zone sweep, deferred under §14.3 rather than absorbed silently: the survivors are pre-existing gaps in code the path-injection PR did not change, and closing 136 of them is its own body of work.

One concrete example worth leading with, because it is in a security guard: in serve_static, mutating not safe_name or safe_name.startswith(".") to not safe_name and safe_name.startswith(".") survives — no test distinguishes the two, so the empty-name and dot-prefixed-name arms of that filter are not independently pinned.

Reproduction

./scripts/mutation_check.sh tests/test_wiki_read.py cortex_viz/infrastructure/wiki_read.py
./scripts/mutation_check.sh tests/test_static_path_traversal.py cortex_viz/server/http_standalone_static.py
./scripts/mutation_check.sh tests/test_git_diff_engine.py,tests/test_file_diff.py cortex_viz/server/http_file_diff.py

Acceptance criteria

  • Each of the three modules reaches 0 surviving non-equivalent mutants, one module per iteration (§12.5 ratchet).
  • Every survivor is either killed by a new test asserting the observable behaviour it pins, or recorded as a provable equivalent mutant with a written rationale — no third option.
  • Survivors that turn out to mark dead code are removed rather than tested (§9); the containment primitive's own rstrip was exactly this case and was deleted, not covered.
  • Run notes recorded alongside the existing JS ones (tests/js/MUTATION_NOTES.md has the precedent) so the numbers are reproducible.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions