feat(sp41): cyclone rebill-from-835 CLI (--window, --override-filing, --status)
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
"""SP41 — tests for the `cyclone rebill-from-835` CLI.
|
||||
|
||||
Three smoke tests:
|
||||
1. --help renders cleanly and surfaces --window and --override-filing.
|
||||
2. --status (with no other args) renders cleanly and exits 0 — the
|
||||
option is recognized even when no prior summary.csv exists.
|
||||
3. End-to-end run against a 2-row visits CSV (one in-window, one
|
||||
ancient) and a zero-SVC 835 fixture produces a summary.csv with
|
||||
the in-window visit classified as REBILLED_B.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
from pathlib import Path
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
from cyclone.cli import main
|
||||
|
||||
|
||||
def test_rebill_from_835_help():
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["rebill-from-835", "--help"])
|
||||
assert result.exit_code == 0, result.output
|
||||
assert "--window" in result.output
|
||||
assert "--override-filing" in result.output
|
||||
|
||||
|
||||
def test_rebill_from_835_status_help():
|
||||
"""Either bare --status or --status --help should exit 0.
|
||||
|
||||
The spec note: the help-test ensures the option is recognized, so
|
||||
even a "no prior runs" 0-exit path is acceptable. We try both
|
||||
invocations because click short-circuits --help ahead of any
|
||||
required-option check; the bare --status path is the one that
|
||||
proves --status works without --visits / --ingest.
|
||||
"""
|
||||
runner = CliRunner()
|
||||
result_help = runner.invoke(main, ["rebill-from-835", "--status", "--help"])
|
||||
assert result_help.exit_code == 0, result_help.output
|
||||
|
||||
result_bare = runner.invoke(main, ["rebill-from-835", "--status"])
|
||||
assert result_bare.exit_code == 0, result_bare.output
|
||||
|
||||
|
||||
def test_rebill_from_835_runs(tmp_path: Path):
|
||||
"""End-to-end: 1 in-window visit + 1 ancient visit + zero-SVC 835 → REBILLED_B for the in-window row."""
|
||||
visits_path = tmp_path / "visits.csv"
|
||||
visits_path.write_text(
|
||||
"Visit Date,Member ID,Procedure Code,Billable Amount\n"
|
||||
"06/27/2026,J813715,T1019,$2.32\n"
|
||||
"01/01/2020,ANCIENT,T1019,$2.32\n"
|
||||
)
|
||||
|
||||
ingest_dir = tmp_path / "ingest"
|
||||
ingest_dir.mkdir()
|
||||
(ingest_dir / "x.835").write_text("ST*835*0001~SE*0*0001~")
|
||||
|
||||
out_dir = tmp_path / "out"
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, [
|
||||
"rebill-from-835",
|
||||
"--visits", str(visits_path),
|
||||
"--ingest", str(ingest_dir),
|
||||
"--out", str(out_dir),
|
||||
"--as-of", "2026-07-07",
|
||||
])
|
||||
assert result.exit_code == 0, (
|
||||
f"CLI exited {result.exit_code}, output={result.output!r}, "
|
||||
f"exception={result.exception!r}"
|
||||
)
|
||||
|
||||
summary_path = out_dir / "summary.csv"
|
||||
assert summary_path.exists(), summary_path
|
||||
|
||||
with summary_path.open(newline="") as f:
|
||||
rows = list(csv.DictReader(f))
|
||||
# Two visits in, two summary rows out (one REBILLED_B, one
|
||||
# EXCLUDED_TIMELY_FILING). PAID outcomes would be dropped, but
|
||||
# neither of these is PAID — the 835 has no SVCs.
|
||||
assert len(rows) == 2, rows
|
||||
|
||||
by_member = {r["member_id"]: r for r in rows}
|
||||
in_window = by_member["J813715"]
|
||||
assert in_window["disposition"] == "REBILLED_B", in_window
|
||||
assert in_window["dos"] == "2026-06-27", in_window
|
||||
|
||||
ancient = by_member["ANCIENT"]
|
||||
assert ancient["disposition"] == "EXCLUDED_TIMELY_FILING", ancient
|
||||
assert ancient["dos"] == "2020-01-01", ancient
|
||||
Reference in New Issue
Block a user