"""Tests for the AuthProvider protocol and the DI factory (U11).""" from __future__ import annotations import pytest from agentkit.server.auth.providers import ( AuthProvider, LocalAuthProvider, StubOIDCProvider, get_auth_provider, reset_auth_provider, ) from agentkit.server.auth.providers.base import AuthProvider as AuthProviderFromBase from agentkit.server.auth.providers.exceptions import ( AuthProviderError, InvalidCredentials, ProviderNotImplemented, ) class TestProtocolConformance: """The runtime_checkable Protocol accepts both real implementations.""" def test_local_passes_isinstance_check(self): provider = LocalAuthProvider() assert isinstance(provider, AuthProvider) assert isinstance(provider, AuthProviderFromBase) def test_stub_passes_isinstance_check(self): provider = StubOIDCProvider() assert isinstance(provider, AuthProvider) assert isinstance(provider, AuthProviderFromBase) class TestProviderNames: def test_local_provider_name(self): assert LocalAuthProvider.name == "local" def test_stub_oidc_provider_name(self): assert StubOIDCProvider.name == "oidc-stub" class TestDiFactory: """``get_auth_provider`` is a memoized singleton, env-driven.""" def setup_method(self): """Reset cache and env before each test.""" reset_auth_provider() self._saved_env = {} for key in ("AGENTKIT_AUTH_PROVIDER",): self._saved_env[key] = __import__("os").environ.pop(key, None) def teardown_method(self): for key, value in self._saved_env.items(): if value is not None: __import__("os").environ[key] = value reset_auth_provider() def test_default_provider_is_local(self): provider = get_auth_provider() assert isinstance(provider, LocalAuthProvider) assert provider.name == "local" def test_oidc_stub_provider(self, monkeypatch): monkeypatch.setenv("AGENTKIT_AUTH_PROVIDER", "oidc-stub") reset_auth_provider() provider = get_auth_provider() assert isinstance(provider, StubOIDCProvider) assert provider.name == "oidc-stub" def test_unknown_provider_raises_value_error(self, monkeypatch): monkeypatch.setenv("AGENTKIT_AUTH_PROVIDER", "ldap-unknown") reset_auth_provider() with pytest.raises(ValueError, match="unknown auth provider"): get_auth_provider() def test_factory_is_memoized(self): first = get_auth_provider() second = get_auth_provider() assert first is second def test_reset_clears_cache(self, monkeypatch): first = get_auth_provider() reset_auth_provider() monkeypatch.setenv("AGENTKIT_AUTH_PROVIDER", "oidc-stub") second = get_auth_provider() assert first is not second assert isinstance(second, StubOIDCProvider) class TestExceptionHierarchy: def test_invalid_credentials_inherits_auth_provider_error(self): assert issubclass(InvalidCredentials, AuthProviderError) assert issubclass(InvalidCredentials, Exception) def test_provider_not_implemented_inherits_auth_provider_error(self): assert issubclass(ProviderNotImplemented, AuthProviderError) assert issubclass(ProviderNotImplemented, Exception) def test_invalid_credentials_can_be_raised_and_caught(self): with pytest.raises(InvalidCredentials): raise InvalidCredentials("test message") def test_invalid_credentials_caught_as_base(self): """Route layer catches AuthProviderError to handle all provider errors.""" with pytest.raises(AuthProviderError): raise InvalidCredentials("test") def test_provider_not_implemented_caught_as_base(self): with pytest.raises(AuthProviderError): raise ProviderNotImplemented("test")