core/rules.py format-tolerant readers + stable registry (R001-R004 plan-scoped, R100-R102 universal per LCD L33822). payer_rules.json wrapped in the same commit as both engine readers (coupling hazard closed). Every DocItem now cites the rule that decided it; NOT_EVALUATED cites nothing. Independent audit: SHIP (registry-drift test added per its L2 finding). 138 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
611 lines
22 KiB
Python
611 lines
22 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
|
|
|
|
|
|
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"
|