"""SP24 — tests for the `cyclone reissue-claims` CLI subcommand. Five smoke tests covering the canonical cyclone-cli pattern: 1. --help renders cleanly and lists the long-form flags. 2. happy path: a single-file input dir produces the expected number of output files. 3. empty input dir: exits 2 with a "PARSE FAILED" message. 4. IG-correctness guard fires when the constant is monkeypatched to True; CLI exits 1 with the REFUSING log line. 5. --zip-output produces a valid zip with round-trip integrity. """ from __future__ import annotations import zipfile from pathlib import Path import pytest from click.testing import CliRunner from cyclone.cli import main # Canonical minimal-claim fixture — flat, module-level Path constant # per the cyclone-tests convention. NEVER reach into docs/prodfiles/. MINIMAL_837P = Path(__file__).parent / "fixtures" / "minimal_837p.txt" @pytest.fixture def _input_dir(tmp_path: Path) -> Path: """Drop the minimal_837p fixture into tmp_path/in/.""" in_dir = tmp_path / "in" in_dir.mkdir() (in_dir / "real.x12").write_text(MINIMAL_837P.read_text()) return in_dir def test_reissue_claims_help_renders(): """--help exits 0 and lists every long-form flag in the SP24 spec.""" runner = CliRunner() result = runner.invoke(main, ["reissue-claims", "--help"]) assert result.exit_code == 0, result.output # Required flag. assert "--input-dir" in result.output # Optional flags documented in the spec §2 Decision 3. for flag in [ "--output-root", "--date", "--pipeline", "--payer", "--sender-id", "--receiver-id", "--submitter-name", "--submitter-contact-name", "--submitter-contact-email", "--receiver-name", "--zip-output", "--no-clean", "--log-level", ]: assert flag in result.output, f"missing {flag} in help output" def test_reissue_claims_happy_path(_input_dir: Path, tmp_path: Path): """A single-file input dir produces 1 .x12 output + summary sidecar.""" runner = CliRunner() out_root = tmp_path / "out" result = runner.invoke( main, [ "reissue-claims", "--input-dir", str(_input_dir), "--output-root", str(out_root), "--date", "2026-07-08", "--pipeline", "initial", ], catch_exceptions=False, ) assert result.exit_code == 0, ( f"CLI exited {result.exit_code}, output={result.output!r}, " f"exception={result.exception!r}" ) # 1 .x12 + 1 _serialize_summary.json = 2 entries. emitted_dir = out_root / "2026-07-08" / "initial" assert emitted_dir.is_dir() files = sorted(emitted_dir.iterdir()) x12s = [f for f in files if f.suffix == ".x12"] assert len(x12s) == 1 assert (emitted_dir / "_serialize_summary.json").is_file() # HCPF-spec filename. assert x12s[0].name.startswith("tp11525703-837P-") assert x12s[0].name.endswith("-1of1.x12") # DONE summary line is in stdout. assert "DONE files=1 errors=0" in result.output def test_reissue_claims_empty_input_exits_2(tmp_path: Path): """An empty input dir exits 2 with a clear PARSE FAILED message.""" empty_dir = tmp_path / "empty" empty_dir.mkdir() out_root = tmp_path / "out" runner = CliRunner() result = runner.invoke( main, [ "reissue-claims", "--input-dir", str(empty_dir), "--output-root", str(out_root), ], catch_exceptions=False, ) # No claims → exit 2. assert result.exit_code == 2, ( f"CLI exited {result.exit_code}, output={result.output!r}" ) # The error message should explain the failure. combined = result.output + (result.stderr or "") assert "PARSE FAILED" in combined or "no claims" in combined.lower() def test_reissue_claims_ig_correctness_guard_fires(monkeypatch, _input_dir: Path, tmp_path: Path): """When PATIENT_LOOP_DEFAULT_INCLUDED is True, the CLI refuses to run.""" # Monkeypatch the serializer constant to the broken value. # `raising=False` lets us set an attribute that didn't exist # pre-import (defensive against pytest collection order). from cyclone.parsers import serialize_837 as ser_mod monkeypatch.setattr(ser_mod, "PATIENT_LOOP_DEFAULT_INCLUDED", True) runner = CliRunner() out_root = tmp_path / "out" result = runner.invoke( main, [ "reissue-claims", "--input-dir", str(_input_dir), "--output-root", str(out_root), ], catch_exceptions=False, ) # Guard fires → exit 1. assert result.exit_code == 1, ( f"CLI exited {result.exit_code} (expected 1); output={result.output!r}" ) # The refusal message is in stderr (click.echo(..., err=True)). combined = result.output + (result.stderr or "") assert "REFUSING to run" in combined assert "PATIENT_LOOP_DEFAULT_INCLUDED" in combined def test_reissue_claims_zip_output_round_trip(_input_dir: Path, tmp_path: Path): """--zip-output writes a zip whose testzip() returns None.""" runner = CliRunner() out_root = tmp_path / "out" zip_path = tmp_path / "out.zip" result = runner.invoke( main, [ "reissue-claims", "--input-dir", str(_input_dir), "--output-root", str(out_root), "--date", "2026-07-08", "--zip-output", str(zip_path), ], catch_exceptions=False, ) assert result.exit_code == 0, result.output assert zip_path.is_file() with zipfile.ZipFile(zip_path) as zf: # None = no corrupt entries. assert zf.testzip() is None names = zf.namelist() # 1 X12 file in the zip (the summary sidecar is not zipped). assert len(names) == 1 assert names[0].startswith("tp11525703-837P-") assert names[0].endswith("-1of1.x12")