feat: add Playwright configuration and initial e2e test for authentication
- Created Playwright configuration file to set up testing environment. - Added a new e2e test for user authentication in login.spec.ts. - Updated tsconfig.node.json to include playwright.config.ts. - Enhanced vite.config.ts to include API proxying for backend integration. - Added a placeholder for last run test results in .last-run.json.
This commit is contained in:
@@ -1,17 +1,40 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, List
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, List, cast
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from backend.app.models import StationCreate, TrackCreate, TrainCreate
|
||||
from backend.app.repositories import StationRepository, TrackRepository, TrainRepository
|
||||
from backend.app.db.models import TrainSchedule, User
|
||||
from backend.app.db.unit_of_work import SqlAlchemyUnitOfWork
|
||||
from backend.app.models import (
|
||||
StationCreate,
|
||||
TrackCreate,
|
||||
TrainCreate,
|
||||
TrainScheduleCreate,
|
||||
UserCreate,
|
||||
)
|
||||
from backend.app.repositories import (
|
||||
StationRepository,
|
||||
TrackRepository,
|
||||
TrainRepository,
|
||||
TrainScheduleRepository,
|
||||
UserRepository,
|
||||
)
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
|
||||
@dataclass
|
||||
class DummySession:
|
||||
added: List[Any] = field(default_factory=list)
|
||||
scalars_result: List[Any] = field(default_factory=list)
|
||||
scalar_result: Any = None
|
||||
statements: List[Any] = field(default_factory=list)
|
||||
committed: bool = False
|
||||
rolled_back: bool = False
|
||||
closed: bool = False
|
||||
|
||||
def add(self, instance: Any) -> None:
|
||||
self.added.append(instance)
|
||||
@@ -19,12 +42,26 @@ class DummySession:
|
||||
def add_all(self, instances: list[Any]) -> None:
|
||||
self.added.extend(instances)
|
||||
|
||||
def scalars(self, _statement: Any) -> list[Any]: # pragma: no cover - not used here
|
||||
return []
|
||||
def scalars(self, statement: Any) -> list[Any]:
|
||||
self.statements.append(statement)
|
||||
return list(self.scalars_result)
|
||||
|
||||
def scalar(self, statement: Any) -> Any:
|
||||
self.statements.append(statement)
|
||||
return self.scalar_result
|
||||
|
||||
def flush(self, _objects: list[Any] | None = None) -> None: # pragma: no cover - optional
|
||||
return None
|
||||
|
||||
def commit(self) -> None: # pragma: no cover - optional
|
||||
self.committed = True
|
||||
|
||||
def rollback(self) -> None: # pragma: no cover - optional
|
||||
self.rolled_back = True
|
||||
|
||||
def close(self) -> None: # pragma: no cover - optional
|
||||
self.closed = True
|
||||
|
||||
|
||||
def test_station_repository_create_generates_geometry() -> None:
|
||||
session = DummySession()
|
||||
@@ -104,3 +141,96 @@ def test_train_repository_create_supports_optional_ids() -> None:
|
||||
assert train.designation == "ICE 123"
|
||||
assert str(train.home_station_id).endswith("1")
|
||||
assert train.operator_id is None
|
||||
|
||||
|
||||
def test_user_repository_create_persists_user() -> None:
|
||||
session = DummySession()
|
||||
repo = UserRepository(session) # type: ignore[arg-type]
|
||||
|
||||
user = repo.create(
|
||||
UserCreate(
|
||||
username="demo",
|
||||
password_hash="hashed",
|
||||
email="demo@example.com",
|
||||
full_name="Demo Engineer",
|
||||
role="admin",
|
||||
)
|
||||
)
|
||||
|
||||
assert session.added and session.added[0] is user
|
||||
assert user.username == "demo"
|
||||
assert user.role == "admin"
|
||||
|
||||
|
||||
def test_user_repository_get_by_username_is_case_insensitive() -> None:
|
||||
existing = User(username="Demo", password_hash="hashed", role="player")
|
||||
session = DummySession(scalar_result=existing)
|
||||
repo = UserRepository(session) # type: ignore[arg-type]
|
||||
|
||||
result = repo.get_by_username("demo")
|
||||
|
||||
assert result is existing
|
||||
assert session.statements
|
||||
|
||||
|
||||
def test_train_schedule_repository_create_converts_identifiers() -> None:
|
||||
session = DummySession()
|
||||
repo = TrainScheduleRepository(session) # type: ignore[arg-type]
|
||||
train_id = uuid4()
|
||||
station_id = uuid4()
|
||||
|
||||
schedule = repo.create(
|
||||
TrainScheduleCreate(
|
||||
train_id=str(train_id),
|
||||
station_id=str(station_id),
|
||||
sequence_index=1,
|
||||
scheduled_arrival=datetime.now(timezone.utc),
|
||||
dwell_seconds=90,
|
||||
)
|
||||
)
|
||||
|
||||
assert session.added and session.added[0] is schedule
|
||||
assert schedule.train_id == train_id
|
||||
assert schedule.station_id == station_id
|
||||
|
||||
|
||||
def test_train_schedule_repository_list_for_train_orders_results() -> None:
|
||||
train_id = uuid4()
|
||||
schedules = [
|
||||
TrainSchedule(train_id=train_id, station_id=uuid4(), sequence_index=2),
|
||||
TrainSchedule(train_id=train_id, station_id=uuid4(), sequence_index=1),
|
||||
]
|
||||
session = DummySession(scalars_result=schedules)
|
||||
repo = TrainScheduleRepository(session) # type: ignore[arg-type]
|
||||
|
||||
result = repo.list_for_train(train_id)
|
||||
|
||||
assert result == schedules
|
||||
statement = session.statements[-1]
|
||||
assert getattr(statement, "_order_by_clauses", ())
|
||||
|
||||
|
||||
def test_unit_of_work_commits_and_closes_session() -> None:
|
||||
session = DummySession()
|
||||
uow = SqlAlchemyUnitOfWork(lambda: cast(Session, session))
|
||||
|
||||
with uow as active:
|
||||
active.users.create(
|
||||
UserCreate(username="demo", password_hash="hashed")
|
||||
)
|
||||
active.commit()
|
||||
|
||||
assert session.committed
|
||||
assert session.closed
|
||||
|
||||
|
||||
def test_unit_of_work_rolls_back_on_exception() -> None:
|
||||
session = DummySession()
|
||||
uow = SqlAlchemyUnitOfWork(lambda: cast(Session, session))
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
with uow:
|
||||
raise RuntimeError("boom")
|
||||
|
||||
assert session.rolled_back
|
||||
assert session.closed
|
||||
|
||||
Reference in New Issue
Block a user