mtg_python_deckbuilder/code/tests/test_diagnostics.py
mwisnowski 46637cf27f
Some checks are pending
CI / build (push) Waiting to run
test-cleanup: fix 21 failures, prune stale tests, consolidate fragmented files (#66)
* test-cleanup: fix 21 failures, prune stale tests, consolidate fragmented files

* test-cleanup: remove permanently-skipped M4/perf tests, fix pydantic ConfigDict warning

* docs: update changelog and release notes for test-cleanup changes

* ci: fix editorial governance workflow stale test file reference
2026-03-31 17:38:08 -07:00

219 lines
7.6 KiB
Python

import os
import importlib
import types
import pytest
from starlette.testclient import TestClient
from code.deck_builder.summary_telemetry import _reset_metrics_for_test, record_partner_summary
fastapi = pytest.importorskip("fastapi") # skip tests if FastAPI isn't installed
def load_app_with_env(**env: str) -> types.ModuleType:
for key in (
"SHOW_LOGS",
"SHOW_DIAGNOSTICS",
"SHOW_SETUP",
"SHOW_COMMANDERS",
"ENABLE_THEMES",
"ENABLE_PWA",
"ENABLE_PRESETS",
"APP_VERSION",
"THEME",
):
os.environ.pop(key, None)
for k, v in env.items():
os.environ[k] = v
import code.web.app as app_module
importlib.reload(app_module)
return app_module
def test_healthz_ok_and_request_id_header():
app_module = load_app_with_env()
client = TestClient(app_module.app)
r = client.get("/healthz")
assert r.status_code == 200
data = r.json()
assert data.get("status") in {"ok", "degraded"}
assert "uptime_seconds" in data
assert r.headers.get("X-Request-ID")
def test_404_renders_html_when_accept_html():
app_module = load_app_with_env()
client = TestClient(app_module.app)
r = client.get("/this-does-not-exist", headers={"Accept": "text/html"})
assert r.status_code == 404
body = r.text.lower()
assert "page not found" in body
assert "go home" in body
assert r.headers.get("X-Request-ID")
def test_htmx_http_exception_returns_json_with_request_id():
app_module = load_app_with_env(SHOW_DIAGNOSTICS="1")
client = TestClient(app_module.app)
r = client.get("/diagnostics/trigger-error", headers={"HX-Request": "true"})
assert r.status_code == 418
data = r.json()
assert data.get("error") is True
assert data.get("status") == 418
assert data.get("request_id")
assert r.headers.get("X-Request-ID")
def test_unhandled_exception_returns_500_json_with_request_id():
app_module = load_app_with_env(SHOW_DIAGNOSTICS="1")
# Configure client to not re-raise server exceptions so we can assert the 500 response
client = TestClient(app_module.app, raise_server_exceptions=False)
r = client.get("/diagnostics/trigger-error?kind=unhandled", headers={"HX-Request": "true"})
assert r.status_code == 500
data = r.json()
assert data.get("error") is True
assert data.get("status") == 500
assert data.get("request_id")
assert r.headers.get("X-Request-ID")
def test_status_sys_summary_and_flags():
app_module = load_app_with_env(
SHOW_LOGS="1",
SHOW_DIAGNOSTICS="1",
SHOW_SETUP="1",
SHOW_COMMANDERS="1",
ENABLE_THEMES="1",
ENABLE_PWA="1",
ENABLE_PRESETS="1",
APP_VERSION="testver",
THEME="dark",
)
client = TestClient(app_module.app)
r = client.get("/status/sys")
assert r.status_code == 200
data = r.json()
assert data.get("version") == "testver"
assert isinstance(data.get("uptime_seconds"), int)
assert isinstance(data.get("server_time_utc"), str)
flags = data.get("flags") or {}
assert flags.get("SHOW_LOGS") is True
assert flags.get("SHOW_DIAGNOSTICS") is True
assert flags.get("SHOW_SETUP") is True
assert flags.get("SHOW_COMMANDERS") is True
# Theme-related flags
assert flags.get("ENABLE_THEMES") is True
assert flags.get("ENABLE_PWA") is True
assert flags.get("ENABLE_PRESETS") is True
assert flags.get("DEFAULT_THEME") == "dark"
def test_commanders_nav_hidden_when_flag_disabled():
app_module = load_app_with_env(SHOW_COMMANDERS="0")
client = TestClient(app_module.app)
r = client.get("/")
assert r.status_code == 200
body = r.text
assert '<a href="/commanders"' not in body
def test_commanders_nav_visible_by_default():
app_module = load_app_with_env()
client = TestClient(app_module.app)
r = client.get("/")
assert r.status_code == 200
body = r.text
assert '<a href="/commanders"' in body
def test_partner_metrics_endpoint_reports_color_sources():
app_module = load_app_with_env(SHOW_DIAGNOSTICS="1")
_reset_metrics_for_test()
record_partner_summary(
{
"primary": "Tana, the Bloodsower",
"secondary": "Nadir Kraken",
"names": ["Tana, the Bloodsower", "Nadir Kraken"],
"partner_mode": "partner",
"combined": {
"partner_mode": "partner",
"color_identity": ["G", "U"],
"color_code": "GU",
"color_label": "Simic (GU)",
"color_sources": [
{"color": "G", "providers": [{"name": "Tana, the Bloodsower", "role": "primary"}]},
{"color": "U", "providers": [{"name": "Nadir Kraken", "role": "partner"}]},
],
"color_delta": {
"added": ["U"],
"removed": [],
"primary": ["G"],
"secondary": ["U"],
},
"secondary_role": "partner",
"secondary_role_label": "Partner commander",
},
}
)
client = TestClient(app_module.app)
resp = client.get("/status/partner_metrics")
assert resp.status_code == 200
payload = resp.json()
assert payload.get("ok") is True
metrics = payload.get("metrics") or {}
assert metrics.get("total_pairs", 0) >= 1
last = metrics.get("last_summary")
assert last is not None
sources = last.get("color_sources") or []
assert any(entry.get("color") == "G" for entry in sources)
assert any(
provider.get("role") == "partner"
for entry in sources
for provider in entry.get("providers", [])
)
def test_diagnostics_page_gated_and_visible(monkeypatch):
monkeypatch.delenv("SHOW_DIAGNOSTICS", raising=False)
import code.web.app as app_module
importlib.reload(app_module)
client = TestClient(app_module.app)
r = client.get("/diagnostics")
assert r.status_code == 404
monkeypatch.setenv("SHOW_DIAGNOSTICS", "1")
importlib.reload(app_module)
client2 = TestClient(app_module.app)
r2 = client2.get("/diagnostics")
assert r2.status_code == 200
body = r2.text
assert "Diagnostics" in body
assert "Combos & Synergies" in body
def test_diagnostics_combos_endpoint(tmp_path, monkeypatch):
import json as json_mod
monkeypatch.setenv("SHOW_DIAGNOSTICS", "1")
importlib.reload(__import__('code.web.app', fromlist=['app']))
import code.web.app as app_module
importlib.reload(app_module)
client = TestClient(app_module.app)
def _write_json(path, obj):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json_mod.dumps(obj), encoding="utf-8")
cpath = tmp_path / "config/card_lists/combos.json"
spath = tmp_path / "config/card_lists/synergies.json"
_write_json(cpath, {"list_version": "0.1.0", "pairs": [{"a": "Thassa's Oracle", "b": "Demonic Consultation", "cheap_early": True, "setup_dependent": False}]})
_write_json(spath, {"list_version": "0.1.0", "pairs": [{"a": "Grave Pact", "b": "Phyrexian Altar"}]})
payload = {"names": ["Thassa's Oracle", "Demonic Consultation", "Grave Pact", "Phyrexian Altar"], "combos_path": str(cpath), "synergies_path": str(spath)}
resp = client.post("/diagnostics/combos", json=payload)
assert resp.status_code == 200
data = resp.json()
assert data["counts"]["combos"] == 1
assert data["counts"]["synergies"] == 1
assert data["versions"]["combos"] == "0.1.0"
c = data["combos"][0]
assert c.get("cheap_early") is True
assert c.get("setup_dependent") is False