fix: finalize semantic repair and test updates

This commit is contained in:
2026-03-21 15:07:06 +03:00
parent 005797334b
commit 9b47b9b667
99 changed files with 2484 additions and 985 deletions

View File

@@ -13,13 +13,19 @@ from src.dependencies import get_clean_release_repository, get_config_manager
from datetime import datetime, timezone
from uuid import uuid4
from src.models.clean_release import CleanPolicySnapshot, ComplianceReport, ReleaseCandidate, SourceRegistrySnapshot
from src.models.clean_release import (
CleanPolicySnapshot,
ComplianceReport,
ReleaseCandidate,
SourceRegistrySnapshot,
)
from src.services.clean_release.enums import CandidateStatus, ComplianceDecision
from src.scripts.clean_release_cli import main as cli_main
# [DEF:test_cli_candidate_register_scaffold:Function]
# @RELATION: BINDS_TO -> test_clean_release_cli
# @PURPOSE: Verify candidate-register command exits successfully for valid required arguments.
def test_cli_candidate_register_scaffold() -> None:
"""Candidate register CLI command smoke test."""
exit_code = cli_main(
@@ -40,8 +46,10 @@ def test_cli_candidate_register_scaffold() -> None:
# [/DEF:test_cli_candidate_register_scaffold:Function]
# [DEF:test_cli_manifest_build_scaffold:Function]
# @RELATION: BINDS_TO -> test_clean_release_cli
# @PURPOSE: Verify candidate-register/artifact-import/manifest-build smoke path succeeds end-to-end.
def test_cli_manifest_build_scaffold() -> None:
"""Manifest build CLI command smoke test."""
register_exit = cli_main(
@@ -90,8 +98,10 @@ def test_cli_manifest_build_scaffold() -> None:
# [/DEF:test_cli_manifest_build_scaffold:Function]
# [DEF:test_cli_compliance_run_scaffold:Function]
# @RELATION: BINDS_TO -> test_clean_release_cli
# @PURPOSE: Verify compliance run/status/violations/report commands complete for prepared candidate.
def test_cli_compliance_run_scaffold() -> None:
"""Compliance CLI command smoke test for run/status/report/violations."""
repository = get_clean_release_repository()
@@ -119,6 +129,7 @@ def test_cli_compliance_run_scaffold() -> None:
config = config_manager.get_config()
if getattr(config, "settings", None) is None:
# @INVARIANT: SimpleNamespace substitutes for GlobalSettings — any field rename in GlobalSettings will silently not propagate here; re-verify on GlobalSettings schema changes.
config.settings = SimpleNamespace()
config.settings.clean_release = SimpleNamespace(
active_policy_id=policy.id,
@@ -180,7 +191,11 @@ def test_cli_compliance_run_scaffold() -> None:
)
assert run_exit == 0
run_id = next(run.id for run in repository.check_runs.values() if run.candidate_id == "cli-candidate-3")
run_id = next(
run.id
for run in repository.check_runs.values()
if run.candidate_id == "cli-candidate-3"
)
status_exit = cli_main(["compliance-status", "--run-id", run_id, "--json"])
assert status_exit == 0
@@ -194,8 +209,10 @@ def test_cli_compliance_run_scaffold() -> None:
# [/DEF:test_cli_compliance_run_scaffold:Function]
# [DEF:test_cli_release_gate_commands_scaffold:Function]
# @RELATION: BINDS_TO -> test_clean_release_cli
# @PURPOSE: Verify approve/reject/publish/revoke release-gate commands execute with valid fixtures.
def test_cli_release_gate_commands_scaffold() -> None:
"""Release gate CLI smoke test for approve/reject/publish/revoke commands."""
repository = get_clean_release_repository()
@@ -231,7 +248,11 @@ def test_cli_release_gate_commands_scaffold() -> None:
run_id=f"run-{uuid4()}",
candidate_id=approved_candidate_id,
final_status=ComplianceDecision.PASSED.value,
summary_json={"operator_summary": "ok", "violations_count": 0, "blocking_violations_count": 0},
summary_json={
"operator_summary": "ok",
"violations_count": 0,
"blocking_violations_count": 0,
},
generated_at=datetime.now(timezone.utc),
immutable=True,
)
@@ -242,7 +263,11 @@ def test_cli_release_gate_commands_scaffold() -> None:
run_id=f"run-{uuid4()}",
candidate_id=rejected_candidate_id,
final_status=ComplianceDecision.PASSED.value,
summary_json={"operator_summary": "ok", "violations_count": 0, "blocking_violations_count": 0},
summary_json={
"operator_summary": "ok",
"violations_count": 0,
"blocking_violations_count": 0,
},
generated_at=datetime.now(timezone.utc),
immutable=True,
)
@@ -317,5 +342,5 @@ def test_cli_release_gate_commands_scaffold() -> None:
assert revoke_exit == 0
# [/DEF:test_clean_release_cli:Module]
# [/DEF:test_cli_release_gate_commands_scaffold:Function]
# [/DEF:test_clean_release_cli:Module]

View File

@@ -17,6 +17,7 @@ from src.scripts.clean_release_tui import CleanReleaseTUI, main
# [DEF:_build_mock_stdscr:Function]
# @RELATION: BINDS_TO -> test_clean_release_tui_v2
# @PURPOSE: Build deterministic curses screen mock with default terminal geometry and exit key.
def _build_mock_stdscr() -> MagicMock:
stdscr = MagicMock()
stdscr.getmaxyx.return_value = (40, 120)
@@ -26,9 +27,11 @@ def _build_mock_stdscr() -> MagicMock:
# [/DEF:_build_mock_stdscr:Function]
@patch("src.scripts.clean_release_tui.curses")
# [DEF:test_tui_f5_dispatches_run_action:Function]
# @RELATION: BINDS_TO -> test_clean_release_tui_v2
# @PURPOSE: Verify F5 key dispatch invokes run_checks exactly once before graceful exit.
def test_tui_f5_dispatches_run_action(mock_curses_module: MagicMock) -> None:
"""F5 should dispatch run action from TUI loop."""
mock_curses_module.KEY_F10 = curses.KEY_F10
@@ -48,9 +51,11 @@ def test_tui_f5_dispatches_run_action(mock_curses_module: MagicMock) -> None:
# [/DEF:test_tui_f5_dispatches_run_action:Function]
@patch("src.scripts.clean_release_tui.curses")
# [DEF:test_tui_f5_run_smoke_reports_blocked_state:Function]
# @RELATION: BINDS_TO -> test_clean_release_tui_v2
# @PURPOSE: Verify blocked compliance state is surfaced after F5-triggered run action.
def test_tui_f5_run_smoke_reports_blocked_state(mock_curses_module: MagicMock) -> None:
"""F5 smoke test should expose blocked outcome state after run action."""
mock_curses_module.KEY_F10 = curses.KEY_F10
@@ -77,8 +82,10 @@ def test_tui_f5_run_smoke_reports_blocked_state(mock_curses_module: MagicMock) -
# [/DEF:test_tui_f5_run_smoke_reports_blocked_state:Function]
# [DEF:test_tui_non_tty_refuses_startup:Function]
# @RELATION: BINDS_TO -> test_clean_release_tui_v2
# @PURPOSE: Verify non-TTY execution returns exit code 2 with actionable stderr guidance.
def test_tui_non_tty_refuses_startup(capsys) -> None:
"""Non-TTY startup must refuse TUI mode and redirect operator to CLI/API flow."""
with patch("sys.stdout.isatty", return_value=False):
@@ -92,9 +99,11 @@ def test_tui_non_tty_refuses_startup(capsys) -> None:
# [/DEF:test_tui_non_tty_refuses_startup:Function]
@patch("src.scripts.clean_release_tui.curses")
# [DEF:test_tui_f8_blocked_without_facade_binding:Function]
# @RELATION: BINDS_TO -> test_clean_release_tui_v2
# @PURPOSE: Verify F8 path reports disabled action instead of mutating hidden facade state.
def test_tui_f8_blocked_without_facade_binding(mock_curses_module: MagicMock) -> None:
"""F8 should not perform hidden state mutation when facade action is not bound."""
mock_curses_module.KEY_F10 = curses.KEY_F10
@@ -112,5 +121,5 @@ def test_tui_f8_blocked_without_facade_binding(mock_curses_module: MagicMock) ->
assert "F8 disabled" in app.last_error
# [/DEF:test_clean_release_tui_v2:Module]
# [/DEF:test_tui_f8_blocked_without_facade_binding:Function]
# [/DEF:test_clean_release_tui_v2:Module]