Portal Delete run (Super Admin), lab codes #3/#4, Z23 pointer #45, T1017 #46, payer note, login security-image wait
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a8af44e673
commit
fafa5285eb
21 files changed
+1509
-63
No files matched your search
@@ -520,6 +520,26 @@ def tracking_columns_fill_only_for_claims_the_run_read():
|
||||
assert unread[_col(h)] == "", (h, unread)
|
||||
|
||||
|
||||
@case
|
||||
def a_payer_the_sheet_and_ecw_disagree_on_is_noted_and_ecws_is_used():
|
||||
# Operator, 2026-10-28: the rules go by eCW's payer; the sheet's is
|
||||
# compared, and a difference is a note — not a finding (Category kept).
|
||||
ecw = _ub(insurance=[{"Name": "Medicare"}])
|
||||
r = _build({"1000001": cs.claim_outcome(True, ecw)})[1]
|
||||
assert r[_col("Category")] == "Pass", r
|
||||
assert r[_col("Comments")].endswith(
|
||||
"; Payer: sheet says Medicaid, eCW says Medicare - eCW used"), r
|
||||
assert r[_col("PAYER")] == "Medicaid", "the sheet's column stays"
|
||||
|
||||
|
||||
@case
|
||||
def no_payer_note_when_they_agree_or_ecw_shows_none():
|
||||
for ecw in ([{"Name": " medicaid "}], [], [{"Name": ""}]):
|
||||
r = _build({"1000001": cs.claim_outcome(True,
|
||||
_ub(insurance=ecw))})[1]
|
||||
assert "Payer:" not in r[_col("Comments")], (ecw, r)
|
||||
|
||||
|
||||
@case
|
||||
def a_claim_id_only_test_sheet_still_builds():
|
||||
rows = cs.build_rows([["Claim No"], ["9000002"], ["9000001"]], 0, 0,
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
r"""
|
||||
conditions/lab_codes.py — document #3 and #4 (EEWA-38, operator
|
||||
2026-10-28): a lab code (5 digits starting with 8) by the claim's primary
|
||||
payer —
|
||||
|
||||
* a Medicare payer (#25's exact eCW names): no lab code at all;
|
||||
* Cigna Salud Employee: any lab code;
|
||||
* every other payer, blank included: only the 13 in-house codes.
|
||||
|
||||
A lab code the payer does not take is unchecked and its Billed Fee zeroed.
|
||||
36416 and every non-8 code are never touched. Not on Dental claims.
|
||||
|
||||
python tests\test_conditions_lab_codes.py
|
||||
|
||||
Uses hcfa_claim.html like the Medicaid uncheck tests: `_inject_cpt_row()`
|
||||
adds the lab lines.
|
||||
"""
|
||||
|
||||
import io
|
||||
import sys
|
||||
import pathlib
|
||||
import inspect
|
||||
import contextlib
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
sys.path.insert(0, str(ROOT / "tests"))
|
||||
|
||||
FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "hcfa_claim.html"
|
||||
|
||||
from ecw_bot.conditions import lab_codes as cond # noqa: E402
|
||||
from ecw_bot.conditions import CONDITIONS # noqa: E402
|
||||
from ecw_bot.conditions.base import ConditionResult, run_all # noqa: E402
|
||||
from ecw_bot.core import charge_summary as cs # noqa: E402
|
||||
from ecw_bot.selectors import claim_form as sel # noqa: E402
|
||||
from test_conditions_medicaid_uncheck_cpts import _inject_cpt_row # noqa: E402
|
||||
|
||||
CASES = []
|
||||
|
||||
|
||||
def case(fn):
|
||||
CASES.append(fn)
|
||||
return fn
|
||||
|
||||
|
||||
def _claim(payer, *codes, screen=None):
|
||||
data = {"insurance": [{"Name": payer}] if payer is not None else [],
|
||||
"cpt": [{"code": c, "checked": True, "fee": "45.00"}
|
||||
for c in codes]}
|
||||
if screen:
|
||||
data["screen"] = screen
|
||||
return data
|
||||
|
||||
|
||||
# ── detect (no browser) ──────────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def a_lab_code_is_five_digits_starting_with_8():
|
||||
for code in ("80053", "85025", " 87636 ", "89999", "80000"):
|
||||
assert cond.is_lab_code(code), code
|
||||
for code in ("36416", "99213", "8005", "800530", "G0466", "8005F",
|
||||
"J2001", ""):
|
||||
assert not cond.is_lab_code(code), code
|
||||
|
||||
|
||||
@case
|
||||
def medicare_payers_take_no_lab_code():
|
||||
for payer in ("Medicare", "medicare-novitas", "HCFA Medicare",
|
||||
"Humana Medicare", "Devoted MCR UB", "Cigna Healthspring",
|
||||
" Cigna Health Spring/ Bravo "):
|
||||
assert cond.detect("C1", _claim(payer, "99213", "82948")), payer
|
||||
assert cond.detect("C1", _claim(payer, "80053")), payer
|
||||
|
||||
|
||||
@case
|
||||
def cigna_salud_takes_any_lab_code():
|
||||
assert cond.detect("C1", _claim("Cigna Salud Employee", "80053",
|
||||
"85025", "82948")) is False
|
||||
assert cond.detect("C1", _claim(" cigna salud employee ",
|
||||
"80053")) is False
|
||||
|
||||
|
||||
@case
|
||||
def other_payers_take_only_the_13_in_house_codes():
|
||||
in_house = ("82948", "81025", "85018", "82270", "82274", "87806",
|
||||
"85610", "87880", "80305", "81001", "81003", "87210",
|
||||
"87636")
|
||||
assert len(set(in_house)) == 13
|
||||
for payer in ("Medicaid", "Cigna HealthCare Of Colorado", "AARP", "",
|
||||
None):
|
||||
assert cond.detect("C1", _claim(payer, "99213", *in_house)) is False, \
|
||||
payer
|
||||
assert cond.detect("C1", _claim(payer, "82948", "80053")), payer
|
||||
|
||||
|
||||
@case
|
||||
def non_lab_codes_never_fire_it():
|
||||
assert cond.detect("C1", _claim("Medicare", "99213", "36416",
|
||||
"G0467")) is False
|
||||
assert cond.detect("C1", {}) is False
|
||||
|
||||
|
||||
@case
|
||||
def not_on_a_dental_claim():
|
||||
assert cond.detect("C1", _claim("Medicare", "80053",
|
||||
screen="dental")) is False
|
||||
|
||||
|
||||
# ── apply (browser) ──────────────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def medicare_every_lab_line_unchecked_and_zeroed(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "82948", row_id="CPTROW_3", fee="15.00")
|
||||
_inject_cpt_row(page, "36416", row_id="CPTROW_4", fee="8.00")
|
||||
result = cond.apply(page, "C1", _claim("Medicare", "82948", "36416"))
|
||||
assert isinstance(result, ConditionResult)
|
||||
assert result.condition_name == "lab_codes"
|
||||
assert [c["code"] for c in result.changes] == ["82948"], result.changes
|
||||
c = result.changes[0]
|
||||
assert c["unchecked"] is True and c["fee"] == {"before": "15.00",
|
||||
"after": "0.00"}, c
|
||||
rows = page.locator(sel.SEL_CPT_ROWS)
|
||||
assert rows.nth(3).locator(sel.SEL_CPT_BILLED_FEE).first.input_value() \
|
||||
== "0.00"
|
||||
assert rows.nth(4).locator(sel.SEL_CPT_BILLED_FEE).first.input_value() \
|
||||
== "8.00", "36416 is not a lab code"
|
||||
assert rows.nth(0).locator(sel.SEL_CPT_BILLED_FEE).first.input_value() \
|
||||
== "210.00", "99213 must be left alone"
|
||||
|
||||
|
||||
@case
|
||||
def other_payer_only_the_codes_outside_the_13(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "87880", row_id="CPTROW_3", fee="30.00")
|
||||
_inject_cpt_row(page, "80053", row_id="CPTROW_4", fee="55.00")
|
||||
result = cond.apply(page, "C1", _claim("Cigna HealthCare Of Colorado",
|
||||
"87880", "80053"))
|
||||
assert [c["code"] for c in result.changes] == ["80053"], result.changes
|
||||
rows = page.locator(sel.SEL_CPT_ROWS)
|
||||
assert rows.nth(3).locator(sel.SEL_CPT_BILLED_FEE).first.input_value() \
|
||||
== "30.00", "87880 is in-house"
|
||||
|
||||
|
||||
@case
|
||||
def apply_is_a_no_op_once_already_excluded(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "80053", checked=False, fee="0.00")
|
||||
result = cond.apply(page, "C1", _claim("Medicare", "80053"))
|
||||
assert result.changed is False and result.changes == []
|
||||
|
||||
|
||||
@case
|
||||
def the_comment_names_the_codes_and_the_rule(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "80053", fee="55.00")
|
||||
text = cond.summary_comment(cond.apply(page, "C1",
|
||||
_claim("Medicare", "80053")))
|
||||
assert "80053" in text and "Medicare" in text, text
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "80053", fee="55.00")
|
||||
text = cond.summary_comment(cond.apply(page, "C1",
|
||||
_claim("AARP", "80053")))
|
||||
assert "80053" in text and "in-house" in text, text
|
||||
|
||||
|
||||
@case
|
||||
def describe_names_the_code_and_the_fee_change(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "80053", fee="55.00")
|
||||
text = cond.describe(cond.apply(page, "C1", _claim("AARP", "80053")))
|
||||
assert "80053" in text and "55.00" in text and "0.00" in text, text
|
||||
|
||||
|
||||
# ── wiring ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def it_is_registered_before_priced_line_check():
|
||||
names = [m.NAME for m in CONDITIONS]
|
||||
assert "lab_codes" in names
|
||||
assert names.index("lab_codes") < names.index("priced_line_check"), names
|
||||
|
||||
|
||||
@case
|
||||
def the_charge_summary_has_a_fallback_comment():
|
||||
assert "Lab code" in cs.COMMENTS["lab_codes"]
|
||||
|
||||
|
||||
@case
|
||||
def run_all_leaves_the_lab_line_unchecked_at_the_end(page):
|
||||
# priced_line_check runs last: the zeroed lab line must stay unchecked.
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "80053", fee="55.00")
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
results = run_all(page, "C1", _claim("Medicare", "80053"))
|
||||
assert "lab_codes" in [r.condition_name for r in results], results
|
||||
row = page.locator(sel.SEL_CPT_ROWS).nth(3)
|
||||
assert row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() \
|
||||
is False
|
||||
assert row.locator(sel.SEL_CPT_BILLED_FEE).first.input_value() == "0.00"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
failures = 0
|
||||
with sync_playwright() as pw:
|
||||
browser = pw.chromium.launch(headless=True)
|
||||
page = browser.new_page()
|
||||
for fn in CASES:
|
||||
name = fn.__name__.replace("_", " ")
|
||||
try:
|
||||
if len(inspect.signature(fn).parameters) == 0:
|
||||
fn()
|
||||
else:
|
||||
fn(page)
|
||||
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
|
||||
browser.close()
|
||||
|
||||
print(f"\n{len(CASES) - failures}/{len(CASES)} passed")
|
||||
return 1 if failures else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -56,12 +56,13 @@ from ecw_bot.conditions import (bh_h_code_modifier, # noqa: E402
|
||||
depression_dx_pointer, dummy_codes,
|
||||
injection_admin_code,
|
||||
medicare_pps_code,
|
||||
j2930_j2001_update, medicaid_uncheck_cpts,
|
||||
j2930_j2001_update, lab_codes, medicaid_uncheck_cpts,
|
||||
t1017_non_billable,
|
||||
medicare_billing_screening,
|
||||
medicare_telehealth,
|
||||
medicare_vaccine_a6, medicare_wellness_history,
|
||||
medicaid_pregnancy_th,
|
||||
telehealth, vaccine_admin_codes,
|
||||
telehealth, vaccine_admin_codes, vaccine_z23_pointer,
|
||||
zero_fee_uncheck)
|
||||
from ecw_bot.conditions.base import ( # noqa: E402
|
||||
CPT_TAB_NOT_CONFIRMED, ConditionResult, run_all)
|
||||
@@ -578,10 +579,12 @@ def registered_last_with_zero_fee_uncheck_first():
|
||||
zero_fee_uncheck,
|
||||
telehealth, medicare_telehealth, depression_dx_pointer,
|
||||
j2930_j2001_update, dummy_codes, dental_dummy_codes,
|
||||
dental_payer_check, medicaid_uncheck_cpts,
|
||||
dental_payer_check, medicaid_uncheck_cpts, lab_codes,
|
||||
t1017_non_billable,
|
||||
medicare_billing_screening, medicare_vaccine_a6,
|
||||
medicare_wellness_history, medicaid_pregnancy_th,
|
||||
bh_h_code_modifier, vaccine_admin_codes, injection_admin_code,
|
||||
bh_h_code_modifier, vaccine_z23_pointer, vaccine_admin_codes,
|
||||
injection_admin_code,
|
||||
medicare_pps_code,
|
||||
plc,
|
||||
], conditions.CONDITIONS
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
r"""
|
||||
conditions/t1017_non_billable.py — document #46 (EEWA-40, operator
|
||||
2026-10-28): T1017 by the claim's primary payer —
|
||||
|
||||
* a Medicare payer (#25's exact eCW names) or Medicaid (the dashboard's
|
||||
"Medicaid" group only, as #43/#44): unchecked and Billed Fee 0;
|
||||
* any other payer (RAE, CHP+, blank, unmapped included): nothing
|
||||
changed, Review.
|
||||
|
||||
python tests\test_conditions_t1017_non_billable.py
|
||||
|
||||
Uses hcfa_claim.html like the Medicaid uncheck tests.
|
||||
"""
|
||||
|
||||
import io
|
||||
import sys
|
||||
import pathlib
|
||||
import inspect
|
||||
import contextlib
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
sys.path.insert(0, str(ROOT / "tests"))
|
||||
|
||||
FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "hcfa_claim.html"
|
||||
|
||||
from ecw_bot.conditions import t1017_non_billable as cond # noqa: E402
|
||||
from ecw_bot.conditions import CONDITIONS # noqa: E402
|
||||
from ecw_bot.conditions.base import ConditionResult, run_all # noqa: E402
|
||||
from ecw_bot.core import charge_summary as cs # noqa: E402
|
||||
from ecw_bot.selectors import claim_form as sel # noqa: E402
|
||||
from test_conditions_medicaid_uncheck_cpts import _inject_cpt_row # noqa: E402
|
||||
|
||||
CASES = []
|
||||
|
||||
|
||||
def case(fn):
|
||||
CASES.append(fn)
|
||||
return fn
|
||||
|
||||
|
||||
def _claim(payer, *codes, screen=None):
|
||||
data = {"insurance": [{"Name": payer}] if payer is not None else [],
|
||||
"cpt": [{"code": c, "checked": True, "fee": "45.00"}
|
||||
for c in codes]}
|
||||
if screen:
|
||||
data["screen"] = screen
|
||||
return data
|
||||
|
||||
|
||||
def _fee(page, n):
|
||||
return page.locator(sel.SEL_CPT_ROWS).nth(n).locator(
|
||||
sel.SEL_CPT_BILLED_FEE).first.input_value()
|
||||
|
||||
|
||||
# ── detect (no browser) ──────────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def detect_t1017_on_any_payer():
|
||||
for payer in ("Medicare", "Medicaid", "AARP", "", None):
|
||||
assert cond.detect("C1", _claim(payer, "99213", "T1017")), payer
|
||||
assert cond.detect("C1", _claim("Medicaid", " t1017 "))
|
||||
|
||||
|
||||
@case
|
||||
def no_t1017_or_dental_never_fires_it():
|
||||
assert not cond.detect("C1", _claim("Medicaid", "99213", "T1016"))
|
||||
assert not cond.detect("C1", {})
|
||||
assert not cond.detect("C1", _claim("Medicaid", "T1017",
|
||||
screen="dental"))
|
||||
|
||||
|
||||
@case
|
||||
def which_payers_make_it_non_billable():
|
||||
for payer in ("Medicare", "HCFA Medicare", "Humana Medicare",
|
||||
"Devoted MCR UB", "Medicaid", "HCFA Medicaid"):
|
||||
assert cond.non_billable_for(_claim(payer, "T1017")), payer
|
||||
for payer in ("BH COLORADO ACCESS", "Child Health Plan Plus", "AARP",
|
||||
"NO SUCH PAYER XYZ", "", None):
|
||||
assert not cond.non_billable_for(_claim(payer, "T1017")), payer
|
||||
|
||||
|
||||
# ── apply (browser) ──────────────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def medicaid_t1017_unchecked_and_zeroed(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "T1017", fee="80.00")
|
||||
result = cond.apply(page, "C1", _claim("Medicaid", "99213", "T1017"))
|
||||
assert isinstance(result, ConditionResult)
|
||||
assert result.changed and not result.review, result
|
||||
assert [c["code"] for c in result.changes] == ["T1017"], result.changes
|
||||
assert _fee(page, 3) == "0.00"
|
||||
row = page.locator(sel.SEL_CPT_ROWS).nth(3)
|
||||
assert row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() \
|
||||
is False
|
||||
assert _fee(page, 0) == "210.00", "99213 must be left alone"
|
||||
|
||||
|
||||
@case
|
||||
def medicare_t1017_unchecked_and_zeroed(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "T1017", fee="80.00")
|
||||
result = cond.apply(page, "C1", _claim("Humana Medicare", "T1017"))
|
||||
assert result.changed and _fee(page, 3) == "0.00", result
|
||||
|
||||
|
||||
@case
|
||||
def any_other_payer_is_review_and_nothing_changes(page):
|
||||
for payer in ("Cigna HealthCare Of Colorado", "BH COLORADO ACCESS", ""):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "T1017", fee="80.00")
|
||||
result = cond.apply(page, "C1", _claim(payer, "T1017"))
|
||||
assert result.changed is False, (payer, result)
|
||||
assert "T1017" in result.review and "check by hand" in \
|
||||
result.review, (payer, result)
|
||||
assert _fee(page, 3) == "80.00", payer
|
||||
assert "Cigna HealthCare Of Colorado" in cond.apply(
|
||||
page, "C1", _claim("Cigna HealthCare Of Colorado", "T1017")).review
|
||||
|
||||
|
||||
@case
|
||||
def the_comment_names_the_payer_group(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "T1017", fee="80.00")
|
||||
text = cond.summary_comment(cond.apply(page, "C1",
|
||||
_claim("Medicaid", "T1017")))
|
||||
assert "T1017" in text and "Medicaid" in text, text
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "T1017", fee="80.00")
|
||||
text = cond.describe(cond.apply(page, "C1", _claim("Medicare", "T1017")))
|
||||
assert "T1017" in text and "80.00" in text, text
|
||||
|
||||
|
||||
# ── wiring ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def it_is_registered_before_priced_line_check():
|
||||
names = [m.NAME for m in CONDITIONS]
|
||||
assert "t1017_non_billable" in names
|
||||
assert names.index("t1017_non_billable") < names.index(
|
||||
"priced_line_check"), names
|
||||
|
||||
|
||||
@case
|
||||
def the_charge_summary_has_a_fallback_comment():
|
||||
assert "T1017" in cs.COMMENTS["t1017_non_billable"]
|
||||
|
||||
|
||||
@case
|
||||
def run_all_leaves_t1017_unchecked_at_the_end(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "T1017", fee="80.00")
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
results = run_all(page, "C1", _claim("Medicaid", "T1017"))
|
||||
assert "t1017_non_billable" in [r.condition_name for r in results], \
|
||||
results
|
||||
row = page.locator(sel.SEL_CPT_ROWS).nth(3)
|
||||
assert row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() \
|
||||
is False
|
||||
assert _fee(page, 3) == "0.00"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
failures = 0
|
||||
with sync_playwright() as pw:
|
||||
browser = pw.chromium.launch(headless=True)
|
||||
page = browser.new_page()
|
||||
for fn in CASES:
|
||||
name = fn.__name__.replace("_", " ")
|
||||
try:
|
||||
if len(inspect.signature(fn).parameters) == 0:
|
||||
fn()
|
||||
else:
|
||||
fn(page)
|
||||
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
|
||||
browser.close()
|
||||
|
||||
print(f"\n{len(CASES) - failures}/{len(CASES)} passed")
|
||||
return 1 if failures else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,228 @@
|
||||
r"""
|
||||
conditions/vaccine_z23_pointer.py — document #45 (EEWA-39, operator
|
||||
2026-10-28): "Make sure you are assigning the proper diagnosis code (Z23)
|
||||
for ALL VACCINES".
|
||||
|
||||
Every line with one of the client's 19 vaccine codes (#18's list; not the
|
||||
admin codes) carries Z23's ICD pointer — its number in the ICD list — in
|
||||
ICD1-4. Missing → the first empty slot, the line's other pointers kept. No
|
||||
Z23 on the claim, or no free slot → Review. Other lines are never touched.
|
||||
All payers; not Dental.
|
||||
|
||||
python tests\test_conditions_vaccine_z23_pointer.py
|
||||
|
||||
Uses hcfa_claim.html; `_inject_vaccine_row()` adds the vaccine lines.
|
||||
"""
|
||||
|
||||
import io
|
||||
import sys
|
||||
import pathlib
|
||||
import inspect
|
||||
import contextlib
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
|
||||
FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "hcfa_claim.html"
|
||||
|
||||
from ecw_bot.conditions import vaccine_z23_pointer as cond # noqa: E402
|
||||
from ecw_bot.conditions import CONDITIONS # noqa: E402
|
||||
from ecw_bot.conditions.base import ConditionResult, run_all # noqa: E402
|
||||
from ecw_bot.core import charge_summary as cs # noqa: E402
|
||||
from ecw_bot.selectors import claim_form as sel # noqa: E402
|
||||
|
||||
CASES = []
|
||||
|
||||
|
||||
def case(fn):
|
||||
CASES.append(fn)
|
||||
return fn
|
||||
|
||||
|
||||
_ICD = [{"#": "1", "Code": "J06.9", "Name": "..."},
|
||||
{"#": "2", "Code": "Z23", "Name": "Encounter for immunization"}]
|
||||
|
||||
|
||||
def _claim(*codes, icd=_ICD, screen=None):
|
||||
data = {"insurance": [{"Name": "Medicaid"}], "icd": list(icd),
|
||||
"cpt": [{"code": c, "checked": True, "fee": "45.00"}
|
||||
for c in codes]}
|
||||
if screen:
|
||||
data["screen"] = screen
|
||||
return data
|
||||
|
||||
|
||||
def _inject_vaccine_row(page, code, row_id, icd=("1", "", "", "")):
|
||||
page.evaluate(
|
||||
"""(args) => {
|
||||
const tbody = document.querySelector(
|
||||
'#claimCPTCodesTable8888888888888 tbody');
|
||||
const addRowTr = tbody.querySelector('tr:last-child');
|
||||
const tr = document.createElement('tr');
|
||||
tr.id = args.rowId;
|
||||
tr.innerHTML =
|
||||
'<td><input data-fieldname="claimCPTCode" value="' + args.code + '" readonly></td>'
|
||||
+ '<td><input type="checkbox" ng-click="updateBillToIns(3)" checked></td>'
|
||||
+ args.icd.map((v, i) => '<td><input data-fieldname="ClaimCPTICD'
|
||||
+ (i + 1) + '" value="' + v + '"></td>').join('')
|
||||
+ '<td><input data-fieldname="ClaimCPTBilledFee" value="45.00"></td>';
|
||||
tbody.insertBefore(tr, addRowTr);
|
||||
}""", {"code": code, "rowId": row_id, "icd": list(icd)})
|
||||
|
||||
|
||||
def _icd_of(page, n):
|
||||
row = page.locator(sel.SEL_CPT_ROWS).nth(n)
|
||||
return [row.locator(s).first.input_value() for s in sel.SEL_CPT_ICD]
|
||||
|
||||
|
||||
# ── detect (no browser) ──────────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def detect_a_vaccine_code_on_any_payer():
|
||||
assert cond.detect("C1", _claim("99213", "90707"))
|
||||
data = _claim("90656")
|
||||
data["insurance"] = [{"Name": "Medicare"}]
|
||||
assert cond.detect("C1", data), "Medicare too"
|
||||
|
||||
|
||||
@case
|
||||
def detect_even_without_z23_so_the_claim_goes_to_review():
|
||||
assert cond.detect("C1", _claim("90707", icd=_ICD[:1]))
|
||||
|
||||
|
||||
@case
|
||||
def admin_codes_alone_or_no_vaccine_never_fire_it():
|
||||
assert not cond.detect("C1", _claim("99213", "90471", "90460"))
|
||||
assert not cond.detect("C1", _claim("99213"))
|
||||
assert not cond.detect("C1", {})
|
||||
|
||||
|
||||
@case
|
||||
def not_on_a_dental_claim():
|
||||
assert not cond.detect("C1", _claim("90707", screen="dental"))
|
||||
|
||||
|
||||
@case
|
||||
def z23_is_found_with_or_without_a_dot():
|
||||
for code in ("Z23", "z23", " Z23. ", "Z23.0"):
|
||||
data = _claim("90707", icd=[{"#": "3", "Code": code}])
|
||||
assert cond.z23_pointer(data) == "3", code
|
||||
assert cond.z23_pointer(_claim("90707", icd=[{"#": "1",
|
||||
"Code": "Z23.5"}])) is None
|
||||
|
||||
|
||||
# ── apply (browser) ──────────────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def the_pointer_goes_in_the_first_empty_slot_of_each_vaccine_line(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_vaccine_row(page, "90707", "CPTROW_3", ("1", "", "", ""))
|
||||
_inject_vaccine_row(page, "90656", "CPTROW_4", ("", "", "", ""))
|
||||
first_before = _icd_of(page, 0)
|
||||
result = cond.apply(page, "C1", _claim("99213", "90707", "90656"))
|
||||
assert isinstance(result, ConditionResult)
|
||||
assert result.changed and not result.review, result
|
||||
assert [c["code"] for c in result.changes] == ["90707", "90656"]
|
||||
assert _icd_of(page, 3) == ["1", "2", "", ""]
|
||||
assert _icd_of(page, 4) == ["2", "", "", ""]
|
||||
assert _icd_of(page, 0) == first_before, "99213 must be left alone"
|
||||
|
||||
|
||||
@case
|
||||
def a_line_that_already_carries_it_is_left_alone(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_vaccine_row(page, "90707", "CPTROW_3", ("1", "2", "", ""))
|
||||
result = cond.apply(page, "C1", _claim("90707"))
|
||||
assert result.changed is False and not result.review, result
|
||||
assert _icd_of(page, 3) == ["1", "2", "", ""]
|
||||
|
||||
|
||||
@case
|
||||
def no_z23_on_the_claim_is_review_and_nothing_changes(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_vaccine_row(page, "90707", "CPTROW_3", ("1", "", "", ""))
|
||||
result = cond.apply(page, "C1", _claim("90707", icd=_ICD[:1]))
|
||||
assert result.changed is False
|
||||
assert "Z23 not on the claim" in result.review, result
|
||||
assert _icd_of(page, 3) == ["1", "", "", ""]
|
||||
|
||||
|
||||
@case
|
||||
def a_line_with_no_free_slot_is_review_the_others_still_fixed(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_vaccine_row(page, "90707", "CPTROW_3", ("1", "3", "4", "5"))
|
||||
_inject_vaccine_row(page, "90656", "CPTROW_4", ("1", "", "", ""))
|
||||
result = cond.apply(page, "C1", _claim("90707", "90656"))
|
||||
assert [c["code"] for c in result.changes] == ["90656"], result
|
||||
assert "no free pointer slot" in result.review and "90707" in \
|
||||
result.review, result
|
||||
assert _icd_of(page, 3) == ["1", "3", "4", "5"]
|
||||
|
||||
|
||||
@case
|
||||
def the_comment_names_the_codes(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_vaccine_row(page, "90707", "CPTROW_3")
|
||||
text = cond.summary_comment(cond.apply(page, "C1", _claim("90707")))
|
||||
assert "Z23" in text and "90707" in text, text
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_vaccine_row(page, "90707", "CPTROW_3")
|
||||
text = cond.describe(cond.apply(page, "C1", _claim("90707")))
|
||||
assert "90707" in text and "Z23" in text, text
|
||||
|
||||
|
||||
# ── wiring ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def it_is_registered_before_priced_line_check():
|
||||
names = [m.NAME for m in CONDITIONS]
|
||||
assert "vaccine_z23_pointer" in names
|
||||
assert names.index("vaccine_z23_pointer") < names.index(
|
||||
"priced_line_check"), names
|
||||
|
||||
|
||||
@case
|
||||
def the_charge_summary_has_a_fallback_comment():
|
||||
assert "Z23" in cs.COMMENTS["vaccine_z23_pointer"]
|
||||
|
||||
|
||||
@case
|
||||
def run_all_adds_the_pointer(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_vaccine_row(page, "90707", "CPTROW_3", ("1", "", "", ""))
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
results = run_all(page, "C1", _claim("90707"))
|
||||
assert "vaccine_z23_pointer" in [r.condition_name for r in results], \
|
||||
results
|
||||
assert _icd_of(page, 3) == ["1", "2", "", ""]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
failures = 0
|
||||
with sync_playwright() as pw:
|
||||
browser = pw.chromium.launch(headless=True)
|
||||
page = browser.new_page()
|
||||
for fn in CASES:
|
||||
name = fn.__name__.replace("_", " ")
|
||||
try:
|
||||
if len(inspect.signature(fn).parameters) == 0:
|
||||
fn()
|
||||
else:
|
||||
fn(page)
|
||||
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
|
||||
browser.close()
|
||||
|
||||
print(f"\n{len(CASES) - failures}/{len(CASES)} passed")
|
||||
return 1 if failures else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -55,13 +55,14 @@ from ecw_bot.conditions import (bh_h_code_modifier, # noqa: E402
|
||||
depression_dx_pointer, dummy_codes,
|
||||
injection_admin_code,
|
||||
medicare_pps_code,
|
||||
j2930_j2001_update, medicaid_uncheck_cpts,
|
||||
j2930_j2001_update, lab_codes, medicaid_uncheck_cpts,
|
||||
t1017_non_billable,
|
||||
medicare_billing_screening,
|
||||
medicare_telehealth,
|
||||
medicare_vaccine_a6, medicare_wellness_history,
|
||||
medicaid_pregnancy_th,
|
||||
priced_line_check, telehealth,
|
||||
vaccine_admin_codes)
|
||||
vaccine_admin_codes, vaccine_z23_pointer)
|
||||
from ecw_bot.conditions.base import ( # noqa: E402
|
||||
CPT_TAB_NOT_CONFIRMED, ConditionResult, run_all)
|
||||
from ecw_bot.pages import claim_form # noqa: E402
|
||||
@@ -520,10 +521,12 @@ def registered_first_in_conditions():
|
||||
assert conditions.CONDITIONS[1:] == [
|
||||
telehealth, medicare_telehealth, depression_dx_pointer,
|
||||
j2930_j2001_update, dummy_codes, dental_dummy_codes,
|
||||
dental_payer_check, medicaid_uncheck_cpts,
|
||||
dental_payer_check, medicaid_uncheck_cpts, lab_codes,
|
||||
t1017_non_billable,
|
||||
medicare_billing_screening, medicare_vaccine_a6,
|
||||
medicare_wellness_history, medicaid_pregnancy_th,
|
||||
bh_h_code_modifier, vaccine_admin_codes, injection_admin_code,
|
||||
bh_h_code_modifier, vaccine_z23_pointer, vaccine_admin_codes,
|
||||
injection_admin_code,
|
||||
medicare_pps_code,
|
||||
priced_line_check,
|
||||
], conditions.CONDITIONS
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
r"""
|
||||
auth/login.py `wait_after_username()` — what comes after the username's
|
||||
Next (live 2026-10-28 17:59: the security image page came late, the old
|
||||
one-look check missed it, and the bot clicked a password box that stayed
|
||||
disabled until it timed out).
|
||||
|
||||
It waits, up to its timeout, for whichever comes first: eCW's "VERIFY YOUR
|
||||
SECURITY IMAGE" page or a password box that is visible AND enabled. Neither
|
||||
→ a screenshot (with the DOM) and an error that says so.
|
||||
|
||||
python tests\test_login_username_step.py
|
||||
|
||||
Local headless chromium, `page.set_content()` only. No eCW, no login.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
import pathlib
|
||||
import tempfile
|
||||
|
||||
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.auth import login # noqa: E402
|
||||
|
||||
CASES = []
|
||||
|
||||
|
||||
def case(fn):
|
||||
CASES.append(fn)
|
||||
return fn
|
||||
|
||||
|
||||
# The password box is on the page from the start, disabled — the shape
|
||||
# seen live; eCW enables it once the username step is done.
|
||||
_PAGE = """
|
||||
<input id="doctorID" value="someone">
|
||||
<input disabled type="password" id="passwordField">
|
||||
<div id="img" style="display:none">VERIFY YOUR SECURITY IMAGE</div>
|
||||
<script>%s</script>
|
||||
"""
|
||||
|
||||
|
||||
@case
|
||||
def a_late_security_image_page_is_seen(page):
|
||||
page.set_content(_PAGE % "setTimeout(() => document.getElementById("
|
||||
"'img').style.display = '', 3000);")
|
||||
assert login.wait_after_username(page, 10_000) == "image"
|
||||
|
||||
|
||||
@case
|
||||
def a_password_box_enabled_late_is_waited_for(page):
|
||||
page.set_content(_PAGE % "setTimeout(() => document.getElementById("
|
||||
"'passwordField').disabled = false, 2500);")
|
||||
t0 = time.monotonic()
|
||||
assert login.wait_after_username(page, 10_000) == "password"
|
||||
assert time.monotonic() - t0 >= 2, "returned before it was enabled"
|
||||
|
||||
|
||||
@case
|
||||
def after_the_image_only_the_password_box_counts(page):
|
||||
page.set_content(_PAGE % "document.getElementById('img').style.display"
|
||||
" = ''; setTimeout(() => document.getElementById("
|
||||
"'passwordField').disabled = false, 1500);")
|
||||
assert login.wait_after_username(page, 10_000, image=False) == "password"
|
||||
|
||||
|
||||
@case
|
||||
def a_password_box_that_stays_disabled_is_an_error_with_a_screenshot(page):
|
||||
page.set_content(_PAGE % "")
|
||||
try:
|
||||
login.wait_after_username(page, 2_000)
|
||||
except RuntimeError as exc:
|
||||
assert "password box stayed disabled" in str(exc), exc
|
||||
else:
|
||||
raise AssertionError("no error for a box that never enabled")
|
||||
shots = list(pathlib.Path(cfg.SCREENSHOTS).glob("*_username_step_disabled.*"))
|
||||
assert {p.suffix for p in shots} == {".png", ".html"}, shots
|
||||
|
||||
|
||||
def main() -> int:
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
failures = 0
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
cfg.SCREENSHOTS = pathlib.Path(tmp) # never the real folder
|
||||
with sync_playwright() as pw:
|
||||
browser = pw.chromium.launch(headless=True)
|
||||
page = browser.new_page()
|
||||
for fn in CASES:
|
||||
name = fn.__name__.replace("_", " ")
|
||||
try:
|
||||
fn(page)
|
||||
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
|
||||
browser.close()
|
||||
|
||||
print(f"\n{len(CASES) - failures}/{len(CASES)} passed")
|
||||
return 1 if failures else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -105,6 +105,16 @@ class FakeRepo:
|
||||
self._maybe_fail()
|
||||
return [dict(r) for r in self._rows if claim_no in r["claim_no"]]
|
||||
|
||||
delete_fails = None
|
||||
|
||||
def delete_run(self, run_at):
|
||||
self.calls.append(("delete_run", run_at))
|
||||
if self.delete_fails:
|
||||
raise self.delete_fails
|
||||
kept = [r for r in self._rows if r["run_at"] != run_at]
|
||||
gone, self._rows = len(self._rows) - len(kept), kept
|
||||
return gone
|
||||
|
||||
|
||||
TESTER_PASSWORD = "tester-password-1"
|
||||
|
||||
@@ -581,6 +591,63 @@ def the_error_page_names_what_went_wrong():
|
||||
assert "Smith" not in r.text, "the driver's message is never shown"
|
||||
|
||||
|
||||
# ── Delete a run (Super Admin only, operator 2026-10-28) ─────────────────────
|
||||
|
||||
def _delete_run(client, app, run_at=RUN_PM, token=None):
|
||||
return client.post("/admin/runs/delete", headers=_GOOD_ORIGIN,
|
||||
data={"run_at": run_at.isoformat(),
|
||||
"token": token or app.state.run_token},
|
||||
follow_redirects=False)
|
||||
|
||||
|
||||
@case
|
||||
def a_super_admin_deletes_one_run_and_the_delete_is_recorded():
|
||||
repo = FakeRepo()
|
||||
client, app = signed_in(repo, FakeRunner(), role="Super Admin")
|
||||
page = client.get(f"/day/{DAY.isoformat()}").text
|
||||
assert page.count("Delete run") == 2, "one button per run of the day"
|
||||
r = _delete_run(client, app)
|
||||
assert r.status_code == 303, r.status_code
|
||||
assert r.headers["location"].startswith(f"/day/{DAY.isoformat()}?"), \
|
||||
r.headers["location"]
|
||||
assert [x["run_at"] for x in repo._rows] == [RUN_AM], \
|
||||
"only that run's rows go"
|
||||
shown = client.get(r.headers["location"]).text
|
||||
assert "2 claims deleted" in shown, shown
|
||||
history = client.get("/admin/signins").text
|
||||
assert "run deleted" in history and "10/16/2026 2:15 PM" in history, \
|
||||
history
|
||||
|
||||
|
||||
@case
|
||||
def an_admin_or_viewer_cannot_see_or_use_delete_run():
|
||||
for role in ("Admin", "Viewer"):
|
||||
repo = FakeRepo()
|
||||
client, app = signed_in(repo, FakeRunner(), role=role)
|
||||
assert "Delete run" not in client.get(f"/day/{DAY.isoformat()}").text
|
||||
assert _delete_run(client, app).status_code == 403, role
|
||||
assert len(repo._rows) == 3, (role, "nothing deleted")
|
||||
repo = FakeRepo()
|
||||
client, app = signed_in(repo, FakeRunner(), role="Super Admin")
|
||||
assert _delete_run(client, app, token="wrong").status_code == 403, \
|
||||
"a page on another site cannot send it"
|
||||
assert len(repo._rows) == 3
|
||||
|
||||
|
||||
@case
|
||||
def delete_run_without_the_sql_permission_says_so():
|
||||
class DbError(Exception):
|
||||
pass
|
||||
|
||||
repo = FakeRepo()
|
||||
repo.delete_fails = DbError("42000", "The DELETE permission was denied")
|
||||
client, app = signed_in(repo, FakeRunner(), role="Super Admin")
|
||||
r = _quiet(_delete_run, client, app)
|
||||
assert r.status_code == 503, r.status_code
|
||||
assert "No permission to delete" in r.text, r.text
|
||||
assert "GRANT DELETE" in r.text, r.text
|
||||
|
||||
|
||||
# ── repo SQL ─────────────────────────────────────────────────────────────────
|
||||
|
||||
class FakeCursor:
|
||||
@@ -590,6 +657,7 @@ class FakeCursor:
|
||||
|
||||
def execute(self, sql, params):
|
||||
self.log.append((sql, list(params)))
|
||||
self.rowcount = len(self.rows)
|
||||
return self
|
||||
|
||||
def fetchall(self):
|
||||
@@ -599,10 +667,14 @@ class FakeCursor:
|
||||
class FakeConn:
|
||||
def __init__(self, rows=(), cols=()):
|
||||
self.log, self.rows, self.cols, self.closed = [], list(rows), cols, False
|
||||
self.committed = False
|
||||
|
||||
def cursor(self):
|
||||
return FakeCursor(self.log, self.rows, self.cols)
|
||||
|
||||
def commit(self):
|
||||
self.committed = True
|
||||
|
||||
def close(self):
|
||||
self.closed = True
|
||||
|
||||
@@ -611,6 +683,17 @@ def _repo(conn):
|
||||
return SqlResultsRepo("DSN=x", connect=lambda cs, timeout: conn)
|
||||
|
||||
|
||||
@case
|
||||
def repo_deletes_exactly_one_run_and_commits():
|
||||
conn = FakeConn(rows=[None] * 20) # the driver's rowcount: 20
|
||||
assert _repo(conn).delete_run(RUN_PM) == 20
|
||||
(sql, params), = conn.log
|
||||
assert re.fullmatch(r"DELETE FROM dbo\.CV_ChargeSummary WHERE run_at = \?",
|
||||
sql), sql
|
||||
assert params == [RUN_PM], params
|
||||
assert conn.committed and conn.closed
|
||||
|
||||
|
||||
@case
|
||||
def repo_turns_a_text_run_at_or_day_into_a_datetime_or_date():
|
||||
"""The legacy {SQL Server} driver returns DATETIME2 / DATE as text."""
|
||||
|
||||
Reference in new issue
Block a user