RRC / tests /test_web_app.py
pablogrois's picture
Web: selector de liga/temporada (data completa) + post-partido funcional
5409901
Raw
History Blame Contribute Delete
4.67 kB
from __future__ import annotations
import time
from pathlib import Path
from fastapi.testclient import TestClient
from racing_reports.config import Settings
from racing_reports.models import ReportResult
from racing_reports.web import app as web_app
def test_home_is_spa_with_teams_and_report_cards():
"""El nuevo home es un SPA que consume /api/. Equipos/ligas/partidos se cargan
client-side; verificamos los selectores y las cards de reportes."""
client = TestClient(web_app.app)
resp = client.get("/")
assert resp.status_code == 200
assert 'x-model="league"' in resp.text # selector de liga
assert 'x-model="season"' in resp.text # selector de temporada
assert "Reporte previo" in resp.text
assert "Bloques" in resp.text
assert "post-partido" in resp.text # card de post-partido
assert "runPostMatch" in resp.text # flujo de post-partido funcional
assert "/api/leagues" in resp.text # alpine consume la API meta
assert "/api/reports/" in resp.text
def test_meta_endpoints_are_dataset_driven():
"""leagues/seasons/teams/matches salen del dataset bundleado (offline)."""
from racing_reports.api import routes_meta as rm
ls = rm._league_seasons()
assert ls, "debe haber al menos una liga/temporada"
league = next(iter(ls))
season = ls[league][0]
assert rm.teams(league, season), "debe listar equipos"
# matches puede estar vacío para temporadas sin jugados, pero no debe romper
assert isinstance(rm.matches(league, season, played_only=True), list)
def test_legacy_home_still_works():
"""La UI vieja sigue accesible vía /legacy para retrocompatibilidad."""
client = TestClient(web_app.app)
resp = client.get("/legacy")
assert resp.status_code == 200
assert "Reporte previo / Bloques" in resp.text
assert "Reporte post-partido" in resp.text
def test_legacy_routes_require_password_when_set(monkeypatch, tmp_path):
"""Si ACCESS_PASSWORD está seteada, las rutas /view, /download, /status,
/legacy, / NO deben servir contenido sin cookie válida."""
# Settings con password forzada + DEFAULT_SETTINGS también, porque ambos
# se usan en el app code.
new_settings = Settings(
cache_dir=tmp_path / "cache",
output_dir=tmp_path / "outputs",
access_password="secret-1234",
)
monkeypatch.setattr(web_app, "DEFAULT_SETTINGS", new_settings)
from racing_reports import config as cfg
monkeypatch.setattr(cfg, "DEFAULT_SETTINGS", new_settings)
from racing_reports.api import deps
monkeypatch.setattr(deps, "DEFAULT_SETTINGS", new_settings)
client = TestClient(web_app.app)
# / → redirect a /login
assert client.get("/", follow_redirects=False).status_code in (303, 307)
# /legacy → redirect a /login
assert client.get("/legacy", follow_redirects=False).status_code in (303, 307)
# /view sin cookie → 401
assert client.get("/view", params={"path": str(tmp_path / "x.html")}).status_code == 401
# /download sin cookie → 401
assert client.get("/download", params={"path": str(tmp_path / "x.html")}).status_code == 401
# /status/<id> sin cookie → 401
assert client.get("/status/deadbeef").status_code == 401
# API también
assert client.get("/api/leagues").status_code == 401
# /api/health sigue siendo público (no expone data)
assert client.get("/api/health").status_code == 200
def test_generate_enqueues_job_and_reports_links(monkeypatch, tmp_path: Path):
out = tmp_path / "pre_match.html"
out.write_text("<html>ok</html>", encoding="utf-8")
manifest = tmp_path / "manifest.json"
manifest.write_text("{}", encoding="utf-8")
class FakeRunner:
def run(self, request):
return ReportResult(
output_dir=tmp_path,
html_paths={"pre_match": out},
manifest_path=manifest,
)
monkeypatch.setattr(web_app, "ReportRunner", lambda *a, **k: FakeRunner())
client = TestClient(web_app.app)
resp = client.post(
"/generate",
data={
"flow": "pre",
"team_a": "Racing de Santander",
"team_b": "Huesca",
"pre_match": "on",
},
follow_redirects=False,
)
assert resp.status_code == 303
status_url = resp.headers["location"]
body = ""
for _ in range(50):
body = client.get(status_url).text
if "Volver al inicio" in body or "problema" in body:
break
time.sleep(0.05)
assert "Reporte previo (head-to-head)" in body
assert "/view?path=" in body