| 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"]) |
| |
| assert md == "● Routing… ✓ WASH \n● Selecting… ✓ 9 found" |
| |
| 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"] |
|
|
| |
| |
| frames = list(app.run_pipeline( |
| PATH_CHIPS, "", ["WASH", "Food Security"], [], [], [], "", |
| "/tmp/kobo.json", False, "survey", "choices", |
| )) |
|
|
| |
| 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 |
|
|
| |
| last = frames[-1] |
| assert visible(last[2]) is True |
| assert last[1]["visible"] is False |
|
|
| |
| 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() |
|
|