Tests für auth.py: Token-Extraction, Auth-Enabled-Detection, _pick_best_title
This commit is contained in:
parent
07507de24a
commit
8bd311dbc8
107
tests/test_auth.py
Normal file
107
tests/test_auth.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
"""Tests for app/auth.py — Keycloak JWT authentication (#43).
|
||||||
|
|
||||||
|
These tests cover the auth module WITHOUT a running Keycloak server:
|
||||||
|
- Token extraction from headers/cookies
|
||||||
|
- Auth-disabled detection (Dev-Modus)
|
||||||
|
- _pick_best_title helper (in main.py, tested here for convenience)
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
# Stub jose if not installed locally
|
||||||
|
if "jose" not in sys.modules:
|
||||||
|
jose_stub = types.ModuleType("jose")
|
||||||
|
jose_jwt = types.ModuleType("jose.jwt")
|
||||||
|
jose_stub.jwt = jose_jwt
|
||||||
|
jose_stub.JWTError = Exception
|
||||||
|
jose_stub.ExpiredSignatureError = Exception
|
||||||
|
jose_jwt.get_unverified_header = lambda t: {"kid": "test"}
|
||||||
|
jose_jwt.decode = lambda *a, **kw: {"sub": "test", "email": "t@t.de"}
|
||||||
|
sys.modules["jose"] = jose_stub
|
||||||
|
sys.modules["jose.jwt"] = jose_jwt
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
from app.auth import _extract_token, _is_auth_enabled
|
||||||
|
|
||||||
|
|
||||||
|
class TestExtractToken:
|
||||||
|
def test_bearer_header(self):
|
||||||
|
req = MagicMock()
|
||||||
|
req.headers = {"authorization": "Bearer abc123"}
|
||||||
|
req.cookies = {}
|
||||||
|
assert _extract_token(req) == "abc123"
|
||||||
|
|
||||||
|
def test_cookie_fallback(self):
|
||||||
|
req = MagicMock()
|
||||||
|
req.headers = {}
|
||||||
|
req.cookies = {"access_token": "cookie_token"}
|
||||||
|
assert _extract_token(req) == "cookie_token"
|
||||||
|
|
||||||
|
def test_no_token(self):
|
||||||
|
req = MagicMock()
|
||||||
|
req.headers = {}
|
||||||
|
req.cookies = {}
|
||||||
|
assert _extract_token(req) is None
|
||||||
|
|
||||||
|
def test_non_bearer_header_ignored(self):
|
||||||
|
req = MagicMock()
|
||||||
|
req.headers = {"authorization": "Basic dXNlcjpwYXNz"}
|
||||||
|
req.cookies = {}
|
||||||
|
assert _extract_token(req) is None
|
||||||
|
|
||||||
|
|
||||||
|
class TestIsAuthEnabled:
|
||||||
|
def test_disabled_when_url_empty(self, monkeypatch):
|
||||||
|
from app import config
|
||||||
|
monkeypatch.setattr(config.settings, "keycloak_url", "")
|
||||||
|
monkeypatch.setattr(config.settings, "keycloak_realm", "test")
|
||||||
|
monkeypatch.setattr(config.settings, "keycloak_client_id", "test")
|
||||||
|
assert _is_auth_enabled() is False
|
||||||
|
|
||||||
|
def test_disabled_when_realm_empty(self, monkeypatch):
|
||||||
|
from app import config
|
||||||
|
monkeypatch.setattr(config.settings, "keycloak_url", "https://sso.test")
|
||||||
|
monkeypatch.setattr(config.settings, "keycloak_realm", "")
|
||||||
|
monkeypatch.setattr(config.settings, "keycloak_client_id", "test")
|
||||||
|
assert _is_auth_enabled() is False
|
||||||
|
|
||||||
|
def test_enabled_when_all_set(self, monkeypatch):
|
||||||
|
from app import config
|
||||||
|
monkeypatch.setattr(config.settings, "keycloak_url", "https://sso.test")
|
||||||
|
monkeypatch.setattr(config.settings, "keycloak_realm", "realm")
|
||||||
|
monkeypatch.setattr(config.settings, "keycloak_client_id", "client")
|
||||||
|
assert _is_auth_enabled() is True
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from app.main import _pick_best_title
|
||||||
|
_HAS_MAIN = True
|
||||||
|
except ImportError:
|
||||||
|
_HAS_MAIN = False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not _HAS_MAIN, reason="app.main not importable (missing slowapi/etc)")
|
||||||
|
class TestPickBestTitle:
|
||||||
|
"""Test _pick_best_title from main.py."""
|
||||||
|
|
||||||
|
def test_prefer_real_doc_title(self):
|
||||||
|
assert _pick_best_title(
|
||||||
|
"LLM-Titel", "Echte Antrag-Bezeichnung aus OPAL", "18/123"
|
||||||
|
) == "Echte Antrag-Bezeichnung aus OPAL"
|
||||||
|
|
||||||
|
def test_reject_generic_doc_title(self):
|
||||||
|
from app.main import _pick_best_title
|
||||||
|
result = _pick_best_title(
|
||||||
|
"Lehrkräfte stärken", "Drucksache 18/18085", "18/18085"
|
||||||
|
)
|
||||||
|
assert result == "Lehrkräfte stärken"
|
||||||
|
|
||||||
|
def test_fallback_to_llm_title(self):
|
||||||
|
assert _pick_best_title("LLM-Titel", None, "18/123") == "LLM-Titel"
|
||||||
|
|
||||||
|
def test_fallback_to_generic(self):
|
||||||
|
assert _pick_best_title("", None, "18/123") == "Drucksache 18/123"
|
||||||
|
|
||||||
|
def test_empty_doc_title_uses_llm(self):
|
||||||
|
assert _pick_best_title("Guter LLM-Titel", "", "18/123") == "Guter LLM-Titel"
|
||||||
Loading…
Reference in New Issue
Block a user