"""Fixtures de test : base SQLite temporaire, isolee du volume de production.""" from __future__ import annotations import os import tempfile from collections.abc import Iterator import pytest # Doit etre defini AVANT le premier import de l'application. _TMP_DIR = tempfile.mkdtemp(prefix="openmind-tests-") os.environ["DATABASE_URL"] = f"sqlite:///{_TMP_DIR}/test.db" os.environ["STATIC_DIR"] = f"{_TMP_DIR}/static" os.environ["APP_NAME"] = "OpenMind" os.environ["MAX_NODES"] = "5000" os.environ["MAX_MAP_SIZE_MB"] = "10" # Les limites de debit sont desactivees par defaut dans les tests : chaque test # qui les concerne les active explicitement via la fixture `configure`. os.environ["SHARE_CREATE_RATE_LIMIT"] = "0" os.environ["SHARE_READ_RATE_LIMIT"] = "0" os.environ["SHARE_WRITE_RATE_LIMIT"] = "0" from fastapi.testclient import TestClient # noqa: E402 from sqlalchemy import text # noqa: E402 from app.config import get_settings # noqa: E402 from app.db.base import Base # noqa: E402 from app.db.migrations import run_migrations # noqa: E402 from app.db.session import engine # noqa: E402 from app.main import app # noqa: E402 from app.services.rate_limit import limiter # noqa: E402 @pytest.fixture(autouse=True) def _fresh_database() -> Iterator[None]: """Base neuve avant chaque test, construite par les vraies migrations. On passe deliberement par Alembic plutot que par `create_all` : le schema teste est exactement celui qu'obtiendra une installation reelle. """ get_settings.cache_clear() Base.metadata.drop_all(bind=engine) with engine.begin() as connection: connection.execute(text("DROP TABLE IF EXISTS alembic_version")) run_migrations(engine) # Les compteurs de debit vivent dans le processus : sans cette remise a # zero, les tests se limiteraient les uns les autres. limiter.reset() yield get_settings.cache_clear() @pytest.fixture() def configure(monkeypatch: pytest.MonkeyPatch): # type: ignore[no-untyped-def] """Surcharge des reglages le temps d'un test. Les valeurs sont posees dans l'environnement puis le cache de `get_settings` est vide, exactement comme au demarrage de l'application. """ def _apply(**values: object) -> None: for key, value in values.items(): monkeypatch.setenv(key.upper(), str(value)) get_settings.cache_clear() yield _apply get_settings.cache_clear() @pytest.fixture() def client() -> Iterator[TestClient]: with TestClient(app) as test_client: yield test_client def build_map(node_count: int = 3, name: str = "Carte de test") -> dict: """Construit un document carte minimal mais valide.""" nodes = [ { "id": "root", "type": "root", "text": name, "position": {"x": 0, "y": 0}, "parentId": None, } ] edges = [] for index in range(1, node_count): node_id = f"n{index}" nodes.append( { "id": node_id, "type": "standard", "text": f"Noeud {index}", "position": {"x": 200, "y": index * 80}, "parentId": "root", } ) edges.append({"id": f"e-root-{node_id}", "source": "root", "target": node_id}) return { "version": 1, "id": "11111111-1111-4111-8111-111111111111", "name": name, "createdAt": "2026-01-01T00:00:00.000Z", "updatedAt": "2026-01-01T00:00:00.000Z", "nodes": nodes, "edges": edges, "viewport": {"x": 0, "y": 0, "zoom": 1}, "settings": {"theme": "default", "layout": "mindmap-right", "branchStyle": "bezier"}, } @pytest.fixture() def created_share(client: TestClient) -> dict: response = client.post("/api/shares", json={"title": "Carte de test", "map": build_map()}) assert response.status_code == 201, response.text return response.json()