from __future__ import annotations from datetime import datetime, timezone from typing import Any import pytest from fastapi.testclient import TestClient from backend.app.api import tracks as tracks_api from backend.app.main import app from backend.app.models import CombinedTrackModel, TrackModel client = TestClient(app) def _track_model(track_id: str = "track-1") -> TrackModel: now = datetime.now(timezone.utc) return TrackModel( id=track_id, start_station_id="station-a", end_station_id="station-b", length_meters=None, max_speed_kph=None, status="planned", coordinates=[(52.5, 13.4), (52.6, 13.5)], is_bidirectional=True, created_at=now, updated_at=now, ) def _combined_model(track_id: str = "combined-1") -> CombinedTrackModel: now = datetime.now(timezone.utc) return CombinedTrackModel( id=track_id, start_station_id="station-a", end_station_id="station-b", length_meters=1000, max_speed_kph=120, status="operational", coordinates=[(52.5, 13.4), (52.6, 13.5)], constituent_track_ids=["track-1", "track-2"], is_bidirectional=True, created_at=now, updated_at=now, ) def _authenticate() -> str: response = client.post( "/api/auth/login", json={"username": "demo", "password": "railgame123"}, ) assert response.status_code == 200 return response.json()["accessToken"] def test_list_tracks(monkeypatch: pytest.MonkeyPatch) -> None: token = _authenticate() monkeypatch.setattr(tracks_api, "list_tracks", lambda db: [_track_model()]) response = client.get( "/api/tracks", headers={"Authorization": f"Bearer {token}"}, ) assert response.status_code == 200 payload = response.json() assert isinstance(payload, list) assert payload[0]["id"] == "track-1" def test_get_track_returns_404(monkeypatch: pytest.MonkeyPatch) -> None: token = _authenticate() monkeypatch.setattr(tracks_api, "get_track", lambda db, track_id: None) response = client.get( "/api/tracks/not-found", headers={"Authorization": f"Bearer {token}"}, ) assert response.status_code == 404 def test_create_track_calls_service(monkeypatch: pytest.MonkeyPatch) -> None: token = _authenticate() captured: dict[str, Any] = {} payload = { "startStationId": "station-a", "endStationId": "station-b", "coordinates": [[52.5, 13.4], [52.6, 13.5]], } def fake_create(db: Any, data: Any) -> TrackModel: assert data.start_station_id == "station-a" captured["payload"] = data return _track_model("track-new") monkeypatch.setattr(tracks_api, "create_track", fake_create) response = client.post( "/api/tracks", json=payload, headers={"Authorization": f"Bearer {token}"}, ) assert response.status_code == 201 body = response.json() assert body["id"] == "track-new" assert captured["payload"].end_station_id == "station-b" def test_delete_track_returns_404(monkeypatch: pytest.MonkeyPatch) -> None: token = _authenticate() monkeypatch.setattr( tracks_api, "delete_track", lambda db, tid, regenerate=False: False ) response = client.delete( "/api/tracks/missing", headers={"Authorization": f"Bearer {token}"}, ) assert response.status_code == 404 def test_delete_track_success(monkeypatch: pytest.MonkeyPatch) -> None: token = _authenticate() seen: dict[str, Any] = {} def fake_delete(db: Any, track_id: str, regenerate: bool = False) -> bool: seen["track_id"] = track_id seen["regenerate"] = regenerate return True monkeypatch.setattr(tracks_api, "delete_track", fake_delete) response = client.delete( "/api/tracks/track-99", params={"regenerate": "true"}, headers={"Authorization": f"Bearer {token}"}, ) assert response.status_code == 204 assert seen["track_id"] == "track-99" assert seen["regenerate"] is True def test_list_combined_tracks(monkeypatch: pytest.MonkeyPatch) -> None: token = _authenticate() monkeypatch.setattr( tracks_api, "list_combined_tracks", lambda db: [_combined_model()] ) response = client.get( "/api/tracks/combined", headers={"Authorization": f"Bearer {token}"}, ) assert response.status_code == 200 payload = response.json() assert len(payload) == 1 assert payload[0]["id"] == "combined-1"