Rename the app from scopa to tavolo
CI / Build and push docker image (push) Successful in 3m12s

The platform now hosts multiple card games, with scopone scientifico as
the first one. Rename the brand wherever it is not a game rule:

- move the Python package to server/src/tavolo and update imports
- rename the Postgres database/user, OIDC issuer path, client id and
  Redis key prefixes to tavolo (clean break: existing pgdata volumes and
  live games are not migrated)
- rename the Cargo package to tavolo-web and set the page title to Tavolo
- update docs and the Docker image path to woggioni/tavolo

The scopa game term (clearing the table) in the engine, state and web UI
is intentionally left untouched.
This commit is contained in:
2026-09-16 21:46:06 +08:00
parent 89cf0a4c96
commit 6932a3272c
46 changed files with 141 additions and 140 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
"""Test package init.
Sets environment overrides BEFORE any test module imports
:mod:`scopa.app` (which evaluates :data:`scopa.config.settings`
:mod:`tavolo.app` (which evaluates :data:`tavolo.config.settings`
at import time). Works under both ``python -m unittest discover`` and
``pytest``; conftest.py mirrors this for pytest-only collection.
"""
@@ -10,8 +10,8 @@ from __future__ import annotations
import os
os.environ.setdefault("DATABASE_URL", "sqlite://:memory:")
os.environ.setdefault("OIDC_ISSUER", "http://localhost:8180/scopa")
os.environ.setdefault("OIDC_CLIENT_ID", "scopa")
os.environ.setdefault("OIDC_ISSUER", "http://localhost:8180/tavolo")
os.environ.setdefault("OIDC_CLIENT_ID", "tavolo")
os.environ.setdefault("OIDC_REDIRECT_URI", "http://localhost:8080/auth/callback")
# Unset REDIS_URL: sessions and live games use the in-memory fallbacks.
os.environ.pop("REDIS_URL", None)
+4 -4
View File
@@ -1,7 +1,7 @@
"""Helpers for faking the OIDC authenticated user during tests.
HTTP handlers funnel through ``oidc_mixin.get_user``; websocket handlers
through :func:`scopa.auth.get_ws_user`. Patching those two entry points
through :func:`tavolo.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,10 @@ from typing import Iterator, Optional, Sequence
from kaya.oidc import OIDCUser
# Import the app first: it pulls in the route modules, which import
# ``scopa.auth`` themselves. Importing ``auth`` before ``app`` would hit a
# ``tavolo.auth`` themselves. Importing ``auth`` before ``app`` would hit a
# partially initialized module (same constraint as reimpasto).
from scopa.app import oidc_mixin
from scopa import auth
from tavolo.app import oidc_mixin
from tavolo import auth
def make_user(sub: str, name: Optional[str] = None) -> OIDCUser:
+3 -3
View File
@@ -4,15 +4,15 @@ from __future__ import annotations
import random
import unittest
from scopa.game import engine
from scopa.game.errors import (
from tavolo.game import engine
from tavolo.game.errors import (
CardNotInHand,
GameFinished,
GameNotStarted,
IllegalMove,
NotYourTurn,
)
from scopa.game.state import (
from tavolo.game.state import (
PHASE_FINISHED,
PHASE_PLAYING,
Card,
+1 -1
View File
@@ -6,7 +6,7 @@ import unittest
from httpx import ASGITransport, AsyncClient
from pwo import async_test
from scopa.app import app
from tavolo.app import app
from tests.helpers import oidc_user
+4 -4
View File
@@ -10,8 +10,8 @@ from unittest import mock
from httpx import ASGITransport, AsyncClient
from pwo import async_test
from scopa.app import app
from scopa.config import settings
from tavolo.app import app
from tavolo.config import settings
from tests.helpers import oidc_user
@@ -45,7 +45,7 @@ class StaticRouteTest(unittest.TestCase):
(cards / "07D.svg").write_text("<svg/>")
patched = dataclasses.replace(settings, static_dir=dist)
with mock.patch("scopa.routes.static.settings", patched):
with mock.patch("tavolo.routes.static.settings", patched):
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://127.0.0.1") as client:
index = await client.get("/")
@@ -74,7 +74,7 @@ class StaticRouteTest(unittest.TestCase):
@async_test
async def test_missing_dist_returns_404(self) -> None:
patched = dataclasses.replace(settings, static_dir="/nonexistent-dist")
with mock.patch("scopa.routes.static.settings", patched):
with mock.patch("tavolo.routes.static.settings", patched):
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://127.0.0.1") as client:
response = await client.get("/")
+6 -6
View File
@@ -8,11 +8,11 @@ from datetime import datetime, timezone
from httpx import ASGITransport, AsyncClient
from pwo import async_test
from scopa.app import app, tortoise_mixin
from scopa.game import engine
from scopa.game.state import GameState
from scopa.models import Match, MatchPlayer
from scopa.stats import save_match_result
from tavolo.app import app, tortoise_mixin
from tavolo.game import engine
from tavolo.game.state import GameState
from tavolo.models import Match, MatchPlayer
from tavolo.stats import save_match_result
from tests.helpers import oidc_user
@@ -37,7 +37,7 @@ def _finished_state() -> GameState:
turn=0,
table=[engine.parse_card("02C")],
)
from scopa.game.state import PlayerState, Card
from tavolo.game.state import PlayerState, Card
state.players = [
PlayerState(sub="alice", name="alice", seat=0, hand=[Card.parse("02D")]),
+2 -2
View File
@@ -6,8 +6,8 @@ import unittest
from pwo import async_test
from scopa.game import engine
from scopa.store import InMemoryGameStore
from tavolo.game import engine
from tavolo.store import InMemoryGameStore
class InMemoryGameStoreTest(unittest.TestCase):
+3 -3
View File
@@ -9,9 +9,9 @@ from httpx_ws import WebSocketDisconnect, aconnect_ws
from httpx_ws.transport import ASGIWebSocketTransport
from pwo import async_test
from scopa.app import app, game_store
from scopa.game import engine
from scopa.game.state import Card, GameState, PlayerState
from tavolo.app import app, game_store
from tavolo.game import engine
from tavolo.game.state import Card, GameState, PlayerState
from tests.helpers import make_user, oidc_user, ws_users
PLAYERS = ("alice", "bob", "carol", "dave")