feat(parsers+api+ui+db): 999 ACK transaction set end-to-end (SP3 P3)

This commit is contained in:
Tyler
2026-06-20 08:03:37 -06:00
parent 7a20f732f2
commit fb2a98fc7a
24 changed files with 2618 additions and 1 deletions
+81
View File
@@ -0,0 +1,81 @@
"""Tests for the 999 ACK parser orchestrator."""
from __future__ import annotations
from datetime import date
from pathlib import Path
import pytest
from cyclone.parsers.exceptions import CycloneParseError
from cyclone.parsers.parse_999 import parse_999_text
ACCEPTED = Path(__file__).parent / "fixtures" / "minimal_999.txt"
REJECTED = Path(__file__).parent / "fixtures" / "minimal_999_rejected.txt"
def test_parse_minimal_999_returns_accepted():
"""The minimal accepted fixture must surface an 'A' ack code on the
functional group and a single accepted set."""
text = ACCEPTED.read_text()
result = parse_999_text(text, input_file=ACCEPTED.name)
assert len(result.functional_group_acks) == 1
fg = result.functional_group_acks[0]
assert fg.ack_code == "A"
assert fg.received_count == 1
assert fg.accepted_count == 1
assert fg.rejected_count == 0
assert fg.ak1.functional_id_code == "HC"
assert fg.ak1.group_control_number == "0001"
def test_parse_999_set_response_accepted():
"""The single set response is an AK5=A (accepted)."""
text = ACCEPTED.read_text()
result = parse_999_text(text, input_file=ACCEPTED.name)
assert len(result.set_responses) == 1
s = result.set_responses[0]
assert s.set_accept_reject.code == "A"
assert s.transaction_set_identifier == "837"
assert s.set_control_number == "0001"
assert s.ak2.functional_id_code == "837"
assert s.segment_errors == [] # no AK3/AK4 in the accepted fixture
def test_parse_999_envelope_built():
"""Envelope must capture sender/receiver/control number from ISA/GS/ST."""
text = ACCEPTED.read_text()
result = parse_999_text(text, input_file=ACCEPTED.name)
env = result.envelope
assert env.sender_id == "SUBMITTERID" # ISA06
assert env.receiver_id == "RECEIVERID" # ISA08
assert env.control_number == "000000001" # ISA13
assert env.implementation_guide == "005010X231A1"
# transaction_date parsed from the GS04 (20240101)
assert env.transaction_date == date(2024, 1, 1)
def test_parse_999_ak3_ak4_optional():
"""A 999 with no AK3/AK4 segments parses cleanly with empty errors."""
text = ACCEPTED.read_text()
result = parse_999_text(text, input_file=ACCEPTED.name)
for s in result.set_responses:
assert s.segment_errors == []
def test_parse_999_rejected_set():
"""The rejected fixture has AK5=R; the parser must surface it."""
text = REJECTED.read_text()
result = parse_999_text(text, input_file=REJECTED.name)
assert len(result.functional_group_acks) == 1
fg = result.functional_group_acks[0]
assert fg.ack_code == "R"
assert fg.rejected_count == 1
assert fg.accepted_count == 0
s = result.set_responses[0]
assert s.set_accept_reject.code == "R"
def test_parse_999_garbage_raises():
"""Non-EDI input must raise CycloneParseError, not return a half-built result."""
with pytest.raises(CycloneParseError):
parse_999_text("not edi at all", input_file="bad.txt")