File size: 3,733 Bytes
a612609 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# 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()
|