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/ 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("ok", 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