Group normalized shipment rows into deduped shipments and gradeable coverage lines (patient -> device -> shipment), the structural fix for the row-keyed worklist bug (six months of orders no longer = six worklist lines). - Locked dedup key (patient_id, device_type, date_of_service); order_number is display-only. Monthly resupplies stay separate, not duplicates. - Client-mapped plan_type carried through, never guessed from payer name; conflicting plan types in a group route to "plan type needed". - Quantity merge: sum across distinct component configs, max within a config (duplicate guard) with a warning breadcrumb on disagreement. - CGM gradeable this phase; pumps preserved/displayed but not graded. Additive only: not yet wired into the live pipeline, grading unchanged. 30 tests green (full suite 46), code-auditor reviewed (M1/M2/L1/L2/L3 fixed). Design: docs/dedup-design-2026-06-24.md, docs/readiness-model-brief-2026-06-23.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
347 lines
12 KiB
Python
347 lines
12 KiB
Python
"""
|
|
Tests for the dedup / grouping foundation of the readiness model.
|
|
|
|
Source design: docs/dedup-design-2026-06-24.md (Pi, locked by Kisa 2026-06-24)
|
|
docs/readiness-model-brief-2026-06-23.md sections 3, 5, 7
|
|
|
|
Locked dedup key: (patient_id, device_type, date_of_service).
|
|
order_number is a displayed attribute, NEVER part of the dedup key.
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "python-backend"))
|
|
|
|
from datetime import date, timedelta
|
|
|
|
from core.coverage_calculator import ShipmentRecord
|
|
from core.dedup import (
|
|
dedup,
|
|
assign_to_coverage_lines,
|
|
MergedShipment,
|
|
CoverageLine,
|
|
is_cgm_device,
|
|
)
|
|
|
|
DOS = date(2026, 6, 1)
|
|
|
|
|
|
def make_row(**kwargs):
|
|
defaults = dict(
|
|
patient_id="P1",
|
|
device_type="dexcom_g7",
|
|
shipment_date=DOS,
|
|
quantity=3,
|
|
payer="Medicare Part B",
|
|
component="sensor",
|
|
plan_type="medicare",
|
|
)
|
|
defaults.update(kwargs)
|
|
return ShipmentRecord(**defaults)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Dedup: grouping by (patient_id, device_type, DOS)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_dedup_component_split():
|
|
"""Two rows, same patient+device+DOS, different components -> one MergedShipment, summed qty."""
|
|
rows = [
|
|
make_row(quantity=3, component="sensor", order_number="ORD-100"),
|
|
make_row(quantity=1, component="transmitter", order_number="ORD-101"),
|
|
]
|
|
result = dedup(rows)
|
|
assert len(result) == 1
|
|
assert result[0].total_quantity == 4
|
|
assert set(result[0].components) == {"sensor", "transmitter"}
|
|
assert len(result[0].order_numbers) == 2
|
|
|
|
|
|
def test_dedup_identical_duplicate():
|
|
"""Two identical rows (same component) -> max quantity, not sum (duplicate guard)."""
|
|
rows = [
|
|
make_row(quantity=3, component="sensor"),
|
|
make_row(quantity=3, component="sensor"),
|
|
]
|
|
result = dedup(rows)
|
|
assert len(result) == 1
|
|
assert result[0].total_quantity == 3 # max within config, not 6
|
|
|
|
|
|
def test_dedup_mixed_duplicate_and_split():
|
|
"""Two duplicate sensor rows + one transmitter row -> max(sensor)+max(transmitter) = 4."""
|
|
rows = [
|
|
make_row(quantity=3, component="sensor"),
|
|
make_row(quantity=3, component="sensor"),
|
|
make_row(quantity=1, component="transmitter"),
|
|
]
|
|
result = dedup(rows)
|
|
assert result[0].total_quantity == 4
|
|
|
|
|
|
def test_dedup_zero_quantity_row_does_not_add():
|
|
"""qty=3 sensor + qty=0 sensor -> 3 (zero row is informational)."""
|
|
rows = [
|
|
make_row(quantity=3, component="sensor"),
|
|
make_row(quantity=0, component="sensor"),
|
|
]
|
|
result = dedup(rows)
|
|
assert result[0].total_quantity == 3
|
|
|
|
|
|
def test_dedup_wildly_different_same_component_takes_max():
|
|
"""qty=3 + qty=100 same component -> max 100 (likely error, not a real split)."""
|
|
rows = [
|
|
make_row(quantity=3, component="sensor"),
|
|
make_row(quantity=100, component="sensor"),
|
|
]
|
|
result = dedup(rows)
|
|
assert result[0].total_quantity == 100
|
|
|
|
|
|
def test_dedup_different_dos():
|
|
"""Same patient+device, different DOS -> two separate shipments."""
|
|
rows = [
|
|
make_row(shipment_date=DOS, quantity=3),
|
|
make_row(shipment_date=DOS + timedelta(days=30), quantity=3),
|
|
]
|
|
result = dedup(rows)
|
|
assert len(result) == 2
|
|
|
|
|
|
def test_dedup_different_device():
|
|
"""Same patient+DOS, different device -> two separate shipments."""
|
|
rows = [
|
|
make_row(device_type="dexcom_g7", component="sensor"),
|
|
make_row(device_type="omnipod_5", component="pod"),
|
|
]
|
|
result = dedup(rows)
|
|
assert len(result) == 2
|
|
|
|
|
|
def test_dedup_no_quantity_defaults_to_one():
|
|
"""Missing quantity -> default to 1 (a row without qty is still a shipment)."""
|
|
rows = [make_row(quantity=None, component="sensor")]
|
|
result = dedup(rows)
|
|
assert result[0].total_quantity == 1
|
|
|
|
|
|
def test_dedup_missing_order_number():
|
|
"""No order_number -> order_numbers list is empty, no crash."""
|
|
rows = [make_row(order_number=None)]
|
|
result = dedup(rows)
|
|
assert result[0].order_numbers == []
|
|
|
|
|
|
def test_dedup_order_numbers_collected_and_deduped():
|
|
"""Repeated order numbers across rows are collected once, in first-seen order."""
|
|
rows = [
|
|
make_row(component="sensor", order_number="ORD-100"),
|
|
make_row(component="transmitter", order_number="ORD-100"),
|
|
]
|
|
result = dedup(rows)
|
|
assert result[0].order_numbers == ["ORD-100"]
|
|
|
|
|
|
def test_dedup_hcpcs_collected():
|
|
"""HCPCS codes collected across rows in the group."""
|
|
rows = [
|
|
make_row(component="sensor", hcpcs="A4239"),
|
|
make_row(component="transmitter", hcpcs="E2103"),
|
|
]
|
|
result = dedup(rows)
|
|
assert set(result[0].hcpcs_codes) == {"A4239", "E2103"}
|
|
|
|
|
|
def test_dedup_payer_latest_non_null_wins():
|
|
"""payer is display-only; latest non-null row wins."""
|
|
rows = [
|
|
make_row(component="sensor", payer="Old Payer"),
|
|
make_row(component="transmitter", payer="Medicare Part B"),
|
|
]
|
|
result = dedup(rows)
|
|
assert result[0].payer == "Medicare Part B"
|
|
|
|
|
|
def test_dedup_csv_field_latest_non_null_wins():
|
|
"""csv_swo_status: a later non-null value wins over an earlier null."""
|
|
rows = [
|
|
make_row(component="sensor", csv_swo_status=None),
|
|
make_row(component="transmitter", csv_swo_status="On File"),
|
|
]
|
|
result = dedup(rows)
|
|
assert result[0].csv_swo_status == "On File"
|
|
|
|
|
|
def test_dedup_csv_field_earlier_non_null_kept_when_later_is_null():
|
|
"""csv_swo_status: an earlier non-null is kept when the later row is null."""
|
|
rows = [
|
|
make_row(component="sensor", csv_swo_status="On File"),
|
|
make_row(component="transmitter", csv_swo_status=None),
|
|
]
|
|
result = dedup(rows)
|
|
assert result[0].csv_swo_status == "On File"
|
|
|
|
|
|
def test_dedup_single_row_passthrough():
|
|
"""A single row produces a MergedShipment carrying its fields."""
|
|
rows = [make_row(quantity=2, component="sensor", order_number="ORD-1")]
|
|
result = dedup(rows)
|
|
assert len(result) == 1
|
|
assert isinstance(result[0], MergedShipment)
|
|
assert result[0].patient_id == "P1"
|
|
assert result[0].device_type == "dexcom_g7"
|
|
assert result[0].shipment_date == DOS
|
|
assert result[0].total_quantity == 2
|
|
assert result[0].plan_type == "medicare"
|
|
|
|
|
|
def test_dedup_plan_type_never_derived_from_payer():
|
|
"""plan_type carries the client-mapped value; None stays None even with a known payer name."""
|
|
rows = [make_row(payer="Aetna PPO", plan_type=None)]
|
|
result = dedup(rows)
|
|
assert result[0].plan_type is None # never guessed from "Aetna"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# CoverageLine assignment: group MergedShipments into gradeable lines
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_is_cgm_device():
|
|
assert is_cgm_device("dexcom_g7") is True
|
|
assert is_cgm_device("freestyle_libre_3") is True
|
|
assert is_cgm_device("omnipod_5") is False
|
|
|
|
|
|
def test_assign_groups_by_patient_device_plan():
|
|
"""One patient, one device, one plan -> one CoverageLine."""
|
|
merged = dedup([
|
|
make_row(shipment_date=DOS, quantity=3),
|
|
make_row(shipment_date=DOS + timedelta(days=30), quantity=3),
|
|
])
|
|
lines = assign_to_coverage_lines(merged)
|
|
assert len(lines) == 1
|
|
|
|
|
|
def test_assign_latest_dos_is_current():
|
|
"""The latest DOS shipment is the current one; older ones are prior."""
|
|
merged = dedup([
|
|
make_row(shipment_date=DOS, quantity=3),
|
|
make_row(shipment_date=DOS + timedelta(days=30), quantity=3),
|
|
])
|
|
line = list(assign_to_coverage_lines(merged).values())[0]
|
|
assert line.current_shipment.shipment_date == DOS + timedelta(days=30)
|
|
assert len(line.prior_shipments) == 1
|
|
assert line.prior_shipments[0].shipment_date == DOS
|
|
|
|
|
|
def test_assign_cgm_line_is_gradeable():
|
|
merged = dedup([make_row(device_type="dexcom_g7", component="sensor")])
|
|
line = list(assign_to_coverage_lines(merged).values())[0]
|
|
assert line.gradeable is True
|
|
|
|
|
|
def test_assign_pump_line_not_gradeable():
|
|
merged = dedup([make_row(device_type="omnipod_5", component="pod")])
|
|
line = list(assign_to_coverage_lines(merged).values())[0]
|
|
assert line.gradeable is False
|
|
|
|
|
|
def test_assign_different_plan_type_separate_lines():
|
|
"""Same patient+device, different mapped plan_type -> two CoverageLines (dual-eligible)."""
|
|
merged = dedup([
|
|
make_row(shipment_date=DOS, plan_type="medicare"),
|
|
make_row(shipment_date=DOS + timedelta(days=30), plan_type="medicaid"),
|
|
])
|
|
lines = assign_to_coverage_lines(merged)
|
|
assert len(lines) == 2
|
|
|
|
|
|
def test_assign_none_plan_type_groups_as_unknown():
|
|
"""plan_type None is keyed as 'unknown', not guessed, and still forms a line."""
|
|
merged = dedup([make_row(plan_type=None)])
|
|
lines = assign_to_coverage_lines(merged)
|
|
assert len(lines) == 1
|
|
line = list(lines.values())[0]
|
|
assert line.plan_type == "unknown"
|
|
|
|
|
|
def test_assign_starts_ungraded():
|
|
"""A freshly assigned line has no status yet (gradeLine runs later)."""
|
|
merged = dedup([make_row()])
|
|
line = list(assign_to_coverage_lines(merged).values())[0]
|
|
assert line.line_status is None
|
|
assert line.timing_flag is None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Hardening (code-auditor findings M1, L1, L3 + edge cases)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_dedup_conflicting_plan_type_routes_to_unknown():
|
|
"""Two rows in one group with DIFFERENT non-null plan types -> never guess.
|
|
|
|
The plan type is grading-critical and must never be silently picked. When a
|
|
dedup group disagrees, route to None so the line keys as 'plan type needed'.
|
|
"""
|
|
rows = [
|
|
make_row(component="sensor", plan_type="medicare"),
|
|
make_row(component="transmitter", plan_type="medicaid"),
|
|
]
|
|
result = dedup(rows)
|
|
assert len(result) == 1
|
|
assert result[0].plan_type is None
|
|
line = list(assign_to_coverage_lines(result).values())[0]
|
|
assert line.plan_type == "unknown"
|
|
|
|
|
|
def test_dedup_agreeing_plan_type_preserved():
|
|
"""Same non-null plan type across rows is kept (no false conflict)."""
|
|
rows = [
|
|
make_row(component="sensor", plan_type="medicare"),
|
|
make_row(component="transmitter", plan_type="medicare"),
|
|
]
|
|
result = dedup(rows)
|
|
assert result[0].plan_type == "medicare"
|
|
|
|
|
|
def test_dedup_empty_string_csv_field_does_not_clobber():
|
|
"""An empty-string csv value must not overwrite an earlier real value."""
|
|
rows = [
|
|
make_row(component="sensor", csv_swo_status="On File"),
|
|
make_row(component="transmitter", csv_swo_status=""),
|
|
]
|
|
result = dedup(rows)
|
|
assert result[0].csv_swo_status == "On File"
|
|
|
|
|
|
def test_dedup_datetime_dos_coerced_to_date():
|
|
"""Same calendar day with different timestamps -> one group (timezone edge)."""
|
|
from datetime import datetime
|
|
rows = [
|
|
make_row(shipment_date=datetime(2026, 6, 1, 9, 0), component="sensor"),
|
|
make_row(shipment_date=datetime(2026, 6, 1, 17, 0), component="transmitter"),
|
|
]
|
|
result = dedup(rows)
|
|
assert len(result) == 1
|
|
assert result[0].shipment_date == date(2026, 6, 1)
|
|
|
|
|
|
def test_dedup_empty_input():
|
|
assert dedup([]) == []
|
|
assert assign_to_coverage_lines([]) == {}
|
|
|
|
|
|
def test_cgm_device_types_exist_in_payer_rules():
|
|
"""Drift guard: every gradeable CGM device must exist in payer_rules.json,
|
|
and the known pump must not be in the gradeable set."""
|
|
import json
|
|
rules_path = (
|
|
Path(__file__).parent.parent
|
|
/ "python-backend" / "config" / "payer_rules.json"
|
|
)
|
|
devices = set(json.loads(rules_path.read_text())["devices"].keys())
|
|
from core.dedup import CGM_DEVICE_TYPES
|
|
missing = CGM_DEVICE_TYPES - devices
|
|
assert not missing, f"CGM devices missing from payer_rules.json: {missing}"
|
|
assert "omnipod_5" not in CGM_DEVICE_TYPES
|