Signal/tests/test_dedup.py
Kisa c07d054ca5 Add readiness-model dedup + coverage-line grouping foundation
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>
2026-06-27 00:22:01 -04:00

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