refactor(store): code quality fixes per review
- iter_claims/iter_remittances now return list[dict] (snapshot under the lock, return outside) so slow consumers no longer hold the write lock during iteration - iter_claims gains payer (case-insensitive substring) and date_from/date_to filters via the new _date_in_bounds helper - iter_remittances wires date_from/date_to and payer - BatchRecord split into BatchRecord837 / BatchRecord835 with __new__ dispatch so isinstance narrows result correctly, eliminating the four 'type: ignore[assignment]' band-aids - BatchRecord now rejects naive parsed_at via model_validator - to_ui_remittance's adjustmentAmount=0.0 is documented as a TODO for sub-project 2 reconciliation 151 passed, 1 skipped (no regressions; +6 tests for new filters)
This commit is contained in:
@@ -485,3 +485,113 @@ def test_iter_remittances_fills_payer_name_from_batch():
|
||||
out = list(s.iter_remittances())
|
||||
assert len(out) == 1
|
||||
assert out[0]["payerName"] == "CO_TXIX"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Review fixes: payer / date filters, tz-aware validator
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _make_claim_with_payer(claim_id: str, payer_name: str) -> ClaimOutput:
|
||||
"""ClaimOutput variant that lets us pick a payer name per row."""
|
||||
c = _make_claim(claim_id)
|
||||
c.payer = Payer(name=payer_name, id="P1")
|
||||
return c
|
||||
|
||||
|
||||
def test_iter_claims_filters_by_payer_substring_case_insensitive():
|
||||
"""iter_claims(payer=...) is a case-insensitive substring match on payerName."""
|
||||
s = InMemoryStore()
|
||||
result = _make_result()
|
||||
result.claims = [
|
||||
_make_claim_with_payer("A", "Aetna Health"),
|
||||
_make_claim_with_payer("B", "Blue Cross"),
|
||||
_make_claim_with_payer("C", "aetna better health"),
|
||||
]
|
||||
s.add(BatchRecord(
|
||||
id="b1", kind="837p", input_filename="a.txt",
|
||||
parsed_at=_PARSED_AT, result=result,
|
||||
))
|
||||
out = s.iter_claims(payer="Aetna")
|
||||
ids = sorted(c["id"] for c in out)
|
||||
assert ids == ["A", "C"], f"expected Aetna matches, got {ids}"
|
||||
|
||||
|
||||
def test_iter_claims_filters_by_date_from_includes_in_range_only():
|
||||
"""date_from excludes items whose submissionDate is before the bound."""
|
||||
s = InMemoryStore()
|
||||
old = datetime(2026, 6, 10, 12, 0, 0, tzinfo=timezone.utc)
|
||||
new = datetime(2026, 6, 25, 12, 0, 0, tzinfo=timezone.utc)
|
||||
old_result = _make_result()
|
||||
old_result.claims = [_make_claim("OLD")]
|
||||
new_result = _make_result()
|
||||
new_result.claims = [_make_claim("NEW")]
|
||||
s.add(BatchRecord(
|
||||
id="b-old", kind="837p", input_filename="a.txt",
|
||||
parsed_at=old, result=old_result,
|
||||
))
|
||||
s.add(BatchRecord(
|
||||
id="b-new", kind="837p", input_filename="b.txt",
|
||||
parsed_at=new, result=new_result,
|
||||
))
|
||||
out = s.iter_claims(date_from="2026-06-20")
|
||||
assert [c["id"] for c in out] == ["NEW"]
|
||||
|
||||
|
||||
def test_iter_claims_filters_by_date_to_includes_in_range_only():
|
||||
"""date_to excludes items whose submissionDate is after the bound."""
|
||||
s = InMemoryStore()
|
||||
old = datetime(2026, 6, 10, 12, 0, 0, tzinfo=timezone.utc)
|
||||
new = datetime(2026, 6, 25, 12, 0, 0, tzinfo=timezone.utc)
|
||||
old_result = _make_result()
|
||||
old_result.claims = [_make_claim("OLD")]
|
||||
new_result = _make_result()
|
||||
new_result.claims = [_make_claim("NEW")]
|
||||
s.add(BatchRecord(
|
||||
id="b-old", kind="837p", input_filename="a.txt",
|
||||
parsed_at=old, result=old_result,
|
||||
))
|
||||
s.add(BatchRecord(
|
||||
id="b-new", kind="837p", input_filename="b.txt",
|
||||
parsed_at=new, result=new_result,
|
||||
))
|
||||
out = s.iter_claims(date_to="2026-06-20")
|
||||
assert [c["id"] for c in out] == ["OLD"]
|
||||
|
||||
|
||||
def test_iter_remittances_filters_by_date_from_and_date_to():
|
||||
"""Remit receivedDate respects both date_from and date_to bounds."""
|
||||
s = InMemoryStore()
|
||||
old = datetime(2026, 6, 10, 12, 0, 0, tzinfo=timezone.utc)
|
||||
mid = datetime(2026, 6, 20, 12, 0, 0, tzinfo=timezone.utc)
|
||||
new = datetime(2026, 6, 30, 12, 0, 0, tzinfo=timezone.utc)
|
||||
for ts, tag in [(old, "OLD"), (mid, "MID"), (new, "NEW")]:
|
||||
s.add(BatchRecord(
|
||||
id=f"835-{tag}", kind="835", input_filename="a.txt",
|
||||
parsed_at=ts, result=_make_835_result(claims=[_make_remit()]),
|
||||
))
|
||||
out = s.iter_remittances(date_from="2026-06-15", date_to="2026-06-25")
|
||||
assert [r["batchId"] for r in out] == ["835-MID"]
|
||||
|
||||
|
||||
def test_iter_claims_unbounded_date_filter_includes_all():
|
||||
"""With no date bounds, the filter is a no-op (all items returned)."""
|
||||
s = InMemoryStore()
|
||||
result = _make_result()
|
||||
result.claims = [_make_claim("A"), _make_claim("B")]
|
||||
s.add(BatchRecord(
|
||||
id="b1", kind="837p", input_filename="a.txt",
|
||||
parsed_at=_PARSED_AT, result=result,
|
||||
))
|
||||
out = s.iter_claims()
|
||||
assert len(out) == 2
|
||||
|
||||
|
||||
def test_batch_record_rejects_naive_parsed_at():
|
||||
"""tz-aware validator: a naive datetime should raise ValueError."""
|
||||
import pytest
|
||||
with pytest.raises(ValueError, match="tz-aware"):
|
||||
BatchRecord(
|
||||
id="x", kind="837p", input_filename="a.txt",
|
||||
parsed_at=datetime(2025, 1, 1), result=_make_result(),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user