Files
kleiderboerse/tests/conftest.py
T
StefanandClaude Opus 5 3d7b7c706a Phase 1: Datenmodell, CRUD-API und Stammdaten
FastAPI mit SQLAlchemy und SQLite, Migrationen über Alembic. Die
Endpunkte aus projekt.md für items, categories und sizes stehen samt
Reservierung; Bilder und Anmeldung folgen in Phase 2 und 3.

Umgesetzt wie in Plan.md festgelegt:

- Feste Wertelisten als Enum UND als CHECK in der Datenbank. Die
  CHECK-Bedingung ist der eigentliche Schutz: an SQLAlchemy vorbei (Import,
  sqlite3 von Hand) käme sonst "Gril" durch, und die Filter griffen still
  nicht mehr.
- Status "draft" für den Stapel-Import. Entwürfe erscheinen weder in der
  Galerie noch in GET /items, solange nicht ausdrücklich status=draft
  angefragt wird, und lassen sich nicht reservieren.
- Titel ist freiwillig und wird sonst beim ANZEIGEN aus Kategorie und
  Grösse gebildet ("Jacken 98/104") - nicht beim Speichern, damit er einer
  späteren Korrektur der Grösse folgt.
- reservation_token: nur wer es hat, kann die eigene Reservierung aufheben.
  Verglichen mit compare_digest, gelöscht beim Freigeben und beim
  Erledigen, damit ein alter Link nicht später eine fremde Reservierung
  aufhebt.
- Eigene sizes-Tabelle mit sort_order statt SELECT DISTINCT: sonst stünde
  "104" vor "56" und jeder Tippfehler würde zur Filteroption.
- Pagination auf GET /items, in projekt.md nicht vorgesehen.

Zwei SQLite-Eigenheiten, die leicht untergehen: foreign_keys ist
standardmässig AUS (ohne PRAGMA greift ON DELETE CASCADE nicht), und
check_same_thread muss für FastAPI abgeschaltet werden. Beides in
database.py, dazu WAL fürs gleichzeitige Lesen.

24 Tests, alle grün. Zusätzlich von Hand geprüft: Migration anwenden,
Eintrag anlegen, reservieren, ohne Token freigeben (403), mit Token
freigeben (200), Swagger UI erreichbar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GR4bNaj9GtRu57J4Niii8o
2026-08-29 22:00:58 +02:00

46 lines
1.3 KiB
Python

"""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 pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import sessionmaker
from app.database import Basis, engine_erzeugen, get_db
from app.main import app
from app.seed import stammdaten_anlegen
@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 client(db_sitzung):
"""TestClient, der auf dieselbe Testdatenbank zeigt wie db_sitzung."""
def _db():
yield db_sitzung
app.dependency_overrides[get_db] = _db
# 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).
yield TestClient(app)
app.dependency_overrides.clear()
@pytest.fixture
def kategorie_id(client):
return client.get("/api/v1/categories").json()[0]["id"]