"""Gemeinsame pytest-Fixtures: App mit temporärer Datenbank, Test-Client, Run-Tokens.""" from __future__ import annotations import time from collections.abc import Callable, Iterator from pathlib import Path import pytest from flask import Flask from flask.testing import FlaskClient from itsdangerous.timed import TimestampSigner from app import create_app @pytest.fixture() def db_path(tmp_path: Path) -> Path: return tmp_path / "data" / "test.sqlite3" @pytest.fixture() def app(db_path: Path) -> Iterator[Flask]: app = create_app( { "TESTING": True, "SECRET_KEY": "test-secret-key", "DATABASE_PATH": str(db_path), "RATE_LIMIT_MAX_SUBMISSIONS": 5, "RATE_LIMIT_WINDOW_SECONDS": 600, } ) yield app @pytest.fixture() def client(app: Flask) -> FlaskClient: return app.test_client() @pytest.fixture() def issue_token(client: FlaskClient, monkeypatch: pytest.MonkeyPatch) -> Callable[..., str]: """Holt ein Run-Token über die API; ``started_ago`` simuliert eine bereits laufende Runde. Die Signatur enthält den Ausgabezeitpunkt. Um eine lange Runde zu simulieren, ohne wirklich zu warten, wird der Zeitstempel beim Signieren zurückdatiert. """ def _issue(started_ago: int = 0) -> str: original = TimestampSigner.get_timestamp monkeypatch.setattr( TimestampSigner, "get_timestamp", lambda _self: int(time.time()) - started_ago ) try: response = client.post("/api/runs") finally: monkeypatch.setattr(TimestampSigner, "get_timestamp", original) assert response.status_code == 201 return response.get_json()["run_token"] return _issue @pytest.fixture() def valid_payload(issue_token: Callable[..., str]) -> Callable[..., dict]: """Erzeugt eine gültige Einreichung; einzelne Felder lassen sich überschreiben.""" def _payload(**overrides: object) -> dict: payload: dict = { "name": "Kadettin Ada", "score": 1234, "wave": 3, "duration_s": 95, "run_token": issue_token(started_ago=120), } payload.update(overrides) return payload return _payload