"""Gemeinsame Test-Vorbereitung. Jeder Test bekommt eine eigene, frische SQLite-Datei in einem temporären Verzeichnis - keine geteilten Zustände zwischen Tests, und die echte Datenbank unter data/ wird nie angefasst. """ import os TEST_PASSWORT = "test-passwort-123" # MUSS vor dem Import von app.main stehen: dort wird die SessionMiddleware # beim Import aufgebaut und liest cookie_nur_https genau einmal. Käme das # erst als Fixture, bliebe das Cookie auf "Secure" - und der TestClient # spricht über http://testserver, verwürfe es also stillschweigend. Die # Anmeldung schlüge dann ohne erkennbaren Grund fehl. os.environ.setdefault("COOKIE_HTTPS_ONLY", "false") os.environ.setdefault("ADMIN_PASSWORD", TEST_PASSWORT) os.environ.setdefault("SECRET_KEY", "nur-fuer-tests") # bcrypt ist absichtlich langsam. Bei rund 160 Hash-Vorgängen über # alle Tests kostete der Standardaufwand knapp eine Minute; hier # geht es um die Ablauflogik, nicht um die Stärke des Hashes. os.environ.setdefault("BCRYPT_ROUNDS", "4") import pytest # noqa: E402 from fastapi.testclient import TestClient # noqa: E402 from sqlalchemy.orm import sessionmaker # noqa: E402 from app import security # noqa: E402 from app.config import einstellungen # noqa: E402 from app.database import Basis, engine_erzeugen, get_db # noqa: E402 from app.main import app # noqa: E402 from app.seed import stammdaten_anlegen # noqa: E402 @pytest.fixture(autouse=True) def admin_eingerichtet(monkeypatch): """Setzt ein Passwort für die Tests und leert die Zwischenspeicher. autouse, weil sonst jeder Schreibzugriff mit 503 ("nicht eingerichtet") scheitern würde - das ist das gewollte Verhalten ohne Passwort. """ monkeypatch.setenv("ADMIN_PASSWORD", TEST_PASSWORT) monkeypatch.setenv("COOKIE_HTTPS_ONLY", "false") einstellungen.cache_clear() security.hash_zwischenspeicher_leeren() # Rate-Limits zwischen Tests zurücksetzen, sonst schlägt der zehnte # Anmeldeversuch über alle Tests hinweg zu. security.limiter.reset() yield einstellungen.cache_clear() security.hash_zwischenspeicher_leeren() @pytest.fixture(autouse=True) def upload_ordner(tmp_path, monkeypatch): """Jeder Test lädt in ein eigenes Verzeichnis hoch. Ohne das schrieben die Tests in das echte uploads/ und liessen dort ihre Bilder liegen. autouse, damit das auch für Tests gilt, die den Ordner nicht selbst anfordern. """ ziel = tmp_path / "uploads" ziel.mkdir() monkeypatch.setenv("UPLOAD_PATH", str(ziel)) einstellungen.cache_clear() yield ziel einstellungen.cache_clear() @pytest.fixture def db_sitzung(tmp_path): engine = engine_erzeugen(f"sqlite:///{tmp_path/'test.sqlite'}") Basis.metadata.create_all(engine) Sitzung = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False) with Sitzung() as db: stammdaten_anlegen(db) yield db engine.dispose() @pytest.fixture def gast(db_sitzung): """Nicht angemeldeter Besucher - sieht die Galerie, darf reservieren. Bewusst ohne "with": nur dann startet Starlette den Lifespan, und der würde die echte Datenbank unter data/ anlegen und befüllen. Die Stammdaten stehen in der Testdatenbank ohnehin schon (db_sitzung). """ def _db(): yield db_sitzung app.dependency_overrides[get_db] = _db yield TestClient(app) app.dependency_overrides.clear() @pytest.fixture def client(gast): """Angemeldeter Betreiber - darf alles. Die meisten Tests brauchen das, weil Anlegen und Ändern seit Phase 3 eine Anmeldung voraussetzen. """ antwort = gast.post("/api/v1/auth/login", json={"passwort": TEST_PASSWORT}) assert antwort.status_code == 200, antwort.text return gast @pytest.fixture def kategorie_id(gast): return gast.get("/api/v1/categories").json()[0]["id"]