import textwrap import pytest from unittest.mock import patch from apim_apps import load_manifest, AppCredential from apim_apps import normalize_env, EnvConfig, Manifest, OrgSpec, AppSpec # --------------------------------------------------------------------------- # Loader — parsing credentials from YAML # --------------------------------------------------------------------------- WITH_CREDENTIAL = textwrap.dedent("""\ organizations: [] applications: - name: "MyApp" organization: "MyOrg" credentials: - client_id: "ext-client-id" """) WITH_SECRET = textwrap.dedent("""\ organizations: [] applications: - name: "MyApp" organization: "MyOrg" credentials: - client_id: "ext-client-id" secret: "s3cr3t" """) MULTIPLE_CREDENTIALS = textwrap.dedent("""\ organizations: [] applications: - name: "MyApp" organization: "MyOrg" credentials: - client_id: "client-one" - client_id: "client-two" """) WITHOUT_CREDENTIAL = textwrap.dedent("""\ organizations: [] applications: - name: "MyApp" organization: "MyOrg" """) def test_credential_is_parsed(tmp_path): f = tmp_path / "apps.yaml" f.write_text(WITH_CREDENTIAL) m = load_manifest(str(f)) assert m.applications[0].credentials == [AppCredential(client_id="ext-client-id")] def test_credential_with_secret_parsed(tmp_path): f = tmp_path / "apps.yaml" f.write_text(WITH_SECRET) m = load_manifest(str(f)) assert m.applications[0].credentials == [AppCredential(client_id="ext-client-id", secret="s3cr3t")] def test_multiple_credentials_parsed(tmp_path): f = tmp_path / "apps.yaml" f.write_text(MULTIPLE_CREDENTIALS) m = load_manifest(str(f)) assert m.applications[0].credentials == [ AppCredential(client_id="client-one"), AppCredential(client_id="client-two"), ] def test_credentials_default_to_empty(tmp_path): f = tmp_path / "apps.yaml" f.write_text(WITHOUT_CREDENTIAL) m = load_manifest(str(f)) assert m.applications[0].credentials == [] # --------------------------------------------------------------------------- # Engine — credentials set on new apps only, never on existing or dry-run # --------------------------------------------------------------------------- @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 = {"MyOrg": {"id": "org-1", "name": "MyOrg"}} instance.list_apps.return_value = {} instance.list_app_api_access.return_value = [] instance.create_app.return_value = {"id": "app-1", "name": "MyApp"} yield instance def _app(**kwargs): return AppSpec(name="MyApp", org_name="MyOrg", **kwargs) def test_ext_credential_created_for_new_app(env, mock_client): manifest = Manifest( applications=[_app(credentials=[AppCredential(client_id="ext-id")])] ) result = normalize_env(env, manifest, dry_run=False) mock_client.create_app_ext_credential.assert_called_once_with("app-1", "ext-id", "") assert len(result.errors) == 0 def test_ext_credential_with_secret(env, mock_client): manifest = Manifest( applications=[_app(credentials=[AppCredential(client_id="ext-id", secret="s3cr3t")])] ) normalize_env(env, manifest, dry_run=False) mock_client.create_app_ext_credential.assert_called_once_with("app-1", "ext-id", "s3cr3t") def test_multiple_ext_credentials_created(env, mock_client): manifest = Manifest( applications=[_app(credentials=[ AppCredential(client_id="client-one"), AppCredential(client_id="client-two"), ])] ) normalize_env(env, manifest, dry_run=False) assert mock_client.create_app_ext_credential.call_count == 2 mock_client.create_app_ext_credential.assert_any_call("app-1", "client-one", "") mock_client.create_app_ext_credential.assert_any_call("app-1", "client-two", "") def test_no_ext_credential_when_credentials_absent(env, mock_client): manifest = Manifest(applications=[_app()]) normalize_env(env, manifest, dry_run=False) mock_client.create_app_ext_credential.assert_not_called() def test_ext_credential_not_set_on_existing_app(env, mock_client): mock_client.list_apps.return_value = {("MyOrg", "MyApp"): {"id": "app-1", "name": "MyApp"}} manifest = Manifest( applications=[_app(credentials=[AppCredential(client_id="ext-id")])] ) normalize_env(env, manifest, dry_run=False) mock_client.create_app.assert_not_called() mock_client.create_app_ext_credential.assert_not_called() def test_ext_credential_not_set_in_dry_run(env, mock_client): manifest = Manifest( applications=[_app(credentials=[AppCredential(client_id="ext-id")])] ) normalize_env(env, manifest, dry_run=True) mock_client.create_app_ext_credential.assert_not_called() def test_credential_error_recorded_in_result(env, mock_client): mock_client.create_app_ext_credential.side_effect = RuntimeError("API failure") manifest = Manifest( applications=[_app(credentials=[AppCredential(client_id="bad-id")])] ) result = normalize_env(env, manifest, dry_run=False) assert any("MyApp" in e for e in result.errors)