Zum Inhalt

tests/test_gui_config_flow.py

# tests/test_gui_config_flow.py
from fastapi.testclient import TestClient

from touchstone.gui import app as appmod
from touchstone.gui.control import RunRegistry


class _FakeLauncher:
    def spawn(self, argv):
        return 1

    def alive(self, pid):
        return False

    def terminate(self, pid):
        return None


def _client(tmp_path):
    reg = RunRegistry(runs_dir=tmp_path, launcher=_FakeLauncher())
    return TestClient(appmod.create_app(runs_dir=tmp_path, registry=reg))


def test_config_renders_flow_stations(tmp_path):
    body = _client(tmp_path).get("/config").text
    # the flow markers (Eval → Judge → Ergebnis), no 1/2/3 station numbers
    assert "Eval starten" in body
    assert "Judge starten" in body
    assert "Ergebnis" in body
    # an Alpine scope holding the active station
    assert "x-data" in body and "station" in body
    # result step links to the overview
    assert 'href="/"' in body


def test_config_preserves_eval_and_judge_function_markers(tmp_path):
    body = _client(tmp_path).get("/config").text
    # model picker + judge picker + their hidden/submit machinery survive the reorder
    assert "modelPicker(" in body
    assert "judgeModelPicker(" in body
    assert 'name="models_json"' in body
    assert 'action="/runs/eval"' in body and 'action="/runs/judge"' in body


def test_flow_bar_class_and_css_rule_present(tmp_path):
    """flow-bar carries the right class in HTML and the CSS rule exists."""
    from pathlib import Path

    body = _client(tmp_path).get("/config").text
    assert 'class="flow-bar"' in body, "flow-bar wrapper must carry .flow-bar class"
    css_path = Path(__file__).parent.parent / "touchstone" / "gui" / "static" / "app.css"
    css = css_path.read_text()
    assert ".flow-bar" in css, "app.css must contain a .flow-bar rule"
    assert ".flow-bar button.active" in css, "app.css must contain .flow-bar button.active rule"


def test_config_links_to_pack_editor(tmp_path):
    # the pack editor must be discoverable from the config flow (not only via a pack's viewer)
    body = _client(tmp_path).get("/config").text
    assert 'href="/pack-editor"' in body
    assert "Pack bearbeiten" in body


def test_config_default_station_is_eval_not_judge(tmp_path):
    # Bug: arriving at "Konfig + Start" while unjudged bundles exist defaulted to the Judge tab.
    # Plain /config must default to the Eval tab; the Judge tab is for an explicit ?bundle.
    body = _client(tmp_path).get("/config").text
    assert "station: 'eval'" in body
    body_judge = _client(tmp_path).get("/config?bundle=2026-01-01_eval_x").text
    assert "station: 'judge'" in body_judge


def test_sidebar_station_numbers_are_consecutive(tmp_path):
    # Bug: the sidebar showed vestigial 1/3/6. The three top-level destinations are now 1/2/3.
    body = _client(tmp_path).get("/config").text
    assert "2 · Konfig + Start" in body and "3 · Konfig + Start" not in body
    assert "3 · Vergleich" in body and "6 · Vergleich" not in body


def test_config_picker_shows_inline_endpoint_summary(tmp_path):
    # Bug: you picked a config blind. The picker now previews endpoint/machine/engine inline.
    body = _client(tmp_path).get("/config").text
    assert "summary()" in body  # the inline preview line is rendered
    assert '"base_url"' in body  # config_summaries embedded for the Alpine component


def test_config_sidesteps_import_always_resume_conditional(tmp_path):
    # no resume → import present, resume hint absent
    body = _client(tmp_path).get("/config").text
    assert "Bundle importieren" in body
    assert 'action="/import-bundle"' in body
    assert "Fortsetzen" not in body  # resume hint only in resume mode
    # resume mode → resume hint present
    body2 = _client(tmp_path).get("/config?resume=2026-01-01_run").text
    assert "Fortsetzen" in body2
    assert "2026-01-01_run" in body2