Files
cyclone/backend/tests/test_sftp_stub.py
Tyler 9bca4b608a feat(release): v0.2.0 — batch 837 export, ClaimCard, theme tokens
Backend:

- New POST /api/batches/{id}/export-837: regenerate X12 837 files
  for a list of claim_ids into a ZIP using HCPF file naming standards,
  with a unique interchange/group control number per export. Wire
  the clearhouse Loop 1000A (NM1*41 + PER) and per-payer receiver
  (NM1*40) blocks so the serializer no longer falls back to
  CYCLONE / RECEIVER placeholders.
- /api/parse-837 and /api/parse-835 now surface the server-side
  batch_id in both JSON and NDJSON response shapes so the frontend
  can hit batch-scoped endpoints without an extra listBatches
  round-trip.
- Filename helpers and the 837 serializer updated to match the new
  HCPF envelope; tests cover batch export, parse batch_id, and the
  serializer's control-number uniqueness guarantee.

Frontend:
- New shared components: ClaimCard, ClaimCard837, DominantKpiCard,
  EditorialNote, ExportBar, TickerTape, and a charts/ set
  (BarChart, HBarChart, SegmentedBar, AgingBars).
- New useBatchExport hook driving ExportBar's download flow against
  the new endpoint.
- ClaimDrawer, Lane, and Layout migrated from raw CSS-variable
  colors to Tailwind theme tokens (bg-card, text-foreground,
  border/60, etc.) for consistency with the rest of the instrument
  chrome; the active tab indicator gains a subtle accent glow.
- Upload, Inbox, Batches, BatchDiff, Reconciliation, and Acks pages
  reworked to compose the new shared components and consume the new
  batch-scoped API surface (notably ExportBar wired into Batches).

Tooling / Docs:
- Add audit-uiux.mjs and a docs/goodclaim.x12 sample fixture.
- Update ClaimDrawer testids and add coverage for the new
  components and the useBatchExport hook.

Rolls up into the v0.2.0 release tag.
2026-06-22 11:01:58 -06:00

95 lines
3.3 KiB
Python

"""SP9 — SFTP stub tests."""
from __future__ import annotations
import json
from pathlib import Path
import pytest
from cyclone.clearhouse import SftpClient
from cyclone.providers import SftpBlock
@pytest.fixture
def sftp_block(tmp_path):
staging = tmp_path / "staging"
return SftpBlock(
host="mft.gainwelltechnologies.com",
port=22,
username="colorado-fts\\coxix_prod_11525703",
paths={
"outbound": "/CO XIX/PROD/coxix_prod_11525703/FromHPE",
"inbound": "/CO XIX/PROD/coxix_prod_11525703/ToHPE",
},
stub=True,
staging_dir=str(staging),
poll_seconds=300,
auth={"method": "keychain", "secret_ref": "sftp.gainwell.password"},
)
def test_stub_writes_preserving_remote_path(sftp_block, tmp_path):
client = SftpClient(sftp_block)
remote = "/CO XIX/PROD/coxix_prod_11525703/FromHPE/tp11525703-837P-20260620132243505-1of1.x12"
target = client.write_file(remote, b"ISA*00*...~IEA*1*1~")
assert target.exists()
assert target.read_bytes() == b"ISA*00*...~IEA*1*1~"
# Confirm the full nested MFT path is preserved under staging
rel = target.relative_to(sftp_block.staging_dir)
assert str(rel) == "CO XIX/PROD/coxix_prod_11525703/FromHPE/tp11525703-837P-20260620132243505-1of1.x12"
def test_stub_creates_parent_dirs(sftp_block):
client = SftpClient(sftp_block)
remote = "/deep/nested/path/file.x12"
target = client.write_file(remote, b"data")
assert target.exists()
assert target.parent.is_dir()
def test_stub_list_inbound_empty_when_no_local_files(sftp_block):
client = SftpClient(sftp_block)
assert client.list_inbound() == []
def test_stub_list_inbound_returns_local_files(sftp_block):
# Simulate operator dropping a file in the inbound staging dir
inbound_dir = Path(sftp_block.staging_dir) / "CO XIX/PROD/coxix_prod_11525703/ToHPE"
inbound_dir.mkdir(parents=True, exist_ok=True)
(inbound_dir / "TP11525703-837P_M019048402-20260520231513488-1of1_999.x12").write_bytes(b"X")
client = SftpClient(sftp_block)
files = client.list_inbound()
assert len(files) == 1
assert files[0].name.startswith("TP11525703-837P_M019048402")
assert files[0].size == 1
def test_stub_get_secret_returns_stub_when_keychain_empty(sftp_block):
client = SftpClient(sftp_block)
# Keychain is empty on the test box, so we get the stub sentinel
secret = client.get_secret("sftp.gainwell.password")
assert secret == "<stub-secret>"
def test_stub_read_file_returns_bytes(sftp_block, tmp_path):
"""SP16: the stub read_file returns bytes from the staging dir.
Lets the inbound scheduler exercise the same code path on a
workstation without a real MFT connection.
"""
inbound = tmp_path / "staging" / "ToHPE"
inbound.mkdir(parents=True)
(inbound / "TP11525703-837P_M019048402-20260618130000000-1of1_TA1.x12").write_bytes(
b"hello-world",
)
client = SftpClient(sftp_block)
body = client.read_file("/ToHPE/TP11525703-837P_M019048402-20260618130000000-1of1_TA1.x12")
assert body == b"hello-world"
def test_stub_read_file_missing_raises(sftp_block):
client = SftpClient(sftp_block)
with pytest.raises(FileNotFoundError):
client.read_file("/ToHPE/does-not-exist.x12")