Files
hockey_new/tests/test_build196_roster_first_selection.py

115 lines
5.4 KiB
Python

from pathlib import Path
import asyncio
from datetime import datetime
import json
import subprocess
from types import SimpleNamespace
import pytest
from sqlalchemy import select
from hockey_data.config import HockeySettingsStore
from hockey_data.models import Game, GameRoster, Tournament
from hockey_data.service import HockeyDataService
from tests.support import LocalTestDatabase
@pytest.mark.parametrize("case", [
"first_selection_unchanged_live_hydrates",
"finished_empty_loaded_cache_repairs_once",
"finished_complete_cache_remains_offline",
"unchanged_newer_details_preserve_new_score",
"unchanged_incomplete_live_preserves_loaded_roster",
"restore_empty_finished_cache_repairs",
"late_unchanged_roster_cannot_cross_selection",
])
def test_roster_first_selection_frontend(case):
root = Path(__file__).resolve().parents[1]
result = subprocess.run(
["node", "tests/roster_first_selection.cjs", case],
cwd=root, capture_output=True, text=True, timeout=15,
)
assert result.returncode == 0, result.stdout + result.stderr
assert f"PASS first roster selection: {case}" in result.stdout
@pytest.mark.parametrize("missing_side", ["home", "away", "both", "none", "empty_source"])
def test_unchanged_live_card_repairs_actual_missing_sql_roster(tmp_path, monkeypatch, missing_side):
database = LocalTestDatabase(tmp_path / "roster.sqlite")
database.create_all()
settings = HockeySettingsStore(tmp_path / "settings")
settings.update_public({
"match_json_endpoint_template": "match-ru-{game_id}.json",
"match_json_en_endpoint_template": "match-en-{game_id}.json",
"players_endpoint_template": "players.xml",
"player_countries_endpoint_template": "countries.xml",
"referees_endpoint_template": "referees.xml",
})
raw = json.dumps({
"game": {"idschedule": 900001, "teamA": "Home", "teamB": "Away",
"idclubA": 1, "idteamA": 11, "idclubB": 2, "idteamB": 22},
"players": {} if missing_side == "empty_source" else {
"A": {"17": {"id": "101", "name": "Home Player", "ps": "f", "pig": 1}},
"B": {"30": {"id": "202", "name": "Away Player", "ps": "g", "pig": 1}},
},
})
service = HockeyDataService(base_dir=tmp_path, database=database, settings=settings)
with database.session() as session:
session.add(Tournament(external_id="t", season_part="regular", level="khl"))
session.add(Game(external_id="900001", tournament_external_id="t", home_team_external_id="1",
away_team_external_id="2", details_raw_payload_ru=raw,
details_raw_payload_en=raw, details_synced_at=datetime(2026, 10, 22)))
requests = []
async def local_resource(endpoint, **kwargs):
requests.append(endpoint)
content = raw.encode() if endpoint.startswith("match-") else b"<root/>"
return SimpleNamespace(content=content, auth_mode="fixture")
async def local_schedule(**kwargs):
return service.game("900001")
async def local_shots(**kwargs):
return {"available": False}
monkeypatch.setattr(service, "_fetch_cached_resource", local_resource)
monkeypatch.setattr(service, "sync_selected_game", local_schedule)
monkeypatch.setattr(service, "_sync_optional_shots", local_shots)
async def scenario():
# Establish a real parsed/persisted card, then simulate an incomplete
# local hydration independently of the unchanged upstream JSON.
await service.sync_match_details(tournament_external_id="t", game_external_id="900001")
with database.session() as session:
for row in session.scalars(select(GameRoster).where(GameRoster.game_external_id == "900001")):
if missing_side == "both" or row.side == missing_side:
session.delete(row)
before = service.game_details("900001")
assert before is not None and before["details"]["loaded"] is True
actual_before = sum(len(before["game"][side]["players"]) for side in ("home", "away"))
assert actual_before == (2 if missing_side == "none" else 0 if missing_side in {"both", "empty_source"} else 1)
requests.clear()
try:
result = await service.sync_live_game(tournament_external_id="t", game_external_id="900001")
if missing_side == "empty_source":
assert result["game"]["home"]["players"] == []
assert result["game"]["away"]["players"] == []
assert result["changed"] is False
assert requests == ["match-ru-900001.json"]
return
assert result["game"]["home"]["players"][0]["id"] == "101"
assert result["game"]["away"]["players"][0]["id"] == "202"
assert result["changed"] is (missing_side != "none"), "Roster hydration must reach already-open browsers"
if missing_side == "none":
assert requests == ["match-ru-900001.json"], "A complete unchanged roster was imported again"
with database.session() as session:
rows = list(session.scalars(select(GameRoster).where(GameRoster.game_external_id == "900001")))
assert {(row.side, row.player_external_id) for row in rows} == {("home", "101"), ("away", "202")}
finally:
await service.stop_live_refreshes()
try:
asyncio.run(scenario())
finally:
database.engine.dispose()