From 60ad1c43d4a321c5e4018cc9e2c05ed7d9c19536 Mon Sep 17 00:00:00 2001 From: leynos Date: Mon, 20 Jul 2026 00:43:38 +0200 Subject: [PATCH] Document mutation-testing workflow contract tests Add a section to the developer guide explaining the thin mutation-testing caller workflow, its two run modes, the caller configuration inputs, and what the contract test in tests/workflow_contracts/mutation_testing_test.py pins, so contributors know how to run and interpret it locally via `make test-workflow-contracts`. --- docs/developers-guide.md | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/developers-guide.md b/docs/developers-guide.md index 9d26bde..1395916 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -93,3 +93,55 @@ def test_uses_pinned_full_sha(caller_step): If a workflow's behaviour genuinely depends on a feature only present from a particular commit onwards, express that as a comment or a changelog note, not as a test assertion on the SHA string. + +## Mutation-testing workflow contract tests + +This repository runs scheduled, informational mutation testing through a thin +caller workflow, [`.github/workflows/mutation-testing.yml`](../.github/workflows/mutation-testing.yml), +which delegates to the shared reusable workflow +`leynos/shared-actions/.github/workflows/mutation-cargo.yml`. The heavy +lifting — running `cargo-mutants`, sharding, and summarizing survivors — +lives in `shared-actions`; this repository carries only declarative +configuration. The run is **informational only**: it never gates a pull +request. Survivors are reported through the job summary and downloadable +artefacts so they can be triaged into tests, not enforced as a blocking +check. + +The workflow runs in two modes. A **daily schedule** (09:20 UTC) fires a +change-scoped run that mutates only the source files touched within the +detection window, so quiet days are cheap no-ops. A **manual dispatch** (the +Actions "Run workflow" control) mutates the whole crate; select a branch in +that control to exercise a feature branch. + +The caller passes a small set of configuration inputs, each carrying intent: + +- `extra-args` — `--all-features`, so the mutation run mirrors the CI test + baseline; a mismatch would report feature-gated code as untested. +- `exclude-globs` — `src/tests/**,tests/support/**,src/bin/bless_traces.rs`, + keeping the re-exported test module, test-support helpers, and the + manually run golden-snapshot regenerator out of the survivors table, since + none of them is guarded by the test suite. + +The `uses:` reference pins the shared workflow to a full 40-character commit +SHA rather than a branch or tag, so a force-push upstream cannot silently +change what runs here. The contract test asserts only that the pin is a full +commit SHA, not a particular value, so Dependabot bumps it automatically +without any accompanying test edit. + +Because the caller is configuration rather than code, a contract test in +`tests/workflow_contracts/mutation_testing_test.py` pins the shape it must +uphold, failing the pull request when the caller drifts — repointing the pin +at a branch, widening the token scope, or dropping a configuration input — +rather than letting the breakage surface only in a scheduled run. Run it +locally with `make test-workflow-contracts`. The test validates: + +- the `uses:` reference targets `mutation-cargo.yml` pinned to a full commit + SHA; +- the `with:` block carries exactly the expected `extra-args` and + `exclude-globs` configuration; +- job permissions are least-privilege (`contents: read`, `id-token: write`) + and the workflow-level default token scope is empty; +- `concurrency` serializes runs per ref without cancelling one in progress; + and +- the triggers keep the daily schedule and a plain `workflow_dispatch` with + no legacy branch input.