Signal/tests/test_worklist_readiness.py
Kisa b4f5707937 fix: engine-dropped rows surface in the skipped list (failed-data honesty)
Kisa ruling 2026-07-07: failed CSV info must be visible and Signal never acts
on rows whose required information was not provided. Rows that survive
normalization but die in the scoring engine (no coverage rule for the
component) previously vanished from the response; they now append to
skipped_reasons before persistence so the stored count matches what the
supplier saw. Auditor SHIP; its MEDIUM (persist-order) fixed in-commit.
Known bound (auditor LOW, tracked): a dropped component inside an otherwise
scored same-DOS group is covered by the scored line and not separately
reported. 169 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 06:15:27 -04:00

724 lines
26 KiB
Python

"""
Tests for the readiness-model wiring (build plan 01).
Engine behavior is covered by test_dedup.py / test_readiness.py; these tests
target the SEAMS the wiring adds: the orchestration module, the /api/upload
augmentation, the /api/confirm-visit recompute, and back-compat invariants.
PHI CONTRACT: fixtures use synthetic patient_ids only. No names, DOBs, SSNs.
"""
import hashlib
import io
import sys
from datetime import date, timedelta
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).parent.parent / "python-backend"))
from core.coverage_calculator import ShipmentRecord, calculate_batch # noqa: E402
from core.worklist_readiness import ( # noqa: E402
build_readiness_index,
merged_to_record,
readiness_by_line,
readiness_for_record,
readiness_stats,
resolve_group,
)
TODAY = date.today()
def _record(**kwargs) -> ShipmentRecord:
defaults = dict(
patient_id="PT-1001",
device_type="dexcom_g7",
shipment_date=TODAY - timedelta(days=5),
quantity=3,
payer="Medicare Part B",
component="sensor",
)
defaults.update(kwargs)
return ShipmentRecord(**defaults)
def _hash(pid: str) -> str:
return hashlib.sha256(pid.encode()).hexdigest()
# --- orchestration module (unit level) --------------------------------------
def test_readiness_by_line_full_evidence_is_clear_to_ship():
rec = _record(
plan_type="medicare",
csv_swo_status="On File",
csv_visit_date=TODAY - timedelta(days=30),
csv_pecos_verified="Yes",
csv_diagnosis_on_file="Yes",
)
verdicts = readiness_by_line([rec])
key = ("PT-1001", "dexcom_g7", "medicare")
assert key in verdicts
assert verdicts[key].line_status.value == "Clear to Ship"
def test_readiness_by_line_skips_non_gradeable_lines():
rec = _record(device_type="omnipod_5", plan_type="medicare")
verdicts = readiness_by_line([rec])
assert verdicts == {}
def test_no_plan_type_reads_plan_type_needed():
rec = _record(
csv_swo_status="On File",
csv_visit_date=TODAY - timedelta(days=30),
csv_pecos_verified="Yes",
csv_diagnosis_on_file="Yes",
)
verdicts = readiness_by_line([rec])
key = ("PT-1001", "dexcom_g7", "unknown")
assert verdicts[key].line_status.value == "Plan Type Needed"
by_type = {i.doc_type: i for i in verdicts[key].doc_items}
assert by_type["pecos"].required_state.value == "NOT_EVALUATED"
assert by_type["pa"].required_state.value == "NOT_EVALUATED"
def test_same_dos_different_order_numbers_is_one_line_verdict():
a = _record(order_number="ORD-1", plan_type="medicare")
b = _record(order_number="ORD-2", plan_type="medicare")
verdicts = readiness_by_line([a, b])
assert len(verdicts) == 1
def test_verdict_graded_from_latest_dos():
older = _record(
shipment_date=TODAY - timedelta(days=40),
plan_type="medicare",
csv_swo_status="On File",
csv_visit_date=TODAY - timedelta(days=45),
csv_pecos_verified="Yes",
csv_diagnosis_on_file="Yes",
)
# Latest DOS is missing PECOS -> the line verdict must reflect THIS row.
newer = _record(
shipment_date=TODAY - timedelta(days=5),
plan_type="medicare",
csv_swo_status="On File",
csv_visit_date=TODAY - timedelta(days=45),
csv_diagnosis_on_file="Yes",
)
verdicts = readiness_by_line([older, newer])
assert len(verdicts) == 1
verdict = verdicts[("PT-1001", "dexcom_g7", "medicare")]
assert verdict.line_status.value == "Action Needed"
def test_conflicting_plan_types_route_to_unknown_line():
a = _record(order_number="ORD-1", plan_type="medicare")
b = _record(order_number="ORD-2", plan_type="medicaid")
index = build_readiness_index([a, b])
assert set(index.by_line) == {("PT-1001", "dexcom_g7", "unknown")}
verdict, line_key, merged = resolve_group(
index, "PT-1001", "dexcom_g7", a.shipment_date
)
assert verdict is not None
assert verdict.line_status.value == "Plan Type Needed"
assert line_key == ("PT-1001", "dexcom_g7", "unknown")
assert merged is not None
def test_mixed_plan_population_resolves_to_merged_line():
"""Review finding (HIGH family): a group where only SOME rows carry
plan_type merges to the typed line; row resolution must follow the MERGE,
not any single row's own plan_type."""
blank = _record(
order_number="ORD-1",
csv_swo_status="On File",
csv_visit_date=TODAY - timedelta(days=30),
csv_pecos_verified="Yes",
csv_diagnosis_on_file="Yes",
)
typed = _record(
order_number="ORD-2",
plan_type="medicare",
csv_swo_status="On File",
csv_visit_date=TODAY - timedelta(days=30),
csv_pecos_verified="Yes",
csv_diagnosis_on_file="Yes",
)
index = build_readiness_index([blank, typed])
assert set(index.by_line) == {("PT-1001", "dexcom_g7", "medicare")}
verdict, line_key, _ = resolve_group(
index, "PT-1001", "dexcom_g7", blank.shipment_date
)
assert verdict is not None, "blank-plan row must inherit its merged line's verdict"
assert verdict.line_status.value == "Clear to Ship"
assert line_key == ("PT-1001", "dexcom_g7", "medicare")
def test_conflicted_dos_never_borrows_another_lines_verdict():
"""Review finding (HIGH): a conflicted newer DOS must read its own
unknown-line verdict, never the older clean line's green."""
old_clean = _record(
shipment_date=TODAY - timedelta(days=45),
plan_type="medicare",
csv_swo_status="On File",
csv_visit_date=TODAY - timedelta(days=50),
csv_pecos_verified="Yes",
csv_diagnosis_on_file="Yes",
)
new_a = _record(order_number="ORD-1", plan_type="medicare")
new_b = _record(order_number="ORD-2", plan_type="medicaid")
index = build_readiness_index([old_clean, new_a, new_b])
assert set(index.by_line) == {
("PT-1001", "dexcom_g7", "medicare"),
("PT-1001", "dexcom_g7", "unknown"),
}
old_verdict, _, _ = resolve_group(
index, "PT-1001", "dexcom_g7", old_clean.shipment_date
)
new_verdict, _, _ = resolve_group(
index, "PT-1001", "dexcom_g7", new_a.shipment_date
)
assert old_verdict.line_status.value == "Clear to Ship"
assert new_verdict.line_status.value == "Plan Type Needed"
def test_merged_to_record_matches_dedup_field_policy():
"""Review finding (MEDIUM): fallback record fields must come from the
dedup merge (latest-non-null, collected order numbers), so the legacy
doc_state cannot contradict the readiness verdict."""
first = _record(order_number="ORD-1", csv_pecos_verified="Yes")
second = _record(order_number="ORD-2", plan_type="medicare")
index = build_readiness_index([first, second])
_, _, merged = resolve_group(
index, "PT-1001", "dexcom_g7", first.shipment_date
)
rec = merged_to_record(merged)
assert rec.csv_pecos_verified == "Yes" # latest-non-null keeps it
assert rec.order_number == "ORD-1, ORD-2" # collected, not arbitrary
assert rec.plan_type == "medicare"
def test_confirmed_visit_counts_as_positive_evidence():
rec = _record(
plan_type="medicare",
csv_swo_status="On File",
csv_pecos_verified="Yes",
csv_diagnosis_on_file="Yes",
)
confirmed = {_hash("PT-1001"): TODAY - timedelta(days=20)}
verdicts = readiness_by_line([rec], confirmed_visits=confirmed)
verdict = verdicts[("PT-1001", "dexcom_g7", "medicare")]
assert verdict.line_status.value == "Clear to Ship"
visit = next(i for i in verdict.doc_items if i.doc_type == "visit")
assert visit.satisfied
def test_readiness_stats_counts_lines():
good = _record(
patient_id="PT-A",
plan_type="medicare",
csv_swo_status="On File",
csv_visit_date=TODAY - timedelta(days=30),
csv_pecos_verified="Yes",
csv_diagnosis_on_file="Yes",
)
unknown = _record(patient_id="PT-B")
stats = readiness_stats(readiness_by_line([good, unknown]))
assert stats["Clear to Ship"] == 1
assert stats["Plan Type Needed"] == 1
assert stats["lines_total"] == 2
def test_no_raw_patient_id_in_logs(caplog):
# Conflicting plan types force the dedup warning path.
a = _record(patient_id="RAW-SECRET-ID-42", order_number="1", plan_type="medicare")
b = _record(patient_id="RAW-SECRET-ID-42", order_number="2", plan_type="medicaid")
with caplog.at_level("WARNING"):
readiness_by_line([a, b])
assert caplog.text # the warning fired
assert "RAW-SECRET-ID-42" not in caplog.text
def test_readiness_for_record_single_path():
rec = _record(
plan_type="medicare",
csv_swo_status="On File",
csv_pecos_verified="Yes",
csv_diagnosis_on_file="Yes",
)
verdict = readiness_for_record(
rec, confirmed_visit_date=TODAY - timedelta(days=10)
)
assert verdict.line_status.value == "Clear to Ship"
# --- API level (FastAPI TestClient) ------------------------------------------
httpx = pytest.importorskip("httpx", reason="httpx required for TestClient")
from fastapi.testclient import TestClient # noqa: E402
import api.main as api_main # noqa: E402
client = TestClient(api_main.app)
def _upload(csv_text: str):
return client.post(
"/api/upload",
files={"file": ("fixture.csv", io.BytesIO(csv_text.encode()), "text/csv")},
)
# Dates engineered so the legacy timing flag is RESUPPLY_READY (coverage ends
# within the 30-day window) and no renewal flag fires (visit recent).
SHIP = (TODAY - timedelta(days=5)).isoformat()
VISIT = (TODAY - timedelta(days=30)).isoformat()
HEADER = (
"patient_id,device_type,shipment_date,quantity,payer,plan_type,"
"swo_status,visit_date,pecos_verified,pa_status,diagnosis_on_file"
)
def test_upload_false_green_blocked_at_api_layer():
"""Acceptance case 1: legacy label green-ish, readiness verdict honest."""
csv_text = (
HEADER
+ f"\nPT-FG-1,Dexcom G7,{SHIP},3,Medicare Part B,medicare,"
+ f"On File,{VISIT},,,Yes"
)
resp = _upload(csv_text)
assert resp.status_code == 200
rec = resp.json()["records"][0]
assert rec["flag"] == "RESUPPLY_READY"
assert rec["status_label"] == "Clear to Ship" # legacy timing label
assert rec["readiness_status"] == "Action Needed" # PECOS absent blocks green
def test_upload_without_plan_type_reads_plan_type_needed():
"""Acceptance case 2: today's default for every record — honest, never guessed."""
header = (
"patient_id,device_type,shipment_date,quantity,payer,"
"swo_status,visit_date,pecos_verified,pa_status,diagnosis_on_file"
)
csv_text = (
header
+ f"\nPT-PTN-1,Dexcom G7,{SHIP},3,Medicare Part B,"
+ f"On File,{VISIT},Yes,,Yes"
)
resp = _upload(csv_text)
assert resp.status_code == 200
body = resp.json()
graded = [r for r in body["records"] if r["readiness_status"] is not None]
assert graded, "expected at least one graded record"
assert all(r["readiness_status"] == "Plan Type Needed" for r in graded)
assert body["readiness_stats"]["Plan Type Needed"] == body[
"readiness_stats"
]["lines_total"]
def test_upload_clear_to_ship_reachable_end_to_end():
"""Acceptance case 3: full positive evidence + mapped plan type = true green."""
csv_text = (
HEADER
+ f"\nPT-GREEN-1,Dexcom G7,{SHIP},3,Medicare Part B,medicare,"
+ f"On File,{VISIT},Yes,,Yes"
)
resp = _upload(csv_text)
assert resp.status_code == 200
rec = resp.json()["records"][0]
assert rec["plan_type"] == "medicare"
assert rec["readiness_status"] == "Clear to Ship"
items = {i["doc_type"]: i for i in rec["readiness_items"]}
assert items["pa"]["required_state"] == "NOT_REQUIRED" # Medicare FFS
assert all(i["satisfied"] for i in items.values())
def test_upload_with_order_numbers_still_resolves_records():
"""Regression for the record_lookup miss: CoverageResult has no
order_number, so exact-key lookups miss for order-numbered CSVs; the
fallback must recover doc_state AND readiness fields."""
header = HEADER + ",order_number"
csv_text = (
header
+ f"\nPT-ORD-1,Dexcom G7,{SHIP},3,Medicare Part B,medicare,"
+ f"On File,{VISIT},Yes,,Yes,ORD-778899"
)
resp = _upload(csv_text)
assert resp.status_code == 200
rec = resp.json()["records"][0]
assert rec["order_number"] == "ORD-778899"
assert rec["doc_state"] is not None
assert rec["readiness_status"] == "Clear to Ship"
def test_upload_backcompat_legacy_fields_unchanged():
"""Acceptance case 6: the wiring is additive — every legacy field matches
an independent reconstruction from the engines for an order-less CSV
(where the exact record lookup must hit and byte-identity must hold)."""
csv_text = (
HEADER
+ f"\nPT-BC-1,Dexcom G7,{SHIP},3,Medicare Part B,medicare,"
+ f"On File,{VISIT},Yes,,Yes"
+ f"\nPT-BC-2,Libre 3,{(TODAY - timedelta(days=90)).isoformat()},1,"
+ "Aetna,commercial,Pending,,,Requested,Yes"
)
resp = _upload(csv_text)
assert resp.status_code == 200
body = resp.json()
from api.normalizer import normalize_csv
from core.doc_state_machine import compute_doc_state
records, _, _ = normalize_csv(csv_text)
by_pid = {r.patient_id: r for r in records}
expected = calculate_batch(records, as_of=TODAY, confirmed_visits={})
assert len(body["records"]) == len(expected)
for got, want in zip(body["records"], expected):
rec = by_pid[want.patient_id]
assert got["patient_id"] == want.patient_id
assert got["flag"] == want.flag.value
assert got["priority_score"] == want.priority_score
assert got["status_label"] == api_main.FLAG_LABELS.get(
want.flag.value, want.flag.value
)
assert got["payer"] == want.payer
assert got["days_until_coverage_end"] == want.days_until_coverage_end
assert got["coverage_end_date"] == want.coverage_end_date.isoformat()
assert got["rule_version"] == want.rule_version
assert got["action"] == api_main.FLAG_ACTIONS.get(
want.flag.value, "Review"
)
# Record-echo fields resolve through the exact lookup (order-less CSV)
assert got["quantity"] == rec.quantity
assert got["order_number"] == rec.order_number
assert got["csv_swo_status"] == rec.csv_swo_status
assert got["csv_pecos_verified"] == rec.csv_pecos_verified
assert got["csv_pa_status"] == rec.csv_pa_status
assert got["csv_diagnosis_on_file"] == rec.csv_diagnosis_on_file
# doc_state reconstructed independently from the same inputs
doc = compute_doc_state(
payer_type=api_main._normalize_payer_type(want.payer),
csv_swo_status=rec.csv_swo_status,
csv_visit_date=rec.csv_visit_date,
confirmed_visit_date=None,
csv_pecos_verified=rec.csv_pecos_verified,
csv_pa_status=rec.csv_pa_status,
csv_diagnosis_on_file=rec.csv_diagnosis_on_file,
is_transfer=bool(rec.csv_transfer_from),
)
assert got["doc_state"]["visit"] == doc.visit
assert got["doc_state"]["pecos"] == doc.pecos
assert got["doc_state"]["diagnosis"] == doc.diagnosis
assert got["cascade"] == doc.cascade
# Full legacy stats dict reconstructed independently from the flags
flags = [w.flag.value for w in expected]
assert body["stats"] == {
"total": len(expected),
"supply_lapsed": flags.count("SUPPLY_LAPSED"),
"visit_required": flags.count("VISIT_REQUIRED"),
"transfer_pending": flags.count("TRANSFER_PENDING"),
"renewal_critical": flags.count("RENEWAL_CRITICAL"),
"renewal_elevated": flags.count("RENEWAL_ELEVATED"),
"renewal_soon": flags.count("RENEWAL_SOON"),
"resupply_ready": flags.count("RESUPPLY_READY"),
"active": flags.count("ACTIVE"),
"no_recent_shipment": flags.count("NO_RECENT_SHIPMENT"),
"prescriber_action": (
flags.count("SUPPLY_LAPSED")
+ flags.count("VISIT_REQUIRED")
+ flags.count("RENEWAL_CRITICAL")
+ flags.count("RENEWAL_ELEVATED")
),
}
def test_upload_mixed_plan_group_both_rows_carry_merged_verdict():
"""Review finding (HIGH family), API level: duplicate order-numbered rows
where only one carries plan_type — both output rows must carry the merged
line's verdict, and stats must reconcile with the rows."""
header = HEADER + ",order_number"
csv_text = (
header
+ f"\nPT-MIX-1,Dexcom G7,{SHIP},3,Medicare Part B,,"
+ f"On File,{VISIT},Yes,,Yes,ORD-1"
+ f"\nPT-MIX-1,Dexcom G7,{SHIP},3,Medicare Part B,medicare,"
+ f"On File,{VISIT},Yes,,Yes,ORD-2"
)
resp = _upload(csv_text)
assert resp.status_code == 200
body = resp.json()
assert len(body["records"]) == 2
for rec in body["records"]:
assert rec["readiness_status"] == "Clear to Ship"
assert rec["plan_type"] == "medicare"
assert rec["order_number"] == "ORD-1, ORD-2"
assert body["readiness_stats"] == {"Clear to Ship": 1, "lines_total": 1}
def test_upload_conflicted_dos_reads_own_line_not_older_green():
"""Review finding (HIGH), API level: the false-green repro. A conflicted
newer DOS must read Plan Type Needed even when an older clean DOS on the
same patient+device graded Clear to Ship."""
header = HEADER + ",order_number"
old_ship = (TODAY - timedelta(days=45)).isoformat()
old_visit = (TODAY - timedelta(days=50)).isoformat()
csv_text = (
header
+ f"\nPT-CONF-1,Dexcom G7,{old_ship},3,Medicare Part B,medicare,"
+ f"On File,{old_visit},Yes,,Yes,ORD-OLD"
+ f"\nPT-CONF-1,Dexcom G7,{SHIP},3,Medicare Part B,medicare,"
+ f"On File,{VISIT},Yes,,Yes,ORD-N1"
+ f"\nPT-CONF-1,Dexcom G7,{SHIP},3,Medicaid - GA,medicaid,"
+ f"On File,{VISIT},Yes,,Yes,ORD-N2"
)
resp = _upload(csv_text)
assert resp.status_code == 200
body = resp.json()
by_ship = {}
for rec in body["records"]:
by_ship.setdefault(rec["last_shipment_date"], []).append(rec)
for rec in by_ship[old_ship]:
assert rec["readiness_status"] == "Clear to Ship"
for rec in by_ship[SHIP]:
assert rec["readiness_status"] == "Plan Type Needed"
assert body["readiness_stats"] == {
"Clear to Ship": 1,
"Plan Type Needed": 1,
"lines_total": 2,
}
def test_upload_skipped_record_leaves_no_phantom_line_in_stats():
"""Review finding (LOW): a record calculate_batch skips (G7 transmitter
has no wear-day rule) must not leave a phantom line in readiness_stats."""
header = HEADER + ",component"
csv_text = (
header
+ f"\nPT-OK-1,Dexcom G7,{SHIP},3,Medicare Part B,medicare,"
+ f"On File,{VISIT},Yes,,Yes,sensor"
+ f"\nPT-TX-1,Dexcom G7,{SHIP},1,Medicare Part B,medicare,"
+ f"On File,{VISIT},Yes,,Yes,transmitter"
)
resp = _upload(csv_text)
assert resp.status_code == 200
body = resp.json()
pids = {r["patient_id"] for r in body["records"]}
assert pids == {"PT-OK-1"} # transmitter row skipped by the engine
assert body["readiness_stats"]["lines_total"] == 1
# Failed-data honesty: the dropped row is visible in the skipped list,
# never silently vanished (Kisa ruling 2026-07-07).
assert body["skipped"] >= 1
assert any(
"PT-TX-1" in r and "not scored" in r for r in body["skipped_reasons"]
)
def test_upload_non_gradeable_rows_carry_no_verdict():
csv_text = (
HEADER
+ f"\nPT-POD-1,Omnipod 5,{SHIP},1,Medicare Part B,medicare,"
+ f"On File,{VISIT},Yes,,Yes"
)
resp = _upload(csv_text)
assert resp.status_code == 200
rec = resp.json()["records"][0]
assert rec["readiness_status"] is None
assert rec["readiness_items"] is None
def test_confirm_visit_recompute_flips_to_clear(monkeypatch):
"""Acceptance case 5: confirming the visit satisfies the last open
required item in the same response."""
monkeypatch.setattr(api_main, "get_or_create_org", lambda **kw: "org-test")
monkeypatch.setattr(
api_main, "upsert_confirmed_visit", lambda *a, **kw: True
)
# Upload path first: everything good except visit evidence.
csv_text = (
HEADER
+ f"\nPT-CV-1,Dexcom G7,{SHIP},3,Medicare Part B,medicare,"
+ "On File,,Yes,,Yes"
)
up = _upload(csv_text)
assert up.status_code == 200
assert up.json()["records"][0]["readiness_status"] == "Action Needed"
resp = client.post(
"/api/confirm-visit",
json={
"patient_id": "PT-CV-1",
"confirmed_date": (TODAY - timedelta(days=15)).isoformat(),
"shipment_date": SHIP,
"payer": "Medicare Part B",
"device_type": "dexcom_g7",
"quantity": 3,
"csv_swo_status": "On File",
"csv_pecos_verified": "Yes",
"csv_diagnosis_on_file": "Yes",
"plan_type": "medicare",
},
)
assert resp.status_code == 200
rec = resp.json()
assert rec["readiness_status"] == "Clear to Ship"
visit = next(i for i in rec["readiness_items"] if i["doc_type"] == "visit")
assert visit["satisfied"]
def test_confirm_visit_normalizes_plan_type(monkeypatch):
"""Review finding (LOW): both paths must return the same normalized
plan_type representation."""
monkeypatch.setattr(api_main, "get_or_create_org", lambda **kw: "org-test")
monkeypatch.setattr(
api_main, "upsert_confirmed_visit", lambda *a, **kw: True
)
resp = client.post(
"/api/confirm-visit",
json={
"patient_id": "PT-NORM-1",
"confirmed_date": (TODAY - timedelta(days=15)).isoformat(),
"shipment_date": SHIP,
"payer": "Medicare Part B",
"device_type": "dexcom_g7",
"quantity": 3,
"csv_swo_status": "On File",
"csv_pecos_verified": "Yes",
"csv_diagnosis_on_file": "Yes",
"plan_type": " Medicare ",
},
)
assert resp.status_code == 200
rec = resp.json()
assert rec["plan_type"] == "medicare"
assert rec["readiness_status"] == "Clear to Ship"
def test_upload_readiness_items_carry_rule_ids():
"""P2 contract W2: the serialized doc_items cite their rules."""
csv_text = (
HEADER
+ f"\nPT-RID-1,Dexcom G7,{SHIP},3,Medicare Part B,medicare,"
+ f"On File,{VISIT},Yes,,Yes"
)
resp = _upload(csv_text)
assert resp.status_code == 200
items = {i["doc_type"]: i for i in resp.json()["records"][0]["readiness_items"]}
assert items["pecos"]["rule_id"] == "R004"
assert items["pa"]["rule_id"] == "R003"
assert items["swo"]["rule_id"] == "R100"
# ---------------------------------------------------------------------------
# P5: plan-type enforcement in the live path (plan 02)
# ---------------------------------------------------------------------------
def test_plan_type_value_normalization():
"""Synonyms canonicalize; the same string in a PAYER column changes
nothing about readiness."""
from api.normalizer import _normalize_plan_type
for raw, want in [
("Medicare Part B", "medicare"), ("FFS", "medicare"),
("Original Medicare", "medicare"), ("MA", "medicare_advantage"),
("Part C", "medicare_advantage"), ("state medicaid", "medicaid"),
("Private", "commercial"), ("Employer", "commercial"),
("", None), (" ", None), ("Tricare Select", None),
]:
assert _normalize_plan_type(raw) == want, raw
def test_payer_name_never_sets_plan_type():
"""A payer of 'Medicare Part B' with NO plan_type column yields
Plan Type Needed — never a guess from the payer name."""
header = (
"patient_id,device_type,shipment_date,quantity,payer,"
"swo_status,visit_date,pecos_verified,pa_status,diagnosis_on_file"
)
csv_text = (
header
+ f"\nPT-NG-1,Dexcom G7,{SHIP},3,Medicare Part B,"
+ f"On File,{VISIT},Yes,,Yes"
)
resp = _upload(csv_text)
rec = resp.json()["records"][0]
assert rec["plan_type"] is None
assert rec["readiness_status"] == "Plan Type Needed"
def test_plan_type_synonym_flows_end_to_end():
csv_text = (
HEADER
+ f"\nPT-SYN-1,Dexcom G7,{SHIP},3,Medicare Part B,Medicare Part B,"
+ f"On File,{VISIT},Yes,,Yes"
)
resp = _upload(csv_text)
rec = resp.json()["records"][0]
assert rec["plan_type"] == "medicare" # value map canonicalized it
assert rec["readiness_status"] == "Clear to Ship"
def test_unknown_plan_type_value_yields_plan_type_needed():
csv_text = (
HEADER
+ f"\nPT-UNK-1,Dexcom G7,{SHIP},3,Tricare,Tricare Select,"
+ f"On File,{VISIT},Yes,,Yes"
)
resp = _upload(csv_text)
rec = resp.json()["records"][0]
assert rec["plan_type"] is None
assert rec["readiness_status"] == "Plan Type Needed"
def test_default_config_never_reaches_readiness():
"""'default' is not a known plan type; it must grade Plan Type Needed,
never through the default payer_rules entry."""
from core.readiness import evaluate_readiness
v = evaluate_readiness(
"default",
csv_swo_status="On File",
csv_pecos_verified="Yes",
csv_diagnosis_on_file="Yes",
)
assert v.line_status.value == "Plan Type Needed"
def test_deprecation_warning_logged_once_per_batch(caplog):
header = (
"patient_id,device_type,shipment_date,quantity,payer,"
"swo_status,visit_date,pecos_verified,pa_status,diagnosis_on_file"
)
csv_text = (
header
+ f"\nPT-DW-1,Dexcom G7,{SHIP},3,Medicare Part B,On File,{VISIT},Yes,,Yes"
+ f"\nPT-DW-2,Libre 3,{SHIP},1,Aetna,On File,{VISIT},,,Yes"
)
with caplog.at_level("WARNING"):
resp = _upload(csv_text)
assert resp.status_code == 200
hits = [r for r in caplog.records if "plan_type not provided" in r.getMessage()]
assert len(hits) == 1 # once per batch, not per record
assert "2/2 records" in hits[0].getMessage()
def test_unrecognized_plan_value_warning_hashes_patient(caplog):
csv_text = (
HEADER
+ f"\nPT-RAW-SECRET,Dexcom G7,{SHIP},3,Tricare,Tricare Select,"
+ f"On File,{VISIT},Yes,,Yes"
)
with caplog.at_level("WARNING"):
_upload(csv_text)
hits = [r for r in caplog.records if "Unrecognized plan_type" in r.getMessage()]
assert len(hits) == 1
assert "PT-RAW-SECRET" not in hits[0].getMessage()