Files
ecw-charge-validation/tests/test_summary_per_claim.py
T
ManikandanandClaude Opus 5.5 ba15a574ab Initial import: eCW Charge Validation bot and results Portal
Snapshot of the working project as of 2026-10-23 (local 2d4d4fc), one
commit with no earlier history: the tracked files were de-identified
first (synthetic claim ids and patient data only; see Context.md
section 7). Credentials, run logs, screenshots, claim reports and client
workbooks are gitignored and not included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 18:38:17 +05:30

141 lines
4.9 KiB
Python

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