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