# tests/integration_v2.py """ Integration sanity check for AI E-Consult V2 core layers. Run with: python -m tests.integration_v2 """ import sys import json from pathlib import Path from typing import Dict, Any # Ensure repo root on path REPO_ROOT = Path(__file__).resolve().parents[1] if str(REPO_ROOT) not in sys.path: sys.path.insert(0, str(REPO_ROOT)) from src import config, billing, modal_templates, store # noqa: E402 RESULTS = {"passed": 0, "failed": 0} def _ok(name: str): RESULTS["passed"] += 1 print(f"[PASS] {name}") def _fail(name: str, e: Exception): RESULTS["failed"] += 1 print(f"[FAIL] {name}: {e}") def check(name: str): def deco(fn): def wrapper(): try: fn() _ok(name) except Exception as e: _fail(name, e) return wrapper return deco @check("seed_cases_and_v2_schema") def test_seed_cases_and_v2_schema(): created = store.seed_cases(reset=True) assert len(created) == 4 for cid in created: case = store.read_case(cid) assert case and case["schema_version"] == config.SCHEMA_VERSION for k in ["status", "created_at", "updated_at", "billing", "explainability"]: assert k in case @check("load_sample_case") def test_load_sample_case(): items = store.list_cases() assert items, "No cases after seeding" case = store.read_case(items[0]["case_id"]) assert case is not None print(" case_id:", case["case_id"], "status:", case["status"]) @check("billing_autosuggest_representative") def test_billing_autosuggest_representative(): combos = [(5, False), (12, True), (35, True)] for minutes, spoke in combos: sugg = billing.autosuggest_cpt(minutes=minutes, spoke=spoke) assert isinstance(sugg, list) and len(sugg) >= 1 codes = [s.code for s in sugg] print(f" minutes={minutes} spoke={spoke} -> {codes} (eligible={[s.code for s in sugg if s.eligible]})") @check("modal_previews_safe_without_streamlit") def test_modal_previews_safe_without_streamlit(): # Build a simple note and claim, then render via modal helpers. cases = store.list_cases() case = store.read_case(cases[0]["case_id"]) assert case note_md = f"# Consult Note for {case['patient']['name']}\n\nGenerated for integration preview." pick = billing.autosuggest_cpt(minutes=12, spoke=True) code = next((s.code for s in pick if s.eligible), "99447") rate = next((s.rate for s in pick if s.code == code), 55.0) claim = billing.build_837_claim(case, code=code, rate=rate, minutes=12, spoke=True, attested=True) # These calls are safe even if Streamlit is missing—helpers no-op or use fallbacks modal_templates.show_consult_note_preview(note_md) modal_templates.show_837_claim_preview(claim) @check("export_filename_determinism") def test_export_filename_determinism(): p1 = config.make_export_path("INTEG", "consult_note.md") p2 = config.make_export_path("INTEG", "claim_837.json") assert p1.parent.exists() and p2.parent.exists() assert p1.name.startswith("EC-INTEG_") and p1.name.endswith("_consult_note.md") assert p2.name.startswith("EC-INTEG_") and p2.name.endswith("_claim_837.json") def main(): test_seed_cases_and_v2_schema() test_load_sample_case() test_billing_autosuggest_representative() test_modal_previews_safe_without_streamlit() test_export_filename_determinism() total = RESULTS["passed"] + RESULTS["failed"] print(f"\n=== Integration Summary: {RESULTS['passed']} passed, {RESULTS['failed']} failed, {total} total ===") if RESULTS["failed"]: raise SystemExit(1) if __name__ == "__main__": main()