fix(coverage): resolve fail-under from pyproject in threshold-enforcement step (WS5)
CI / lint (pull_request) Successful in 42s
CI / typecheck (pull_request) Successful in 55s
CI / quality (pull_request) Successful in 59s
CI / security (pull_request) Successful in 1m16s
CI / helm (pull_request) Successful in 40s
CI / build (pull_request) Successful in 43s
CI / push-validation (pull_request) Successful in 20s
CI / unit_tests (pull_request) Successful in 4m26s
CI / docker (pull_request) Successful in 1m25s
CI / coverage (pull_request) Successful in 9m34s
CI / integration_tests (pull_request) Failing after 21m41s
CI / status-check (pull_request) Failing after 3s

The coverage_report noxfile session now delegates COVERAGE_THRESHOLD to the
pyproject single source (`COVERAGE_THRESHOLD = _read_coverage_fail_under()`),
so the AST value is a Call, not a Constant. The
coverage_threshold_enforcement.feature step "I parse the COVERAGE_THRESHOLD
constant from noxfile.py" raised ValueError (errored scenario) because it only
handled a literal constant — this was the missed third WS5 step file (the
config + consolidated steps were already reconciled).

Add the same pyproject `[tool.coverage.report].fail_under` fallback used by
coverage_threshold_config_steps.py, and fix the phantom-97 in the feature
description prose. Verified: the four coverage feature files now pass
(136 scenarios, 0 errored); previously unit_tests errored on
coverage_threshold_enforcement.feature:7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 17:37:24 -04:00
parent a49f37eb1f
commit b499834c0f
2 changed files with 22 additions and 2 deletions
@@ -1,5 +1,5 @@
Feature: Coverage threshold enforcement
The project enforces a minimum 97% code coverage threshold.
The project enforces a minimum 96.5% code coverage threshold.
This feature validates that the coverage configuration and nox session
are properly set up to enforce this requirement.
@@ -41,6 +41,18 @@ def step_ci_workflow_exists(context: Any) -> None:
context.ci_file_content = ci_file.read_text(encoding="utf-8")
def _pyproject_fail_under() -> float | None:
"""The canonical coverage floor: pyproject ``[tool.coverage.report]``
``fail_under`` (the single source the noxfile delegates to via
``_read_coverage_fail_under()``). None if absent/unreadable."""
pyproject = PROJECT_ROOT / "pyproject.toml"
try:
data = tomllib.loads(pyproject.read_text(encoding="utf-8"))
return float(data["tool"]["coverage"]["report"]["fail_under"])
except (OSError, KeyError, TypeError, ValueError, tomllib.TOMLDecodeError):
return None
@when("I parse the COVERAGE_THRESHOLD constant from noxfile.py")
def step_parse_threshold(context: Any) -> None:
content = context.noxfile_content
@@ -55,8 +67,16 @@ def step_parse_threshold(context: Any) -> None:
and isinstance(node.value, ast.Constant)
):
threshold = node.value.value
# WS5: the noxfile now delegates the constant to pyproject's single source
# (``COVERAGE_THRESHOLD = _read_coverage_fail_under()``), so the AST value is
# a Call, not a Constant. Resolve the effective floor from pyproject.
if threshold is None:
raise ValueError("COVERAGE_THRESHOLD constant not found in noxfile.py")
threshold = _pyproject_fail_under()
if threshold is None:
raise ValueError(
"COVERAGE_THRESHOLD not found in noxfile.py and pyproject "
"[tool.coverage.report].fail_under is unreadable"
)
context.coverage_threshold = threshold