# #region Test.CleanRelease.Facade [C:3] [TYPE Module] [SEMANTICS test,clean-release,facade,orchestration] # @BRIEF Tests for CleanReleaseFacade — policy/registry resolution, candidate overview, list candidates. # @RELATION BINDS_TO -> [clean_release_facade] from datetime import datetime from unittest.mock import MagicMock, PropertyMock import pytest from src.models.clean_release import CleanPolicySnapshot, SourceRegistrySnapshot from src.services.clean_release.dto import CandidateOverviewDTO from src.services.clean_release.enums import CandidateStatus, ComplianceDecision, RunStatus from src.services.clean_release.facade import CleanReleaseFacade from src.services.clean_release.repositories import ( ApprovalRepository, ArtifactRepository, AuditRepository, CandidateRepository, ComplianceRepository, ManifestRepository, PolicyRepository, PublicationRepository, ReportRepository, ) @pytest.fixture def mock_repos(): """Create mock repositories and config manager.""" return { "candidate_repo": MagicMock(spec=CandidateRepository), "artifact_repo": MagicMock(spec=ArtifactRepository), "manifest_repo": MagicMock(spec=ManifestRepository), "policy_repo": MagicMock(spec=PolicyRepository), "compliance_repo": MagicMock(spec=ComplianceRepository), "report_repo": MagicMock(spec=ReportRepository), "approval_repo": MagicMock(spec=ApprovalRepository), "publication_repo": MagicMock(spec=PublicationRepository), "audit_repo": MagicMock(spec=AuditRepository), "config_manager": MagicMock(), } @pytest.fixture def facade(mock_repos): """Create CleanReleaseFacade with mock repos.""" return CleanReleaseFacade(**mock_repos) class TestResolveActivePolicySnapshot: """resolve_active_policy_snapshot — policy resolution from config.""" # #region test_resolve_policy_found [C:2] [TYPE Function] def test_resolve_policy_found(self, facade, mock_repos): """Active policy_id found → returns snapshot.""" mock_repos["config_manager"].get_config.return_value.settings.clean_release.active_policy_id = "policy-1" mock_repos["policy_repo"].get_policy_snapshot.return_value = MagicMock(spec=CleanPolicySnapshot) result = facade.resolve_active_policy_snapshot() assert result is not None mock_repos["policy_repo"].get_policy_snapshot.assert_called_with("policy-1") # #endregion test_resolve_policy_found # #region test_resolve_policy_missing_id [C:2] [TYPE Function] def test_resolve_policy_missing_id(self, facade, mock_repos): """No active_policy_id → returns None.""" mock_repos["config_manager"].get_config.return_value.settings.clean_release.active_policy_id = None result = facade.resolve_active_policy_snapshot() assert result is None # #endregion test_resolve_policy_missing_id class TestResolveActiveRegistrySnapshot: """resolve_active_registry_snapshot — registry resolution from config.""" # #region test_resolve_registry_found [C:2] [TYPE Function] def test_resolve_registry_found(self, facade, mock_repos): """Active registry_id found → returns snapshot.""" mock_repos["config_manager"].get_config.return_value.settings.clean_release.active_registry_id = "reg-1" mock_repos["policy_repo"].get_registry_snapshot.return_value = MagicMock(spec=SourceRegistrySnapshot) result = facade.resolve_active_registry_snapshot() assert result is not None mock_repos["policy_repo"].get_registry_snapshot.assert_called_with("reg-1") # #endregion test_resolve_registry_found # #region test_resolve_registry_missing_id [C:2] [TYPE Function] def test_resolve_registry_missing_id(self, facade, mock_repos): """No active_registry_id → returns None.""" mock_repos["config_manager"].get_config.return_value.settings.clean_release.active_registry_id = None result = facade.resolve_active_registry_snapshot() assert result is None # #endregion test_resolve_registry_missing_id class TestGetCandidateOverview: """get_candidate_overview — comprehensive candidate overview.""" def _make_candidate(self, **kwargs): """Create a candidate mock with proper attribute values.""" c = MagicMock() for k, v in kwargs.items(): setattr(c, k, v) return c # #region test_get_overview_missing_candidate [C:2] [TYPE Function] def test_get_overview_missing_candidate(self, facade, mock_repos): """Candidate not found → returns None.""" mock_repos["candidate_repo"].get_by_id.return_value = None result = facade.get_candidate_overview("nonexistent") assert result is None # #endregion test_get_overview_missing_candidate # #region test_get_overview_full [C:2] [TYPE Function] def test_get_overview_full(self, facade, mock_repos): """Full candidate overview with all data.""" candidate = self._make_candidate( id="cand-1", version="1.0.0", source_snapshot_ref="git:sha", status=CandidateStatus.APPROVED.value, ) manifest = self._make_candidate( id="man-1", manifest_digest="digest", ) run = self._make_candidate( id="run-1", status=RunStatus.SUCCEEDED.value, ) report = self._make_candidate( id="CCR-1", final_status=ComplianceDecision.PASSED.value, ) approval = self._make_candidate(decision="APPROVED") publication = self._make_candidate(id="pub-1", status="ACTIVE") policy = MagicMock(spec=CleanPolicySnapshot) policy.id = "policy-1" policy.policy_version = "1.0" registry = MagicMock(spec=SourceRegistrySnapshot) registry.id = "reg-1" registry.registry_version = "1.0" mock_repos["candidate_repo"].get_by_id.return_value = candidate mock_repos["manifest_repo"].get_latest_for_candidate.return_value = manifest mock_repos["compliance_repo"].list_runs_by_candidate.return_value = [run] mock_repos["report_repo"].get_by_run.return_value = report mock_repos["approval_repo"].get_latest_for_candidate.return_value = approval mock_repos["publication_repo"].get_latest_for_candidate.return_value = publication mock_repos["config_manager"].get_config.return_value.settings.clean_release.active_policy_id = "policy-1" mock_repos["config_manager"].get_config.return_value.settings.clean_release.active_registry_id = "reg-1" mock_repos["policy_repo"].get_policy_snapshot.return_value = policy mock_repos["policy_repo"].get_registry_snapshot.return_value = registry result = facade.get_candidate_overview("cand-1") assert isinstance(result, CandidateOverviewDTO) assert result.candidate_id == "cand-1" assert result.latest_manifest_id == "man-1" assert result.latest_run_status == RunStatus.SUCCEEDED assert result.latest_report_final_status == ComplianceDecision.PASSED assert result.latest_approval_decision == "APPROVED" # #endregion test_get_overview_full # #region test_get_overview_no_runs [C:2] [TYPE Function] def test_get_overview_no_runs(self, facade, mock_repos): """Candidate without runs → report/run fields are None.""" candidate = self._make_candidate( id="cand-1", version="1.0.0", source_snapshot_ref="git:sha", status=CandidateStatus.DRAFT.value, ) mock_repos["candidate_repo"].get_by_id.return_value = candidate mock_repos["manifest_repo"].get_latest_for_candidate.return_value = None mock_repos["compliance_repo"].list_runs_by_candidate.return_value = [] mock_repos["approval_repo"].get_latest_for_candidate.return_value = None mock_repos["publication_repo"].get_latest_for_candidate.return_value = None mock_repos["config_manager"].get_config.return_value.settings.clean_release.active_policy_id = None mock_repos["config_manager"].get_config.return_value.settings.clean_release.active_registry_id = None result = facade.get_candidate_overview("cand-1") assert result.latest_manifest_id is None assert result.latest_run_id is None assert result.latest_report_id is None assert result.latest_approval_decision is None # #endregion test_get_overview_no_runs class TestListCandidates: """list_candidates — list all candidates with overviews.""" # #region test_list_candidates_empty [C:2] [TYPE Function] def test_list_candidates_empty(self, facade, mock_repos): """No candidates → returns empty list.""" mock_repos["candidate_repo"].list_all.return_value = [] result = facade.list_candidates() assert result == [] # #endregion test_list_candidates_empty # #region test_list_candidates_with_data [C:2] [TYPE Function] def test_list_candidates_with_data(self, facade, mock_repos): """Candidates exist → returns list of overviews.""" candidate = MagicMock() candidate.id = "cand-1" candidate.version = "1.0.0" candidate.source_snapshot_ref = "ref" candidate.status = CandidateStatus.DRAFT.value mock_repos["candidate_repo"].list_all.return_value = [candidate] mock_repos["candidate_repo"].get_by_id.return_value = candidate mock_repos["manifest_repo"].get_latest_for_candidate.return_value = None mock_repos["compliance_repo"].list_runs_by_candidate.return_value = [] mock_repos["approval_repo"].get_latest_for_candidate.return_value = None mock_repos["publication_repo"].get_latest_for_candidate.return_value = None mock_repos["config_manager"].get_config.return_value.settings.clean_release.active_policy_id = None mock_repos["config_manager"].get_config.return_value.settings.clean_release.active_registry_id = None result = facade.list_candidates() assert len(result) == 1 assert result[0].candidate_id == "cand-1" # #endregion test_list_candidates_with_data