Skip to content

Add scheduled mutation testing to generated repos#24

Open
leynos wants to merge 2 commits into
mainfrom
add-mutation-testing-template
Open

Add scheduled mutation testing to generated repos#24
leynos wants to merge 2 commits into
mainfrom
add-mutation-testing-template

Conversation

@leynos

@leynos leynos commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Summary

Rolls the estate-wide scheduled mutation testing pattern into the copier template so newly generated repositories get it by default.

  • template/.github/workflows/{% if mutmut_supported or use_rust %}mutation-testing.yml{% endif %}.jinja — thin caller of the shared reusable workflows, pinned to leynos/shared-actions@859416a90eb3987b46a57682c5d6b8964ad3f0a6:
    • mutation job calls mutation-mutmut.yml against the flat package layout (paths: "{{ package_name }}/", module-prefix-strip: "", python-version: "{{ python_version }}").
    • When use_rust is enabled, a mutation-rust job calls mutation-cargo.yml for the extension crate.
  • template/pyproject.toml.jinja — adds [tool.mutmut] with source_paths = ["{{ package_name }}/"] and pytest_add_cli_args_test_selection = ["tests/"] (no runner key; mutmut 3.6 ignores it), plus a comment pointing tests that read repository-root paths at also_copy/skipif and the caller guide.

Caller guides: mutation-mutmut-workflow.md and mutation-cargo-workflow.md.

Gating semantics (rework)

Review caught that the original caller passed the template's default python-version (3.10) into the shared mutmut workflow: setup-uv exports UV_PYTHON job-wide and the workflow's helper scripts require >= 3.13, so change detection failed before any mutation testing ran. Per maintainer direction:

  • python_version default bumped 3.10 → 3.12 — the estate baselines libraries for external consumption at 3.12, applications and internal work at 3.14. requires-python and the ruff target-version derive from the answer; test helpers and the requires-python contract assertion were updated to match. Generic doc examples mentioning 3.10 were left alone.
  • mutmut is conditional on the baseline interpreter: a hidden computed answer mutmut_supported (python_version minor >= 13, numeric comparison) gates both the mutation job and the [tool.mutmut] pyproject section. A 3.12 baseline therefore gets no mutmut mutation testing.
  • cargo-mutants stays gated on use_rust independently.
  • When both gates are off, the workflow file is not generated at all — the filename itself is conditional ({% if mutmut_supported or use_rust %}…{% endif %}, copier skips files whose rendered name is empty), so no empty or invalid workflow can ship.
  • Pin bumped to 859416a90eb3987b46a57682c5d6b8964ad3f0a6, which includes the sub-3.13 python-version fail-fast guard and the setup-commands input.
  • python-version: "{{ python_version }}" is retained on the mutmut job — now always >= 3.13 by construction whenever the job renders.

Scheduling

The template default is 30 9 * * * (09:30 UTC). Existing estate repositories occupy the 03:05–09:05 UTC band; the workflow carries a comment advising per-repository staggering, and downstream adopters should adjust the cron when generating a repository.

Judgement calls

  • Cargo job included. The PyO3 stub crate is crate-type = ["cdylib", "rlib"] with in-crate #[cfg(test)] tests, and the generated Makefile already runs cargo test for it. Verified empirically: a rendered use_rust=true project passes cargo test --manifest-path rust_extension/Cargo.toml (pyo3 0.29 links cleanly on Linux). The prosidy-darn/stilyagi opt-out precedent therefore does not apply here.
  • extra-crate-dirs rather than root paths. Generated repositories have no root Cargo.toml — the sole crate lives in rust_extension/ — so the crate is declared via extra-crate-dirs: "rust_extension" with paths: "". Scheduled scoped runs bucket rust_extension/src/** changes to that target correctly. (The cargo job's with-block is unchanged by the rework.)
  • Known limitation on manual dispatch of the cargo job. mutation_detect_changes.py unconditionally emits root-target shards for workflow_dispatch full runs, and this repository shape has no root crate, so that leg fails (the rust_extension leg still runs; fail-fast: false). The caller sets shard-count: 1 to confine this to a single leg and documents it in-line. A shared-actions follow-up allowing an empty root target would remove the wart.
  • .yml.jinja with ${{ "{{" }} … {{ "}}" }} escaping matches the existing ci.yml.jinja/act-validation.yml.jinja convention (copier's default envops; no raw blocks in this template).

Validation

  • Rendered python_version 3.12 (with and without use_rust), 3.13 (both), and 3.14: the workflow is absent for 3.12 pure, cargo-only for 3.12 + Rust, and includes the mutmut job with the correct python-version input for 3.13/3.14; [tool.mutmut] renders only at >= 3.13.
  • Every rendered workflow YAML-parses and passes actionlint; every rendered pyproject.toml TOML-parses with requires-python/ruff target derived correctly.
  • Full template contract suite: 37 passed, 2 skipped (act-gated), including four new gating contract cases.
  • cargo test in the rendered Rust variant: 1 passed.

🤖 Generated with Claude Code

Generated projects now ship a thin mutation-testing caller workflow
pinned to the shared reusable workflows: mutmut for the Python package
and, when the Rust extension is enabled, cargo-mutants for the
rust_extension crate. The templated pyproject gains the matching
[tool.mutmut] configuration, with a note on also_copy for tests that
read repository-root paths.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR raises the copier template's minimum supported Python version to 3.12, adds a computed mutmut_supported gating flag, introduces a Jinja-templated GitHub Actions mutation-testing workflow (mutmut and Rust cargo jobs), adds a [tool.mutmut] pyproject block, and updates tests accordingly.

Changes

Mutation testing setup

Layer / File(s) Summary
Python version default and mutmut_supported gating flag
copier.yml
Raises python_version default to 3.12 and adds a computed mutmut_supported boolean (minor version >= 13), gated with when: false.
Mutation-testing workflow template
template/.github/workflows/{% if mutmut_supported or use_rust %}mutation-testing.yml{% endif %}.jinja
New workflow with daily cron and manual dispatch triggers, minimal default permissions, queueing concurrency, and conditional mutation/mutation-rust jobs calling shared reusable workflows with repo-specific inputs.
Mutmut tool configuration
template/pyproject.toml.jinja
Adds a Jinja-guarded [tool.mutmut] block setting source_paths and pytest_add_cli_args_test_selection.
Test defaults and mutation gating regression tests
tests/helpers/pyproject_contracts.py, tests/helpers/rendering.py, tests/test_template.py
Bumps expected requires-python and render_project default to 3.12, adds YAML helper imports, and adds test_generated_mutation_testing_gating with MUTATION_WORKFLOW_PIN to verify workflow presence, job gating, and shared-workflow pinning.

Sequence Diagram(s)

sequenceDiagram
    participant Scheduler as Cron/Dispatch
    participant Workflow as mutation-testing.yml
    participant MutmutJob as mutation job
    participant CargoJob as mutation-rust job
    participant Shared as Shared reusable workflows

    Scheduler->>Workflow: trigger (daily cron or manual dispatch)
    Workflow->>MutmutJob: run if mutmut_supported
    MutmutJob->>Shared: call mutation-mutmut.yml with paths, python_version
    Workflow->>CargoJob: run if use_rust
    CargoJob->>Shared: call mutation-cargo.yml with sharding, crate dirs
Loading

Possibly related PRs

  • leynos/shared-actions#319: Adds copier template gating and workflow/pyproject generation targeting the same reusable mutation-mutmut.yml/mutation-cargo.yml workflows.
  • leynos/hecate#24: Adds/validates GitHub Actions mutation-testing wiring around the shared mutation-mutmut.yml workflow with similar [tool.mutmut] configuration.
  • leynos/netsuke#406: Introduces a scheduled mutation-testing caller workflow delegating to pinned shared reusable workflows with matching contract test structure.

Suggested labels: Roadmap

Poem

A rabbit hops through mutant trees,
Testing code with sly finesse,
3.12 now the floor we set,
Mutmut hums beneath the vet,
Workflows spin on cron's own beat—
Bugs beware, the tests compete! 🐇🧬


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 3 warnings)

Check name Status Explanation Resolution
Unit Architecture ❌ Error FAIL: copier.yml computes mutmut_supported via python_version.split('.')[1] with no validator, so malformed input can blow up during render instead of at a visible boundary. Add a validator or parse helper for python_version before deriving mutmut_supported, and reuse the existing render_project helper instead of duplicating copier.copy.
User-Facing Documentation ⚠️ Warning template/docs/users-guide.md.jinja lacks any mention of the new mutation-testing workflow or mutmut gating. Add a users-guide section covering the scheduled mutation-testing workflow, its Python 3.13+ gate, Rust gating, and how generated projects use it.
Developer Documentation ⚠️ Warning Document the new mutation-testing workflow and mutmut gating in docs/developers-guide.md or an ADR; the docs search found no such entries. Add a short developers-guide or ADR note covering the scheduled mutation workflow, mutmut_supported gating, the Python 3.13+ requirement, and the shared-workflow pin.
Architectural Complexity And Maintainability ⚠️ Warning The new gating test duplicates copier.copy(...) instead of reusing render_project, creating a parallel render path with no stated need. Extend render_project with a destination/subdirectory argument and call it from the gating test so render logic stays in one helper.
✅ Passed checks (16 passed)
Check name Status Explanation
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.
Testing (Overall) ✅ Passed The new tests render four real variants and assert generated TOML/YAML, workflow presence, job gating, and pinning, so they would fail on plausible bad implementations.
Module-Level Documentation ✅ Passed PASS: all 19 Python modules have top-level docstrings, and the touched helpers/tests explain their purpose and how they relate to the surrounding tooling.
Testing (Unit And Behavioural) ✅ Passed The new regression test renders real template variants and inspects generated workflow/pyproject outputs, exercising the behavioural boundary rather than internals.
Testing (Property / Proof) ✅ Passed PASS: the invariant is a simple threshold and the PR already covers boundary cases with parametrized tests; no property-based or proof-style test is warranted.
Testing (Compile-Time / Ui) ✅ Passed No Rust/TypeScript compile-time surface exists here (count=0), and the new workflow/pyproject assertions are focused semantic contracts rather than brittle snapshots.
Domain Architecture ✅ Passed Pass: Keep this as template scaffolding and tests; no domain code or boundary-crossing logic appears in the patch.
Observability ✅ Passed Template and workflow-only changes add no production observability surface; GitHub Actions already supplies run logs for the new CI jobs.
Security And Privacy ✅ Passed PASS: No secrets, credentials, or auth logic were introduced; the workflow keeps top-level permissions empty and only grants read/OIDC scopes at job level.
Performance And Resource Use ✅ Passed PASS: Keep the change set template/test-only; it introduces a 4-case param matrix and a single scheduled workflow, with no unbounded loops, retries, matrices, or repeated I/O.
Concurrency And State ✅ Passed Workflow concurrency is explicit (group per ref, cancel-in-progress: false), and the PR introduces no shared mutable state or async lifetimes.
Rust Compiler Lint Integrity ✅ Passed No Rust sources changed; the diff only touches copier/templates/tests and adds no allow(dead_code)/unused_* suppressions, clone anchors, or ownership work.
Title check ✅ Passed The title clearly summarises the main change: scheduled mutation testing added to generated repositories.
Description check ✅ Passed The description directly matches the changeset and explains the new workflow, gating, and pyproject updates.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-mutation-testing-template

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 862e80152e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# repository root, so no module prefix is stripped.
paths: "{{ package_name }}/"
module-prefix-strip: ""
python-version: "{{ python_version }}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use a 3.13+ interpreter for mutation scripts

When the template uses its default python_version (3.10 in copier.yml), this passes 3.10 into the pinned reusable workflow's setup-uv step, which sets UV_PYTHON; the shared workflow then runs uv run --script for mutation_detect_changes.py, whose inline script metadata requires Python >=3.13. With UV_PYTHON=3.10, uv selects/installs 3.10 and the detection step fails before any mutation testing runs, so default-generated repositories get a broken scheduled mutation workflow. Keep the reusable workflow's script runner on 3.13+ or avoid overriding it below the script requirement.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please mark it as such using the API. Otherwise, please provide an AI agent prompt for the remaining work to be done to address this comment.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find copier.yml
fd -a copier.yml

echo "---- copier.yml python_version / mutmut_supported ----"
rg -n -A5 -B5 'mutmut_supported|python_version' copier.yml

echo "---- workflow template filename and content ----"
fd -a 'mutation-testing.yml.jinja'

echo "---- full workflow template content ----"
fd -a 'mutation-testing.yml.jinja' --exec cat -n {}

Length of output: 1167


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- find workflow files with fd literal ----"
fd -a -H 'mutation-testing'

echo "---- find via find ----"
find . -iname '*mutation-testing*'

echo "---- list workflows dir ----"
find . -path '*workflows*' -iname '*.jinja'

Length of output: 969


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n './template/.github/workflows/{% if mutmut_supported or use_rust %}mutation-testing.yml{% endif %}.jinja'

Length of output: 3102


@leynos, confirmed via codegraph analysis — this is resolved.

The gating now prevents the broken combination entirely:

  • copier.yml computes mutmut_supported as python_version.split('.')[1] | int >= 13 (Line 26-29), a when: false derived flag, not a prompt.
  • The mutation job in the workflow template is wrapped in {%- if mutmut_supported %} … {%- endif %} (Line 28-43), and the python-version: "{{ python_version }}" input (Line 42) only renders inside that block.
  • The default python_version is "3.12" (raised from 3.10, but still < 3.13), so mutmut_supported evaluates to false by default and the mutation job — indeed the whole file, per the {% if mutmut_supported or use_rust %} filename guard — does not render for default-baseline repositories.
  • Whenever a repository does opt into a python_version >= 3.13, mutmut_supported flips true and the rendered python-version value is guaranteed to satisfy the shared workflow's mutation_detect_changes.py >=3.13 script requirement.

So the failure mode codex flagged — UV_PYTHON=3.10 breaking the detection step — can no longer occur; the job simply isn't emitted until the baseline interpreter is high enough.

One note: I can't mark this thread resolved via the API myself, since the original comment was posted by chatgpt-codex-connector[bot], not by me — I can only resolve threads I've authored. Go ahead and resolve it manually on your end.

(=^・ω・^=) ノ resolved

@leynos

leynos commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@leynos

leynos commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Review found that the caller passed the template's default
python-version (3.10) into the shared mutmut workflow, whose helper
scripts require 3.13 or greater: setup-uv exported UV_PYTHON job-wide
and change detection failed before any mutation testing ran.

Address this per maintainer direction:

- bump copier.yml's python_version default from 3.10 to 3.12 (the
  estate baselines libraries for external consumption at 3.12), and
  update everything derived from the default (test helpers and the
  requires-python contract assertion);
- add a hidden computed mutmut_supported answer (baseline minor >= 13,
  numeric comparison) and render the mutmut job and the [tool.mutmut]
  pyproject section only when it holds;
- keep the cargo-mutants job gated on use_rust independently, and use
  a conditional filename so the workflow file is not generated at all
  when both gates are off;
- bump the shared workflow pin to
  859416a90eb3987b46a57682c5d6b8964ad3f0a6, which fails fast on
  sub-3.13 python-version values;
- add contract tests covering the four gating combinations.

The mutmut job keeps python-version set to the project's baseline,
which is now 3.13 or greater by construction whenever the job exists.
@leynos leynos force-pushed the add-mutation-testing-template branch from 57d3ac7 to 3a34cba Compare July 7, 2026 10:46
@leynos

leynos commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

Reworked per review and maintainer direction. Credit to the review for catching that the caller passed the template's default python-version (3.10) into the shared mutmut workflow, where setup-uv exports UV_PYTHON job-wide and the helper scripts require >= 3.13 — change detection failed before any mutation testing ran.

Changes in the latest push:

  • copier.yml python_version default bumped 3.10 → 3.12 (estate baseline for libraries; applications and internal work sit at 3.14). Derived content (requires-python, ruff target-version, test helpers, contract assertions) follows the answer.
  • New hidden computed answer mutmut_supported (baseline minor >= 13, numeric comparison): the mutmut job and [tool.mutmut] render only when it holds; cargo-mutants stays gated on use_rust independently. When both gates are off the workflow filename renders empty and copier skips the file entirely — no empty workflow is ever generated.
  • Caller pin bumped 2b09d10859416a90eb3987b46a57682c5d6b8964ad3f0a6 (sub-3.13 fail-fast guard + setup-commands input).
  • python-version: "{{ python_version }}" retained — now >= 3.13 by construction whenever the job exists.
  • Four new contract-test cases cover the gating combinations (3.12 pure: no file; 3.12 + Rust: cargo-only; 3.13 pure; 3.14 + Rust: both jobs).

Validated by rendering all five variants (YAML + actionlint + TOML parses clean) and running the full contract suite: 37 passed, 2 skipped. PR body updated with the new gating semantics.

@coderabbitai coderabbitai Bot added the Roadmap label Jul 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 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 `@copier.yml`:
- Around line 24-29: The mutmut_supported default in copier.yml assumes
python_version always contains a dotted X.Y value, so rendering can fail on
malformed input. Update the computed default to validate or parse python_version
defensively before calling split, and add a copier validator for the
python_version field if needed so the mutmut_supported expression in this
template cannot break on unexpected version formats.

In `@tests/test_template.py`:
- Around line 513-520: Add a narrow Ruff suppression for the boolean positional
parameters in test_generated_mutation_testing_gating by annotating the test
signature with an inline FBT001 waiver and a brief justification, since pytest
parametrization already binds use_rust and expect_mutmut by name. Keep the fix
localized to this test function only and avoid broader lint disables or
signature restructuring.
- Around line 547-553: The test is duplicating the copier invocation instead of
reusing render_project, which risks the two call sites drifting apart. Update
render_project to accept an optional destination/subdirectory argument and have
it call copier.copy with that path internally, then change this test case to
call render_project(tmp_path / target_dir, copier, project_name=...,
package_name=..., use_rust=..., python_version=...) so the shared logic stays in
one place.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f247b32d-cc5a-474d-90ee-c09062f76d79

📥 Commits

Reviewing files that changed from the base of the PR and between 09a4d88 and 3a34cba.

📒 Files selected for processing (6)
  • copier.yml
  • template/.github/workflows/{% if mutmut_supported or use_rust %}mutation-testing.yml{% endif %}.jinja
  • template/pyproject.toml.jinja
  • tests/helpers/pyproject_contracts.py
  • tests/helpers/rendering.py
  • tests/test_template.py

Comment thread copier.yml
Comment on lines +24 to +29
# Computed: mutmut mutation testing needs a base interpreter of 3.13 or
# greater because the shared workflow's helper scripts require >= 3.13.
mutmut_supported:
type: bool
default: "{{ python_version.split('.')[1] | int >= 13 }}"
when: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: check for validator/choices guarding python_version format.
sed -n '1,30p' copier.yml

Repository: leynos/agent-template-python

Length of output: 822


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the full copier config and any repo-level constraints around python_version.
sed -n '1,120p' copier.yml

printf '\n---\n'

# Search for other mentions of python_version, validators, choices, or related constraints.
rg -n --hidden --glob '!**/.git/**' -e 'python_version' -e 'choices' -e 'validator' -e 'validate' -e 'mutmut_supported' .

Repository: leynos/agent-template-python

Length of output: 8588


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the tests that feed python_version into the template.
sed -n '490,610p' tests/test_template.py

printf '\n---\n'

sed -n '40,100p' tests/helpers/rendering.py

Repository: leynos/agent-template-python

Length of output: 5761


Validate python_version before splitting it. python_version.split('.')[1] assumes a dotted X.Y value, so a malformed answer can break rendering. Add a validator or parse the version more defensively before setting mutmut_supported.

🤖 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 `@copier.yml` around lines 24 - 29, The mutmut_supported default in copier.yml
assumes python_version always contains a dotted X.Y value, so rendering can fail
on malformed input. Update the computed default to validate or parse
python_version defensively before calling split, and add a copier validator for
the python_version field if needed so the mutmut_supported expression in this
template cannot break on unexpected version formats.

Comment thread tests/test_template.py
Comment on lines +513 to +520
def test_generated_mutation_testing_gating(
copier: CopierFixture,
tmp_path: Path,
target_dir: str,
use_rust: bool,
python_version: str,
expect_mutmut: bool,
) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Ruff flags boolean-typed positional parameters (FBT001).

use_rust: bool and expect_mutmut: bool trip FBT001 since the signature permits positional bool args, even though pytest always injects parametrize values by name. A blanket restructure isn't warranted here; add a narrow, justified suppression instead of leaving the lint failing.

🔧 Proposed fix
 def test_generated_mutation_testing_gating(
     copier: CopierFixture,
     tmp_path: Path,
     target_dir: str,
-    use_rust: bool,
+    use_rust: bool,  # noqa: FBT001 -- pytest.mark.parametrize always passes by keyword
     python_version: str,
-    expect_mutmut: bool,
+    expect_mutmut: bool,  # noqa: FBT001 -- pytest.mark.parametrize always passes by keyword
 ) -> None:

As per coding guidelines, "Only narrow in-line disables (# noqa: XYZ) are permitted, must be accompanied by justification and used only as a last resort."

📝 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
def test_generated_mutation_testing_gating(
copier: CopierFixture,
tmp_path: Path,
target_dir: str,
use_rust: bool,
python_version: str,
expect_mutmut: bool,
) -> None:
def test_generated_mutation_testing_gating(
copier: CopierFixture,
tmp_path: Path,
target_dir: str,
use_rust: bool, # noqa: FBT001 -- pytest.mark.parametrize always passes by keyword
python_version: str,
expect_mutmut: bool, # noqa: FBT001 -- pytest.mark.parametrize always passes by keyword
) -> None:
🧰 Tools
🪛 Ruff (0.15.20)

[warning] 517-517: Boolean-typed positional argument in function definition

(FBT001)


[warning] 519-519: Boolean-typed positional argument in function definition

(FBT001)

🤖 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 `@tests/test_template.py` around lines 513 - 520, Add a narrow Ruff suppression
for the boolean positional parameters in test_generated_mutation_testing_gating
by annotating the test signature with an inline FBT001 waiver and a brief
justification, since pytest parametrization already binds use_rust and
expect_mutmut by name. Keep the fix localized to this test function only and
avoid broader lint disables or signature restructuring.

Sources: Path instructions, Linters/SAST tools

Comment thread tests/test_template.py
Comment on lines +547 to +553
project = copier.copy(
tmp_path / target_dir,
project_name="MutationProj",
package_name="mutation_pkg",
use_rust=use_rust,
python_version=python_version,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicates render_project's copier invocation instead of reusing it.

This manually re-implements render_project's body (project_name/package_name/use_rust/python_version → copier.copy) purely to pass a per-case subdirectory. Extend render_project with an optional destination/subdirectory argument and reuse it here to avoid drift between the two call sites.

♻️ Proposed direction
 def render_project(
     tmp_path: Path,
     copier: CopierFixture,
     *,
     project_name: str,
     package_name: str,
     use_rust: bool = False,
     python_version: str = "3.12",
+    destination: Path | None = None,
 ) -> CopierProject:
     ...
     return copier.copy(
-        tmp_path,
+        destination or tmp_path,
         ...
     )

Then in the new test: render_project(tmp_path / target_dir, copier, project_name=..., ...).

🤖 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 `@tests/test_template.py` around lines 547 - 553, The test is duplicating the
copier invocation instead of reusing render_project, which risks the two call
sites drifting apart. Update render_project to accept an optional
destination/subdirectory argument and have it call copier.copy with that path
internally, then change this test case to call render_project(tmp_path /
target_dir, copier, project_name=..., package_name=..., use_rust=...,
python_version=...) so the shared logic stays in one place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant