Livrare LOT 1 - Didi
This commit is contained in:
commit
5380c3fc63
990 changed files with 133308 additions and 0 deletions
224
ai_platform/modules/llm-inference/tests/test_auth.py
Normal file
224
ai_platform/modules/llm-inference/tests/test_auth.py
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
"""Tests for API authentication."""
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from llm_inference.api.app import create_app
|
||||
from llm_inference.api.dependencies import init_concurrency_limiter
|
||||
from llm_inference.client import LLMClient
|
||||
from llm_inference.config import LLMSettings, SettingsCache
|
||||
|
||||
|
||||
class TestAuthDisabled:
|
||||
"""Tests when authentication is disabled (no tokens configured)."""
|
||||
|
||||
@pytest.fixture
|
||||
def app_client_no_auth(self, test_settings: LLMSettings) -> TestClient:
|
||||
"""Create test client with auth disabled."""
|
||||
# test_settings has api_tokens=None by default
|
||||
SettingsCache.set(test_settings)
|
||||
init_concurrency_limiter(test_settings.max_concurrent_completions)
|
||||
app = create_app()
|
||||
app.state.settings = test_settings
|
||||
app.state.client = LLMClient(settings=test_settings)
|
||||
return TestClient(app)
|
||||
|
||||
def test_models_accessible_without_token(
|
||||
self, app_client_no_auth: TestClient
|
||||
) -> None:
|
||||
"""Test that models endpoint works without token when auth disabled."""
|
||||
response = app_client_no_auth.get("/v1/models")
|
||||
assert response.status_code != 401
|
||||
|
||||
def test_backends_accessible_without_token(
|
||||
self, app_client_no_auth: TestClient
|
||||
) -> None:
|
||||
"""Test that backends endpoint works without token when auth disabled."""
|
||||
response = app_client_no_auth.get("/v1/backends")
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_health_accessible(self, app_client_no_auth: TestClient) -> None:
|
||||
"""Test health endpoint accessible."""
|
||||
response = app_client_no_auth.get("/health")
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_ready_accessible(self, app_client_no_auth: TestClient) -> None:
|
||||
"""Test ready endpoint accessible."""
|
||||
response = app_client_no_auth.get("/ready")
|
||||
# May return 200 or 503 depending on backend health, but not 401
|
||||
assert response.status_code != 401
|
||||
|
||||
|
||||
class TestAuthEnabled:
|
||||
"""Tests when authentication is enabled."""
|
||||
|
||||
@pytest.fixture
|
||||
def auth_settings(self, test_settings: LLMSettings) -> LLMSettings:
|
||||
"""Create settings with auth enabled."""
|
||||
return LLMSettings(
|
||||
default_backend=test_settings.default_backend,
|
||||
enable_vllm=test_settings.enable_vllm,
|
||||
enable_llamacpp=test_settings.enable_llamacpp,
|
||||
external_url=test_settings.external_url,
|
||||
openrouter_api_key=test_settings.openrouter_api_key,
|
||||
api_tokens=frozenset(["test-token-1", "test-token-2"]),
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def app_client_auth(self, auth_settings: LLMSettings) -> TestClient:
|
||||
"""Create test client with auth enabled."""
|
||||
SettingsCache.set(auth_settings)
|
||||
init_concurrency_limiter(auth_settings.max_concurrent_completions)
|
||||
app = create_app()
|
||||
app.state.settings = auth_settings
|
||||
app.state.client = LLMClient(settings=auth_settings)
|
||||
return TestClient(app)
|
||||
|
||||
def test_missing_token_returns_401(self, app_client_auth: TestClient) -> None:
|
||||
"""Test 401 returned when token missing."""
|
||||
response = app_client_auth.get("/v1/models")
|
||||
assert response.status_code == 401
|
||||
assert "WWW-Authenticate" in response.headers
|
||||
assert response.headers["WWW-Authenticate"] == "Bearer"
|
||||
data = response.json()
|
||||
assert data["detail"]["error"] == "Authentication required"
|
||||
|
||||
def test_invalid_token_returns_401(self, app_client_auth: TestClient) -> None:
|
||||
"""Test 401 returned for invalid token."""
|
||||
response = app_client_auth.get(
|
||||
"/v1/models",
|
||||
headers={"Authorization": "Bearer invalid-token"},
|
||||
)
|
||||
assert response.status_code == 401
|
||||
data = response.json()
|
||||
assert data["detail"]["error"] == "Authentication failed"
|
||||
|
||||
def test_valid_token_allows_access(self, app_client_auth: TestClient) -> None:
|
||||
"""Test valid token grants access."""
|
||||
response = app_client_auth.get(
|
||||
"/v1/models",
|
||||
headers={"Authorization": "Bearer test-token-1"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_second_valid_token_works(self, app_client_auth: TestClient) -> None:
|
||||
"""Test second token also works."""
|
||||
response = app_client_auth.get(
|
||||
"/v1/models",
|
||||
headers={"Authorization": "Bearer test-token-2"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_malformed_header_missing_bearer(self, app_client_auth: TestClient) -> None:
|
||||
"""Test malformed Authorization header without Bearer prefix."""
|
||||
response = app_client_auth.get(
|
||||
"/v1/models",
|
||||
headers={"Authorization": "test-token-1"},
|
||||
)
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_malformed_header_wrong_prefix(self, app_client_auth: TestClient) -> None:
|
||||
"""Test Authorization header with wrong prefix."""
|
||||
response = app_client_auth.get(
|
||||
"/v1/models",
|
||||
headers={"Authorization": "Basic test-token-1"},
|
||||
)
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_health_excluded_from_auth(self, app_client_auth: TestClient) -> None:
|
||||
"""Test /health accessible without token even when auth enabled."""
|
||||
response = app_client_auth.get("/health")
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_ready_excluded_from_auth(self, app_client_auth: TestClient) -> None:
|
||||
"""Test /ready accessible without token even when auth enabled."""
|
||||
response = app_client_auth.get("/ready")
|
||||
# May return 200 or 503 depending on backend health, but not 401
|
||||
assert response.status_code != 401
|
||||
|
||||
def test_backends_requires_auth(self, app_client_auth: TestClient) -> None:
|
||||
"""Test /v1/backends requires authentication."""
|
||||
response = app_client_auth.get("/v1/backends")
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_backends_with_valid_token(self, app_client_auth: TestClient) -> None:
|
||||
"""Test /v1/backends works with valid token."""
|
||||
response = app_client_auth.get(
|
||||
"/v1/backends",
|
||||
headers={"Authorization": "Bearer test-token-1"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
class TestTokenParsing:
|
||||
"""Tests for token configuration parsing."""
|
||||
|
||||
def test_comma_separated_tokens(self) -> None:
|
||||
"""Test parsing comma-separated tokens."""
|
||||
settings = LLMSettings(
|
||||
default_backend="litellm",
|
||||
enable_vllm=False,
|
||||
enable_llamacpp=False,
|
||||
external_url="http://localhost:8100",
|
||||
api_tokens="token1,token2,token3",
|
||||
)
|
||||
assert settings.api_tokens == frozenset(["token1", "token2", "token3"])
|
||||
assert settings.auth_enabled is True
|
||||
|
||||
def test_empty_string_disables_auth(self) -> None:
|
||||
"""Test empty string results in auth disabled."""
|
||||
settings = LLMSettings(
|
||||
default_backend="litellm",
|
||||
enable_vllm=False,
|
||||
enable_llamacpp=False,
|
||||
external_url="http://localhost:8100",
|
||||
api_tokens="",
|
||||
)
|
||||
assert settings.api_tokens is None
|
||||
assert settings.auth_enabled is False
|
||||
|
||||
def test_whitespace_tokens_stripped(self) -> None:
|
||||
"""Test whitespace around tokens is stripped."""
|
||||
settings = LLMSettings(
|
||||
default_backend="litellm",
|
||||
enable_vllm=False,
|
||||
enable_llamacpp=False,
|
||||
external_url="http://localhost:8100",
|
||||
api_tokens=" token1 , token2 , token3 ",
|
||||
)
|
||||
assert settings.api_tokens == frozenset(["token1", "token2", "token3"])
|
||||
|
||||
def test_none_disables_auth(self) -> None:
|
||||
"""Test None results in auth disabled."""
|
||||
settings = LLMSettings(
|
||||
default_backend="litellm",
|
||||
enable_vllm=False,
|
||||
enable_llamacpp=False,
|
||||
external_url="http://localhost:8100",
|
||||
)
|
||||
assert settings.api_tokens is None
|
||||
assert settings.auth_enabled is False
|
||||
|
||||
def test_single_token(self) -> None:
|
||||
"""Test single token without comma."""
|
||||
settings = LLMSettings(
|
||||
default_backend="litellm",
|
||||
enable_vllm=False,
|
||||
enable_llamacpp=False,
|
||||
external_url="http://localhost:8100",
|
||||
api_tokens="single-token",
|
||||
)
|
||||
assert settings.api_tokens == frozenset(["single-token"])
|
||||
assert settings.auth_enabled is True
|
||||
|
||||
def test_whitespace_only_disables_auth(self) -> None:
|
||||
"""Test whitespace-only string results in auth disabled."""
|
||||
settings = LLMSettings(
|
||||
default_backend="litellm",
|
||||
enable_vllm=False,
|
||||
enable_llamacpp=False,
|
||||
external_url="http://localhost:8100",
|
||||
api_tokens=" , , ",
|
||||
)
|
||||
assert settings.api_tokens is None
|
||||
assert settings.auth_enabled is False
|
||||
Loading…
Add table
Add a link
Reference in a new issue