"""Tests for the TA1 (Interchange Acknowledgment) parser orchestrator.""" from __future__ import annotations from datetime import date import pytest from cyclone.parsers.exceptions import CycloneParseError from cyclone.parsers.parse_ta1 import parse_ta1_text # A minimal accepted TA1 in canonical CCYYMMDD form (per X12 spec). # ISA must be exactly 106 chars (15-char sender/receiver IDs). ACCEPTED_CCYYMMDD = ( "ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER " "*260520*1750*^*00501*000000001*0*P*:~" "TA1*000000001*20260520*1750*A*000*20260520~" "IEA*1*000000001~" ) # A rejected TA1 with Colorado's YYMMDD date format (6 digits instead of 8). # This is what FromHPE/tp11525703-837P_M019044596-…_TA1.x12 looks like in # production — same shape but YYMMDD and ack_code=R, note_code=006. REJECTED_YYMMDD = ( "ISA*00* *00* *ZZ*COMEDASSISTPROG*ZZ*TP11525703 " "*260520*1750*^*00501*000000001*0*P*:~" "TA1*320293557*260520*2338*R*006~" "IEA*0*000000001~" ) # Accepted variant with errors (E) — uncommon but spec-legal. ACCEPTED_WITH_ERRORS = ( "ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER " "*260520*1750*^*00501*000000001*0*P*:~" "TA1*000000001*20260520*1750*E*007*20260520~" "IEA*1*000000001~" ) def test_parse_accepted_ccyymmdd_returns_a(): """Spec-compliant TA1 with CCYYMMDD dates surfaces ack_code='A'.""" result = parse_ta1_text(ACCEPTED_CCYYMMDD, input_file="accepted.ta1") assert result.ta1.ack_code == "A" assert result.ta1.control_number == "000000001" assert result.ta1.interchange_date == date(2026, 5, 20) assert result.ta1.interchange_time == "1750" assert result.ta1.note_code == "000" assert result.ta1.ack_generated_date == date(2026, 5, 20) # Envelope mirrors the ISA assert result.envelope.sender_id == "SENDER" assert result.envelope.receiver_id == "RECEIVER" assert result.envelope.control_number == "000000001" # Summary reflects the single ack assert result.summary.total_claims == 1 assert result.summary.passed == 1 assert result.summary.failed == 0 def test_parse_rejected_yymmdd_from_production(): """Colorado's YYMMDD date format (6 digits) must still parse.""" result = parse_ta1_text(REJECTED_YYMMDD, input_file="rejected.ta1") assert result.ta1.ack_code == "R" assert result.ta1.note_code == "006" assert result.ta1.interchange_date == date(2026, 5, 20), ( "YYMMDD must be inferred as 20YY-MM-DD" ) assert result.ta1.interchange_time == "2338" assert result.summary.passed == 0 assert result.summary.failed == 1 def test_parse_accepted_with_errors_returns_e(): """TA1 ack_code='E' (Accepted with errors) is preserved as the literal.""" result = parse_ta1_text(ACCEPTED_WITH_ERRORS, input_file="with_errors.ta1") assert result.ta1.ack_code == "E" assert result.ta1.note_code == "007" def test_source_batch_id_format(): """source_batch_id must be 'TA1-' so the FK target is stable.""" result = parse_ta1_text(ACCEPTED_CCYYMMDD, input_file="x.ta1") assert result.source_batch_id == "TA1-000000001" def test_missing_isa_raises(): """No ISA envelope at file start → CycloneParseError (caught by tokenize).""" text = "TA1*000000001*20260520*1750*A*000*20260520~" with pytest.raises(CycloneParseError, match="ISA"): parse_ta1_text(text, input_file="bad.ta1") def test_missing_ta1_raises(): """ISA but no TA1 segment → CycloneParseError.""" text = ( "ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER " "*260520*1750*^*00501*000000001*0*P*:~" "IEA*0*000000001~" ) with pytest.raises(CycloneParseError, match="No TA1 segment"): parse_ta1_text(text, input_file="no_ta1.ta1") def test_short_ta1_segment_uses_defaults(): """A TA1 with fewer than 6 elements still parses with sensible defaults.""" text = ( "ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER " "*260520*1750*^*00501*000000001*0*P*:~" "TA1*000000001~" "IEA*0*000000001~" ) result = parse_ta1_text(text, input_file="short.ta1") assert result.ta1.ack_code == "R" # default to rejected when missing assert result.ta1.note_code is None assert result.ta1.interchange_date is None assert result.ta1.interchange_time is None def test_garbage_date_returns_none_not_raises(): """A garbage date in TA102 doesn't raise — it surfaces as None.""" text = ( "ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER " "*260520*1750*^*00501*000000001*0*P*:~" "TA1*000000001*not-a-date*1750*A*000~" "IEA*0*000000001~" ) result = parse_ta1_text(text, input_file="bad_date.ta1") assert result.ta1.interchange_date is None assert result.ta1.ack_code == "A" # the rest still parses