Spaces:
Running
Running
| """Unit tests for platform API credential registry and providers.""" | |
| from unittest.mock import AsyncMock, MagicMock, patch | |
| import pytest | |
| from app.core.crypto.credential_crypto import decrypt_secret, encrypt_secret, mask_secret | |
| from app.services.platform_api_registry import ( | |
| EffectiveCredential, | |
| build_credential_views, | |
| fetch_price_via_credential, | |
| resolve_platform_credential, | |
| ) | |
| class TestCredentialCrypto: | |
| def test_encrypt_decrypt_roundtrip(self): | |
| raw = "sk-test-api-key-12345" | |
| token = encrypt_secret(raw) | |
| assert decrypt_secret(token) == raw | |
| assert raw not in token | |
| def test_mask_secret(self): | |
| assert mask_secret("abcd") == "••••" | |
| assert mask_secret("my-long-api-key-9xyz") == "••••9xyz" | |
| class TestBuildCredentialViews: | |
| def test_no_row_no_env_shows_none_source(self): | |
| views = build_credential_views([]) | |
| amazon = next(v for v in views if v.platform_id == "amazon") | |
| assert amazon.source == "none" | |
| assert amazon.api_key_hint is None | |
| assert "rainforest" in amazon.available_providers | |
| def test_env_credential_shows_environment_source(self): | |
| with patch("app.services.platform_api_registry.get_settings") as gs: | |
| gs.return_value = MagicMock(RAINFOREST_API_KEY="rf-env-key-9999") | |
| views = build_credential_views([]) | |
| amazon = next(v for v in views if v.platform_id == "amazon") | |
| assert amazon.source == "environment" | |
| assert amazon.api_key_hint == "••••9999" | |
| assert amazon.is_enabled is True | |
| class TestResolvePlatformCredential: | |
| async def test_superadmin_db_takes_priority_over_env(self): | |
| row = MagicMock() | |
| row.platform_id = "amazon" | |
| row.provider = "rainforest" | |
| row.api_key_encrypted = encrypt_secret("db-key-wins") | |
| row.api_secret_encrypted = None | |
| row.is_enabled = True | |
| db = AsyncMock() | |
| repo = MagicMock() | |
| repo.get_enabled = AsyncMock(return_value=row) | |
| with patch( | |
| "app.services.platform_api_registry.PlatformApiCredentialRepository", | |
| return_value=repo, | |
| ), patch("app.services.platform_api_registry._env_credential") as env: | |
| env.return_value = EffectiveCredential( | |
| platform_id="amazon", | |
| provider="rainforest", | |
| api_key="env-key", | |
| api_secret=None, | |
| source="environment", | |
| ) | |
| cred = await resolve_platform_credential("amazon", db=db, redis=None) | |
| assert cred is not None | |
| assert cred.source == "superadmin" | |
| assert cred.api_key == "db-key-wins" | |
| async def test_falls_back_to_environment(self): | |
| db = AsyncMock() | |
| repo = MagicMock() | |
| repo.get_enabled = AsyncMock(return_value=None) | |
| with patch( | |
| "app.services.platform_api_registry.PlatformApiCredentialRepository", | |
| return_value=repo, | |
| ), patch("app.services.platform_api_registry._env_credential") as env: | |
| env.return_value = EffectiveCredential( | |
| platform_id="google_books", | |
| provider="google_books", | |
| api_key="gbooks-env", | |
| api_secret=None, | |
| source="environment", | |
| ) | |
| cred = await resolve_platform_credential("google_books", db=db, redis=None) | |
| assert cred is not None | |
| assert cred.source == "environment" | |
| class TestFetchPriceViaCredential: | |
| async def test_rainforest_parses_buybox_price(self): | |
| client = AsyncMock() | |
| response = MagicMock(status_code=200) | |
| response.json.return_value = { | |
| "product": { | |
| "buybox_winner": { | |
| "price": {"value": 15.99, "currency": "USD"}, | |
| }, | |
| }, | |
| } | |
| client.get.return_value = response | |
| cred = EffectiveCredential( | |
| platform_id="amazon", | |
| provider="rainforest", | |
| api_key="rf-key", | |
| api_secret=None, | |
| source="superadmin", | |
| ) | |
| hit = await fetch_price_via_credential( | |
| client, | |
| cred, | |
| "amazon", | |
| "https://www.amazon.com/dp/B012345678", | |
| ) | |
| assert hit is not None | |
| assert hit.formatted == "$15.99" | |
| assert hit.source == "api:rainforest" | |
| async def test_scrapingbee_delegates_to_html_parser(self): | |
| client = AsyncMock() | |
| response = MagicMock( | |
| status_code=200, | |
| text=('{"price": "12.50", "title": "Test Book"}' + " " * 600), | |
| ) | |
| client.get.return_value = response | |
| cred = EffectiveCredential( | |
| platform_id="barnes_noble", | |
| provider="scrapingbee", | |
| api_key="sb-key", | |
| api_secret=None, | |
| source="superadmin", | |
| ) | |
| hit = await fetch_price_via_credential( | |
| client, | |
| cred, | |
| "barnes_noble", | |
| "https://www.barnesandnoble.com/w/book", | |
| ) | |
| assert hit is not None | |
| assert hit.formatted == "$12.50" | |
| assert hit.source == "api:scrapingbee" | |