Files
cc-ci/tests/unit/test_f211_sso_skip.py
autonomic-bot 9a7772563a style: repo-wide lint pass — make the lint gate green again
Push builds have been RED on the lint step since ~build 209 from accumulated
formatting drift. This is the mechanical cleanup: ruff format + ruff --fix
(UP038 isinstance unions, SIM105 contextlib.suppress, UP031 f-strings, SIM115
tempfile context manager), shfmt -i 2 -ci, nixpkgs-fmt/statix/deadnix (merged
attrsets, dropped unused lib args), yamllint, and shell quoting fixes in
tests/lasuite-docs/setup_custom_tests.sh. No behaviour changes intended;
lint: PASS, unit tests: 138 passed.
2026-06-09 21:56:15 +00:00

123 lines
5.0 KiB
Python

"""Unit tests for F2-11 — SSO-dep "deps-not-ready" SKIP must NOT yield a GREEN run.
Two halves of the fix are tested without any real deploy:
1. `run_recipe_ci.sso_dep_unverified` — the pure gate predicate the orchestrator uses to flip
`overall` to fail when a deps-declaring recipe's SSO tests were skipped because deps weren't ready.
2. `conftest.pytest_collection_modifyitems` — when CCCI_DEPS_READY=0 it (a) skips every
`requires_deps` test and (b) records the skipped count to `$CCCI_DEPS_SKIP_REPORT` so the
orchestrator can surface it + gate on it.
The end-to-end hazard (a real SSO-dep recipe going green on a skip) is exercised by e2e against
cc-ci; here we lock the decision logic + the conftest→orchestrator signal that drives it.
"""
from __future__ import annotations
import importlib.util
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
import run_recipe_ci # noqa: E402
# ---- 1. the pure gate predicate ----
def test_sso_dep_unverified_true_when_declared_notready_and_skipped():
"""declares DEPS + deps not ready + ≥1 requires_deps test skipped → run must FAIL (F2-11)."""
assert run_recipe_ci.sso_dep_unverified(["keycloak"], deps_ready=False, requires_deps_skipped=1)
assert run_recipe_ci.sso_dep_unverified(["keycloak"], deps_ready=False, requires_deps_skipped=3)
def test_sso_dep_unverified_false_when_deps_ready():
"""deps ready (setup_custom_tests succeeded) → SSO tests actually ran → not a failure."""
assert not run_recipe_ci.sso_dep_unverified(
["keycloak"], deps_ready=True, requires_deps_skipped=0
)
def test_sso_dep_unverified_false_when_no_deps_declared():
"""A recipe with no DEPS can never trip the SSO-skip gate."""
assert not run_recipe_ci.sso_dep_unverified([], deps_ready=False, requires_deps_skipped=0)
assert not run_recipe_ci.sso_dep_unverified(None, deps_ready=False, requires_deps_skipped=2)
def test_sso_dep_unverified_false_when_nothing_skipped():
"""Deps declared + not ready but ZERO requires_deps tests skipped → don't false-fail
(the recipe has no SSO-marked tests to have been masked)."""
assert not run_recipe_ci.sso_dep_unverified(
["keycloak"], deps_ready=False, requires_deps_skipped=0
)
# ---- 2. conftest skip + record behavior ----
def _load_conftest():
"""Load tests/conftest.py under a private module name (avoid clashing with pytest's own
loaded `conftest`), so we can call pytest_collection_modifyitems directly with fakes."""
path = os.path.join(os.path.dirname(__file__), "..", "conftest.py")
spec = importlib.util.spec_from_file_location("ccci_conftest_under_test", path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
class _FakeItem:
def __init__(self, keywords):
# pytest `item.keywords` supports `in`; a dict suffices.
self.keywords = {k: True for k in keywords}
self.markers = []
def add_marker(self, mark):
self.markers.append(mark)
def test_conftest_skips_and_records_requires_deps_when_not_ready(tmp_path, monkeypatch):
conftest = _load_conftest()
report = tmp_path / "skip.txt"
monkeypatch.setenv("CCCI_DEPS_READY", "0")
monkeypatch.setenv("CCCI_DEPS_NOT_READY_REASON", "keycloak realm setup boom")
monkeypatch.setenv("CCCI_DEPS_SKIP_REPORT", str(report))
sso1 = _FakeItem(["requires_deps"])
sso2 = _FakeItem(["requires_deps"])
plain = _FakeItem([]) # a non-deps custom test — must NOT be skipped
conftest.pytest_collection_modifyitems(config=None, items=[sso1, sso2, plain])
# Both requires_deps items got a skip marker; the plain one did not.
assert len(sso1.markers) == 1 and len(sso2.markers) == 1
assert plain.markers == []
# The skipped count was recorded for the orchestrator.
assert report.read_text().split() == ["2"]
def test_conftest_appends_across_invocations(tmp_path, monkeypatch):
"""The orchestrator runs one pytest per custom file; counts must accumulate (append)."""
conftest = _load_conftest()
report = tmp_path / "skip.txt"
monkeypatch.setenv("CCCI_DEPS_READY", "0")
monkeypatch.setenv("CCCI_DEPS_SKIP_REPORT", str(report))
conftest.pytest_collection_modifyitems(None, [_FakeItem(["requires_deps"])])
conftest.pytest_collection_modifyitems(
None, [_FakeItem(["requires_deps"]), _FakeItem(["requires_deps"])]
)
total = sum(int(x) for x in report.read_text().split())
assert total == 3
def test_conftest_noop_and_no_record_when_deps_ready(tmp_path, monkeypatch):
"""deps ready → no skips, no report file written (early return)."""
conftest = _load_conftest()
report = tmp_path / "skip.txt"
monkeypatch.setenv("CCCI_DEPS_READY", "1")
monkeypatch.setenv("CCCI_DEPS_SKIP_REPORT", str(report))
item = _FakeItem(["requires_deps"])
conftest.pytest_collection_modifyitems(None, [item])
assert item.markers == [] # not skipped
assert not report.exists() # nothing recorded