"""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")