Dateien
AegisSight-Monitor-Verwaltung/tests/test_api_smoke.py
claude-dev bcfdac158e feat(sources): Aufgaben-Posteingang ersetzt Quellen-Health und Klassifikation
- Neuer Unterreiter Aufgaben buendelt Health-Vorschlaege (Tabelle inkl.
  Loesung-suchen), Klassifikations-Review-Karten und den eingeklappten
  Verlauf (erledigte Vorschlaege + letzte Prueflaeufe). Die dritte
  Navigationsebene (healthSubTabs) entfaellt komplett.
- Badge zeigt die Summe aus offenen Vorschlaegen und ausstehenden
  Klassifikationen, gespeist vom neuen billigen GET /api/sources/tasks/summary
  (gleiche Bedingung wie /classification/stats, laeuft nie auseinander).
  Badge laedt jetzt schon beim Klick auf den Quellen-Hauptreiter.
- aufgaben.js NEU (uebernimmt Vorschlags-/Verlauf-Rendering aus
  source-health.js und den kompletten Klassifikations-Block aus sources.js),
  source-health.js GELOESCHT (60s-Cache, Checks-Tabelle mit eigener
  Pagination und Filtern entfaellt, die Details stecken jetzt als
  Ausklapp-Zeile in der Quellenliste).
- Quellen hat damit 3 statt 5 Unterreiter: Quellenliste, Aufgaben,
  X-Recherche-Konten.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 18:11:19 +00:00

99 Zeilen
3.8 KiB
Python

"""Smoke-Tests fuer alle API-Endpoints: Auth-Coverage.
Pruef, dass jeder geschuetzte Endpoint ohne Auth-Header 401/403 liefert -
verhindert, dass jemand versehentlich einen Endpoint ohne `get_current_admin`
schreibt und ihn oeffentlich macht.
"""
import pytest
from fastapi.testclient import TestClient
@pytest.fixture(scope="module")
def client():
from main import app
# raise_server_exceptions=False -> Exceptions werden als 500 ausgeliefert.
# Wir testen nur, dass Auth korrekt vor DB-Aufrufen greift.
return TestClient(app, raise_server_exceptions=False)
# (method, path, expected_status)
# Auth-geschuetzte Endpoints -> 401 (HTTPBearer ohne credentials wirft 403,
# aber FastAPI HTTPBearer auto_error=True liefert 403; wir akzeptieren beides).
AUTH_PROTECTED = [
("GET", "/api/orgs"),
("POST", "/api/orgs"),
("GET", "/api/orgs/1"),
("PUT", "/api/orgs/1"),
("DELETE", "/api/orgs/1"),
("GET", "/api/licenses"),
("POST", "/api/licenses"),
("PUT", "/api/licenses/1/revoke"),
("PUT", "/api/licenses/1/extend"),
("GET", "/api/licenses/expiring"),
("GET", "/api/users"),
("POST", "/api/users"),
("PUT", "/api/users/1/deactivate"),
("PUT", "/api/users/1/activate"),
("PUT", "/api/users/1/globe-access"),
("PUT", "/api/users/1/network-access"),
("PUT", "/api/users/1/role"),
("DELETE", "/api/users/1"),
("GET", "/api/dashboard/stats"),
("GET", "/api/sources/meta"),
("GET", "/api/sources/tasks/summary"),
("GET", "/api/sources"),
("POST", "/api/sources/global"),
("PUT", "/api/sources/global/1"),
("DELETE", "/api/sources/global/1"),
("GET", "/api/sources/stats"),
("GET", "/api/sources/languages"),
("GET", "/api/sources/1/health"),
("POST", "/api/sources/tenant/1/promote"),
("POST", "/api/sources/tenant/bulk-promote"),
("POST", "/api/sources/discover"),
("POST", "/api/sources/discover/add"),
("GET", "/api/sources/health"),
("GET", "/api/sources/suggestions"),
("PUT", "/api/sources/suggestions/1"),
("POST", "/api/sources/health/search-fix/1"),
("GET", "/api/statistik"),
("GET", "/api/token-usage/overview"),
("GET", "/api/token-usage/1"),
("GET", "/api/token-usage/1/current"),
("PUT", "/api/token-usage/budget/1"),
("GET", "/api/audit-log"),
("GET", "/api/audit-log/distinct"),
]
@pytest.mark.parametrize("method,path", AUTH_PROTECTED)
def test_endpoint_requires_auth(client, method, path):
"""Ohne Authorization-Header muss jeder Endpoint 401 oder 403 liefern."""
r = client.request(method, path, json={})
assert r.status_code in (401, 403), (
f"{method} {path} sollte 401/403 sein, war {r.status_code}: {r.text[:200]}"
)
def test_magic_link_endpoint_is_public(client):
"""/api/auth/magic-link ist absichtlich oeffentlich (sonst kann sich keiner einloggen)."""
r = client.post("/api/auth/magic-link", json={"email": "stranger@example.com"})
# Mit gueltigem JSON -> 200 generische Antwort, ohne Auth-Header.
# 200 erwartet (Anti-Enumeration), aber DB-Aufruf koennte mit /tmp/x.db failen ->
# akzeptieren wir auch 500. Wir wollen nur sicherstellen, dass NICHT 401/403 kommt.
assert r.status_code != 401 and r.status_code != 403
def test_verify_endpoint_is_public(client):
"""/api/auth/verify ist auch oeffentlich (ohne Token koennen wir keinen JWT haben)."""
r = client.post("/api/auth/verify", json={"token": "x" * 20})
assert r.status_code != 401 and r.status_code != 403
def test_static_routes_public(client):
"""/ und /dashboard liefern HTML ohne Auth (Frontend regelt Login-Redirect)."""
r = client.get("/")
assert r.status_code == 200
r = client.get("/dashboard")
assert r.status_code == 200