# #region TestPolicyResolutionService [C:2] [TYPE Module] # @SEMANTICS: clean-release, policy-resolution, trusted-snapshots, contracts # @PURPOSE: Verify trusted policy snapshot resolution contract and error guards. # @LAYER Tests # @RELATION DEPENDS_ON -> [EXT:frontend:policy_resolution_service] # @RELATION DEPENDS_ON -> [EXT:frontend:repository] # @RELATION DEPENDS_ON -> [clean_release_exceptions] # @INVARIANT: Resolution uses only ConfigManager active IDs and rejects runtime override attempts. from __future__ import annotations import pytest from types import SimpleNamespace from src.models.clean_release import CleanPolicySnapshot, SourceRegistrySnapshot from src.services.clean_release.exceptions import PolicyResolutionError from src.services.clean_release.policy_resolution_service import ( resolve_trusted_policy_snapshots, ) from src.services.clean_release.repository import CleanReleaseRepository # #region _config_manager [C:2] [TYPE Function] # @RELATION BINDS_TO -> [EXT:frontend:TestPolicyResolutionService] # @PURPOSE: Build deterministic ConfigManager-like stub for tests. # @INVARIANT: Only settings.clean_release.active_policy_id and active_registry_id are populated; any other settings field access raises AttributeError. # @PRE: policy_id and registry_id may be None or non-empty strings. # @POST: Returns object exposing get_config().settings.clean_release active IDs. def _config_manager(policy_id, registry_id): clean_release = SimpleNamespace( active_policy_id=policy_id, active_registry_id=registry_id ) settings = SimpleNamespace(clean_release=clean_release) config = SimpleNamespace(settings=settings) return SimpleNamespace(get_config=lambda: config) # #endregion _config_manager # #region test_resolve_trusted_policy_snapshots_missing_profile [C:2] [TYPE Function] # @RELATION BINDS_TO -> [EXT:frontend:TestPolicyResolutionService] # @PURPOSE: Ensure resolution fails when trusted profile is not configured. # @PRE: active_policy_id is None. # @POST: Raises PolicyResolutionError with missing trusted profile reason. def test_resolve_trusted_policy_snapshots_missing_profile(): repository = CleanReleaseRepository() config_manager = _config_manager(policy_id=None, registry_id="registry-1") with pytest.raises(PolicyResolutionError, match="missing trusted profile"): resolve_trusted_policy_snapshots( config_manager=config_manager, repository=repository, ) # #endregion test_resolve_trusted_policy_snapshots_missing_profile # #region test_resolve_trusted_policy_snapshots_missing_registry [C:2] [TYPE Function] # @RELATION BINDS_TO -> [EXT:frontend:TestPolicyResolutionService] # @PURPOSE: Ensure resolution fails when trusted registry is not configured. # @PRE: active_registry_id is None and active_policy_id is set. # @POST: Raises PolicyResolutionError with missing trusted registry reason. def test_resolve_trusted_policy_snapshots_missing_registry(): repository = CleanReleaseRepository() config_manager = _config_manager(policy_id="policy-1", registry_id=None) with pytest.raises(PolicyResolutionError, match="missing trusted registry"): resolve_trusted_policy_snapshots( config_manager=config_manager, repository=repository, ) # #endregion test_resolve_trusted_policy_snapshots_missing_registry # #region test_resolve_trusted_policy_snapshots_rejects_override_attempt [C:2] [TYPE Function] # @RELATION BINDS_TO -> [EXT:frontend:TestPolicyResolutionService] # @PURPOSE: Ensure runtime override attempt is rejected even if snapshots exist. # @PRE: valid trusted snapshots exist in repository and override is provided. # @POST: Raises PolicyResolutionError with override forbidden reason. def test_resolve_trusted_policy_snapshots_rejects_override_attempt(): repository = CleanReleaseRepository() repository.save_policy( CleanPolicySnapshot( id="policy-1", policy_id="baseline", policy_version="1.0.0", content_json={"rules": []}, registry_snapshot_id="registry-1", immutable=True, ) ) repository.save_registry( SourceRegistrySnapshot( id="registry-1", registry_id="trusted", registry_version="1.0.0", allowed_hosts=["internal.local"], allowed_schemes=["https"], allowed_source_types=["repo"], immutable=True, ) ) config_manager = _config_manager(policy_id="policy-1", registry_id="registry-1") with pytest.raises(PolicyResolutionError, match="override attempt is forbidden"): resolve_trusted_policy_snapshots( config_manager=config_manager, repository=repository, policy_id_override="policy-override", ) # #endregion test_resolve_trusted_policy_snapshots_rejects_override_attempt # #region test_missing_clean_release_settings [C:2] [TYPE Function] def test_missing_clean_release_settings(): """clean_release_settings missing → PolicyResolutionError (line 44).""" repository = CleanReleaseRepository() # settings has no clean_release attribute config = SimpleNamespace(settings=SimpleNamespace()) config_manager = SimpleNamespace(get_config=lambda: config) with pytest.raises(PolicyResolutionError, match="clean_release settings are missing"): resolve_trusted_policy_snapshots( config_manager=config_manager, repository=repository, ) # #endregion test_missing_clean_release_settings # #region test_policy_not_found [C:2] [TYPE Function] def test_policy_not_found(): """get_policy returns None → PolicyResolutionError (line 60).""" repository = CleanReleaseRepository() config_manager = _config_manager(policy_id="policy-missing", registry_id="registry-1") with pytest.raises(PolicyResolutionError, match="trusted policy snapshot.*not found"): resolve_trusted_policy_snapshots( config_manager=config_manager, repository=repository, ) # #endregion test_policy_not_found # #region test_registry_not_found [C:2] [TYPE Function] def test_registry_not_found(): """get_registry returns None → PolicyResolutionError (line 66).""" repository = CleanReleaseRepository() repository.save_policy( CleanPolicySnapshot( id="policy-1", policy_id="policy-1", policy_version="1.0.0", content_json={"rules": []}, registry_snapshot_id="registry-missing", immutable=True, ) ) config_manager = _config_manager(policy_id="policy-1", registry_id="registry-missing") with pytest.raises(PolicyResolutionError, match="trusted registry snapshot.*not found"): resolve_trusted_policy_snapshots( config_manager=config_manager, repository=repository, ) # #endregion test_registry_not_found # #region test_policy_not_immutable [C:2] [TYPE Function] def test_policy_not_immutable(): """Policy snapshot not immutable → PolicyResolutionError (line 71).""" repository = CleanReleaseRepository() repository.save_policy( CleanPolicySnapshot( id="policy-1", policy_id="policy-1", policy_version="1.0.0", content_json={"rules": []}, registry_snapshot_id="registry-1", immutable=False, # not immutable ) ) repository.save_registry( SourceRegistrySnapshot( id="registry-1", registry_id="registry-1", registry_version="1.0.0", allowed_hosts=["internal.local"], allowed_schemes=["https"], allowed_source_types=["repo"], immutable=True, ) ) config_manager = _config_manager(policy_id="policy-1", registry_id="registry-1") with pytest.raises(PolicyResolutionError, match="policy snapshot must be immutable"): resolve_trusted_policy_snapshots( config_manager=config_manager, repository=repository, ) # #endregion test_policy_not_immutable # #region test_registry_not_immutable [C:2] [TYPE Function] def test_registry_not_immutable(): """Registry snapshot not immutable → PolicyResolutionError (line 73).""" repository = CleanReleaseRepository() repository.save_policy( CleanPolicySnapshot( id="policy-1", policy_id="policy-1", policy_version="1.0.0", content_json={"rules": []}, registry_snapshot_id="registry-1", immutable=True, ) ) repository.save_registry( SourceRegistrySnapshot( id="registry-1", registry_id="registry-1", registry_version="1.0.0", allowed_hosts=["internal.local"], allowed_schemes=["https"], allowed_source_types=["repo"], immutable=False, # not immutable ) ) config_manager = _config_manager(policy_id="policy-1", registry_id="registry-1") with pytest.raises(PolicyResolutionError, match="registry snapshot must be immutable"): resolve_trusted_policy_snapshots( config_manager=config_manager, repository=repository, ) # #endregion test_registry_not_immutable # #region test_full_success_path [C:2] [TYPE Function] def test_full_success_path(): """All valid inputs → returns (policy, registry) tuple (line 75).""" repository = CleanReleaseRepository() policy = CleanPolicySnapshot( id="policy-1", policy_id="policy-1", policy_version="1.0.0", content_json={"rules": ["allow-all"]}, registry_snapshot_id="registry-1", immutable=True, ) registry = SourceRegistrySnapshot( id="registry-1", registry_id="registry-1", registry_version="1.0.0", allowed_hosts=["internal.local"], allowed_schemes=["https"], allowed_source_types=["repo"], immutable=True, ) repository.save_policy(policy) repository.save_registry(registry) config_manager = _config_manager(policy_id="policy-1", registry_id="registry-1") policy_result, registry_result = resolve_trusted_policy_snapshots( config_manager=config_manager, repository=repository, ) assert policy_result.id == "policy-1" assert policy_result.immutable is True assert registry_result.id == "registry-1" assert registry_result.immutable is True # #endregion test_full_success_path # #endregion TestPolicyResolutionService