PavlokPager — Testing¶
Mirrors the WorkWingman testing mandate: mocked unit coverage (xUnit + Bogus) and Stryker mutation testing, both enforced in CI.
Run it¶
dotnet test # from repo root (uses PavlokPager.slnx)
dotnet tool restore # once, restores the Stryker local tool
cd tests/PavlokPager.Tests
dotnet stryker # mutation testing; fails under the break threshold
Suite shape¶
59 tests, all isolated: each derives from IsolatedTest, which points PAVLOK_HOME at a
throwaway temp directory, clears the API-key env, injects a fake HttpClient, zeroes the retry
and ladder delays, and captures stdin/stdout — so nothing touches the real profile, network, or
band, and nothing sleeps.
| Area | File | Covers |
|---|---|---|
| Arg parsing | ArgParsingTests |
value/reason/dry-run parsing, strict unknown-arg rejection |
| Budget | BudgetTests |
8-hour page-start window, mode filtering, boundaries |
| Config + crypto | ConfigAndCryptoTests |
defaults, roundtrip, DPAPI/plain encrypt-decrypt, key resolution, exact default ladders |
| Log | LogTests |
well-formed reads, blank skipping, corrupt-line tolerance |
| Send | SendStimulusTests |
dry-run, success, Bearer header, value clamp, 401 fast-fail, 5xx/exception retry, retry ceiling |
| End-to-end | IntegrationTests |
every Main() command: dispatch, page ladder, budget refusal, login/setup via piped stdin, log |
| Mutation-hardening | DefensiveTests |
zap-only clamping, all-steps-fail, log -n, debug-gate redaction, login validation guard |
Fake data is generated with Bogus (Faker). HTTP is mocked with QueueHandler, a
replayable HttpMessageHandler that records URIs, bodies, and auth headers and can simulate
transport failures.
Mutation testing¶
Stryker mutates the production code (flip <, blank a string, negate a condition) and checks the
tests notice. Config: tests/PavlokPager.Tests/stryker-config.json.
Current score: 63.06% (194 killed / 76 survived / 4 timeout / 40 no-coverage) — above the configured break threshold of 60%.
The score climbed 25.8% → 55.7% → 63.06% as coverage moved from the pure seams, to end-to-end
Main() tests, to targeted branch tests. The remaining survivors are almost entirely cosmetic
console-message string mutations, plus the interactive Console.ReadKey password path, which can't
be exercised without a real keyboard. Thresholds: break 60, low 70, high 85.
4-layer matrix¶
| Layer | Status | Notes |
|---|---|---|
| PowerShell (Pester) | N/A | No PowerShell component in this repo — pure C# CLI. |
| C# (xUnit + Bogus + Stryker) | ✓ | This suite. 59 xUnit tests, Bogus fakes, real Stryker.NET mutation testing (score below). |
| Python (pytest + mutmut) | N/A | No Python component. |
| Churn | ✓ | CI churn job runs the suite 5x and fails on any pass/fail-count disagreement (flake detection). |
| Automation / E2E | ✓ (hermetic mock) | IntegrationTests.cs drives every Main() command end-to-end against a fake HttpMessageHandler (QueueHandler) — no real network or band contact. The one path that can't be hermetically exercised is the interactive Console.ReadKey password prompt (needs a real keyboard); it's excluded and documented, not silently skipped. |
CI¶
.github/workflows/tests.yml runs dotnet test on every push/PR, then Stryker (which fails the
job below the break threshold) and uploads the HTML mutation report as an artifact.
.github/workflows/security.yml runs Semgrep. Both add a churn job that repeats the suite 5x
and fails on any disagreement between runs.