Skip to content

test: add StrykerJS mutation testing foundation#847

Open
ToRyVand wants to merge 1 commit into
lnp2pBot:mainfrom
ToRyVand:feat/issue-760-mutation-testing-stryker
Open

test: add StrykerJS mutation testing foundation#847
ToRyVand wants to merge 1 commit into
lnp2pBot:mainfrom
ToRyVand:feat/issue-760-mutation-testing-stryker

Conversation

@ToRyVand

@ToRyVand ToRyVand commented Jun 26, 2026

Copy link
Copy Markdown

Part of #760. First, foundational PR: set up StrykerJS and establish a baseline on util/. Writing tests for surviving mutants and raising the threshold will follow as separate focused PRs.

What this adds

  • @stryker-mutator/core + mocha-runner + typescript-checker
  • stryker.config.json — mocha runner, perTest coverage, non-blocking (break: null), scoped to util/**
  • npm scripts: mutation-test, mutation-test:util, build:test
  • A non-blocking CI workflow (weekly + workflow_dispatch) on node:20-bookworm that uploads the HTML report as an artifact
  • gitignores Stryker outputs

Baseline on util/

File Mutation score (covered) Notes
util/index.ts 57.69% the only util file with tests (90 killed / 66 survived)
rest of util/ 0% no tests yet

This is exactly the signal mutation testing gives: it shows where a bug could be introduced and slip past the suite.

One integration note

Stryker builds in a clean sandbox, which surfaced that tsconfig.test.json (include tests/**/*) does not compile dynamically require()d files such as bot/modules/block/commands.ts — it only works normally because CI runs a full tsc first. Rather than touch that code, I use a build:test script (full tsc + test tsc) as Stryker's build command so the sandbox build is complete.

Validation

  • Normal test suite green (144 passing — no source touched)
  • tsc, lint, prettier clean
  • Mutation run completes locally in ~30s on util/

Summary by CodeRabbit

  • Chores
    • Added automated mutation testing to run on a weekly schedule and manually when needed.
    • Mutation test results are now saved as a downloadable report artifact.
    • Added project scripts to support mutation testing and a test build step.
    • Updated ignored files to exclude local test and mutation-testing artifacts.

Sets up mutation testing to measure how effectively the test suite catches
bugs, scoped to util/ as the initial baseline (issue lnp2pBot#760).

- Add @stryker-mutator core + mocha-runner + typescript-checker
- stryker.config.json: mocha runner, perTest coverage, non-blocking
  (break: null), mutate util/**
- npm scripts: mutation-test, mutation-test:util, build:test
- Non-blocking CI workflow (weekly + manual) on node:20-bookworm that
  uploads the HTML report as an artifact
- gitignore Stryker outputs

The build:test script (full tsc + test tsc) is used as Stryker's build
command so the sandbox build is complete: tsconfig.test.json alone does
not compile dynamically required files (e.g. bot/modules/block/commands.ts),
which only works in CI because it runs a full tsc beforehand.

Baseline on util/: 57.69% mutation score on covered code in util/index.ts
(the only tested util file); the rest of util/ has no tests yet.

Part of lnp2pBot#760
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds Stryker mutation-testing setup for the util scope, including npm scripts, configuration, ignore rules, and a GitHub Actions workflow that runs weekly or manually and uploads the mutation report.

Changes

Mutation testing setup

Layer / File(s) Summary
Configuration and scripts
package.json, stryker.config.json, .gitignore
Adds mutation-testing npm scripts and Stryker dev dependencies, defines the Stryker configuration for util/**/*.ts, and ignores Stryker outputs and local command logs.
Mutation-testing workflow
.github/workflows/mutation-testing.yaml
Adds a scheduled/manual workflow that runs the util mutation test in a Node 20 container and uploads reports/mutation/ as mutation-report.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Poem

I hopped through util paths by moonlit glow,
With Stryker nibbling where the mutants grow.
A cron bell rang; the report took flight,
A carrot-bright build in the midnight light. 🐰

🚥 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: adding a StrykerJS mutation testing foundation.
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 unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 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 @.github/workflows/mutation-testing.yaml:
- Around line 26-40: The workflow still uses mutable GitHub Action tags in the
mutation-testing job, so update the actions referenced by actions/checkout and
actions/upload-artifact to their full 40-character commit SHAs and keep the
original version as an inline comment for readability. Make this change in the
mutation-testing workflow alongside the existing run steps, preserving the same
behavior while removing tag-based supply-chain risk.

In `@stryker.config.json`:
- Around line 1-19: Stryker is not invoking the installed TypeScript checker
because the configuration lacks the explicit checker activation. Update the
Stryker config to enable the TypeScript checker via the checkers setting in the
existing JSON alongside the current testRunner, mutate, and thresholds options.
Keep the rest of the StrykerJS configuration unchanged so mutation testing still
runs with type checking enabled.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a33067e2-79d9-478e-940f-d9d29f1cb9c0

📥 Commits

Reviewing files that changed from the base of the PR and between bf32684 and 542ad3f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • .github/workflows/mutation-testing.yaml
  • .gitignore
  • package.json
  • stryker.config.json

Comment on lines +26 to +40
- uses: actions/checkout@v4

- run: npm ci

- name: Run mutation testing on util/
run: npm run mutation-test:util
env:
NODE_ENV: test

- name: Upload mutation report
if: always()
uses: actions/upload-artifact@v4
with:
name: mutation-report
path: reports/mutation/

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.

🔒 Security & Privacy | 🟠 Major

Pin both GitHub Actions to full SHAs.

actions/checkout@v4 and actions/upload-artifact@v4 use mutable tags. Pin them to their full 40-character commit SHAs to prevent supply-chain attacks where tags are retargeted to malicious code. Include the tag as a comment for readability.

- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
...
- uses: actions/upload-artifact@66337390251171e342b772552734d18b63d645f9 # v4

Use Dependabot or Renovate to automate SHA updates when new releases are available.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 26-26: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 26-26: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 37-37: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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 @.github/workflows/mutation-testing.yaml around lines 26 - 40, The workflow
still uses mutable GitHub Action tags in the mutation-testing job, so update the
actions referenced by actions/checkout and actions/upload-artifact to their full
40-character commit SHAs and keep the original version as an inline comment for
readability. Make this change in the mutation-testing workflow alongside the
existing run steps, preserving the same behavior while removing tag-based
supply-chain risk.

Comment thread stryker.config.json
Comment on lines +1 to +19
{
"$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
"packageManager": "npm",
"testRunner": "mocha",
"buildCommand": "npm run build:test",
"coverageAnalysis": "perTest",
"mutate": ["util/**/*.ts", "!**/*.spec.ts"],
"mochaOptions": {
"spec": ["dist/tests/**/*.spec.js"]
},
"thresholds": {
"high": 80,
"low": 60,
"break": null
},
"reporters": ["html", "clear-text", "progress"],
"timeoutMS": 60000,
"concurrency": 2
}

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.

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🌐 Web query:

According to the official StrykerJS docs for @stryker-mutator/typescript-checker, what config key enables the checker in stryker.config.json?

💡 Result:

To enable the TypeScript checker in StrykerJS, you must add "typescript" to the checkers configuration array in your stryker.config.json file [1][2][3][4]. You can achieve this by adding the following key to your configuration [1][2]: "checkers": ["typescript"] Additionally, while not strictly required to enable the plugin, you may also need to specify the path to your TypeScript configuration file using the tsconfigFile key if it is not the default "tsconfig.json" [1][2][3][5]. Example configuration: { "checkers": ["typescript"], "tsconfigFile": "tsconfig.json" } Note that the @stryker-mutator/typescript-checker package must be installed in your project [2][5]. By default, Stryker automatically loads plugins that start with "@stryker-mutator/", so you generally do not need to explicitly list it in a "plugins" section unless you have restricted the automatic loading [1][2][5].

Citations:


Enable the TypeScript checker explicitly.

The @stryker-mutator/typescript-checker is installed but not activated. StrykerJS requires the "checkers": ["typescript"] property to be defined in stryker.config.json to invoke type-checking during mutation. Without this, type-invalid mutants will proceed to execution, potentially skewing mutation score results.

Suggested change
 {
   "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
   "packageManager": "npm",
   "testRunner": "mocha",
+  "checkers": ["typescript"],
   "buildCommand": "npm run build:test",
   "coverageAnalysis": "perTest",
   "mutate": ["util/**/*.ts", "!**/*.spec.ts"],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
"packageManager": "npm",
"testRunner": "mocha",
"buildCommand": "npm run build:test",
"coverageAnalysis": "perTest",
"mutate": ["util/**/*.ts", "!**/*.spec.ts"],
"mochaOptions": {
"spec": ["dist/tests/**/*.spec.js"]
},
"thresholds": {
"high": 80,
"low": 60,
"break": null
},
"reporters": ["html", "clear-text", "progress"],
"timeoutMS": 60000,
"concurrency": 2
}
{
"$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
"packageManager": "npm",
"testRunner": "mocha",
"checkers": ["typescript"],
"buildCommand": "npm run build:test",
"coverageAnalysis": "perTest",
"mutate": ["util/**/*.ts", "!**/*.spec.ts"],
"mochaOptions": {
"spec": ["dist/tests/**/*.spec.js"]
},
"thresholds": {
"high": 80,
"low": 60,
"break": null
},
"reporters": ["html", "clear-text", "progress"],
"timeoutMS": 60000,
"concurrency": 2
}
🤖 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 `@stryker.config.json` around lines 1 - 19, Stryker is not invoking the
installed TypeScript checker because the configuration lacks the explicit
checker activation. Update the Stryker config to enable the TypeScript checker
via the checkers setting in the existing JSON alongside the current testRunner,
mutate, and thresholds options. Keep the rest of the StrykerJS configuration
unchanged so mutation testing still runs with type checking enabled.

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