from __future__ import annotations import unittest from typing import Iterable from fastapi import HTTPException from starlette.requests import Request from govoplan_access.backend.auth.dependencies import _extract_token, _requires_csrf, _resolve_legacy_principal_ref from govoplan_core.settings import settings def request_for(*, method: str = "GET", headers: Iterable[tuple[str, str]] = ()) -> Request: return Request( { "type": "http", "method": method, "path": "/", "headers": [(key.lower().encode("latin-1"), value.encode("latin-1")) for key, value in headers], } ) class AuthDependencyTests(unittest.TestCase): def test_extract_token_prefers_explicit_api_key(self) -> None: request = request_for(headers=[("authorization", "Bearer session-token")]) self.assertEqual(_extract_token(request, "Bearer session-token", "api-key-token"), ("api-key-token", "api_key")) def test_extract_token_supports_bearer_and_cookie_sources(self) -> None: self.assertEqual(_extract_token(request_for(), "Bearer session-token", None), ("session-token", "bearer")) cookie_request = request_for(headers=[("cookie", f"{settings.auth_session_cookie_name}=cookie-token")]) self.assertEqual(_extract_token(cookie_request, None, None), ("cookie-token", "cookie")) def test_requires_csrf_only_for_mutating_methods(self) -> None: self.assertFalse(_requires_csrf(request_for(method="GET"))) self.assertTrue(_requires_csrf(request_for(method="POST"))) def test_legacy_principal_resolver_rejects_missing_token_before_db_lookup(self) -> None: with self.assertRaises(HTTPException) as raised: _resolve_legacy_principal_ref(request_for(), None, authorization=None, x_api_key=None) # type: ignore[arg-type] self.assertEqual(raised.exception.status_code, 401) self.assertEqual(raised.exception.detail, "Missing API key or session token") if __name__ == "__main__": unittest.main()