import datetime as dt import pytest from fastapi.testclient import TestClient from sqlmodel import Session, SQLModel, create_engine, select from app import models # noqa: F401 (enregistre les tables sur SQLModel.metadata) from app.config import get_settings from app.db import get_session from app.main import app from app.models import DayPart, Edition @pytest.fixture(scope="session") def engine(): """Vraie base PostgreSQL de test (voir docker/postgres-init).""" eng = create_engine(get_settings().test_database_url, pool_pre_ping=True) SQLModel.metadata.drop_all(eng) SQLModel.metadata.create_all(eng) yield eng SQLModel.metadata.drop_all(eng) @pytest.fixture def session(engine): """Session isolée : chaque test s'exécute dans une transaction annulée à la fin, y compris ses `commit()` (savepoints).""" connection = engine.connect() transaction = connection.begin() s = Session(bind=connection, join_transaction_mode="create_savepoint") try: yield s finally: s.close() transaction.rollback() connection.close() @pytest.fixture def client(session): app.dependency_overrides[get_session] = lambda: session with TestClient(app) as c: yield c app.dependency_overrides.clear() @pytest.fixture def edition(session) -> Edition: """Édition de 4 jours avec 3 demi-journées paramétrées.""" ed = Edition( year=2026, name="Festival Test", start_date=dt.date(2026, 7, 9), end_date=dt.date(2026, 7, 12), retention_days=365, ) session.add(ed) session.commit() session.refresh(ed) session.add_all( [ DayPart( edition_id=ed.id, code="MORNING", label="Matin", start_time=dt.time(6, 0), end_time=dt.time(13, 0), order=0, ), DayPart( edition_id=ed.id, code="AFTERNOON", label="Après-midi", start_time=dt.time(13, 0), end_time=dt.time(19, 0), order=1, ), DayPart( edition_id=ed.id, code="EVENING", label="Soirée", start_time=dt.time(19, 0), end_time=dt.time(6, 0), order=2, ), ] ) session.commit() return ed @pytest.fixture def day_parts(session, edition) -> dict[str, DayPart]: parts = session.exec( select(DayPart).where(DayPart.edition_id == edition.id) ) return {p.code: p for p in parts}