Split backend into tavolo-platform, tavolo-scopone and tavolo-app packages
Move the game-independent machinery (lobby, live-game store, websocket, deadline scheduler, match history, leaderboards) into a new tavolo-platform distribution behind a GameEngine contract, the scopone scientifico rules plus a platform adapter into tavolo-scopone, and keep only the composition root in tavolo-app. The three distributions share the tavolo namespace (PEP 420, kaya-style monorepo). Match history becomes fully generic: Match carries the engine's result JSON and MatchPlayer points/details instead of scopone-shaped team columns (migration 3 backfills existing rows). Lobby creation takes an opaque per-game options object and websocket actions dispatch to the session's engine. Tests: platform suite runs against a DummyEngine toy game, scopone keeps the rules tests plus new adapter tests, server/tests covers the wired stack end to end (194 tests, was 143).
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""Test helpers package."""
|
||||
"""Test helpers package for the tavolo-app integration suite."""
|
||||
from .asynctest import async_test
|
||||
from .oidc import make_user, oidc_user, ws_users
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""An ``async_test`` that also closes the app's Tortoise context.
|
||||
|
||||
``pwo.async_test`` runs every test in a fresh event loop (``asyncio.Runner``).
|
||||
The app's :class:`~tavolo.tortoise_mixin.TortoiseMixin` builds one
|
||||
The app's :class:`~tavolo.platform.tortoise_mixin.TortoiseMixin` builds one
|
||||
``TortoiseContext`` per loop, so without an explicit close each test orphans
|
||||
an aiosqlite connection whose non-daemon worker thread keeps the interpreter
|
||||
alive after the suite reports "OK".
|
||||
@@ -12,18 +12,21 @@ import asyncio
|
||||
from functools import wraps
|
||||
from typing import Any, Callable, Coroutine
|
||||
|
||||
from tavolo.app import tortoise_mixin
|
||||
from tavolo.app import scheduler, tortoise_mixin
|
||||
|
||||
|
||||
def async_test(coro: Callable[..., Coroutine[Any, Any, None]]) -> Callable[..., None]:
|
||||
"""Like ``pwo.async_test``, but close the Tortoise context afterwards."""
|
||||
"""Like ``pwo.async_test``, but close the Tortoise context and stop the
|
||||
deadline consumer afterwards."""
|
||||
|
||||
@wraps(coro)
|
||||
def wrapper(*args: Any, **kwargs: Any) -> None:
|
||||
async def run() -> None:
|
||||
loop = asyncio.get_running_loop()
|
||||
try:
|
||||
await coro(*args, **kwargs)
|
||||
finally:
|
||||
scheduler.stop_consumer(loop)
|
||||
await tortoise_mixin.aclose()
|
||||
|
||||
with asyncio.Runner() as runner:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Helpers for faking the OIDC authenticated user during tests.
|
||||
|
||||
HTTP handlers funnel through ``oidc_mixin.get_user``; websocket handlers
|
||||
through :func:`tavolo.auth.get_ws_user`. Patching those two entry points
|
||||
lets route and websocket tests run entirely in-process with no IdP.
|
||||
through :func:`tavolo.platform.auth.get_ws_user`. Patching those two entry
|
||||
points lets route and websocket tests run entirely in-process with no IdP.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -13,10 +13,9 @@ from typing import Iterator, Optional, Sequence
|
||||
from kaya.oidc import OIDCUser
|
||||
|
||||
# Import the app first: it pulls in the route modules, which import
|
||||
# ``tavolo.auth`` themselves. Importing ``auth`` before ``app`` would hit a
|
||||
# partially initialized module (same constraint as reimpasto).
|
||||
# ``tavolo.platform.auth`` themselves.
|
||||
from tavolo.app import oidc_mixin
|
||||
from tavolo import auth
|
||||
from tavolo.platform import auth
|
||||
|
||||
|
||||
def make_user(sub: str, name: Optional[str] = None) -> OIDCUser:
|
||||
|
||||
Reference in New Issue
Block a user