test(sp41): parametrized test driving shipped SP40 validator on real visit fixture

This commit is contained in:
Tyler Martinez
2026-07-08 01:45:08 -06:00
parent ce61b36504
commit 1978e696d7
2 changed files with 109 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
"Client","Visit Date","Payer","Authorized","Member ID","Auth Start Date","Auth End Date","Authorization #","ICD-10","Service","Procedure Code","Modifiers","Client Classes","Billable Hours","Billable Amount","Invoice #","Claimed"
"Roberts, Alice","02/05/2026","CO Medicaid","Yes","Q944140","01/01/2026","12/31/2026","3125","R69","PCS T1019","T1019","U1","DD Waiver","1","$333.62","INV-2026-02-05-Q944140","Yes"
"Smith, Bob","06/25/2026","CO Medicaid","Yes","R649327","01/01/2026","12/31/2026","4521","R69","S5150","S5150","U8","DD Waiver","1","$235.55","INV-2026-06-25-R649327","Yes"
"Davis, Carol","02/25/2026","CO Medicaid","Yes","Y188426","01/01/2026","12/31/2026","2890","R69","PCS T1019","T1019","KX:SC:U2","DD Waiver","1","$222.72","INV-2026-02-25-Y188426","Yes"
"Lee, David","06/04/2026","CO Medicaid","Yes","R649327","01/01/2026","12/31/2026","4521","R69","S5150","S5150","U8","DD Waiver","1","$214.22","INV-2026-06-04-R649327","Yes"
"Garcia, Eve","06/22/2026","CO Medicaid","Yes","R649327","01/01/2026","12/31/2026","4521","R69","S5150","S5150","U8","DD Waiver","1","$213.25","INV-2026-06-22-R649327","Yes"
1 Client Visit Date Payer Authorized Member ID Auth Start Date Auth End Date Authorization # ICD-10 Service Procedure Code Modifiers Client Classes Billable Hours Billable Amount Invoice # Claimed
2 Roberts, Alice 02/05/2026 CO Medicaid Yes Q944140 01/01/2026 12/31/2026 3125 R69 PCS T1019 T1019 U1 DD Waiver 1 $333.62 INV-2026-02-05-Q944140 Yes
3 Smith, Bob 06/25/2026 CO Medicaid Yes R649327 01/01/2026 12/31/2026 4521 R69 S5150 S5150 U8 DD Waiver 1 $235.55 INV-2026-06-25-R649327 Yes
4 Davis, Carol 02/25/2026 CO Medicaid Yes Y188426 01/01/2026 12/31/2026 2890 R69 PCS T1019 T1019 KX:SC:U2 DD Waiver 1 $222.72 INV-2026-02-25-Y188426 Yes
5 Lee, David 06/04/2026 CO Medicaid Yes R649327 01/01/2026 12/31/2026 4521 R69 S5150 S5150 U8 DD Waiver 1 $214.22 INV-2026-06-04-R649327 Yes
6 Garcia, Eve 06/22/2026 CO Medicaid Yes R649327 01/01/2026 12/31/2026 4521 R69 S5150 S5150 U8 DD Waiver 1 $213.25 INV-2026-06-22-R649327 Yes
+103
View File
@@ -336,6 +336,109 @@ def test_claim_passes_shipped_sp40_local_validator():
assert not report.errors, f"unexpected errors: {report.errors}"
def _load_visit_rows_from_csv(path) -> list[VisitRow]:
"""Load ``VisitRow`` records from the canonical AxisCare visits CSV shape.
The CSV is the same shape as ``ingest/jan1-jun272026.csv`` — see
:func:`cyclone.rebill.spot_check.parse_visits_csv` for the production
parser. We re-parse inline here so the test does not import the
scratch-dir script.
"""
import csv as _csv
from datetime import datetime as _dt
out: list[VisitRow] = []
with open(path, newline="", encoding="utf-8") as f:
reader = _csv.DictReader(f)
for row in reader:
raw_dos = (row.get("Visit Date") or "").strip()
if not raw_dos:
continue
try:
dos = _dt.strptime(raw_dos, "%m/%d/%Y").date()
except ValueError:
continue
member_id = (row.get("Member ID") or "").strip()
procedure = (row.get("Procedure Code") or "").strip()
if not (member_id and procedure):
continue
billed_clean = (row.get("Billable Amount") or "0").replace("$", "").replace(",", "")
try:
billed = Decimal(billed_clean or "0")
except Exception:
billed = Decimal("0")
mods_raw = (row.get("Modifiers") or "").strip()
modifiers = tuple(m.strip() for m in mods_raw.split(",") if m.strip())
out.append(VisitRow(
dos=dos, member_id=member_id,
client_name=(row.get("Client") or "").strip(),
procedure_code=procedure, modifiers=modifiers,
billed_amount=billed,
icd10=(row.get("ICD-10") or "").strip() or None,
prior_auth=(row.get("Authorization #") or "").strip() or None,
))
return out
@pytest.mark.parametrize("fixture_csv", ["tests/fixtures/spot_check_visits.csv"])
def test_every_visit_in_fixture_passes_full_pipeline(fixture_csv):
"""For each visit in the fixture CSV, the full pipeline (build → serialize →
SP40 local validator) passes.
This is the "drives shipped code on the real path" test: the fixture
CSV uses the exact same column shape and value shapes as
``ingest/jan1-jun272026.csv`` (the production visits export), and
each visit row runs through:
1. :func:`cyclone.rebill.spot_check.build_claim_output` (the
canonical helper)
2. :func:`cyclone.parsers.serialize_837.serialize_837` (the
shipped serializer)
3. :func:`cyclone.parsers.validator.validate` (the shipped SP40
local validator — 25 R-codes)
4. :func:`cyclone.rebill.spot_check.structural_spot_check`
(segment-class check)
The test pins all four on real visit data; if any step regresses
(e.g., SBR09 default flips, SVC date format breaks, NPI checksum
changes), the test fails.
"""
from cyclone.parsers.payer import PayerConfig
from cyclone.parsers.validator import validate as validate_claim
visits = _load_visit_rows_from_csv(fixture_csv)
assert visits, f"no visits parsed from {fixture_csv}"
cfg = PayerConfig.co_medicaid()
for idx, visit in enumerate(visits, start=1):
claim_id = f"{visit.dos.strftime('%Y%m%d')}-{visit.member_id}-{idx:02d}"
claim = build_claim_output(visit, claim_id=claim_id)
edi = serialize_837(
claim,
sender_id=SENDER_ID,
receiver_id="CO_TXIX",
submitter_name="Dzinesco",
submitter_contact_name="Tyler Martinez",
submitter_contact_email="tyler@dzinesco.com",
claim_filing_indicator_code=SBR09_DEFAULT,
)
# Structural segment-class check.
struct = structural_spot_check(edi)
assert struct.passed, (
f"{claim_id} failed structural spot check: "
f"missing={list(struct.missing)}"
)
# Shipped SP40 local validator (25 R-codes).
report = validate_claim(claim, cfg)
assert report.passed, (
f"{claim_id} failed SP40 local validator: "
f"errors={[(i.rule, i.message) for i in report.errors]}"
)
assert not report.errors, f"{claim_id}: unexpected errors: {report.errors}"
# --- Helpers -----------------------------------------------------------