Files
ss-tools/backend/tests/services/clean_release/test_report_builder.py

122 lines
4.9 KiB
Python

# #region Test.CleanRelease.ReportBuilder [C:3] [TYPE Module] [SEMANTICS test,clean-release,report,builder,compliance]
# @BRIEF Tests for report_builder.py — ComplianceReportBuilder.build_report_payload, persist_report.
# @RELATION BINDS_TO -> [ReportBuilder]
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
from datetime import UTC, datetime
from unittest.mock import MagicMock
import pytest
# #region TestBuildReportPayload [C:2] [TYPE Module]
class TestBuildReportPayload:
@pytest.fixture
def repo(self):
return MagicMock()
@pytest.fixture
def builder(self, repo):
from src.services.clean_release.report_builder import ComplianceReportBuilder
return ComplianceReportBuilder(repo)
def test_raises_for_running_run(self, builder):
"""Non-terminal (RUNNING) run raises ValueError (line 37)."""
from src.services.clean_release.enums import RunStatus
run = MagicMock()
run.status = RunStatus.RUNNING
with pytest.raises(ValueError, match="non-terminal"):
builder.build_report_payload(run, [])
def test_raises_for_blocked_with_no_violations(self, builder):
"""BLOCKED final_status with zero blocking violations raises ValueError."""
from src.services.clean_release.enums import ComplianceDecision, RunStatus
run = MagicMock()
run.status = RunStatus.SUCCEEDED
run.final_status = ComplianceDecision.BLOCKED
violations = []
with pytest.raises(ValueError, match="blocking violation"):
builder.build_report_payload(run, violations)
def test_raises_for_blocked_with_non_blocking_violations(self, builder):
"""BLOCKED run with violations but none blocking raises ValueError."""
from src.services.clean_release.enums import ComplianceDecision, RunStatus
from src.models.clean_release import ComplianceViolation
run = MagicMock()
run.status = RunStatus.SUCCEEDED
run.final_status = ComplianceDecision.BLOCKED
violations = [
ComplianceViolation(
id="v1", run_id="run-1", stage_name="DATA_PURITY",
code="WARN001", message="warning", severity="MINOR",
evidence_json={"blocked_release": False},
)
]
with pytest.raises(ValueError, match="blocking violation"):
builder.build_report_payload(run, violations)
def test_returns_report_for_passed_run(self, builder):
"""PASSED run returns ComplianceReport with correct summary."""
from src.services.clean_release.enums import ComplianceDecision, RunStatus
from src.models.clean_release import ComplianceViolation
run = MagicMock()
run.id = "check-run-1"
run.candidate_id = "cand-1"
run.status = RunStatus.SUCCEEDED
run.final_status = ComplianceDecision.PASSED
report = builder.build_report_payload(run, [])
assert report is not None
assert report.run_id == "check-run-1"
assert report.final_status == ComplianceDecision.PASSED
assert report.immutable is True
assert "no blocking violations" in report.summary_json["operator_summary"]
def test_returns_report_for_blocked_run(self, builder):
"""BLOCKED run with blocking violations returns report."""
from src.services.clean_release.enums import ComplianceDecision, RunStatus
from src.models.clean_release import ComplianceViolation
run = MagicMock()
run.id = "check-run-2"
run.candidate_id = "cand-2"
run.status = RunStatus.SUCCEEDED
run.final_status = ComplianceDecision.BLOCKED
violations = [
ComplianceViolation(
id="v1", run_id="run-1", stage_name="DATA_PURITY",
code="POL001", message="prohibited artifact", severity="CRITICAL",
evidence_json={"blocked_release": True},
)
]
report = builder.build_report_payload(run, violations)
assert report is not None
assert report.summary_json["violations_count"] == 1
assert report.summary_json["blocking_violations_count"] == 1
assert "Blocked" in report.summary_json["operator_summary"]
# #endregion TestBuildReportPayload
# #region TestPersistReport [C:2] [TYPE Module]
class TestPersistReport:
def test_persist_report(self):
"""persist_report delegates to repository."""
repo = MagicMock()
repo.save_report.return_value = "saved_report"
from src.services.clean_release.report_builder import ComplianceReportBuilder
builder = ComplianceReportBuilder(repo)
mock_report = MagicMock()
result = builder.persist_report(mock_report)
assert result == "saved_report"
repo.save_report.assert_called_once_with(mock_report)
# #endregion TestPersistReport
# #endregion Test.CleanRelease.ReportBuilder