"""Tests for the 270 (Eligibility Benefit Inquiry) Pydantic models. SP3 Phase 4 (T18): minimal typed shape needed by the parser (T20), serializer (T22), and ``/api/eligibility/request`` endpoint (T23). The spec defines richer models (gender, address, etc.), but the v1 round-trip only needs the minimum surface — see the inline rationale in ``parsers/models_270.py``. """ from __future__ import annotations import json from datetime import date import pytest from pydantic import ValidationError from cyclone.parsers.models import BatchSummary, Envelope from cyclone.parsers.models_270 import ( EligibilityBenefitInquiry, InformationReceiver270, InformationSource270, ParseResult270, Patient270, Subscriber270, ) def _build_result() -> ParseResult270: return ParseResult270( envelope=Envelope( sender_id="SUBMITTERID", receiver_id="RECEIVERID", control_number="000000001", transaction_date=date(2024, 1, 1), implementation_guide="005010X279A1", ), information_source=InformationSource270( name="PAYER NAME", id="SKCO0", ), information_receiver=InformationReceiver270( name="PROVIDER NAME", npi="1234567890", ), subscriber=Subscriber270( member_id="MEMBER123", first_name="JOHN", last_name="DOE", dob=date(1980, 1, 1), ), inquiries=[ EligibilityBenefitInquiry(service_type_code="1"), EligibilityBenefitInquiry(service_type_code="30"), ], summary=BatchSummary( input_file="minimal_270.txt", control_number="000000001", transaction_date=date(2024, 1, 1), total_claims=1, passed=1, failed=0, ), ) def test_parse_result_270_minimal_round_trip(): """Build a minimal ParseResult270 and round-trip via JSON.""" r = _build_result() blob = json.loads(r.model_dump_json()) assert blob["envelope"]["control_number"] == "000000001" assert blob["envelope"]["implementation_guide"] == "005010X279A1" assert blob["information_source"]["name"] == "PAYER NAME" assert blob["information_source"]["id"] == "SKCO0" assert blob["information_receiver"]["npi"] == "1234567890" assert blob["subscriber"]["first_name"] == "JOHN" assert blob["subscriber"]["last_name"] == "DOE" assert blob["subscriber"]["member_id"] == "MEMBER123" assert blob["subscriber"]["dob"] == "1980-01-01" assert blob["patient"] is None assert len(blob["inquiries"]) == 2 assert blob["inquiries"][0]["service_type_code"] == "1" assert blob["inquiries"][1]["service_type_code"] == "30" # Round-trip back to the model. r2 = ParseResult270.model_validate(blob) assert r2.information_source.name == "PAYER NAME" assert r2.subscriber.member_id == "MEMBER123" assert r2.inquiries[0].service_type_code == "1" assert r2.summary.total_claims == 1 def test_eligibility_benefit_inquiry_required_fields(): """``service_type_code`` is the only required field on the EQ model.""" # Without it: validation error. with pytest.raises(ValidationError) as exc: EligibilityBenefitInquiry() # type: ignore[call-arg] assert "service_type_code" in str(exc.value) # With it: parses cleanly. ``coverage_info`` is optional. ebi = EligibilityBenefitInquiry(service_type_code="1") assert ebi.coverage_info is None # coverage_info supplied → round-trips. ebi2 = EligibilityBenefitInquiry(service_type_code="30", coverage_info="C") blob = json.loads(ebi2.model_dump_json()) assert blob["coverage_info"] == "C" assert EligibilityBenefitInquiry.model_validate(blob).coverage_info == "C" def test_patient_270_optional_fields(): """Patient270's fields are all optional; relationship carries HL/INS qualifiers.""" p = Patient270() blob = json.loads(p.model_dump_json()) assert blob["first_name"] is None assert blob["relationship"] is None p2 = Patient270(first_name="JANE", last_name="DOE", relationship="19") # 19 = child assert p2.relationship == "19" def test_parse_result_270_inquiries_default_empty(): """Inquiries default to an empty list when omitted (Pydantic default_factory).""" r = ParseResult270( envelope=Envelope( sender_id="S", receiver_id="R", control_number="000000001", transaction_date=date(2024, 1, 1), ), information_source=InformationSource270(name="P"), information_receiver=InformationReceiver270(name="P"), subscriber=Subscriber270(member_id="M"), summary=BatchSummary(input_file="", total_claims=0, passed=0, failed=0), ) assert r.inquiries == [] assert r.patient is None