Files
cyclone/backend/tests/test_payer_config_277ca.py
T
Tyler 2c0afbe9c5 feat(sp10): 277CA parser + Payer-Rejected Inbox lane
- Add cyclone.parsers.models_277ca + parse_277ca (X12 005010X214)
  - Per-Patient HL ClaimStatus with REF*1K (PCN), REF*EJ (tax ID),
    STC category code, amount, service date
  - STC classifier: A1-A3 accepted, A4/A6/A7 rejected, A8/A9 pended,
    P1-P5 paid, anything else unknown
  - Multiple STCs per Patient HL: last wins (canonical pattern for
    'first pended, then paid' acknowledgments)
  - Subscriber-level STCs surface in unscoped_statuses
- Add apply_277ca_rejections — stamps Claim.payer_rejected_* fields on
  matching rows (lookup by patient_control_number, mirrors 999 path)
- New /api/parse-277ca, /api/277ca-acks, /api/277ca-acks/{id} endpoints
- New two77ca_acks table + Two77caAck ORM model
- New Claim columns: payer_rejected_at, _reason, _status_code, _by_277ca_id
- New payer_rejected lane in /api/inbox/lanes (distinct from 999
  envelope rejected lane)
- New PayerConfig277CA block in config/payers.yaml + Pydantic model
- Migration 0008 bumps user_version to 8

Tests: 654 -> 688 (parser 22 + apply 6 + API 8 + config 6 + adjustments).
All 688 backend tests pass; 1 pre-existing skipped test class unaffected.
2026-06-20 23:41:01 -06:00

105 lines
3.6 KiB
Python

"""Tests for PayerConfig277CA loading from YAML.
SP10 T4. Mirrors ``test_payer_config_loading.py`` but for the new
``PayerConfig277CA`` block that the 277CA parser and Inbox lane
consume for status-code classification.
"""
from __future__ import annotations
from pathlib import Path
import pytest
import yaml
from cyclone.payers import (
DEFAULT_CONFIG_PATH,
_tx_to_model,
all_configs,
get_config,
load_payer_configs,
reset,
)
from cyclone.providers import PayerConfig277CA
@pytest.fixture(autouse=True)
def _clear_registry():
"""Each test starts with an empty config registry."""
reset()
yield
reset()
class TestPayerConfig277CALoading:
def test_loads_default_config_with_277ca_block(self):
"""The default config/payers.yaml must have a CO_TXIX 277CA block."""
load_payer_configs(DEFAULT_CONFIG_PATH)
cfg = get_config("CO_TXIX", "277CA")
assert cfg is not None
# Defaults from PayerConfig277CA.
assert "A6" in cfg["rejected_status_codes"]
assert "A8" in cfg["pended_status_codes"]
assert "A3" in cfg["accepted_status_codes"]
assert "P1" in cfg["paid_status_codes"]
assert "277CA" in cfg["transaction_set_ids_allowed"]
def test_model_rejects_bad_status_code_type(self, tmp_path: Path):
"""A non-list rejected_status_codes value must fail validation."""
bad = {
"payers": [
{
"payer_id": "BAD_PAYER",
"name": "Bad",
"receiver_name": "BAD",
"receiver_id": "BADID",
"configs": {
"277CA": {
"rejected_status_codes": "A6", # must be list
"pended_status_codes": ["A8"],
"accepted_status_codes": ["A1"],
"paid_status_codes": ["P1"],
"transaction_set_ids_allowed": ["277", "277CA"],
"implementation_guide": "005010X214",
},
},
},
],
}
p = tmp_path / "bad.yaml"
p.write_text(yaml.safe_dump(bad))
with pytest.raises(ValueError, match="BAD_PAYER"):
load_payer_configs(p)
class TestPayerConfig277CAModel:
def test_default_factory_values(self):
"""A minimal PayerConfig277CA defaults to the canonical HCPF sets."""
cfg = PayerConfig277CA()
assert "A4" in cfg.rejected_status_codes
assert "A6" in cfg.rejected_status_codes
assert "A7" in cfg.rejected_status_codes
assert "A8" in cfg.pended_status_codes
assert "277CA" in cfg.transaction_set_ids_allowed
def test_explicit_override(self):
cfg = PayerConfig277CA(
rejected_status_codes=["X1"],
pended_status_codes=["X2"],
accepted_status_codes=["X3"],
paid_status_codes=["X4"],
)
assert cfg.rejected_status_codes == ["X1"]
assert cfg.pended_status_codes == ["X2"]
class TestTxToModelFor277CA:
def test_int_key_routes_to_277ca_model(self):
"""PyYAML may parse numeric keys as int — _tx_to_model must handle both."""
# 277CA as int isn't likely (not pure digits), but 277 as int is.
# Test the canonical string.
assert _tx_to_model("277CA") is PayerConfig277CA
def test_string_key_routes_correctly(self):
assert _tx_to_model("277CA") is PayerConfig277CA
assert _tx_to_model("835") is not PayerConfig277CA # it's 835 model