def test_select_step_determinism(): """Same sectors -> byte-identical ordered id list (the Step 2 invariant / D-exit gate).""" from core.select import select_step a = select_step(["WASH"]) b = select_step(["WASH"]) assert a.selected_ids == b.selected_ids # identical order on every call assert len(a.selected_ids) > 0 def test_select_step_defs_keys_match_ids(): """Every selected id resolves to an indicator_defs entry; no extras.""" from core.select import select_step r = select_step(["WASH", "Food Security", "Shelter"]) assert set(r.indicator_defs.keys()) == set(r.selected_ids) assert len(r.selected_ids) > 0 def test_select_step_returns_sliced_fields_not_whole_catalog(): """Each def carries the projected bind-loop fields (proves get_indicators slice, not raw catalog).""" from core.select import select_step r = select_step(["WASH"]) sample = r.indicator_defs[r.selected_ids[0]] for key in ("id", "label", "definition", "cluster"): assert key in sample # ki_assessment_note is optional — not all indicators carry it (e.g. jmp_water_safely_managed # has no KI note, sphere_wash_sanitation does). Phase E bind loop uses .get() to handle both. def test_select_step_count_by_sector_present(): from core.select import select_step r = select_step(["WASH"]) assert r.count_by_sector.get("WASH", 0) > 0 def test_select_step_unknown_sector_is_empty(): """Unknown sector -> empty selection, empty defs (no crash, no get_indicators call).""" from core.select import select_step r = select_step(["INVALID_SECTOR_XYZ_NOT_REAL"]) assert r.selected_ids == () assert r.indicator_defs == {}