| """Tests for app/pages/4_gap_analysis.py — GAP_FOLLOWUP state.""" |
|
|
| import pytest |
| from streamlit.testing.v1 import AppTest |
|
|
| from app.services.mock_data import ( |
| MOCK_ELIGIBILITY_LEDGERS, |
| MOCK_PATIENT_PROFILE, |
| MOCK_TRIAL_CANDIDATES, |
| ) |
|
|
|
|
| @pytest.fixture |
| def gap_app(): |
| at = AppTest.from_file("app/pages/4_gap_analysis.py", default_timeout=10) |
| at.session_state["journey_state"] = "GAP_FOLLOWUP" |
| at.session_state["patient_profile"] = MOCK_PATIENT_PROFILE |
| at.session_state["trial_candidates"] = MOCK_TRIAL_CANDIDATES |
| at.session_state["eligibility_ledger"] = MOCK_ELIGIBILITY_LEDGERS |
| at.session_state["parlant_session_id"] = None |
| at.session_state["parlant_agent_id"] = None |
| at.session_state["uploaded_files"] = [] |
| at.session_state["search_anchors"] = None |
| at.session_state["last_event_offset"] = 0 |
| return at.run() |
|
|
|
|
| def test_page_renders_without_error(gap_app): |
| assert len(gap_app.exception) == 0 |
|
|
|
|
| def test_displays_gaps(gap_app): |
| expander_labels = [str(e.label) for e in gap_app.expander] |
| all_labels = " ".join(expander_labels) |
| all_md = " ".join(str(m.value) for m in gap_app.markdown) |
| combined = all_labels + " " + all_md |
| assert "Brain MRI" in combined or "EGFR" in combined |
|
|
|
|
| def test_displays_recommended_actions(gap_app): |
| all_md = " ".join(str(m.value) for m in gap_app.markdown) |
| assert "upload" in all_md.lower() or "request" in all_md.lower() |
|
|
|
|
| def test_displays_would_match_phrasing(gap_app): |
| """PRD core value proposition: 'You would match [trial] IF you had' phrasing.""" |
| all_md = " ".join(str(m.value) for m in gap_app.markdown) |
| assert "You would match" in all_md |
| assert "IF you had" in all_md |
|
|
|
|
| def test_has_summary_button(gap_app): |
| labels = [str(b.label) for b in gap_app.button] |
| assert any("summary" in lbl.lower() or "generate" in lbl.lower() for lbl in labels) |
|
|
|
|
| def test_summary_button_advances_state(gap_app): |
| summary_btns = [ |
| b |
| for b in gap_app.button |
| if "summary" in str(b.label).lower() or "generate" in str(b.label).lower() |
| ] |
| if summary_btns: |
| summary_btns[0].click() |
| at = gap_app.run() |
| assert at.session_state["journey_state"] == "SUMMARY" |
| assert len(at.exception) == 0 |
|
|
|
|
| def test_no_gaps_message(): |
| """When no gaps exist, page should indicate so.""" |
| from trialpath.models import ( |
| CriterionAssessment, |
| CriterionDecision, |
| EligibilityLedger, |
| OverallAssessment, |
| ) |
|
|
| clean_ledger = EligibilityLedger( |
| patient_id="P001", |
| nct_id="NCT04000001", |
| overall_assessment=OverallAssessment.LIKELY_ELIGIBLE, |
| criteria=[ |
| CriterionAssessment( |
| criterion_id="inc_1", |
| type="inclusion", |
| text="NSCLC confirmed", |
| decision=CriterionDecision.MET, |
| ), |
| ], |
| gaps=[], |
| ) |
| at = AppTest.from_file("app/pages/4_gap_analysis.py", default_timeout=10) |
| at.session_state["journey_state"] = "GAP_FOLLOWUP" |
| at.session_state["patient_profile"] = MOCK_PATIENT_PROFILE |
| at.session_state["trial_candidates"] = MOCK_TRIAL_CANDIDATES |
| at.session_state["eligibility_ledger"] = [clean_ledger] |
| at.session_state["parlant_session_id"] = None |
| at.session_state["parlant_agent_id"] = None |
| at.session_state["uploaded_files"] = [] |
| at.session_state["search_anchors"] = None |
| at.session_state["last_event_offset"] = 0 |
| at = at.run() |
| assert len(at.exception) == 0 |
| all_text = " ".join(str(m.value) for m in at.markdown) |
| all_success = " ".join(str(s.value) for s in at.success) |
| combined = (all_text + " " + all_success).lower() |
| assert "no gap" in combined or "no unresolved" in combined or "resolved" in combined |
|
|