|
|
|
|
|
""" |
|
|
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 |
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
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(): |
|
|
|
|
|
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) |
|
|
|
|
|
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() |
|
|
|