from unittest.mock import MagicMock, patch import pytest from fastapi import HTTPException, Request from free_claude_code.api.dependencies import ( get_services, get_settings, require_proxy_auth, resolve_provider, ) from free_claude_code.api.ports import ApiServices from free_claude_code.application.errors import ApplicationUnavailableError from free_claude_code.application.ports import RequestRuntimeLease from free_claude_code.config.settings import Settings from tests.api.support import create_test_app def _request(*, headers: dict[str, str], token: str) -> tuple[Request, Settings]: request = Request( { "type": "http", "method": "GET", "path": "/", "headers": [ (key.lower().encode(), value.encode()) for key, value in headers.items() ], } ) settings = Settings.model_construct(anthropic_auth_token=token) return request, settings def _lease(*, provider=None, error: Exception | None = None): lease = MagicMock(spec=RequestRuntimeLease) lease.is_provider_cached.return_value = False if error is None: lease.resolve_provider.return_value = provider or MagicMock() else: lease.resolve_provider.side_effect = error return lease def test_get_services_reads_the_single_app_state_boundary() -> None: app = create_test_app() request = Request({"type": "http", "app": app}) services = get_services(request) assert services is app.state.services assert isinstance(services, ApiServices) def test_get_settings_reads_current_request_runtime_settings() -> None: app = create_test_app( Settings.model_construct( model="deepseek/test-model", anthropic_auth_token="", ) ) assert get_settings(app.state.services).model == "deepseek/test-model" def test_resolve_provider_uses_retained_lease_and_logs_first_initialization() -> None: provider = MagicMock() lease = _lease(provider=provider) with patch("free_claude_code.api.dependencies.logger.info") as log_info: result = resolve_provider("nvidia_nim", lease=lease) assert result is provider lease.resolve_provider.assert_called_once_with("nvidia_nim") log_info.assert_called_once_with("Provider initialized: {}", "nvidia_nim") def test_resolve_provider_skips_initialization_log_for_cached_provider() -> None: lease = _lease() lease.is_provider_cached.return_value = True with patch("free_claude_code.api.dependencies.logger.info") as log_info: resolve_provider("nvidia_nim", lease=lease) log_info.assert_not_called() def test_resolve_provider_missing_key_preserves_readiness_error() -> None: lease = _lease( error=ApplicationUnavailableError( "OPENROUTER_API_KEY is required. Get one at https://openrouter.ai" ) ) with pytest.raises(ApplicationUnavailableError) as exc_info: resolve_provider("open_router", lease=lease) assert exc_info.value.status_code == 503 assert "OPENROUTER_API_KEY" in exc_info.value.message assert "openrouter.ai" in exc_info.value.message def test_resolve_provider_unrelated_error_is_not_reclassified() -> None: lease = _lease(error=ValueError("unrelated config")) with pytest.raises(ValueError, match="unrelated config"): resolve_provider("nvidia_nim", lease=lease) def test_require_proxy_auth_allows_when_no_token_configured(): request, settings = _request(headers={}, token="") require_proxy_auth(request, settings) def test_require_proxy_auth_rejects_missing_authorization(): request, settings = _request(headers={}, token="secret") with pytest.raises(HTTPException) as exc_info: require_proxy_auth(request, settings) assert exc_info.value.status_code == 401 assert exc_info.value.detail == "Missing proxy authentication token" @pytest.mark.parametrize("header_name", ["x-api-key", "anthropic-auth-token"]) def test_require_proxy_auth_rejects_legacy_header_only(header_name: str): request, settings = _request(headers={header_name: "secret"}, token="secret") with pytest.raises(HTTPException) as exc_info: require_proxy_auth(request, settings) assert exc_info.value.status_code == 401 assert exc_info.value.detail == "Missing proxy authentication token" def test_require_proxy_auth_accepts_exact_bearer_token(): request, settings = _request( headers={"authorization": "bEaReR secret"}, token="secret", ) require_proxy_auth(request, settings) def test_require_proxy_auth_accepts_colons_in_configured_token(): request, settings = _request( headers={"authorization": "Bearer secret:with:colons"}, token="secret:with:colons", ) require_proxy_auth(request, settings) def test_require_proxy_auth_accepts_valid_bearer_with_conflicting_legacy_headers(): request, settings = _request( headers={ "authorization": "Bearer secret", "x-api-key": "wrong", "anthropic-auth-token": "also-wrong", }, token="secret", ) require_proxy_auth(request, settings) @pytest.mark.parametrize( "authorization", [ "secret", "Basic secret", "Bearer", "Bearer wrong", "Bearer secret:claude-sonnet", ], ) def test_require_proxy_auth_rejects_malformed_or_invalid_authorization( authorization: str, ): request, settings = _request( headers={"authorization": authorization}, token="secret", ) with pytest.raises(HTTPException) as exc_info: require_proxy_auth(request, settings) assert exc_info.value.status_code == 401 assert exc_info.value.detail == "Invalid proxy authentication token" def test_require_proxy_auth_rejects_invalid_bearer_when_legacy_header_matches(): request, settings = _request( headers={"authorization": "Bearer wrong", "x-api-key": "secret"}, token="secret", ) with pytest.raises(HTTPException) as exc_info: require_proxy_auth(request, settings) assert exc_info.value.status_code == 401 assert exc_info.value.detail == "Invalid proxy authentication token"