new-site/scripts/tests/test_paper_batch.py
justin e6a630ada1 healthcare: verify CMS-10114 update path, correct NPI Enumerator address, build CMS-10114 filler
Verified firsthand against the live CMS-10114 (Rev. 02/25, OMB 0938-0931):
- Section 1A confirms paper is valid for Change of Information (#2) AND
  Reactivation (#4), not just initial enumeration. Resolves the UNCERTAIN flag.
- Current mailing address is CMS NPI Enumerator Services, Mail Stop DO-01-51,
  7500 Security Blvd, Baltimore MD 21244. The old Fargo PO Box 6059 is retired;
  corrected in mac_routing.NPI_ENUMERATOR + all docs.
- No electronic no-login equivalent exists for CMS (NPI Registry API is
  read-only; PECOS/NPPES-IA require login), unlike FMCSA's ask.fmcsa ticket form.
  So tiers stay: Standard=paper CMS-10114 (no login), Expedited=NPPES surrogate.

New: cms10114_pdf_filler.py fills the flat official form via text overlay
(reason checkbox + NPI + Section 2A identity + Section 4A cert name + signature
anchor); wired into npi_provider._generate_10114_for_signing for nppes-update.
Signed forms route to the NPI Enumerator via the existing daily batch.

Tests: test_cms10114.py 27/27, test_paper_batch.py 15/15, Astro build 58 pages.
2026-06-07 02:04:41 -05:00

104 lines
3.7 KiB
Python

"""Tests for the daily paper-filing batch (Standard no-login CMS filing path).
Covers the pure logic that doesn't need a DB/MinIO:
- postal working-day gating (weekends + federal/USPS holidays)
- destination routing (state -> MAC; CMS-10114 -> NPI Enumerator)
- cover-sheet rendering + multi-page pagination
- merge of cover sheet + filing PDFs
Run: python scripts/tests/test_paper_batch.py
"""
from __future__ import annotations
import importlib.util
import io
import sys
from datetime import date
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "scripts"))
sys.path.insert(0, str(ROOT / "scripts" / "workers"))
def _load(name: str, rel: str):
spec = importlib.util.spec_from_file_location(name, ROOT / rel)
m = importlib.util.module_from_spec(spec)
spec.loader.exec_module(m)
return m
dpb = _load("dpb", "scripts/workers/daily_paper_batch.py")
bcs = _load("bcs", "scripts/document_gen/templates/batch_cover_sheet.py")
PASS = 0
FAIL = 0
def check(label, cond):
global PASS, FAIL
if cond:
PASS += 1
print(f" PASS {label}")
else:
FAIL += 1
print(f" FAIL {label}")
def test_working_days():
print("working-day gating")
check("Saturday is not a working day", not dpb._is_postal_working_day(date(2026, 6, 6)))
check("Sunday is not a working day", not dpb._is_postal_working_day(date(2026, 6, 7)))
check("Monday is a working day", dpb._is_postal_working_day(date(2026, 6, 8)))
check("Memorial Day (holiday) is not a working day", not dpb._is_postal_working_day(date(2026, 5, 25)))
check("Christmas (holiday) is not a working day", not dpb._is_postal_working_day(date(2026, 12, 25)))
def test_routing():
print("destination routing")
ca = dpb._destination_for("CA", "cms855i")
check("CA 855 -> Noridian JE", ca and ca[0] == "noridian_je")
ny = dpb._destination_for("NY", "cms855i")
check("NY 855 -> NGS JK", ny and ny[0] == "ngs_jk")
tx = dpb._destination_for("TX", "cms855b")
check("TX 855B -> Novitas JH", tx and tx[0] == "novitas_jh")
enum = dpb._destination_for("TX", "cms10114")
check("any-state CMS-10114 -> NPI Enumerator (Baltimore)", enum and enum[0] == "npi_enumerator")
check("CMS-10114 ignores state for routing", dpb._destination_for("CA", "cms10114")[0] == "npi_enumerator")
check("unknown state -> None (human review)", dpb._destination_for("ZZ", "cms855i") is None)
check("blank state -> None", dpb._destination_for("", "cms855i") is None)
def test_cover_sheet():
print("cover sheet + merge")
items = [
{"order_number": f"CO-X{i:03d}", "provider": f"Provider {i} MD",
"npi": f"1{i:09d}", "form": "855i"}
for i in range(40)
]
cover = bcs.build_cover_sheet(
destination_name="Noridian JE",
destination_address_lines=("Provider Enrollment (JE)", "P.O. Box VERIFY", "Fargo, ND"),
batch_date=date(2026, 6, 8),
items=items,
)
from pypdf import PdfReader
n_cover = len(PdfReader(io.BytesIO(cover)).pages)
check("40-item cover sheet paginates to >1 page", n_cover > 1)
merged = bcs.merge_batch_pdf(cover, [cover, cover])
n_merged = len(PdfReader(io.BytesIO(merged)).pages)
check("merge concatenates cover + 2 filings", n_merged == n_cover * 3)
# empty-items cover still renders one page
empty = bcs.build_cover_sheet(
destination_name="X", destination_address_lines=("a",),
batch_date=date(2026, 6, 8), items=[],
)
check("empty batch cover sheet renders", len(PdfReader(io.BytesIO(empty)).pages) >= 1)
if __name__ == "__main__":
test_working_days()
test_routing()
test_cover_sheet()
print(f"\n{PASS} passed, {FAIL} failed")
sys.exit(1 if FAIL else 0)