import httpx from qonto_assistant.contracts import QontoCredentials from qonto_assistant.qonto_client import QontoClient class StubCredentialProvider: def __init__(self) -> None: self.invalidated = False def get_credentials(self) -> QontoCredentials: return QontoCredentials(api_user="binky-user", api_key="top-secret") def invalidate(self) -> None: self.invalidated = True def test_client_sends_legacy_api_key_header_and_params() -> None: provider = StubCredentialProvider() seen: dict[str, object] = {} def handler(request: httpx.Request) -> httpx.Response: seen["auth"] = request.headers["Authorization"] seen["path"] = request.url.path seen["params"] = dict(request.url.params) return httpx.Response(200, json={"transactions": []}) client = QontoClient( base_url="https://example.test", organization_path="/v2/organization", transactions_path="/v2/transactions", auth_mode="legacy_api_key", timeout_seconds=1, max_retries=0, credential_provider=provider, transport=httpx.MockTransport(handler), ) client.list_transactions( iban="DE1234567890", page=2, page_size=25, window_days=14, status="completed", side="debit", ) assert seen["auth"] == "binky-user:top-secret" assert seen["path"] == "/v2/transactions" assert seen["params"] == { "iban": "DE1234567890", "current_page": "2", "per_page": "25", "window_days": "14", "status": "completed", "side": "debit", } def test_client_invalidates_cached_credentials_on_auth_failure() -> None: provider = StubCredentialProvider() def handler(_: httpx.Request) -> httpx.Response: return httpx.Response(401, json={"error": "unauthorized"}) client = QontoClient( base_url="https://example.test", organization_path="/v2/organization", transactions_path="/v2/transactions", auth_mode="legacy_api_key", timeout_seconds=1, max_retries=0, credential_provider=provider, transport=httpx.MockTransport(handler), ) try: client.get_organization() except Exception as exc: # noqa: BLE001 assert getattr(exc, "error_code", None) == "qonto_auth_failed" else: raise AssertionError("Expected Qonto auth failure") assert provider.invalidated is True