Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ Examples:
- Mock ansible-vault subprocess calls in unit tests
- Use real ansible-vault in integration tests (test fixtures)
- Minimum 70% coverage enforced
- **No skips in CI**: `tests/conftest.py` rewrites any skipped test into a
setup error when `GITHUB_ACTIONS=true`. `requires_cue` /
`requires_ansible_vault` markers stay useful for local dev (graceful
fallback when the tool is missing) but never fire in CI because both
tools are installed there. New skip markers without their corresponding
CI tool will fail loudly. See #55 for the rationale.

### Commit Convention

Expand Down
33 changes: 33 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,39 @@
TEST_PASSWORD = "test-vault-password-12345"


# --- CI policy: no skips ---
#
# Local dev keeps `requires_cue` / `requires_ansible_vault` markers as graceful
# fallbacks. In CI we install both tools (#54), so those markers never fire —
# unless someone adds a new skip marker without its CI tool. The hook below
# rewrites any skip report into a failure when running under GitHub Actions,
# so the gap turns into a real test failure rather than silent green.


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item: pytest.Item, call: pytest.CallInfo[None]) -> object:
"""Convert skip → failure under GITHUB_ACTIONS=true.

Skip outcomes appear during the "setup" phase (skipif markers fire there).
Rewriting the report's outcome makes pytest count the skip as a failed
test, which propagates to a non-zero exit code through pytest's normal
accounting.
"""
outcome = yield
if os.environ.get("GITHUB_ACTIONS") != "true":
return outcome
report = outcome.get_result()
if report.skipped and report.when == "setup":
original = report.longrepr
report.outcome = "failed"
report.longrepr = (
f"CI policy: skipped tests are forbidden under GITHUB_ACTIONS=true. "
f"Install the required tool in CI instead of skipping. "
f"Original skip reason: {original}"
)
return outcome


@pytest.fixture
def tmp_dir(tmp_path):
"""Return a temporary directory as Path."""
Expand Down
Loading