"""Unit tests for the level ladder (harness.level) — phase lvl5 semantics. Pure function — no I/O. Proves the operator-decided rule (plan-phase-lvl5-lint-rung.md, DECISIONS.md phase lvl5): level = max i such that rung_i == "pass" and every rung j < i is "pass" or "skip" — a real FAIL blocks, an UNVERIFIED rung blocks exactly like a fail, an INTENTIONAL skip is climbed past. Includes the mission's four worked examples verbatim, and the old N/A cases (single-published-version recipe, non-backup-capable recipe) now climbing past their former caps. Run cold with: cc-ci-run -m pytest tests/unit/test_level.py -q """ from __future__ import annotations import os import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner")) from harness import level as L # noqa: E402 def _rungs( install="pass", upgrade="pass", backup_restore="pass", functional="pass", lint="pass", ): return { "install": install, "upgrade": upgrade, "backup_restore": backup_restore, "functional": functional, "lint": lint, } # ---- the ladder: five essential rungs, top is L5 (lint) ---- def test_full_clean_climb_is_L5(): assert L.compute_level(_rungs()) == 5 def test_ladder_is_five_rungs_lint_on_top(): assert L.RUNGS == ("install", "upgrade", "backup_restore", "functional", "lint") assert "lint" in L.RUNG_LABEL[5] # ---- mission worked examples (operator Q&A 2026-06-11, verbatim) ---- def test_mission_example_fail_blocks(): # install ✔, upgrade ✘, backup ✔, functional ✔, lint ✔ → level 1 (fail blocks). assert L.compute_level(_rungs(upgrade="fail")) == 1 def test_mission_example_intentional_skip_climbs(): # install ✔, upgrade ✔, backup skip (not capable), functional ✔, lint ✔ → level 5 # (previously capped at 2 — the confusing part the operator removed). assert L.compute_level(_rungs(backup_restore="skip")) == 5 def test_mission_example_unverified_blocks(): # install ✔, upgrade ✔, backup UNVER (harness error), functional ✔, lint ✔ → level 2 # (we cannot claim what we didn't check). assert L.compute_level(_rungs(backup_restore="unver")) == 2 def test_mission_example_unverified_top_rung_not_earned(): # all four ✔, lint unver (abra missing) → level 4. assert L.compute_level(_rungs(lint="unver")) == 4 # ---- blocking semantics ---- def test_install_fail_is_L0(): assert L.compute_level(_rungs(install="fail")) == 0 def test_install_unver_is_L0(): assert L.compute_level(_rungs(install="unver")) == 0 def test_higher_pass_never_rescues_a_fail(): # everything above a failed rung is dead, however green. assert L.compute_level(_rungs(upgrade="fail", backup_restore="pass", functional="pass")) == 1 def test_lint_fail_blocks_at_4(): assert L.compute_level(_rungs(lint="fail")) == 4 def test_unver_blocks_even_after_a_skip(): # skip at L2 is climbed past, but the unver at L3 still blocks → level 1. assert L.compute_level(_rungs(upgrade="skip", backup_restore="unver")) == 1 # ---- intentional-skip climbing (the de-cap) ---- def test_single_version_recipe_climbs_past_upgrade_skip(): # old rule: upgrade N/A capped at L1. New rule: skip is climbed past → full climb 5. assert L.compute_level(_rungs(upgrade="skip")) == 5 def test_stateless_recipe_climbs_past_backup_skip_to_lint(): assert L.compute_level(_rungs(upgrade="skip", backup_restore="skip")) == 5 def test_skip_does_not_count_as_pass(): # ALL skips → nothing passed → level 0 (a skip climbs, but never earns). assert ( L.compute_level( _rungs( install="skip", upgrade="skip", backup_restore="skip", functional="skip", lint="skip", ) ) == 0 ) def test_skip_then_pass_earns_the_higher_rung(): # skip at L4, pass at L5 → level 5 (the skip below doesn't stop the climb). assert L.compute_level(_rungs(functional="skip")) == 5 def test_trailing_skip_keeps_last_pass(): # passes up to L3, skips above → level stays 3 (skips never raise). assert L.compute_level(_rungs(functional="skip", lint="skip")) == 3 # ---- input validation ---- def test_invalid_status_raises(): bad = _rungs() bad["functional"] = "na" # the OLD vocabulary is no longer valid — every N/A is classified try: L.compute_level(bad) except ValueError: return raise AssertionError("expected ValueError on invalid rung status") def test_missing_rung_raises(): bad = _rungs() del bad["lint"] try: L.compute_level(bad) except ValueError: return raise AssertionError("expected ValueError on missing rung") # ---- helpers: backup_restore_status ---- def test_backup_restore_status_pass(): assert L.backup_restore_status("pass", "pass", True) == "pass" def test_backup_restore_status_not_capable_is_intentional_skip(): assert L.backup_restore_status("skip", "skip", False) == "skip" def test_backup_restore_status_fail_on_either(): assert L.backup_restore_status("pass", "fail", True) == "fail" assert L.backup_restore_status("fail", "pass", True) == "fail" def test_backup_restore_partial_is_unverified(): # backup-capable but restore didn't run cleanly (not pass, not fail) → cannot claim L3, # and the non-run is NOT intentional → unver (blocks the level above it). assert L.backup_restore_status("pass", "skip", True) == "unver" assert L.backup_restore_status(None, None, True) == "unver" # ---- helpers: tier_to_rung ---- def test_tier_to_rung_mapping_defaults_unverified(): assert L.tier_to_rung("pass") == "pass" assert L.tier_to_rung("fail") == "fail" # no intentionality information here — a non-run is unver; derive_rungs upgrades to "skip" # only on a declared/structural fact, never the other way. assert L.tier_to_rung("skip") == "unver" assert L.tier_to_rung(None) == "unver"