Skip to content

Merge pull request #1089 from zainabwahab-eth/feat/783-registry-rate-… #153

Merge pull request #1089 from zainabwahab-eth/feat/783-registry-rate-…

Merge pull request #1089 from zainabwahab-eth/feat/783-registry-rate-… #153

name: Mutation Testing (Wallet Import)
on:
push:
branches: [main, master, develop]
paths:
- 'src/utils/wallet_import.rs'
- 'src/utils/crypto.rs'
- '.cargo-mutants.toml'
- '.github/workflows/mutation-testing.yml'
- 'Cargo.toml'
pull_request:
paths:
- 'src/utils/wallet_import.rs'
- 'src/utils/crypto.rs'
- '.cargo-mutants.toml'
- '.github/workflows/mutation-testing.yml'
- 'Cargo.toml'
# Weekly mutation testing run (Monday 3 AM UTC) to detect regressions
schedule:
- cron: '0 3 * * 1'
# Allow manual dispatch with configurable parameters
workflow_dispatch:
inputs:
max_mutants:
description: 'Maximum mutants to generate (0 = unlimited)'
required: false
default: '0'
min_kill_rate:
description: 'Minimum kill rate threshold (0.0-1.0)'
required: false
default: '0.85'
# Cancel in-progress runs when a new commit is pushed to the same branch
concurrency:
group: mutation-testing-${{ github.ref }}
cancel-in-progress: true
jobs:
mutation-testing:
name: Mutation Testing - Wallet Import/Backup
runs-on: ubuntu-latest
timeout-minutes: 120
env:
MIN_KILL_RATE: ${{ github.event.inputs.min_kill_rate || '0.85' }}
MAX_MUTANTS: ${{ github.event.inputs.max_mutants || '0' }}
steps:
- uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-mutants
run: cargo install cargo-mutants
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y libudev-dev
- name: Cache cargo registry and target
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-mutants-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-mutants-
- name: Run baseline tests
run: cargo test --lib wallet_import --locked
- name: Generate mutation testing report for wallet_import
run: |
mkdir -p mutation-reports
cargo mutants \
--file src/utils/wallet_import.rs \
--jobs 4 \
$([ "$MAX_MUTANTS" != "0" ] && echo "--max-mutants $MAX_MUTANTS" || echo "") \
--output mutation-reports/wallet_import.json \
--format json \
2>&1 | tee mutation-reports/wallet_import.log || EXIT_CODE=$?
- name: Parse mutation testing results
id: parse_results
run: |
python3 << 'EOF'
import json
import sys
try:
with open('mutation-reports/wallet_import.json') as f:
report = json.load(f)
except FileNotFoundError:
print("⚠ Mutation report not found. Check test baseline.")
sys.exit(1)
killed = report.get('killed', 0)
survived = report.get('survived', 0)
total = killed + survived
if total == 0:
print("⚠ No mutants generated. Check examine_globs in .cargo-mutants.toml")
kill_rate = 0.0
else:
kill_rate = killed / total
print(f"Kill Rate: {kill_rate:.1%} ({killed}/{total} mutants killed)")
print(f"::set-output name=kill_rate::{kill_rate}")
print(f"::set-output name=killed::{killed}")
print(f"::set-output name=survived::{survived}")
print(f"::set-output name=total::{total}")
threshold = float("${{ env.MIN_KILL_RATE }}")
if kill_rate < threshold:
print(f"❌ Kill rate {kill_rate:.1%} below threshold {threshold:.1%}")
sys.exit(1)
else:
print(f"✓ Kill rate {kill_rate:.1%} meets threshold {threshold:.1%}")
EOF
- name: Comment PR with mutation results
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const kill_rate = '${{ steps.parse_results.outputs.kill_rate }}';
const killed = '${{ steps.parse_results.outputs.killed }}';
const survived = '${{ steps.parse_results.outputs.survived }}';
const total = '${{ steps.parse_results.outputs.total }}';
const threshold = '${{ env.MIN_KILL_RATE }}';
const comment = `## 🧬 Mutation Testing Results
| Metric | Value |

Check failure on line 138 in .github/workflows/mutation-testing.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/mutation-testing.yml

Invalid workflow file

You have an error in your yaml syntax on line 138
|--------|-------|
| **Kill Rate** | ${(parseFloat(kill_rate) * 100).toFixed(1)}% |
| **Killed** | ${killed}/${total} mutants |
| **Survived** | ${survived}/${total} mutants |
| **Threshold** | ${(parseFloat(threshold) * 100).toFixed(1)}% |
Module: \`src/utils/wallet_import.rs\` (wallet backup/import cryptographic code)
${parseFloat(kill_rate) >= parseFloat(threshold) ? '✓ **PASSED**: Kill rate meets threshold' : '❌ **FAILED**: Kill rate below threshold'}
See [mutation-reports](${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}) for details.
`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
- name: Upload mutation reports as artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: mutation-reports
path: mutation-reports/
retention-days: 30
- name: Check kill rate threshold
run: |
kill_rate=${{ steps.parse_results.outputs.kill_rate }}
threshold=${{ env.MIN_KILL_RATE }}
if (( $(echo "$kill_rate < $threshold" | bc -l) )); then
echo "❌ Mutation kill rate $kill_rate is below threshold $threshold"
exit 1
fi
echo "✓ Mutation kill rate $kill_rate meets threshold $threshold"
- name: Report to pull request
if: failure() && github.event_name == 'pull_request'
run: |
echo "## ❌ Mutation Testing Failed"
echo ""
echo "The wallet import/backup module's test suite is not sensitive enough to catch potential logic errors."
echo ""
echo "**Next steps:**"
echo "1. Review surviving mutants in the artifacts"
echo "2. Add or strengthen tests to kill surviving mutants"
echo "3. See MUTATION_TESTING.md for interpretation guide"