| """Integration tests: verify FE components correctly consume BE models.""" |
|
|
| from __future__ import annotations |
|
|
| import json |
|
|
| from app.components.gap_card import render_gap_card |
| from app.components.profile_card import render_profile_card |
| from app.components.progress_tracker import render_progress_tracker |
| from app.components.trial_card import render_trial_card |
| from app.services.mock_data import ( |
| MOCK_ELIGIBILITY_LEDGERS, |
| MOCK_PATIENT_PROFILE, |
| MOCK_TRIAL_CANDIDATES, |
| ) |
| from app.services.state_manager import JOURNEY_STATES |
| from trialpath.models import ( |
| EligibilityLedger, |
| PatientProfile, |
| SearchAnchors, |
| TrialCandidate, |
| ) |
|
|
|
|
| class TestMockDataIntegrity: |
| """Verify mock data uses real BE models correctly.""" |
|
|
| def test_mock_profile_is_patient_profile(self): |
| assert isinstance(MOCK_PATIENT_PROFILE, PatientProfile) |
|
|
| def test_mock_trials_are_trial_candidates(self): |
| for t in MOCK_TRIAL_CANDIDATES: |
| assert isinstance(t, TrialCandidate) |
|
|
| def test_mock_ledgers_are_eligibility_ledgers(self): |
| for lg in MOCK_ELIGIBILITY_LEDGERS: |
| assert isinstance(lg, EligibilityLedger) |
|
|
| def test_mock_ledger_nct_ids_match_trials(self): |
| trial_ids = {t.nct_id for t in MOCK_TRIAL_CANDIDATES} |
| ledger_ids = {lg.nct_id for lg in MOCK_ELIGIBILITY_LEDGERS} |
| assert ledger_ids == trial_ids |
|
|
| def test_mock_profile_has_minimum_prescreen_data(self): |
| assert MOCK_PATIENT_PROFILE.has_minimum_prescreen_data() |
|
|
| def test_mock_profile_serializes_to_json(self): |
| data = MOCK_PATIENT_PROFILE.model_dump_json() |
| restored = PatientProfile.model_validate_json(data) |
| assert restored.patient_id == MOCK_PATIENT_PROFILE.patient_id |
|
|
| def test_mock_trials_serialize_to_json(self): |
| for t in MOCK_TRIAL_CANDIDATES: |
| data = t.model_dump_json() |
| restored = TrialCandidate.model_validate_json(data) |
| assert restored.nct_id == t.nct_id |
|
|
| def test_mock_ledgers_serialize_to_json(self): |
| for lg in MOCK_ELIGIBILITY_LEDGERS: |
| data = lg.model_dump_json() |
| restored = EligibilityLedger.model_validate_json(data) |
| assert restored.nct_id == lg.nct_id |
|
|
|
|
| class TestComponentModelIntegration: |
| """Verify FE components produce correct output from BE models.""" |
|
|
| def test_profile_card_renders_mock_profile(self): |
| spec = render_profile_card(MOCK_PATIENT_PROFILE) |
| assert spec["patient_id"] == "MOCK-P001" |
| assert spec["has_minimum_prescreen_data"] is True |
| assert len(spec["biomarkers"]) == 3 |
|
|
| def test_trial_card_renders_green_trial(self): |
| |
| trial = MOCK_TRIAL_CANDIDATES[1] |
| ledger = MOCK_ELIGIBILITY_LEDGERS[1] |
| spec = render_trial_card(trial, ledger) |
| assert spec["traffic_light"] == "green" |
| assert spec["nct_id"] == "MOCK-NCT-FLAURA2" |
|
|
| def test_trial_card_renders_yellow_trial(self): |
| |
| trial = MOCK_TRIAL_CANDIDATES[0] |
| ledger = MOCK_ELIGIBILITY_LEDGERS[0] |
| spec = render_trial_card(trial, ledger) |
| assert spec["traffic_light"] == "yellow" |
| assert len(spec["gaps"]) == 1 |
|
|
| def test_trial_card_renders_red_trial(self): |
| |
| trial = MOCK_TRIAL_CANDIDATES[2] |
| ledger = MOCK_ELIGIBILITY_LEDGERS[2] |
| spec = render_trial_card(trial, ledger) |
| assert spec["traffic_light"] == "red" |
|
|
| def test_gap_card_renders_from_ledger_gap(self): |
| ledger = MOCK_ELIGIBILITY_LEDGERS[0] |
| gap = ledger.gaps[0] |
| spec = render_gap_card(gap, affected_trials=["MOCK-NCT-KEYNOTE999"]) |
| assert "Brain MRI" in spec["description"] |
| assert spec["importance_color"] == "red" |
|
|
| def test_progress_tracker_all_states(self): |
| for state in JOURNEY_STATES: |
| spec = render_progress_tracker(state) |
| assert len(spec["steps"]) == 5 |
| current_steps = [s for s in spec["steps"] if s["status"] == "current"] |
| assert len(current_steps) == 1 |
|
|
|
|
| class TestDoctorPacketGeneration: |
| """Verify doctor packet export generates valid JSON/Markdown.""" |
|
|
| def test_json_packet_structure(self): |
| profile = MOCK_PATIENT_PROFILE |
| ledgers = MOCK_ELIGIBILITY_LEDGERS |
| eligible = sum(1 for lg in ledgers if lg.traffic_light == "green") |
| uncertain = sum(1 for lg in ledgers if lg.traffic_light == "yellow") |
| ineligible = sum(1 for lg in ledgers if lg.traffic_light == "red") |
| total_gaps = sum(len(lg.gaps) for lg in ledgers) |
|
|
| packet = { |
| "patient_id": profile.patient_id, |
| "summary": { |
| "eligible_count": eligible, |
| "uncertain_count": uncertain, |
| "ineligible_count": ineligible, |
| "total_gaps": total_gaps, |
| }, |
| "trials": [ |
| { |
| "nct_id": lg.nct_id, |
| "overall_assessment": lg.overall_assessment.value, |
| "met": lg.met_count, |
| "not_met": lg.not_met_count, |
| "unknown": lg.unknown_count, |
| "gaps": [g.description for g in lg.gaps], |
| } |
| for lg in ledgers |
| ], |
| } |
|
|
| serialized = json.dumps(packet, indent=2) |
| restored = json.loads(serialized) |
| assert restored["patient_id"] == "MOCK-P001" |
| assert restored["summary"]["eligible_count"] == 1 |
| assert restored["summary"]["uncertain_count"] == 1 |
| assert restored["summary"]["ineligible_count"] == 1 |
| assert restored["summary"]["total_gaps"] == 2 |
| assert len(restored["trials"]) == 3 |
|
|
| def test_all_trial_nct_ids_in_packet(self): |
| ledgers = MOCK_ELIGIBILITY_LEDGERS |
| packet_ids = [lg.nct_id for lg in ledgers] |
| expected_ids = ["MOCK-NCT-KEYNOTE999", "MOCK-NCT-FLAURA2", "MOCK-NCT-CM817"] |
| assert packet_ids == expected_ids |
|
|
|
|
| class TestSearchAnchorsFromProfile: |
| """Verify BE model can generate SearchAnchors from PatientProfile.""" |
|
|
| def test_profile_to_search_anchors(self): |
| profile = MOCK_PATIENT_PROFILE |
| assert profile.diagnosis is not None |
| assert profile.performance_status is not None |
| anchors = SearchAnchors( |
| condition=profile.diagnosis.primary_condition, |
| subtype=profile.diagnosis.histology, |
| biomarkers=[b.name for b in profile.biomarkers], |
| stage=profile.diagnosis.stage, |
| age=profile.demographics.age, |
| performance_status_max=profile.performance_status.value, |
| ) |
| assert anchors.condition == "Non-Small Cell Lung Cancer" |
| assert "EGFR" in anchors.biomarkers |
| assert anchors.stage == "IIIB" |
| assert anchors.age == 62 |
|
|
| def test_search_anchors_serializes(self): |
| profile = MOCK_PATIENT_PROFILE |
| assert profile.diagnosis is not None |
| anchors = SearchAnchors( |
| condition=profile.diagnosis.primary_condition, |
| biomarkers=[b.name for b in profile.biomarkers], |
| stage=profile.diagnosis.stage, |
| ) |
| data = anchors.model_dump_json() |
| restored = SearchAnchors.model_validate_json(data) |
| assert restored.condition == anchors.condition |
|
|