Files
rail-game/backend/tests/test_stations_service.py
zwitschi 615b63ba76
Some checks failed
Backend CI / lint-and-test (push) Failing after 37s
Add unit tests for station service and enhance documentation
- Introduced unit tests for the station service, covering creation, updating, and archiving of stations.
- Added detailed building block view documentation outlining the architecture of the Rail Game system.
- Created runtime view documentation illustrating key user interactions and system behavior.
- Developed concepts documentation detailing domain models, architectural patterns, and security considerations.
- Updated architecture documentation to reference new detailed sections for building block and runtime views.
2025-10-11 18:52:25 +02:00

176 lines
5.2 KiB
Python

from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Dict, List, cast
from uuid import UUID, uuid4
import pytest
from geoalchemy2.elements import WKTElement
from sqlalchemy.orm import Session
from backend.app.models import StationCreate, StationUpdate
from backend.app.services import stations as stations_service
@dataclass
class DummySession:
flushed: bool = False
committed: bool = False
refreshed: List[object] = field(default_factory=list)
def flush(self) -> None:
self.flushed = True
def refresh(self, instance: object) -> None: # pragma: no cover - simple setter
self.refreshed.append(instance)
def commit(self) -> None:
self.committed = True
@dataclass
class DummyStation:
id: UUID
name: str
location: WKTElement
osm_id: str | None
code: str | None
elevation_m: float | None
is_active: bool
created_at: datetime
updated_at: datetime
class DummyStationRepository:
_store: Dict[UUID, DummyStation] = {}
def __init__(self, session: DummySession) -> None: # pragma: no cover - simple init
self.session = session
@staticmethod
def _point(latitude: float, longitude: float) -> WKTElement:
return WKTElement(f"POINT({longitude} {latitude})", srid=4326)
def list(self) -> list[DummyStation]:
return list(self._store.values())
def list_active(self) -> list[DummyStation]:
return [station for station in self._store.values() if station.is_active]
def get(self, identifier: UUID) -> DummyStation | None:
return self._store.get(identifier)
def create(self, payload: StationCreate) -> DummyStation:
station = DummyStation(
id=uuid4(),
name=payload.name,
location=self._point(payload.latitude, payload.longitude),
osm_id=payload.osm_id,
code=payload.code,
elevation_m=payload.elevation_m,
is_active=payload.is_active,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
)
self._store[station.id] = station
return station
@pytest.fixture(autouse=True)
def reset_store(monkeypatch: pytest.MonkeyPatch) -> None:
DummyStationRepository._store = {}
monkeypatch.setattr(stations_service, "StationRepository", DummyStationRepository)
def test_create_station_persists_and_returns_model(
monkeypatch: pytest.MonkeyPatch,
) -> None:
session = DummySession()
payload = StationCreate(
name="Central",
latitude=52.52,
longitude=13.405,
osm_id="123",
code="BER",
elevation_m=34.5,
is_active=True,
)
result = stations_service.create_station(cast(Session, session), payload)
assert session.flushed is True
assert session.committed is True
assert result.name == "Central"
assert result.latitude == pytest.approx(52.52)
assert result.longitude == pytest.approx(13.405)
assert result.osm_id == "123"
def test_update_station_updates_geometry_and_metadata() -> None:
session = DummySession()
station_id = uuid4()
DummyStationRepository._store[station_id] = DummyStation(
id=station_id,
name="Old Name",
location=DummyStationRepository._point(50.0, 8.0),
osm_id=None,
code=None,
elevation_m=None,
is_active=True,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
)
payload = StationUpdate(name="New Name", latitude=51.0, longitude=9.0)
result = stations_service.update_station(
cast(Session, session), str(station_id), payload
)
assert result.name == "New Name"
assert result.latitude == pytest.approx(51.0)
assert result.longitude == pytest.approx(9.0)
assert DummyStationRepository._store[station_id].name == "New Name"
def test_update_station_requires_both_coordinates() -> None:
session = DummySession()
station_id = uuid4()
DummyStationRepository._store[station_id] = DummyStation(
id=station_id,
name="Station",
location=DummyStationRepository._point(50.0, 8.0),
osm_id=None,
code=None,
elevation_m=None,
is_active=True,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
)
with pytest.raises(ValueError):
stations_service.update_station(
cast(Session, session), str(station_id), StationUpdate(latitude=51.0)
)
def test_archive_station_marks_inactive() -> None:
session = DummySession()
station_id = uuid4()
DummyStationRepository._store[station_id] = DummyStation(
id=station_id,
name="Station",
location=DummyStationRepository._point(50.0, 8.0),
osm_id=None,
code=None,
elevation_m=None,
is_active=True,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
)
result = stations_service.archive_station(cast(Session, session), str(station_id))
assert result.is_active is False
assert DummyStationRepository._store[station_id].is_active is False