from __future__ import annotations from dataclasses import replace from pathlib import Path from types import SimpleNamespace import pytest from fastapi.testclient import TestClient from code.web.app import app from code.web.routes import commanders from code.web.services import commander_catalog_loader from code.web.services.commander_catalog_loader import clear_commander_catalog_cache, load_commander_catalog @pytest.fixture def client(monkeypatch): csv_dir = Path("csv_files/testdata").resolve() monkeypatch.setenv("CSV_FILES_DIR", str(csv_dir)) clear_commander_catalog_cache() with TestClient(app) as test_client: yield test_client clear_commander_catalog_cache() def test_commanders_page_renders(client: TestClient) -> None: response = client.get("/commanders") assert response.status_code == 200 body = response.text assert "data-commander-slug=\"atraxa-praetors-voice\"" in body assert "data-commander-slug=\"krenko-mob-boss\"" in body assert "data-theme-summary=\"" in body assert 'id="commander-loading"' in body def test_commanders_search_filters(client: TestClient) -> None: response = client.get("/commanders", params={"q": "krenko"}) assert response.status_code == 200 body = response.text assert "data-commander-slug=\"krenko-mob-boss\"" in body assert "data-commander-slug=\"atraxa-praetors-voice\"" not in body def test_commanders_color_filter(client: TestClient) -> None: response = client.get("/commanders", params={"color": "W"}) assert response.status_code == 200 body = response.text assert "data-commander-slug=\"isamaru-hound-of-konda\"" in body assert "data-commander-slug=\"krenko-mob-boss\"" not in body def test_commanders_htmx_fragment(client: TestClient) -> None: response = client.get( "/commanders", params={"q": "atraxa"}, headers={"HX-Request": "true"}, ) assert response.status_code == 200 body = response.text assert "commander-row" in body assert "
None: base_catalog = load_commander_catalog() sample = base_catalog.entries[0] records = [] for index in range(total): name = f"Pagination Test {index:02d}" record = replace( sample, name=name, face_name=name, display_name=name, slug=f"pagination-test-{index:02d}", search_haystack=f"{name.lower()}" ) records.append(record) _install_custom_catalog(monkeypatch, records) def _install_custom_catalog(monkeypatch: pytest.MonkeyPatch, records: list) -> None: fake_catalog = SimpleNamespace( entries=tuple(records), by_slug={record.slug: record for record in records}, ) def loader() -> SimpleNamespace: return fake_catalog monkeypatch.setattr(commander_catalog_loader, "load_commander_catalog", loader) monkeypatch.setattr(commanders, "load_commander_catalog", loader) def test_commanders_pagination_limits_results(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None: _install_paginated_catalog(monkeypatch, total=35) response = client.get("/commanders") assert response.status_code == 200 body = response.text assert "Page 1 of 2" in body assert "Showing 1 – 20 of 35" in body assert body.count('href="/commanders?page=2"') == 2 assert body.count('data-commander-slug="pagination-test-') == 20 def test_commanders_second_page_shows_remaining_results(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None: _install_paginated_catalog(monkeypatch, total=35) response = client.get("/commanders", params={"page": 2}) assert response.status_code == 200 body = response.text assert "Page 2 of 2" in body assert 'data-commander-slug="pagination-test-00"' not in body assert 'data-commander-slug="pagination-test-20"' in body assert 'data-commander-slug="pagination-test-34"' in body assert 'href="/commanders?page=1"' in body def test_commanders_show_all_themes_without_overflow(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None: catalog = load_commander_catalog() sample = catalog.entries[0] themes = tuple(f"Theme {idx}" for idx in range(1, 9)) enriched = replace( sample, themes=themes, theme_tokens=tuple(theme.lower() for theme in themes), ) _install_custom_catalog(monkeypatch, [enriched]) response = client.get("/commanders") assert response.status_code == 200 body = response.text assert "commander-theme-chip-more" not in body # no overflow badge rendered for name in themes: assert name in body def _commander_fixture(sample, *, name: str, slug: str, themes: tuple[str, ...] = ()): return replace( sample, name=name, face_name=name, display_name=name, slug=slug, themes=themes, theme_tokens=tuple(theme.lower() for theme in themes), search_haystack="|".join([name.lower(), *[theme.lower() for theme in themes]]), ) def test_commanders_search_ignores_theme_tokens(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None: catalog = load_commander_catalog() sample = catalog.entries[0] target = _commander_fixture( sample, name="Avatar Aang // Aang, Master of Elements", slug="avatar-aang", themes=("Elemental", "Avatar"), ) other = _commander_fixture( sample, name="Generic Guardian", slug="generic-guardian", themes=("Avatar", "Guardian"), ) _install_custom_catalog(monkeypatch, [target, other]) response = client.get("/commanders", params={"q": "Avatar Aang"}) assert response.status_code == 200 body = response.text assert 'data-commander-slug="avatar-aang"' in body assert 'data-commander-slug="generic-guardian"' not in body def test_commanders_search_supports_token_reordering(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None: catalog = load_commander_catalog() sample = catalog.entries[0] target = _commander_fixture( sample, name="Avatar Aang // Aang, Master of Elements", slug="avatar-aang", themes=("Elemental",), ) fallback = _commander_fixture( sample, name="Master of Avatar Arts", slug="master-of-avatar-arts", themes=("Avatar",), ) _install_custom_catalog(monkeypatch, [target, fallback]) response = client.get("/commanders", params={"q": "Aang Avatar"}) assert response.status_code == 200 body = response.text assert 'data-commander-slug="avatar-aang"' in body assert 'data-commander-slug="master-of-avatar-arts"' not in body def test_commanders_theme_search_filters(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None: catalog = load_commander_catalog() sample = catalog.entries[0] aggro_commander = _commander_fixture( sample, name="Aggro Ace", slug="aggro-ace", themes=("Aggro", "Combat"), ) control_commander = _commander_fixture( sample, name="Control Keeper", slug="control-keeper", themes=("Control", "Value"), ) _install_custom_catalog(monkeypatch, [aggro_commander, control_commander]) response = client.get("/commanders", params={"theme": "Aggo"}) assert response.status_code == 200 body = response.text assert 'data-commander-slug="aggro-ace"' in body assert 'data-commander-slug="control-keeper"' not in body assert 'data-theme-suggestion="Aggro"' in body assert 'id="theme-suggestions"' in body assert 'option value="Aggro"' in body def test_commanders_theme_recommendations_render_in_fragment(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None: catalog = load_commander_catalog() sample = catalog.entries[0] aggro_commander = _commander_fixture( sample, name="Aggro Ace", slug="aggro-ace", themes=("Aggro", "Combat"), ) control_commander = _commander_fixture( sample, name="Control Keeper", slug="control-keeper", themes=("Control", "Value"), ) _install_custom_catalog(monkeypatch, [aggro_commander, control_commander]) response = client.get( "/commanders", params={"theme": "Aggo"}, headers={"HX-Request": "true"}, ) assert response.status_code == 200 body = response.text assert 'data-theme-suggestion="Aggro"' in body assert 'data-commander-slug="aggro-ace"' in body def test_commander_name_fuzzy_tightened(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None: catalog = load_commander_catalog() sample = catalog.entries[0] finneas = _commander_fixture( sample, name="Finneas, Ace Archer", slug="finneas-ace-archer", themes=("Aggro", "Counters"), ) torgal = _commander_fixture( sample, name="Torgal, A Fine Hound", slug="torgal-a-fine-hound", themes=("Aggro", "Combat"), ) gorbag = _commander_fixture( sample, name="Gorbag of Minas Morgul", slug="gorbag-of-minas-morgul", themes=("Aggro", "Treasure"), ) _install_custom_catalog(monkeypatch, [finneas, torgal, gorbag]) response = client.get("/commanders", params={"q": "Finneas"}) assert response.status_code == 200 body = response.text assert 'data-commander-slug="finneas-ace-archer"' in body assert 'data-commander-slug="torgal-a-fine-hound"' not in body assert 'data-commander-slug="gorbag-of-minas-morgul"' not in body