feat(sp41): claim-id dedup at SFTP pre-flight (30-day window, configurable)

This commit is contained in:
Nora
2026-07-07 22:02:46 -06:00
parent 13dd240328
commit 0e130579b4
6 changed files with 138 additions and 10 deletions
+5 -5
View File
@@ -51,7 +51,7 @@ def test_migration_0002_creates_acks_table():
def test_migration_latest_idempotent_on_fresh_db():
"""Re-running the migration on the same DB must be a no-op (PRAGMA
user_version already at the latest version — currently 21 after
user_version already at the latest version — currently 22 after
0004-0006 line_reconciliation, 0005 ta1_acks, SP9's 0007
providers/payers/clearhouse, SP10's 0008 payer_rejected,
SP11's 0009 audit_log, SP14's 0010 payer_rejected_acknowledged,
@@ -62,16 +62,16 @@ def test_migration_latest_idempotent_on_fresh_db():
claim.patient_control_number backfill UPDATE, SP28's 0018
claim_acks join table, SP32's 0019
rendering_provider_npi + service_provider_npi, SP37's 0020
transaction_set_control_number, and SP39's 0021 resubmissions
audit table)."""
transaction_set_control_number, SP39's 0021 resubmissions
audit table, and SP41's 0022 submission_dedup)."""
with db.engine().begin() as c:
v1 = c.exec_driver_sql("PRAGMA user_version").scalar() or 0
assert v1 == 21
assert v1 == 22
# A second run should not raise and should not bump the version.
db_migrate.run(db.engine())
with db.engine().begin() as c:
v2 = c.exec_driver_sql("PRAGMA user_version").scalar() or 0
assert v2 == 21
assert v2 == 22
def test_add_ack_persists_row():
+4 -3
View File
@@ -128,14 +128,15 @@ def test_migration_latest_idempotent_on_fresh_db(tmp_path: Path) -> None:
service_provider_npi on claims and remittances.
SP37 bumped it to 20 with batch transaction_set_control_number.
SP39 bumped it to 21 with the resubmissions audit table.
SP41 bumped it to 22 with submission_dedup.
"""
engine = _fresh_engine(tmp_path)
db_migrate.run(engine)
v_after_first = _user_version(engine)
assert v_after_first == 21, f"expected head=21, got {v_after_first}"
assert v_after_first == 22, f"expected head=22, got {v_after_first}"
db_migrate.run(engine)
assert _user_version(engine) == 21, "second run should not bump version"
assert _user_version(engine) == 22, "second run should not bump version"
def test_drop_claims_unique_constraint_migration(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
@@ -161,7 +162,7 @@ def test_drop_claims_unique_constraint_migration(tmp_path: Path, monkeypatch: py
engine = _fresh_engine(tmp_path)
db_migrate.run(engine)
assert _user_version(engine) == 21, f"expected head=21, got {_user_version(engine)}"
assert _user_version(engine) == 22, f"expected head=22, got {_user_version(engine)}"
# Two claims in one batch with the same patient_control_number
# must be insertable. If 0015's table recreation re-introduced a
+2 -2
View File
@@ -102,10 +102,10 @@ def migrated_engine(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
engine = _fresh_engine(tmp_path / "mig0020.db")
db_migrate.run(engine)
# Confirm head is 21 (every migration applied, including SP39's 0021).
# Confirm head is 22 (every migration applied, including SP41's 0022).
with engine.connect() as conn:
v = conn.exec_driver_sql("PRAGMA user_version").scalar() or 0
assert v == 21, f"expected migration head=21, got {v}"
assert v == 22, f"expected migration head=22, got {v}"
yield engine
engine.dispose()
+38
View File
@@ -0,0 +1,38 @@
"""Claim-id dedup at the SFTP pre-flight stage."""
from datetime import datetime
from cyclone.store.submission_dedup import (
DuplicateClaimError,
record_submission,
check_duplicate,
DEFAULT_WINDOW_DAYS,
)
def test_check_duplicate_returns_none_for_first_submission(tmp_path):
db = tmp_path / "test.db"
assert check_duplicate("ORIG-001", db_url=f"sqlite:///{db}") is None
def test_check_duplicate_raises_within_window(tmp_path):
db = tmp_path / "test.db"
record_submission("ORIG-002", submitted_at=datetime(2026, 6, 1), db_url=f"sqlite:///{db}")
try:
check_duplicate("ORIG-002", db_url=f"sqlite:///{db}",
now=datetime(2026, 6, 15))
except DuplicateClaimError as e:
assert e.claim_id == "ORIG-002"
assert e.original_submission_at == datetime(2026, 6, 1)
else:
raise AssertionError("expected DuplicateClaimError")
def test_check_duplicate_passes_after_window(tmp_path):
db = tmp_path / "test.db"
record_submission("ORIG-003", submitted_at=datetime(2026, 1, 1), db_url=f"sqlite:///{db}")
# 100 days later — past the 30-day default window
assert check_duplicate("ORIG-003", db_url=f"sqlite:///{db}",
now=datetime(2026, 4, 11)) is None
def test_default_window_is_30_days():
assert DEFAULT_WINDOW_DAYS == 30