r""" The Charge Summary grows claim by claim (operator, 2026-10-12): each claim's row goes into the Excel summary and the results database as soon as that claim is done, not once at the end of the run. python tests\test_summary_per_claim.py Drives the real `_process_claims` with the search, the form read and both writers stubbed — no browser, no file, no database. """ import io import sys import pathlib import contextlib ROOT = pathlib.Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "src")) from ecw_bot import settings as cfg # noqa: E402 from ecw_bot.core import charge_summary as cs # noqa: E402 from ecw_bot.flows import export_medcor_ready as flow # noqa: E402 CASES = [] def case(fn): CASES.append(fn) return fn def _ub(fired=()): return {"cpt": [{"code": "99213", "checked": True, "fee": "100.00"}], "icd": [{"code": "Z00.00"}], "progress_notes": {"opened": "modal", "text": "Office visit."}, "_conditions": {"fired": list(fired), "saved": False, "failed": [], "review": [], "comments": {}}} def _run(ids, found=None, forms=None): """ `_process_claims` over `ids`. `found` = ids the search finds (default all), `forms` = claim id -> what the form read returns. Returns the event list: ("search", id), ("excel", sorted outcome ids), ("db", outcomes, details). """ found = set(ids if found is None else found) forms = forms or {} events = [] def search(page, cid): events.append(("search", cid)) return {"id": cid, "count": 1 if cid in found else 0, "found": cid in found, "seconds": 1.0} orig = (cfg.SHOW_EACH_CLAIM, cfg.SHOW_CLAIM_IDS, cfg.CLAIM_PROCESS_LIMIT, flow.claim_results.search_claim, flow._read_claim_form, flow.claim_filters.clear_claim_no, flow._write_charge_summary, flow._save_results_db) cfg.SHOW_EACH_CLAIM = False cfg.SHOW_CLAIM_IDS = False cfg.CLAIM_PROCESS_LIMIT = 0 flow.claim_results.search_claim = search flow._read_claim_form = lambda page, cid, head: dict( forms.get(cid) or _ub(), claim_no=cid) flow.claim_filters.clear_claim_no = lambda page: None flow._write_charge_summary = lambda info, outcomes: events.append( ("excel", sorted(outcomes))) flow._save_results_db = lambda info, outcomes, details: events.append( ("db", dict(outcomes), dict(details))) try: with contextlib.redirect_stdout(io.StringIO()): flow._process_claims(None, {"ids": list(ids), "file": None}) finally: (cfg.SHOW_EACH_CLAIM, cfg.SHOW_CLAIM_IDS, cfg.CLAIM_PROCESS_LIMIT, flow.claim_results.search_claim, flow._read_claim_form, flow.claim_filters.clear_claim_no, flow._write_charge_summary, flow._save_results_db) = orig return events @case def each_claim_is_written_to_the_excel_summary_before_the_next_starts(): events = _run(["111", "222"]) order = [e[:2] if e[0] != "db" else ("db",) for e in events] assert order == [("search", "111"), ("excel", ["111"]), ("db",), ("search", "222"), ("excel", ["111", "222"]), ("db",)], \ order @case def each_claim_saves_only_its_own_row_to_the_database(): events = _run(["111", "222"], forms={"222": _ub(["depression_dx_pointer"])}) db = [e for e in events if e[0] == "db"] assert [sorted(e[1]) for e in db] == [["111"], ["222"]], db assert db[0][1]["111"]["Comments"] == cs.CLEAN, db[0] assert db[1][1]["222"]["Category"] == "Fail", db[1] assert "ICD pointer issue" in db[1][1]["222"]["Comments"], db[1] assert sorted(db[1][2]) == ["222"], db[1] # The details are what the conditions reported, never the claim's own # fields (review, 2026-10-12). detail = db[1][2]["222"] assert detail["fired"] == ["depression_dx_pointer"], detail assert "cpt" not in detail and "progress_notes" not in detail, detail @case def a_claim_not_found_updates_the_excel_but_saves_nothing_to_the_database(): events = _run(["111", "222"], found=["222"]) assert ("excel", ["111"]) in events, events db = [e for e in events if e[0] == "db"] assert [sorted(e[1]) for e in db] == [["222"]], db @case def nothing_is_written_again_after_the_last_claim(): events = _run(["111"]) assert [e[0] for e in events] == ["search", "excel", "db"], events def main() -> int: failures = 0 for fn in CASES: name = fn.__name__.replace("_", " ") try: fn() print(f" ok {name}") except AssertionError as exc: print(f" FAIL {name}: {exc}") failures += 1 except Exception as exc: print(f" ERROR {name}: {type(exc).__name__}: {exc}") failures += 1 print(f"\n{len(CASES) - failures}/{len(CASES)} passed") return 1 if failures else 0 if __name__ == "__main__": sys.exit(main())