yannsay's picture
Deploy clean to build-small-hackathon org (32B Qwen, step-3 table)
aa1d0aa verified
Raw
History Blame Contribute Delete
4.47 kB
from pathlib import Path
from ui.components import PATH_CHIPS
def test_trace_md_uses_hard_breaks():
import app
md = app._trace_md(["● Routing… ✓ WASH", "● Selecting… ✓ 9 found"])
# each beat on its own rendered line: markdown hard break = two spaces + newline
assert md == "● Routing… ✓ WASH \n● Selecting… ✓ 9 found"
# single newline (which Markdown would collapse to a space) must NOT be the separator
assert "WASH\n●" not in md
def test_run_pipeline_streams_and_advances(monkeypatch):
"""run_pipeline (chips path) with 10-arg signature: switch to page 2, run route+select+bind(stub), end on page 3."""
import app
from core.route import Step1Result
from core.schemas import RouteResponse, BindResponse
from core.select import SelectResult
fake_route = Step1Result(
route=RouteResponse(sectors=["WASH", "Food Security"], lens=[], type=[]),
in_scope=["WASH", "Food Security"],
out_of_scope=[],
)
fake_select = SelectResult(
selected_ids=("rcsi", "jmp_water_safely_managed"),
indicator_defs={
"rcsi": {"label": "Reduced Coping Strategies Index", "cluster": "Food Security",
"definition": "Coping behaviour."},
"jmp_water_safely_managed": {"label": "JMP Drinking Water — Safely Managed",
"cluster": "WASH", "definition": "Safe water access."},
},
count_by_sector={"WASH": 1, "Food Security": 1},
)
fake_bind_rcsi = BindResponse(
indicator_id="rcsi", variables=["coping_score"], measurable="MEASURABLE",
reasons="Score directly computable.", result_ids=[],
)
fake_bind_jmp = BindResponse(
indicator_id="jmp_water_safely_managed", variables=["water_source"], measurable="PROXY",
reasons="Source type captured but not full ladder.", result_ids=[],
)
bind_map = {"rcsi": fake_bind_rcsi, "jmp_water_safely_managed": fake_bind_jmp}
fake_spec_md_path = Path("/tmp/test_spec.md")
fake_spec_md_path.write_text("## Analysis spec\n\nTest content.")
monkeypatch.setattr(app, "resolve_route", lambda *a, **kw: fake_route)
monkeypatch.setattr(app, "select_step", lambda sectors, **kw: fake_select)
monkeypatch.setattr(app, "new_session", lambda *a, **kw: "test-sid")
monkeypatch.setattr(app, "write_state", lambda sid, patch, **kw: None)
monkeypatch.setattr(app, "ModalChatModel", lambda: object())
monkeypatch.setattr(app, "parse_kobo", lambda *a, **kw: Path("/tmp/kobo_cache.json"))
monkeypatch.setattr(app, "kobo_summary", lambda cache_path: {"water_source": "Water source type", "coping_score": "Coping score"})
monkeypatch.setattr(app, "run_bind_step", lambda ind_id, ind_def, cache_path, summary_map, model: bind_map[ind_id])
monkeypatch.setattr(app, "load_state", lambda sid: {"route": {"sectors": ["WASH", "Food Security"], "lens": [], "type": []}})
monkeypatch.setattr(app, "assemble_spec_yaml", lambda *a, **kw: Path("/tmp/test_spec.yaml"))
monkeypatch.setattr(app, "render_spec_md", lambda *a, **kw: fake_spec_md_path)
def visible(obj):
return obj["visible"]
# New 11-arg signature: path, question, sectors, sectors_no_cat, lens, type, session_id,
# kobo_file_path, demo_mode, survey_sheet, choices_sheet
frames = list(app.run_pipeline(
PATH_CHIPS, "", ["WASH", "Food Security"], [], [], [], "",
"/tmp/kobo.json", False, "survey", "choices",
))
# first frame switches to page 2
assert visible(frames[0][0]) is False
assert visible(frames[0][1]) is True
all_trace = " ".join(str(f[3]) for f in frames)
assert "Routing" in all_trace
assert "Selecting" in all_trace
assert "Mapping" in all_trace
# final frame advances to page 3
last = frames[-1]
assert visible(last[2]) is True
assert last[1]["visible"] is False
# 7-tuple: (page1, page2, page3, trace_md, spec_md, session_id, spec_download)
assert len(last) == 7
def test_run_pipeline_empty_sectors_no_page_switch(monkeypatch):
"""Empty sector list on chips path with 10-arg signature: yields error without switching to page 2."""
import app
frames = list(app.run_pipeline(
PATH_CHIPS, "", [], [], [], [], "",
None, False, "survey", "choices",
))
assert len(frames) == 1
assert "sector" in str(frames[0][3]).lower()