chore(common-utils): add Stryker mutation testing - #2755
chore(common-utils): add Stryker mutation testing#2755jordan-simonovski wants to merge 1 commit into
Conversation
Coverage says a line ran, not that a test would fail if it were wrong. Stryker edits the source and reports which edits the tests miss. Set up in common-utils only, run on demand via `yarn dev:mutation`. Not wired into CI. Pins @stryker-mutator/core/minimatch to ^9: the blanket brace-expansion resolution forces v2, and minimatch v10's ESM build needs v5's named exports.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
🔵 Tier 2 — Low RiskSmall, isolated change with no API route or data model modifications. Why this tier:
Review process: AI review + quick human skim (target: 5–15 min). Reviewer validates AI assessment and checks for domain-specific concerns. Stats
|
Greptile SummaryAdds local Stryker mutation-testing support for
Confidence Score: 5/5The PR appears safe to merge with no actionable defects identified. The mutation-testing command, Jest and TypeScript configuration paths, source globs, generated-output ignores, dependency declarations, and documented package-relative workflow are mutually consistent.
|
| Filename | Overview |
|---|---|
| packages/common-utils/stryker.config.js | Configures Stryker to use the package’s existing Jest and TypeScript configurations, exclude tests, cache results, and generate local reports. |
| packages/common-utils/package.json | Adds aligned Stryker development dependencies and a local mutation-testing command without affecting production dependencies. |
| package.json | Adds a targeted Yarn resolution that supplies minimatch v9 specifically to Stryker core. |
| yarn.lock | Records the added Stryker toolchain and its transitive dependencies, including minimatch v9. |
| CONTRIBUTING.md | Documents scoped mutation-test usage, output interpretation, performance expectations, and the dependency workaround. |
Reviews (1): Last reviewed commit: "chore(common-utils): add Stryker mutatio..." | Re-trigger Greptile
E2E Test Results✅ All tests passed • 243 passed • 1 skipped • 1026s
Tests ran across 4 shards in parallel. |
Deep Review✅ No critical issues found. This is dev-only tooling: no production code path, no CI gate, and 🟡 P2 — recommended
🔵 P3 nitpicks (5)
Reviewers (4): testing, maintainability, project-standards, correctness-scope (orchestrator). Coverage limitations: Unverified claims — worth confirming before merge:
Testing gaps:
|
Adds Stryker to
common-utilsas a local dev tool. Nothing runs in CI; nothing is gated on it.Why
common-utilshas a coverage ratchet injest.config.jssitting at 86% statements, with a comment saying it should only go up. That's a good floor, but coverage only proves a line executed — not that a test would fail if the line were wrong. Mutation testing closes that gap: Stryker edits the source in small ways and reports which edits no test caught. A surviving mutant is a missing assertion.It found real gaps immediately. From a run over five files:
Two concrete examples from
filters.ts, both surviving:if (/\bDate32\b/.test(chType))->if (false)— nothing asserts on the Date32 branch.chType.match(/DateTime64\((\d+)/)->(\d)— no test uses a two-digit precision.dashboardValidation.tshas no unit test reaching it at all.None of these are fixed here. This PR is just the tooling.
Usage
From
packages/common-utils:Scope it with
--mutate; a whole-package run is tens of minutes. Full notes in CONTRIBUTING.md.The one thing to look at
The root
package.jsongets aresolutionsentry pinning@stryker-mutator/core/minimatchto^9.We have a blanket
"brace-expansion": "^2.1.2"resolution, which forces v2 (CJS) onto every consumer in the tree. minimatch v10's ESM build doesimport { expand } from 'brace-expansion', which needs v5's named exports, so Stryker crashes on startup. Pinning its minimatch to v9 sidesteps it without touching the blanket resolution.Worth noting the blanket resolution looks obsolete and slightly harmful on its own terms. The advisory it was presumably added for (CVE-2025-5889) is fixed in 1.1.12 / 2.0.2 / 3.0.1 / 4.0.1, and nothing in the tree requests a range that would resolve below those today — the requested ranges are
^1.1.7,^2.0.1,^5.0.2and^5.0.5. So the override forces v2 onto packages asking for v1 and v5 without buying any security. Narrowing or dropping it would let this pin go away too, but that's a security-adjacent change and deserves its own PR rather than riding along with dev tooling.@stryker-mutator/coreis the only Stryker package depending on minimatch, and it only usesnew Minimatch()andminimatch(), both unchanged between v9 and v10.Checks
yarn install --immutableclean; the lockfile change is purely additive (no existing package's resolved version moved).yarn knipclean.common-utilsci:lintandci:unitpass (1556 tests).typescript-checkerworks against the repo's TypeScript 6 — it discards non-compiling mutants rather than erroring, which is what keeps runs affordable.