Skip to content

test: mutate the rest of the logic, and close what it found - #59

Merged
skjnldsv merged 1 commit into
mainfrom
test/harden-history
Sep 22, 2026
Merged

skjnldsv merged 1 commit into
mainfrom
test/harden-history

Conversation

@skjnldsv

Copy link
Copy Markdown
Contributor

Widens the mutation scope from lib/utils and lib/composables to the entry, the handler registry, the models, the services and the helpers. Components and views stay out on purpose: most of their mutants are DOM glue that a unit test cannot observe, so the score would measure the wrong thing rather than a gap.

Baseline across the new scope is 74.6%.

How I picked what to add

Three questions, in this order.

Would a wrong answer be silent? Code that throws when broken is already telling you. Code that returns a plausible wrong value is where this pays: comparisons, lookup tables, permission checks, a version election.

Is the coverage suspiciously high? 100% line coverage is a red flag, not a green one. It says every line ran and nothing about whether anything was checked.

Can a mutant be observed at all? String tables, log messages and anything behind a mock are excluded, because scoring them measures the test strategy rather than the tests.

That put the four models near the top, and they paid out exactly as the second question predicted:

file line coverage mutation score
models/sheetmusic.ts 100% 68.2%
models/images.ts 100% 74.3%
models/videos.ts 100% 86.4%
models/audios.ts 100% 86.8%

What it found

compareVersions was only ever asked one way round. There was an assertion that a release outranks its own prerelease, and the mirror branch was NoCoverage. The election asks in whichever order the two copies of the library happen to load, so on half the page loads a beta could have beaten a release and nothing would have said so. Everything we ship right now is a prerelease.

Now tested both directions, plus a check that the comparison is antisymmetric across a spread of versions, plus sorting a shuffled list, which is the question the election actually asks. scope.ts: 70.5% to 82.5%.

Every handler answers for the whole selection, and every reads exactly like some until something asks. Nothing did. A selection of a score and a PDF would have opened the sheet music viewer. Covered for all four handlers at once, and the mutant is dead in each.

Leftovers, named rather than hidden

logger.ts is excluded alongside plyrTranslations.ts: its only mutant is the app name it labels log lines with.

mediaPreloader.ts reads as 0%. preloadMedia has no unit test, and preloadPreview's five arrive with #51, so I have left it rather than writing tests that will conflict.

usePlyrPlayer.ts at 43% with 44 survivors is the largest remaining gap and the obvious next target. handlers.ts at 71.3% with 17 uncovered mutants is the other.

390 unit tests.

👾 This pull request was assisted by Claude Code, commits carry an Assisted-by trailer.

The scope was lib/utils and lib/composables. This adds the entry, the
handler registry, the models, the services and the helpers: everything
whose behaviour a unit test can actually observe. Components and views
stay out, because most of their mutants are DOM glue and the score would
measure the wrong thing.

Two gaps worth the run, both in code that reads as finished.

compareVersions had an assertion for a release outranking its own
prerelease, and only in one direction. Nobody ever asked the other way
round, and the election asks in whichever order the two copies happen to
load, so a beta could have won a page against a release with nothing to
show for it. There is now a test both ways, one that checks the
comparison is antisymmetric across a spread of versions, and one that
sorts a shuffled list. scope.ts went from 70.5% to 82.5%.

Every handler answers for a whole selection, and `every` reads exactly
like `some` until something asks. Nothing did, so a selection of a score
and a PDF would have opened the sheet music viewer. Covered for all four
handlers at once.

logger.ts is excluded alongside plyrTranslations: its only mutant is the
app name it labels lines with, which no test can see.

Baseline across the widened scope is 74.6%. mediaPreloader reads as 0%
because preloadMedia has no unit test and preloadPreview's arrive with
#51.

Assisted-by: ClaudeCode:claude-opus-5
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
@skjnldsv skjnldsv added status: review Waiting for reviews type: tests 🧪 Tests related issue or changes AI assisted labels Sep 21, 2026
@codecov

codecov Bot commented Sep 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.80%. Comparing base (894058f) to head (43fe373).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #59      +/-   ##
==========================================
+ Coverage   88.62%   88.80%   +0.18%     
==========================================
  Files          39       39              
  Lines        2743     2743              
  Branches      562      564       +2     
==========================================
+ Hits         2431     2436       +5     
+ Misses        305      300       -5     
  Partials        7        7              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@skjnldsv
skjnldsv merged commit 9f93bf5 into main Sep 22, 2026
18 checks passed
@skjnldsv
skjnldsv deleted the test/harden-history branch September 22, 2026 00:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI assisted status: review Waiting for reviews type: tests 🧪 Tests related issue or changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant