From 14d61f82b8ab2e2e6386423a849db3664bb8f89b Mon Sep 17 00:00:00 2001 From: Tyler Date: Sat, 20 Jun 2026 09:56:24 -0600 Subject: [PATCH] test(api)+docs: assert matchedRemittance=null; fail-loud on fixture drift; tighten docstring --- backend/src/cyclone/api.py | 20 ++++++++------------ backend/tests/test_api_claim_detail.py | 17 +++++++++++++++-- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/backend/src/cyclone/api.py b/backend/src/cyclone/api.py index 1ddc1e7..f8cf591 100644 --- a/backend/src/cyclone/api.py +++ b/backend/src/cyclone/api.py @@ -714,20 +714,16 @@ def list_claims( @app.get("/api/claims/{claim_id}") def get_claim_detail_endpoint(claim_id: str) -> dict: - """Return one claim with full drawer context. + """Return one claim with full drawer context (SP4). - SP4 detail-drawer endpoint. The body is the spec-shaped dict - produced by :meth:`CycloneStore.get_claim_detail` — header, state, - service lines, diagnoses, parties, validation, raw segments, - ``stateHistory`` (most-recent-first, capped at 50), and a - populated ``matchedRemittance`` block if the claim is paired. + Body shape is produced by :meth:`CycloneStore.get_claim_detail`: + header, state, service lines, diagnoses, parties, validation, + raw segments, ``stateHistory`` (most-recent-first, capped at 50), + and a populated ``matchedRemittance`` block when paired. - Path param is ``claim_id`` (not ``id``) to avoid shadowing - FastAPI's internal ``id`` name and to keep OpenAPI docs self- - describing (matches the ``/api/acks/{ack_id}`` convention from - SP3). Returns 404 when the claim is missing — never 500 — so the - UI can distinguish "claim doesn't exist" from a transient fetch - failure. + Path param is ``claim_id`` (matches the SP3 ``/api/acks/{ack_id}`` + convention). Returns 404 — never 500 — on a missing claim so the + UI can distinguish "doesn't exist" from a transient fetch error. """ body = store.get_claim_detail(claim_id) if body is None: diff --git a/backend/tests/test_api_claim_detail.py b/backend/tests/test_api_claim_detail.py index 041b110..efd139d 100644 --- a/backend/tests/test_api_claim_detail.py +++ b/backend/tests/test_api_claim_detail.py @@ -103,6 +103,11 @@ def test_get_claim_detail_happy_path(seeded_store): "validation", "rawSegments", "matchedRemittance", "stateHistory", ): assert k in body, f"missing key {k!r}" + # Spec §3.2: matchedRemittance is `null` for an unpaired claim + # (the dominant UI render path). The matched-populated case is + # covered separately in + # test_get_claim_detail_matched_remit_inlines_summary. + assert body["matchedRemittance"] is None assert body["id"] == cid # billedAmount is a number (not a Decimal string). assert isinstance(body["billedAmount"], (int, float)) @@ -140,7 +145,11 @@ def test_get_claim_detail_state_history_includes_manual_match(seeded_store_with_ "/api/reconciliation/unmatched", headers=JSON, ).json() if not unmatched["claims"] or not unmatched["remittances"]: - pytest.skip("seeded fixtures don't produce an unmatched pair") + pytest.fail( + "co_medicaid_837p.txt + co_medicaid_835.txt fixtures no longer " + "produce an unmatched pair; matched-remit tests need updating " + "(see seeded_store_with_pair docstring for the expected invariant)" + ) claim_id = unmatched["claims"][0]["id"] remit_id = unmatched["remittances"][0]["id"] @@ -180,7 +189,11 @@ def test_get_claim_detail_matched_remit_inlines_summary(seeded_store_with_pair): "/api/reconciliation/unmatched", headers=JSON, ).json() if not unmatched["claims"] or not unmatched["remittances"]: - pytest.skip("seeded fixtures don't produce an unmatched pair") + pytest.fail( + "co_medicaid_837p.txt + co_medicaid_835.txt fixtures no longer " + "produce an unmatched pair; matched-remit tests need updating " + "(see seeded_store_with_pair docstring for the expected invariant)" + ) claim_id = unmatched["claims"][0]["id"] remit_id = unmatched["remittances"][0]["id"] seeded_store_with_pair.post(