feat(backend): wire parse-837/parse-835 to persist to InMemoryStore

This commit is contained in:
Tyler
2026-06-19 19:25:23 -06:00
parent ca645db518
commit 86e69d5b95
2 changed files with 107 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
"""Successful parses must persist to the store; failed parses must not."""
from __future__ import annotations
from pathlib import Path
import pytest
from fastapi.testclient import TestClient
from cyclone.api import app
from cyclone.store import store as global_store
FIXTURE = Path(__file__).parent / "fixtures" / "co_medicaid_837p.txt"
@pytest.fixture(autouse=True)
def clear_store():
"""Reset the module-level store before and after each test."""
with global_store._lock:
global_store._batches.clear()
yield
with global_store._lock:
global_store._batches.clear()
@pytest.fixture
def client() -> TestClient:
return TestClient(app)
def test_successful_parse_837_creates_batch(client: TestClient):
assert len(global_store.list()) == 0
text = FIXTURE.read_text()
resp = client.post(
"/api/parse-837",
files={"file": ("test.txt", text, "text/plain")},
headers={"Accept": "application/json"},
)
assert resp.status_code == 200, resp.text
assert len(global_store.list()) == 1
rec = global_store.list()[0]
assert rec.kind == "837p"
assert rec.input_filename == "test.txt"
def test_failed_parse_837_does_not_create_batch(client: TestClient):
"""An empty / garbage file should NOT create a batch."""
assert len(global_store.list()) == 0
resp = client.post(
"/api/parse-837",
files={"file": ("garbage.txt", "not-edi", "text/plain")},
headers={"Accept": "application/json"},
)
# Either a 4xx from the parser, or a 200 with 0 claims — the contract
# is that NO batch is added either way.
assert resp.status_code in (200, 400, 422)
assert len(global_store.list()) == 0
def test_validation_failed_837_does_not_create_batch(client: TestClient):
"""A parse that produced claims with validation errors (422) must not be stored.
The spec only mandates "do not store on CycloneParseError or unhandled
exception"; we choose the stricter interpretation that 422 validation
failures are also excluded so the data on disk is always clean.
"""
# Construct a minimal but technically malformed 837P that the parser
# will accept but validation will reject. We inject a wrong NPI
# length to trigger the per-claim NPI validation rule.
text = FIXTURE.read_text()
# Replace the provider NPI (XX*1881068062) with an invalid 8-digit NPI
# (the rule is NPI must be 10 digits). This keeps the parse step
# green while failing claim validation.
text = text.replace("XX*1881068062", "XX*12345678")
resp = client.post(
"/api/parse-837",
files={"file": ("bad.txt", text, "text/plain")},
headers={"Accept": "application/json"},
)
assert resp.status_code == 422, resp.text
assert len(global_store.list()) == 0