"""TDD: compute_lanes — four Inbox lanes from the DB on read. Plan: docs/superpowers/plans/2026-06-20-cyclone-workflow-automation.md Task 6 (T6). """ from __future__ import annotations from datetime import datetime, timedelta, timezone from decimal import Decimal import pytest from cyclone import db from cyclone.db import ( Base, Batch, Claim, ClaimState, Remittance, init_db, ) from cyclone.inbox_lanes import compute_lanes @pytest.fixture(autouse=True) def _setup(tmp_path, monkeypatch): monkeypatch.setenv("CYCLONE_DB_URL", f"sqlite:///{tmp_path}/lanes.db") db._reset_for_tests() db.init_db() yield def _add_batch() -> None: with db.SessionLocal()() as s: if s.get(Batch, "B-1") is not None: return s.add(Batch( id="B-1", kind="837p", input_filename="x.txt", parsed_at=datetime.now(timezone.utc), totals_json={"total_claims": 1}, validation_json={"passed": True, "warnings": [], "errors": []}, raw_result_json={"_": "stub"}, )) s.commit() def _add_claim(*, claim_id: str, pcn: str = "PCN", state: ClaimState = ClaimState.SUBMITTED, charge: Decimal = Decimal("100.00"), provider_npi: str | None = "1234567890", payer_id: str | None = "PAYER-A", service_date=None) -> None: _add_batch() with db.SessionLocal()() as s: s.add(Claim( id=claim_id, batch_id="B-1", patient_control_number=pcn, state=state, charge_amount=charge, provider_npi=provider_npi, payer_id=payer_id, service_date_from=service_date, )) s.commit() def _add_remit(*, remit_id: str, pcn: str = "PCN", charge: Decimal = Decimal("100.00"), claim_id: str | None = None, payer_id: str | None = "PAYER-A", service_date=None) -> None: _add_batch() with db.SessionLocal()() as s: r = Remittance( id=remit_id, batch_id="B-1", payer_claim_control_number=pcn, claim_id=claim_id, status_code="1", status_label="Processed", total_charge=charge, total_paid=Decimal("0"), received_at=datetime.now(timezone.utc), service_date=service_date, ) # Stash rendering_provider_npi and payer_id in raw_json for the # 835 fields that aren't on the ORM model. r.raw_json = { "rendering_provider_npi": "1234567890", "payer_id": payer_id, "patient_control_number": pcn, } s.add(r) s.commit() def test_rejected_lane_includes_rejected_claims(): _add_claim(claim_id="C1", state=ClaimState.REJECTED) with db.SessionLocal()() as s: lanes = compute_lanes(s, dismissed_pairs=set()) assert "C1" in {r["id"] for r in lanes.rejected} def test_unmatched_lane_includes_submitted_claims_with_no_remit(): _add_claim(claim_id="C1", state=ClaimState.SUBMITTED) with db.SessionLocal()() as s: lanes = compute_lanes(s, dismissed_pairs=set()) assert "C1" in {r["id"] for r in lanes.unmatched} def test_unmatched_remit_appears_with_candidates(): _add_claim(claim_id="C1", pcn="PCN-1", service_date=None) _add_remit(remit_id="R1", pcn="PCN-1") # exact match → candidate with db.SessionLocal()() as s: lanes = compute_lanes(s, dismissed_pairs=set()) assert any(r["id"] == "R1" for r in lanes.candidates) def test_candidate_below_threshold_is_hidden(): _add_claim(claim_id="C1", pcn="A", charge=Decimal("100.00")) _add_remit(remit_id="R1", pcn="Z", charge=Decimal("999.00")) # everything mismatched with db.SessionLocal()() as s: lanes = compute_lanes(s, dismissed_pairs=set()) assert not any(r["id"] == "R1" for r in lanes.candidates) def test_dismissed_pair_excluded_from_candidates(): _add_claim(claim_id="C1", pcn="PCN-1") _add_remit(remit_id="R1", pcn="PCN-1") with db.SessionLocal()() as s: lanes = compute_lanes( s, dismissed_pairs={frozenset({"C1", "R1"})}, ) assert not any(r["id"] == "R1" for r in lanes.candidates) def test_done_today_includes_recent_terminal_states(): _add_claim(claim_id="C1", state=ClaimState.PAID) _add_claim(claim_id="C2", state=ClaimState.PAID) # Force C1's state_changed_at to be recent, C2 to be old. now = datetime.now(timezone.utc) with db.SessionLocal()() as s: c1 = s.get(Claim, "C1") c1.state_changed_at = now - timedelta(hours=2) c2 = s.get(Claim, "C2") c2.state_changed_at = now - timedelta(hours=30) s.commit() with db.SessionLocal()() as s: lanes = compute_lanes(s, dismissed_pairs=set()) ids = {r["id"] for r in lanes.done_today} assert "C1" in ids assert "C2" not in ids