feat(sp27): pin Claim↔Remit matched-pair invariant + startup drift audit
Add check_matched_pair_drift() at module level in store.py — read-only audit of the Claim.matched_remittance_id ↔ Remittance.claim_id FK pair. Logs WARNING on drift with up to 5 examples of each of two cases (claim-side and remit-side), returns the count of drifted rows. Wired into api.py::lifespan right after db.init_db() and ensure_clearhouse_seeded(), wrapped in try/except so a query failure logs but doesn't crash boot. The symmetric-write + symmetric-clear invariants on manual_match / manual_unmatch were already correct; this commit pins them with focused regression tests in test_store_match_invariant.py (8 tests covering both directions, both happy-path, rollback pin, and drift-check unit behavior). PCN asymmetry in the fixture prevents the auto-match inside CycloneStore.add (Task 10) from pre-pairing, so manual_match is the only writer. Migration 0016 adds the missing ix_claims_matched_remittance_id index — the drift check scans WHERE matched_remittance_id IS NOT NULL on every boot and would become a full-table scan past ~10k claims. Symmetric with ix_remittances_claim_id (added in 0007). Migration tests bumped from head=15 to head=16.
This commit is contained in:
@@ -119,14 +119,16 @@ def test_migration_latest_idempotent_on_fresh_db(tmp_path: Path) -> None:
|
||||
"""All migrations up to the current head run cleanly on a fresh DB,
|
||||
and a second run is a no-op (no version bump). SP22 bumped the
|
||||
expected head from 14 to 15 with the new UNIQUE-drop migration.
|
||||
SP27 Task 11 bumped it to 16 with the claims.matched_remittance_id
|
||||
index (drift-check perf).
|
||||
"""
|
||||
engine = _fresh_engine(tmp_path)
|
||||
db_migrate.run(engine)
|
||||
v_after_first = _user_version(engine)
|
||||
assert v_after_first == 15, f"expected head=15, got {v_after_first}"
|
||||
assert v_after_first == 16, f"expected head=16, got {v_after_first}"
|
||||
|
||||
db_migrate.run(engine)
|
||||
assert _user_version(engine) == 15, "second run should not bump version"
|
||||
assert _user_version(engine) == 16, "second run should not bump version"
|
||||
|
||||
|
||||
def test_drop_claims_unique_constraint_migration(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
@@ -152,7 +154,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) == 15, f"expected head=15, got {_user_version(engine)}"
|
||||
assert _user_version(engine) == 16, f"expected head=16, 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
|
||||
|
||||
Reference in New Issue
Block a user