Spaces:
Running
Running
| 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 | |