"""tests/integration/test_recommendations.py — country cards + roadmap (T047). Offline: seeds a completed assessment with real country_lookup records (the same data the assessment attaches at runtime) and verifies the cards, selection, and roadmap behave. No model needed — the assessment output is the real-data input. """ import gradio as gr from agent.tools.country_lookup import lookup_country, work_route_countries from app.phases.recommendations import ( build, card_body_html, is_economic_case, match_strength, roadmap_html, select_country, ) from app.state.session import SessionState, State def _seed_completed_assessment() -> SessionState: s = SessionState() for target in (State.INTAKE, State.SITUATION, State.HISTORY, State.GOALS, State.REVIEW, State.ASSESSMENT, State.RECOMMENDATIONS): s.transition_to(target) s.interview.origin_country = "Ethiopia" s.assessment.risk_level = "high" s.assessment.recommended_countries = [ lookup_country("Kenya"), lookup_country("Uganda"), lookup_country("Egypt"), ] return s def test_two_or_three_cards_with_real_names(): s = _seed_completed_assessment() recs = s.assessment.recommended_countries assert 2 <= len(recs) <= 3 for rec in recs: html = card_body_html(rec) assert rec["country"] in html # real country name, not a placeholder assert "match" in html.lower() # has a match badge def test_match_badge_from_real_fields(): kenya = lookup_country("Kenya") assert match_strength(kenya) == "strong" # signatory + UNHCR + tier 1 def test_selecting_card_sets_selected_country(): s = _seed_completed_assessment() select_country(s, 1) # Uganda assert s.selected_country == "Uganda" select_country(s, 0) # Kenya assert s.selected_country == "Kenya" def test_roadmap_header_names_selected_country(): kenya = lookup_country("Kenya") rm = roadmap_html(kenya) assert "Kenya" in rm assert rm.count('class="step"') >= 4 # SC-035: >= 4 steps uganda = lookup_country("Uganda") assert "Uganda" in roadmap_html(uganda) # SC-036: header changes def test_populate_shows_cards_and_autoselects_first(): s = _seed_completed_assessment() with gr.Blocks(): ui = build(visible=False, session_st=gr.State(None)) updates = ui.populate(s) # render_outputs = [intro] + [slot,card,btn]*3 + [roadmap, proceed] assert len(updates) == len(ui.render_outputs) assert s.selected_country == "Kenya" # top recommendation auto-selected # --- economic (non-protection) case: must NOT present asylum ----------------- def _seed_economic_case() -> SessionState: s = _seed_completed_assessment() s.interview.origin_country = "Bangladesh" s.assessment.case_type = "economic_or_other" s.assessment.recommended_countries = work_route_countries()[:3] s.selected_country = None return s def test_economic_shortlist_is_work_visa_countries_not_asylum(): recs = work_route_countries() assert recs, "expected a curated work-visa shortlist" names = {r["country"] for r in recs} assert "United Arab Emirates" in names # Gulf labour market for r in recs: assert r["workVisa"].get("exists") is True assert r["isSignatory"] is False # not asylum destinations def test_economic_cards_use_work_route_framing(): s = _seed_economic_case() assert is_economic_case(s) is True for rec in s.assessment.recommended_countries: html = card_body_html(rec, economic=True) assert "Work route" in html # not "Strong match" assert "Recognition rate" not in html # no asylum recognition rate assert "Work visa" in html def test_economic_roadmap_is_labour_not_asylum(): rec = work_route_countries()[0] rm = roadmap_html(rec, economic=True) assert "work-route roadmap" in rm.lower() assert "Register with UNHCR" not in rm # not the asylum RSD roadmap assert "asylum" not in rm.lower() def test_pakistan_non_signatory_excluded_from_protection(): # Pakistan has no asylum system; it must read as a non-signatory so the # assessment's protection filter drops it (and the roadmap won't claim UNHCR). pk = lookup_country("Pakistan") assert pk.get("isSignatory") is False assert pk.get("unhcrPresence") is False # the curated strong-asylum fallback is all signatories from agent.tools.country_lookup import strong_asylum_destinations assert all(r.get("isSignatory") for r in strong_asylum_destinations()) def test_roadmap_without_unhcr_does_not_claim_unhcr(): rec = {"country": "Testland", "flag": "", "unhcrPresence": False, "unhcrOffice": None, "processingTimeMonths": 12} rm = roadmap_html(rec) assert "Register with UNHCR" not in rm assert "Register your asylum claim" in rm assert "National asylum authority" in rm def test_roadmap_with_unhcr_uses_unhcr(): rm = roadmap_html(lookup_country("Kenya")) # signatory with UNHCR presence assert "Register with UNHCR" in rm def test_economic_case_hides_document_proceed(): s = _seed_economic_case() with gr.Blocks(): ui = build(visible=False, session_st=gr.State(None)) updates = ui.populate(s) # last update is the "Prepare my documents" button — hidden for economic cases proceed_update = updates[-1] assert proceed_update.get("visible") is False