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:
2026-09-19 07:28:58 +00:00
parent b2b514ab91
commit 5a73601ddf
72 changed files with 4490 additions and 2102 deletions
+4 -5
View File
@@ -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: