import pytest from unittest.mock import MagicMock, patch from apim_apps import normalize_env, EnvConfig, Manifest, OrgSpec, AppSpec, ApiAccess @pytest.fixture def env(): return EnvConfig(name="TEST", url="https://test.example.com:8075", username="u", password="p") @pytest.fixture def mock_client(): with patch("apim_apps.engine.AxwayClient") as MockClient: instance = MockClient.return_value instance.list_orgs.return_value = {} instance.list_apps.return_value = {} instance.list_app_api_access.return_value = [] yield instance def _manifest(orgs=None, apps=None): return Manifest(organizations=orgs or [], applications=apps or []) # --------------------------------------------------------------------------- # Dry-run # --------------------------------------------------------------------------- def test_dry_run_makes_no_api_calls(env, mock_client): manifest = _manifest( orgs=[OrgSpec(name="MyOrg")], apps=[AppSpec(name="MyApp", org_name="MyOrg", apis=[ApiAccess(api_name="MyAPI")])], ) mock_client.list_orgs.return_value = {} mock_client.list_apps.return_value = {} result = normalize_env(env, manifest, dry_run=True) mock_client.create_org.assert_not_called() mock_client.create_app.assert_not_called() mock_client.grant_api_access.assert_not_called() assert len(result.errors) == 0 def test_dry_run_reports_planned_actions(env, mock_client): manifest = _manifest( orgs=[OrgSpec(name="MyOrg")], apps=[AppSpec(name="MyApp", org_name="MyOrg")], ) result = normalize_env(env, manifest, dry_run=True) kinds = {a.kind for a in result.actions_done} assert "create_org" in kinds assert "create_app" in kinds # --------------------------------------------------------------------------- # Apply mode # --------------------------------------------------------------------------- def test_apply_creates_org_and_app(env, mock_client): manifest = _manifest( orgs=[OrgSpec(name="MyOrg")], apps=[AppSpec(name="MyApp", org_name="MyOrg")], ) mock_client.create_org.return_value = {"id": "org-1", "name": "MyOrg"} mock_client.create_app.return_value = {"id": "app-1", "name": "MyApp"} result = normalize_env(env, manifest, dry_run=False) mock_client.create_org.assert_called_once() mock_client.create_app.assert_called_once() assert len(result.errors) == 0 def test_apply_grants_api_subscription(env, mock_client): manifest = _manifest( orgs=[OrgSpec(name="MyOrg")], apps=[AppSpec(name="MyApp", org_name="MyOrg", apis=[ApiAccess(api_name="MyAPI")])], ) mock_client.create_org.return_value = {"id": "org-1", "name": "MyOrg"} mock_client.create_app.return_value = {"id": "app-1", "name": "MyApp"} mock_client.find_frontend_api.return_value = {"id": "api-1", "name": "MyAPI"} normalize_env(env, manifest, dry_run=False) mock_client.grant_api_access.assert_called_once_with("app-1", "api-1") def test_apply_skips_missing_frontend_api(env, mock_client): manifest = _manifest( orgs=[OrgSpec(name="MyOrg")], apps=[AppSpec(name="MyApp", org_name="MyOrg", apis=[ApiAccess(api_name="Ghost")])], ) mock_client.create_org.return_value = {"id": "org-1", "name": "MyOrg"} mock_client.create_app.return_value = {"id": "app-1", "name": "MyApp"} mock_client.find_frontend_api.return_value = None result = normalize_env(env, manifest, dry_run=False) mock_client.grant_api_access.assert_not_called() assert any("Ghost" in e for e in result.errors) # --------------------------------------------------------------------------- # Skip when already present # --------------------------------------------------------------------------- def test_skips_existing_org(env, mock_client): manifest = _manifest(orgs=[OrgSpec(name="MyOrg")]) mock_client.list_orgs.return_value = {"MyOrg": {"id": "org-1", "name": "MyOrg"}} result = normalize_env(env, manifest, dry_run=False) mock_client.create_org.assert_not_called() assert any(a.kind == "create_org" for a in result.actions_skipped) def test_skips_existing_app(env, mock_client): manifest = _manifest( orgs=[OrgSpec(name="MyOrg")], apps=[AppSpec(name="MyApp", org_name="MyOrg")], ) mock_client.list_orgs.return_value = {"MyOrg": {"id": "org-1", "name": "MyOrg"}} mock_client.list_apps.return_value = {("MyOrg", "MyApp"): {"id": "app-1", "name": "MyApp"}} result = normalize_env(env, manifest, dry_run=False) mock_client.create_app.assert_not_called() assert any(a.kind == "create_app" for a in result.actions_skipped) def test_skips_already_granted_api(env, mock_client): manifest = _manifest( orgs=[OrgSpec(name="MyOrg")], apps=[AppSpec(name="MyApp", org_name="MyOrg", apis=[ApiAccess(api_name="MyAPI")])], ) mock_client.list_orgs.return_value = {"MyOrg": {"id": "org-1", "name": "MyOrg"}} mock_client.list_apps.return_value = {("MyOrg", "MyApp"): {"id": "app-1", "name": "MyApp"}} mock_client.list_app_api_access.return_value = [{"apiName": "MyAPI", "id": "api-1"}] result = normalize_env(env, manifest, dry_run=False) mock_client.grant_api_access.assert_not_called() assert any(a.kind == "grant_api" for a in result.actions_skipped) # --------------------------------------------------------------------------- # Environment filtering # --------------------------------------------------------------------------- def test_env_filter_excludes_other_env(env, mock_client): manifest = _manifest( orgs=[ OrgSpec(name="GlobalOrg"), OrgSpec(name="ProdOnly", environments=["PROD"]), ] ) result = normalize_env(env, manifest, dry_run=True) org_names = [a.detail["org"] for a in result.actions_done + result.actions_skipped] assert "GlobalOrg" in org_names assert "ProdOnly" not in org_names def test_env_filter_matches_current_env(env, mock_client): manifest = _manifest( orgs=[OrgSpec(name="TestOnly", environments=["TEST"])] ) result = normalize_env(env, manifest, dry_run=True) org_names = [a.detail["org"] for a in result.actions_done + result.actions_skipped] assert "TestOnly" in org_names # --------------------------------------------------------------------------- # Error handling # --------------------------------------------------------------------------- def test_error_when_org_missing_for_app(env, mock_client): manifest = _manifest( apps=[AppSpec(name="Orphan", org_name="MissingOrg")] ) mock_client.list_orgs.return_value = {} result = normalize_env(env, manifest, dry_run=False) assert any("MissingOrg" in e for e in result.errors) def test_list_orgs_failure_returns_error(env, mock_client): import requests mock_client.list_orgs.side_effect = requests.ConnectionError("timeout") manifest = _manifest(orgs=[OrgSpec(name="MyOrg")]) result = normalize_env(env, manifest, dry_run=False) assert len(result.errors) == 1