# #region Test.ComplianceExecution.Full [C:3] [TYPE Module] [SEMANTICS test,clean-release,compliance,execution] # @BRIEF Full tests for ComplianceExecutionService — manifest resolution, run execution, stage persistence, violation tracking, error handling, report generation. # @RELATION BINDS_TO -> [ComplianceExecutionService] from __future__ import annotations from datetime import UTC, datetime from unittest.mock import AsyncMock, MagicMock, patch import pytest from src.models.clean_release import ( CheckFinalStatus, CleanPolicySnapshot, ComplianceRun, ComplianceStageRun, ComplianceViolation, DistributionManifest, ReleaseCandidate, SourceRegistrySnapshot, ) from src.services.clean_release.compliance_execution_service import ( ComplianceExecutionResult, ComplianceExecutionService, ) from src.services.clean_release.enums import CandidateStatus, ComplianceDecision, RunStatus from src.services.clean_release.exceptions import ComplianceRunError from src.services.clean_release.repository import CleanReleaseRepository from src.services.clean_release.stages.base import ComplianceStage, ComplianceStageContext, StageExecutionResult from src.services.clean_release.enums import ComplianceStageName class _MockStage(ComplianceStage): """Deterministic mock stage for test isolation.""" def __init__(self, name: str = "DATA_PURITY", decision: ComplianceDecision = ComplianceDecision.PASSED): self.stage_name = ComplianceStageName(name) self._decision = decision def execute(self, context: ComplianceStageContext) -> StageExecutionResult: return StageExecutionResult( decision=self._decision, details_json={"checked": True}, violations=[], ) def _make_repo_with_candidate( candidate_id: str = "cand-exe-1", status: str = CandidateStatus.MANIFEST_BUILT.value, policy_id: str = "policy-exe-1", registry_id: str = "registry-exe-1", manifest_id: str = "manifest-exe-1", ) -> CleanReleaseRepository: repo = CleanReleaseRepository() repo.save_candidate(ReleaseCandidate( id=candidate_id, version="1.0.0", source_snapshot_ref="git:sha", created_by="tester", created_at=datetime.now(UTC), status=status, )) repo.save_policy(CleanPolicySnapshot( id=policy_id, policy_id=policy_id, policy_version="1", content_json={}, registry_snapshot_id=registry_id, immutable=True, )) repo.save_registry(SourceRegistrySnapshot( id=registry_id, registry_id=registry_id, registry_version="1", allowed_hosts=["internal.local"], allowed_schemes=["https"], immutable=True, )) repo.save_manifest(DistributionManifest( id=manifest_id, candidate_id=candidate_id, manifest_version=1, manifest_digest="digest1", artifacts_digest="digest1", source_snapshot_ref="git:sha", content_json={"summary": {"included_count": 1, "excluded_count": 0, "prohibited_detected_count": 0}}, created_by="tester", created_at=datetime.now(UTC), immutable=True, )) return repo class TestComplianceExecutionServiceBasic: """ComplianceExecutionService — basic error paths.""" # #region test_missing_candidate_raises [C:2] [TYPE Function] def test_missing_candidate_raises(self): """Missing candidate → ComplianceRunError.""" repo = CleanReleaseRepository() config = MagicMock() svc = ComplianceExecutionService(repository=repo, config_manager=config) with pytest.raises(ComplianceRunError, match="not found"): svc.execute_run(candidate_id="nonexistent", requested_by="tester") # #endregion test_missing_candidate_raises # #region test_resolve_manifest_by_id [C:2] [TYPE Function] def test_resolve_manifest_by_id(self): """Explicit manifest_id resolves correctly.""" repo = _make_repo_with_candidate() config = MagicMock() svc = ComplianceExecutionService(repository=repo, config_manager=config) manifest = svc._resolve_manifest("cand-exe-1", "manifest-exe-1") assert manifest is not None assert manifest.id == "manifest-exe-1" # #endregion test_resolve_manifest_by_id # #region test_resolve_manifest_not_found_raises [C:2] [TYPE Function] def test_resolve_manifest_not_found_raises(self): """Nonexistent manifest_id → ComplianceRunError.""" repo = _make_repo_with_candidate() config = MagicMock() svc = ComplianceExecutionService(repository=repo, config_manager=config) with pytest.raises(ComplianceRunError, match="not found"): svc._resolve_manifest("cand-exe-1", "nonexistent") # #endregion test_resolve_manifest_not_found_raises # #region test_resolve_manifest_not_belonging_raises [C:2] [TYPE Function] def test_resolve_manifest_not_belonging_raises(self): """Manifest not belonging to candidate → ComplianceRunError.""" repo = _make_repo_with_candidate() repo.save_manifest(DistributionManifest( id="manifest-other", candidate_id="cand-other", manifest_version=1, manifest_digest="d", artifacts_digest="d", source_snapshot_ref="ref", content_json={}, created_by="tester", created_at=datetime.now(UTC), immutable=True, )) config = MagicMock() svc = ComplianceExecutionService(repository=repo, config_manager=config) with pytest.raises(ComplianceRunError, match="does not belong"): svc._resolve_manifest("cand-exe-1", "manifest-other") # #endregion test_resolve_manifest_not_belonging_raises # #region test_resolve_manifest_no_manifests_raises [C:2] [TYPE Function] def test_resolve_manifest_no_manifests_raises(self): """Candidate has no manifests → ComplianceRunError.""" repo = _make_repo_with_candidate() repo.manifests.clear() config = MagicMock() svc = ComplianceExecutionService(repository=repo, config_manager=config) with pytest.raises(ComplianceRunError, match="no manifest"): svc._resolve_manifest("cand-exe-1", None) # #endregion test_resolve_manifest_no_manifests_raises # #region test_persist_stage_run [C:2] [TYPE Function] def test_persist_stage_run(self): """Stage run is persisted via repository.""" repo = _make_repo_with_candidate() config = MagicMock() svc = ComplianceExecutionService(repository=repo, config_manager=config) stage_run = ComplianceStageRun( id="stg-test-1", run_id="run-test-1", stage_name="DATA_PURITY", status="SUCCEEDED", decision="PASSED", details_json={}, ) svc._persist_stage_run(stage_run) assert repo.stage_runs.get("stg-test-1") is not None # #endregion test_persist_stage_run # #region test_persist_violations [C:2] [TYPE Function] def test_persist_violations(self): """Violations are persisted via repository.""" repo = _make_repo_with_candidate() config = MagicMock() svc = ComplianceExecutionService(repository=repo, config_manager=config) violations = [ ComplianceViolation( id="viol-1", run_id="run-1", stage_name="DATA_PURITY", code="EXT", severity="MAJOR", message="test", ) ] svc._persist_violations(violations) assert repo.violations.get("viol-1") is not None # #endregion test_persist_violations class TestComplianceExecutionServiceRun: """ComplianceExecutionService.execute_run — full execution flow.""" # #region test_execute_run_success [C:2] [TYPE Function] @patch("src.services.clean_release.compliance_execution_service.resolve_trusted_policy_snapshots") def test_execute_run_success(self, mock_resolve): """Full successful run returns ComplianceExecutionResult.""" repo = _make_repo_with_candidate() mock_resolve.return_value = ( repo.policies["policy-exe-1"], repo.registries["registry-exe-1"], ) config = MagicMock() config.get_config.return_value.settings.clean_release.active_policy_id = "policy-exe-1" config.get_config.return_value.settings.clean_release.active_registry_id = "registry-exe-1" stage = _MockStage(name="DATA_PURITY") svc = ComplianceExecutionService(repository=repo, config_manager=config, stages=[stage]) result = svc.execute_run(candidate_id="cand-exe-1", requested_by="tester") assert isinstance(result, ComplianceExecutionResult) assert result.run is not None assert result.run.candidate_id == "cand-exe-1" assert result.run.manifest_id == "manifest-exe-1" assert len(result.stage_runs) == 1 assert result.stage_runs[0].stage_name == "DATA_PURITY" assert result.report is not None # #endregion test_execute_run_success # #region test_execute_run_stage_failure [C:2] [TYPE Function] @patch("src.services.clean_release.compliance_execution_service.resolve_trusted_policy_snapshots") def test_execute_run_stage_failure(self, mock_resolve): """Stage execution error → run marked FAILED.""" repo = _make_repo_with_candidate() mock_resolve.return_value = ( repo.policies["policy-exe-1"], repo.registries["registry-exe-1"], ) config = MagicMock() config.get_config.return_value.settings.clean_release.active_policy_id = "policy-exe-1" config.get_config.return_value.settings.clean_release.active_registry_id = "registry-exe-1" failing_stage = _MockStage(name="DATA_PURITY", decision=ComplianceDecision.ERROR) svc = ComplianceExecutionService(repository=repo, config_manager=config, stages=[failing_stage]) result = svc.execute_run(candidate_id="cand-exe-1", requested_by="tester") assert result.run.status == RunStatus.SUCCEEDED.value assert result.run.final_status == CheckFinalStatus.FAILED.value # #endregion test_execute_run_stage_failure # #region test_execute_run_policy_resolution_error [C:2] [TYPE Function] @patch("src.services.clean_release.compliance_execution_service.resolve_trusted_policy_snapshots") def test_execute_run_policy_resolution_error(self, mock_resolve): """Policy resolution failure → ComplianceRunError.""" from src.services.clean_release.exceptions import PolicyResolutionError repo = _make_repo_with_candidate() mock_resolve.side_effect = PolicyResolutionError("policy missing") config = MagicMock() svc = ComplianceExecutionService(repository=repo, config_manager=config) with pytest.raises(ComplianceRunError, match="policy missing"): svc.execute_run(candidate_id="cand-exe-1", requested_by="tester") # #endregion test_execute_run_policy_resolution_error # #region test_execute_result_dataclass [C:2] [TYPE Function] def test_execute_result_dataclass(self): """ComplianceExecutionResult dataclass works as expected.""" run = MagicMock(spec=ComplianceRun) stage_runs = [MagicMock(spec=ComplianceStageRun)] violations = [MagicMock(spec=ComplianceViolation)] result = ComplianceExecutionResult( run=run, report=None, stage_runs=stage_runs, violations=violations, ) assert result.run == run assert result.report is None assert result.stage_runs == stage_runs assert result.violations == violations # #endregion test_execute_result_dataclass # #region test_execute_run_stage_raises_exception [C:2] [TYPE Function] @patch("src.services.clean_release.compliance_execution_service.resolve_trusted_policy_snapshots") def test_execute_run_stage_raises_exception(self, mock_resolve): """Stage that raises an exception triggers the except handler (lines 211-217).""" repo = _make_repo_with_candidate() mock_resolve.return_value = ( repo.policies["policy-exe-1"], repo.registries["registry-exe-1"], ) config = MagicMock() config.get_config.return_value.settings.clean_release.active_policy_id = "policy-exe-1" config.get_config.return_value.settings.clean_release.active_registry_id = "registry-exe-1" class CrashingStage(ComplianceStage): stage_name = ComplianceStageName("DATA_PURITY") def execute(self, context): raise RuntimeError("stage crashed unexpectedly") svc = ComplianceExecutionService(repository=repo, config_manager=config, stages=[CrashingStage()]) result = svc.execute_run(candidate_id="cand-exe-1", requested_by="tester") assert result.run.status == RunStatus.FAILED.value assert result.run.final_status == ComplianceDecision.ERROR.value assert "stage crashed" in result.run.failure_reason # #endregion test_execute_run_stage_raises_exception