import pytest from typing import Union import sys import os # Add the src directory to the path so we can import our modules sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) from simplified_access import ( CustomerData, CustomerFactory, ResGuestFactory, HotelReservationIdData, HotelReservationIdFactory, AlpineBitsFactory, PhoneTechType, OtaMessageType, NotifCustomer, RetrieveCustomer, NotifResGuests, RetrieveResGuests, NotifHotelReservationId, RetrieveHotelReservationId, ) @pytest.fixture def sample_customer_data(): """Fixture providing sample customer data for testing.""" return CustomerData( given_name="John", surname="Doe", name_prefix="Mr.", name_title="Jr.", phone_numbers=[ ("+1234567890", PhoneTechType.MOBILE), ("+0987654321", PhoneTechType.VOICE), ("+1111111111", None), ], email_address="john.doe@example.com", email_newsletter=True, address_line="123 Main Street", city_name="Anytown", postal_code="12345", country_code="US", address_catalog=False, gender="Male", birth_date="1980-01-01", language="en", ) @pytest.fixture def minimal_customer_data(): """Fixture providing minimal customer data (only required fields).""" return CustomerData(given_name="Jane", surname="Smith") @pytest.fixture def sample_hotel_reservation_id_data(): """Fixture providing sample hotel reservation ID data for testing.""" return HotelReservationIdData( res_id_type="123", res_id_value="RESERVATION-456", res_id_source="HOTEL_SYSTEM", res_id_source_context="BOOKING_ENGINE", ) @pytest.fixture def minimal_hotel_reservation_id_data(): """Fixture providing minimal hotel reservation ID data (only required fields).""" return HotelReservationIdData(res_id_type="999") class TestCustomerData: """Test the CustomerData dataclass.""" def test_customer_data_creation_full(self, sample_customer_data): """Test creating CustomerData with all fields.""" assert sample_customer_data.given_name == "John" assert sample_customer_data.surname == "Doe" assert sample_customer_data.name_prefix == "Mr." assert sample_customer_data.email_address == "john.doe@example.com" assert sample_customer_data.email_newsletter is True assert len(sample_customer_data.phone_numbers) == 3 def test_customer_data_creation_minimal(self, minimal_customer_data): """Test creating CustomerData with only required fields.""" assert minimal_customer_data.given_name == "Jane" assert minimal_customer_data.surname == "Smith" assert minimal_customer_data.phone_numbers == [] assert minimal_customer_data.email_address is None assert minimal_customer_data.address_line is None def test_phone_numbers_default_initialization(self): """Test that phone_numbers gets initialized to empty list.""" customer_data = CustomerData(given_name="Test", surname="User") assert customer_data.phone_numbers == [] class TestCustomerFactory: """Test the CustomerFactory class.""" def test_create_notif_customer_full(self, sample_customer_data): """Test creating a NotifCustomer with full data.""" customer = CustomerFactory.create_notif_customer(sample_customer_data) assert isinstance(customer, NotifCustomer) assert customer.person_name.given_name == "John" assert customer.person_name.surname == "Doe" assert customer.person_name.name_prefix == "Mr." assert customer.person_name.name_title == "Jr." # Check telephone assert len(customer.telephone) == 3 assert customer.telephone[0].phone_number == "+1234567890" assert customer.telephone[0].phone_tech_type == "5" # MOBILE assert customer.telephone[1].phone_tech_type == "1" # VOICE assert customer.telephone[2].phone_tech_type is None # Check email assert customer.email.value == "john.doe@example.com" assert customer.email.remark == "newsletter:yes" # Check address assert customer.address.address_line == "123 Main Street" assert customer.address.city_name == "Anytown" assert customer.address.postal_code == "12345" assert customer.address.country_name.code == "US" assert customer.address.remark == "catalog:no" # Check other attributes assert customer.gender == "Male" assert customer.birth_date == "1980-01-01" assert customer.language == "en" def test_create_retrieve_customer_full(self, sample_customer_data): """Test creating a RetrieveCustomer with full data.""" customer = CustomerFactory.create_retrieve_customer(sample_customer_data) assert isinstance(customer, RetrieveCustomer) assert customer.person_name.given_name == "John" assert customer.person_name.surname == "Doe" # Same structure as NotifCustomer, so we don't need to test all fields again def test_create_customer_minimal(self, minimal_customer_data): """Test creating customers with minimal data.""" notif_customer = CustomerFactory.create_notif_customer(minimal_customer_data) retrieve_customer = CustomerFactory.create_retrieve_customer( minimal_customer_data ) for customer in [notif_customer, retrieve_customer]: assert customer.person_name.given_name == "Jane" assert customer.person_name.surname == "Smith" assert customer.person_name.name_prefix is None assert customer.person_name.name_title is None assert len(customer.telephone) == 0 assert customer.email is None assert customer.address is None assert customer.gender is None assert customer.birth_date is None assert customer.language is None def test_email_newsletter_options(self): """Test different email newsletter options.""" # Newsletter yes data_yes = CustomerData( given_name="Test", surname="User", email_address="test@example.com", email_newsletter=True, ) customer = CustomerFactory.create_notif_customer(data_yes) assert customer.email.remark == "newsletter:yes" # Newsletter no data_no = CustomerData( given_name="Test", surname="User", email_address="test@example.com", email_newsletter=False, ) customer = CustomerFactory.create_notif_customer(data_no) assert customer.email.remark == "newsletter:no" # Newsletter not specified data_none = CustomerData( given_name="Test", surname="User", email_address="test@example.com", email_newsletter=None, ) customer = CustomerFactory.create_notif_customer(data_none) assert customer.email.remark is None def test_address_catalog_options(self): """Test different address catalog options.""" # Catalog no data_no = CustomerData( given_name="Test", surname="User", address_line="123 Street", address_catalog=False, ) customer = CustomerFactory.create_notif_customer(data_no) assert customer.address.remark == "catalog:no" # Catalog yes data_yes = CustomerData( given_name="Test", surname="User", address_line="123 Street", address_catalog=True, ) customer = CustomerFactory.create_notif_customer(data_yes) assert customer.address.remark == "catalog:yes" # Catalog not specified data_none = CustomerData( given_name="Test", surname="User", address_line="123 Street", address_catalog=None, ) customer = CustomerFactory.create_notif_customer(data_none) assert customer.address.remark is None def test_from_notif_customer_roundtrip(self, sample_customer_data): """Test converting NotifCustomer back to CustomerData.""" customer = CustomerFactory.create_notif_customer(sample_customer_data) converted_data = CustomerFactory.from_notif_customer(customer) assert converted_data == sample_customer_data def test_from_retrieve_customer_roundtrip(self, sample_customer_data): """Test converting RetrieveCustomer back to CustomerData.""" customer = CustomerFactory.create_retrieve_customer(sample_customer_data) converted_data = CustomerFactory.from_retrieve_customer(customer) assert converted_data == sample_customer_data def test_phone_tech_type_conversion(self): """Test that PhoneTechType enum values are properly converted.""" data = CustomerData( given_name="Test", surname="User", phone_numbers=[ ("+1111111111", PhoneTechType.VOICE), ("+2222222222", PhoneTechType.FAX), ("+3333333333", PhoneTechType.MOBILE), ], ) customer = CustomerFactory.create_notif_customer(data) assert customer.telephone[0].phone_tech_type == "1" # VOICE assert customer.telephone[1].phone_tech_type == "3" # FAX assert customer.telephone[2].phone_tech_type == "5" # MOBILE class TestHotelReservationIdData: """Test the HotelReservationIdData dataclass.""" def test_hotel_reservation_id_data_creation_full( self, sample_hotel_reservation_id_data ): """Test creating HotelReservationIdData with all fields.""" assert sample_hotel_reservation_id_data.res_id_type == "123" assert sample_hotel_reservation_id_data.res_id_value == "RESERVATION-456" assert sample_hotel_reservation_id_data.res_id_source == "HOTEL_SYSTEM" assert ( sample_hotel_reservation_id_data.res_id_source_context == "BOOKING_ENGINE" ) def test_hotel_reservation_id_data_creation_minimal( self, minimal_hotel_reservation_id_data ): """Test creating HotelReservationIdData with only required fields.""" assert minimal_hotel_reservation_id_data.res_id_type == "999" assert minimal_hotel_reservation_id_data.res_id_value is None assert minimal_hotel_reservation_id_data.res_id_source is None assert minimal_hotel_reservation_id_data.res_id_source_context is None class TestHotelReservationIdFactory: """Test the HotelReservationIdFactory class.""" def test_create_notif_hotel_reservation_id_full( self, sample_hotel_reservation_id_data ): """Test creating a NotifHotelReservationId with full data.""" reservation_id = HotelReservationIdFactory.create_notif_hotel_reservation_id( sample_hotel_reservation_id_data ) assert isinstance(reservation_id, NotifHotelReservationId) assert reservation_id.res_id_type == "123" assert reservation_id.res_id_value == "RESERVATION-456" assert reservation_id.res_id_source == "HOTEL_SYSTEM" assert reservation_id.res_id_source_context == "BOOKING_ENGINE" def test_create_retrieve_hotel_reservation_id_full( self, sample_hotel_reservation_id_data ): """Test creating a RetrieveHotelReservationId with full data.""" reservation_id = HotelReservationIdFactory.create_retrieve_hotel_reservation_id( sample_hotel_reservation_id_data ) assert isinstance(reservation_id, RetrieveHotelReservationId) assert reservation_id.res_id_type == "123" assert reservation_id.res_id_value == "RESERVATION-456" assert reservation_id.res_id_source == "HOTEL_SYSTEM" assert reservation_id.res_id_source_context == "BOOKING_ENGINE" def test_create_hotel_reservation_id_minimal( self, minimal_hotel_reservation_id_data ): """Test creating hotel reservation IDs with minimal data.""" notif_reservation_id = ( HotelReservationIdFactory.create_notif_hotel_reservation_id( minimal_hotel_reservation_id_data ) ) retrieve_reservation_id = ( HotelReservationIdFactory.create_retrieve_hotel_reservation_id( minimal_hotel_reservation_id_data ) ) for reservation_id in [notif_reservation_id, retrieve_reservation_id]: assert reservation_id.res_id_type == "999" assert reservation_id.res_id_value is None assert reservation_id.res_id_source is None assert reservation_id.res_id_source_context is None def test_from_notif_hotel_reservation_id_roundtrip( self, sample_hotel_reservation_id_data ): """Test converting NotifHotelReservationId back to HotelReservationIdData.""" reservation_id = HotelReservationIdFactory.create_notif_hotel_reservation_id( sample_hotel_reservation_id_data ) converted_data = HotelReservationIdFactory.from_notif_hotel_reservation_id( reservation_id ) assert converted_data == sample_hotel_reservation_id_data def test_from_retrieve_hotel_reservation_id_roundtrip( self, sample_hotel_reservation_id_data ): """Test converting RetrieveHotelReservationId back to HotelReservationIdData.""" reservation_id = HotelReservationIdFactory.create_retrieve_hotel_reservation_id( sample_hotel_reservation_id_data ) converted_data = HotelReservationIdFactory.from_retrieve_hotel_reservation_id( reservation_id ) assert converted_data == sample_hotel_reservation_id_data class TestResGuestFactory: """Test the ResGuestFactory class.""" def test_create_notif_res_guests(self, sample_customer_data): """Test creating NotifResGuests structure.""" res_guests = ResGuestFactory.create_notif_res_guests(sample_customer_data) assert isinstance(res_guests, NotifResGuests) # Navigate down the nested structure customer = res_guests.res_guest.profiles.profile_info.profile.customer assert customer.person_name.given_name == "John" assert customer.person_name.surname == "Doe" assert customer.email.value == "john.doe@example.com" def test_create_retrieve_res_guests(self, sample_customer_data): """Test creating RetrieveResGuests structure.""" res_guests = ResGuestFactory.create_retrieve_res_guests(sample_customer_data) assert isinstance(res_guests, RetrieveResGuests) # Navigate down the nested structure customer = res_guests.res_guest.profiles.profile_info.profile.customer assert customer.person_name.given_name == "John" assert customer.person_name.surname == "Doe" assert customer.email.value == "john.doe@example.com" def test_create_res_guests_minimal(self, minimal_customer_data): """Test creating ResGuests with minimal customer data.""" notif_res_guests = ResGuestFactory.create_notif_res_guests( minimal_customer_data ) retrieve_res_guests = ResGuestFactory.create_retrieve_res_guests( minimal_customer_data ) for res_guests in [notif_res_guests, retrieve_res_guests]: customer = res_guests.res_guest.profiles.profile_info.profile.customer assert customer.person_name.given_name == "Jane" assert customer.person_name.surname == "Smith" assert customer.email is None assert customer.address is None def test_extract_primary_customer_notif(self, sample_customer_data): """Test extracting primary customer from NotifResGuests.""" res_guests = ResGuestFactory.create_notif_res_guests(sample_customer_data) extracted_data = ResGuestFactory.extract_primary_customer(res_guests) assert extracted_data == sample_customer_data def test_extract_primary_customer_retrieve(self, sample_customer_data): """Test extracting primary customer from RetrieveResGuests.""" res_guests = ResGuestFactory.create_retrieve_res_guests(sample_customer_data) extracted_data = ResGuestFactory.extract_primary_customer(res_guests) assert extracted_data == sample_customer_data def test_roundtrip_conversion_notif(self, sample_customer_data): """Test complete roundtrip: CustomerData -> NotifResGuests -> CustomerData.""" res_guests = ResGuestFactory.create_notif_res_guests(sample_customer_data) extracted_data = ResGuestFactory.extract_primary_customer(res_guests) assert extracted_data == sample_customer_data def test_roundtrip_conversion_retrieve(self, sample_customer_data): """Test complete roundtrip: CustomerData -> RetrieveResGuests -> CustomerData.""" res_guests = ResGuestFactory.create_retrieve_res_guests(sample_customer_data) extracted_data = ResGuestFactory.extract_primary_customer(res_guests) assert extracted_data == sample_customer_data class TestPhoneTechType: """Test the PhoneTechType enum.""" def test_enum_values(self): """Test that enum values are correct.""" assert PhoneTechType.VOICE.value == "1" assert PhoneTechType.FAX.value == "3" assert PhoneTechType.MOBILE.value == "5" class TestAlpineBitsFactory: """Test the unified AlpineBitsFactory class.""" def test_create_customer_notif(self, sample_customer_data): """Test creating customer using unified factory for NOTIF.""" customer = AlpineBitsFactory.create(sample_customer_data, OtaMessageType.NOTIF) assert isinstance(customer, NotifCustomer) assert customer.person_name.given_name == "John" assert customer.person_name.surname == "Doe" def test_create_customer_retrieve(self, sample_customer_data): """Test creating customer using unified factory for RETRIEVE.""" customer = AlpineBitsFactory.create( sample_customer_data, OtaMessageType.RETRIEVE ) assert isinstance(customer, RetrieveCustomer) assert customer.person_name.given_name == "John" assert customer.person_name.surname == "Doe" def test_create_hotel_reservation_id_notif(self, sample_hotel_reservation_id_data): """Test creating hotel reservation ID using unified factory for NOTIF.""" reservation_id = AlpineBitsFactory.create( sample_hotel_reservation_id_data, OtaMessageType.NOTIF ) assert isinstance(reservation_id, NotifHotelReservationId) assert reservation_id.res_id_type == "123" assert reservation_id.res_id_value == "RESERVATION-456" def test_create_hotel_reservation_id_retrieve( self, sample_hotel_reservation_id_data ): """Test creating hotel reservation ID using unified factory for RETRIEVE.""" reservation_id = AlpineBitsFactory.create( sample_hotel_reservation_id_data, OtaMessageType.RETRIEVE ) assert isinstance(reservation_id, RetrieveHotelReservationId) assert reservation_id.res_id_type == "123" assert reservation_id.res_id_value == "RESERVATION-456" def test_create_res_guests_notif(self, sample_customer_data): """Test creating ResGuests using unified factory for NOTIF.""" res_guests = AlpineBitsFactory.create_res_guests( sample_customer_data, OtaMessageType.NOTIF ) assert isinstance(res_guests, NotifResGuests) customer = res_guests.res_guest.profiles.profile_info.profile.customer assert customer.person_name.given_name == "John" def test_create_res_guests_retrieve(self, sample_customer_data): """Test creating ResGuests using unified factory for RETRIEVE.""" res_guests = AlpineBitsFactory.create_res_guests( sample_customer_data, OtaMessageType.RETRIEVE ) assert isinstance(res_guests, RetrieveResGuests) customer = res_guests.res_guest.profiles.profile_info.profile.customer assert customer.person_name.given_name == "John" def test_extract_data_from_customer(self, sample_customer_data): """Test extracting data from customer objects.""" # Create both types and extract data back notif_customer = AlpineBitsFactory.create( sample_customer_data, OtaMessageType.NOTIF ) retrieve_customer = AlpineBitsFactory.create( sample_customer_data, OtaMessageType.RETRIEVE ) notif_extracted = AlpineBitsFactory.extract_data(notif_customer) retrieve_extracted = AlpineBitsFactory.extract_data(retrieve_customer) assert notif_extracted == sample_customer_data assert retrieve_extracted == sample_customer_data def test_extract_data_from_hotel_reservation_id( self, sample_hotel_reservation_id_data ): """Test extracting data from hotel reservation ID objects.""" # Create both types and extract data back notif_res_id = AlpineBitsFactory.create( sample_hotel_reservation_id_data, OtaMessageType.NOTIF ) retrieve_res_id = AlpineBitsFactory.create( sample_hotel_reservation_id_data, OtaMessageType.RETRIEVE ) notif_extracted = AlpineBitsFactory.extract_data(notif_res_id) retrieve_extracted = AlpineBitsFactory.extract_data(retrieve_res_id) assert notif_extracted == sample_hotel_reservation_id_data assert retrieve_extracted == sample_hotel_reservation_id_data def test_extract_data_from_res_guests(self, sample_customer_data): """Test extracting data from ResGuests objects.""" # Create both types and extract data back notif_res_guests = AlpineBitsFactory.create_res_guests( sample_customer_data, OtaMessageType.NOTIF ) retrieve_res_guests = AlpineBitsFactory.create_res_guests( sample_customer_data, OtaMessageType.RETRIEVE ) notif_extracted = AlpineBitsFactory.extract_data(notif_res_guests) retrieve_extracted = AlpineBitsFactory.extract_data(retrieve_res_guests) assert notif_extracted == sample_customer_data assert retrieve_extracted == sample_customer_data def test_unsupported_data_type_error(self): """Test that unsupported data types raise ValueError.""" with pytest.raises(ValueError, match="Unsupported data type"): AlpineBitsFactory.create("invalid_data", OtaMessageType.NOTIF) def test_unsupported_object_type_error(self): """Test that unsupported object types raise ValueError in extract_data.""" with pytest.raises(ValueError, match="Unsupported object type"): AlpineBitsFactory.extract_data("invalid_object") def test_complete_workflow_with_unified_factory(self): """Test a complete workflow using only the unified factory.""" # Original data customer_data = CustomerData( given_name="Unified", surname="Factory", email_address="unified@factory.com", phone_numbers=[("+1234567890", PhoneTechType.MOBILE)], ) reservation_data = HotelReservationIdData( res_id_type="999", res_id_value="UNIFIED-TEST" ) # Create using unified factory customer_notif = AlpineBitsFactory.create(customer_data, OtaMessageType.NOTIF) customer_retrieve = AlpineBitsFactory.create( customer_data, OtaMessageType.RETRIEVE ) res_id_notif = AlpineBitsFactory.create(reservation_data, OtaMessageType.NOTIF) res_id_retrieve = AlpineBitsFactory.create( reservation_data, OtaMessageType.RETRIEVE ) res_guests_notif = AlpineBitsFactory.create_res_guests( customer_data, OtaMessageType.NOTIF ) res_guests_retrieve = AlpineBitsFactory.create_res_guests( customer_data, OtaMessageType.RETRIEVE ) # Extract everything back extracted_customer_from_notif = AlpineBitsFactory.extract_data(customer_notif) extracted_customer_from_retrieve = AlpineBitsFactory.extract_data( customer_retrieve ) extracted_res_id_from_notif = AlpineBitsFactory.extract_data(res_id_notif) extracted_res_id_from_retrieve = AlpineBitsFactory.extract_data(res_id_retrieve) extracted_from_res_guests_notif = AlpineBitsFactory.extract_data( res_guests_notif ) extracted_from_res_guests_retrieve = AlpineBitsFactory.extract_data( res_guests_retrieve ) # Verify everything matches assert extracted_customer_from_notif == customer_data assert extracted_customer_from_retrieve == customer_data assert extracted_res_id_from_notif == reservation_data assert extracted_res_id_from_retrieve == reservation_data assert extracted_from_res_guests_notif == customer_data assert extracted_from_res_guests_retrieve == customer_data class TestIntegration: """Integration tests combining both factories.""" def test_both_factories_produce_same_customer_data(self, sample_customer_data): """Test that both factories can work with the same customer data.""" # Create using CustomerFactory notif_customer = CustomerFactory.create_notif_customer(sample_customer_data) retrieve_customer = CustomerFactory.create_retrieve_customer( sample_customer_data ) # Create using ResGuestFactory and extract customers notif_res_guests = ResGuestFactory.create_notif_res_guests(sample_customer_data) retrieve_res_guests = ResGuestFactory.create_retrieve_res_guests( sample_customer_data ) notif_from_res_guests = ( notif_res_guests.res_guest.profiles.profile_info.profile.customer ) retrieve_from_res_guests = ( retrieve_res_guests.res_guest.profiles.profile_info.profile.customer ) # Compare customer names (structure should be identical) assert ( notif_customer.person_name.given_name == notif_from_res_guests.person_name.given_name ) assert ( notif_customer.person_name.surname == notif_from_res_guests.person_name.surname ) assert ( retrieve_customer.person_name.given_name == retrieve_from_res_guests.person_name.given_name ) assert ( retrieve_customer.person_name.surname == retrieve_from_res_guests.person_name.surname ) def test_hotel_reservation_id_factories_produce_same_data( self, sample_hotel_reservation_id_data ): """Test that both HotelReservationId factories produce equivalent results.""" notif_reservation_id = ( HotelReservationIdFactory.create_notif_hotel_reservation_id( sample_hotel_reservation_id_data ) ) retrieve_reservation_id = ( HotelReservationIdFactory.create_retrieve_hotel_reservation_id( sample_hotel_reservation_id_data ) ) # Both should have the same field values assert notif_reservation_id.res_id_type == retrieve_reservation_id.res_id_type assert notif_reservation_id.res_id_value == retrieve_reservation_id.res_id_value assert ( notif_reservation_id.res_id_source == retrieve_reservation_id.res_id_source ) assert ( notif_reservation_id.res_id_source_context == retrieve_reservation_id.res_id_source_context ) def test_complex_customer_workflow(self): """Test a complex workflow with multiple operations.""" # Create original data original_data = CustomerData( given_name="Alice", surname="Johnson", phone_numbers=[ ("+1555123456", PhoneTechType.MOBILE), ("+1555654321", PhoneTechType.VOICE), ], email_address="alice.johnson@company.com", email_newsletter=False, address_line="456 Business Ave", city_name="Metropolis", postal_code="67890", country_code="CA", address_catalog=True, gender="Female", language="fr", ) # Create ResGuests for both types notif_res_guests = ResGuestFactory.create_notif_res_guests(original_data) retrieve_res_guests = ResGuestFactory.create_retrieve_res_guests(original_data) # Extract data back from both notif_extracted = ResGuestFactory.extract_primary_customer(notif_res_guests) retrieve_extracted = ResGuestFactory.extract_primary_customer( retrieve_res_guests ) # All should be equal assert original_data == notif_extracted assert original_data == retrieve_extracted assert notif_extracted == retrieve_extracted def test_complex_hotel_reservation_id_workflow(self): """Test a complex workflow with HotelReservationId operations.""" # Create original reservation ID data original_data = HotelReservationIdData( res_id_type="456", res_id_value="COMPLEX-RESERVATION-789", res_id_source="INTEGRATION_SYSTEM", res_id_source_context="API_CALL", ) # Create HotelReservationId for both types notif_reservation_id = ( HotelReservationIdFactory.create_notif_hotel_reservation_id(original_data) ) retrieve_reservation_id = ( HotelReservationIdFactory.create_retrieve_hotel_reservation_id( original_data ) ) # Extract data back from both notif_extracted = HotelReservationIdFactory.from_notif_hotel_reservation_id( notif_reservation_id ) retrieve_extracted = ( HotelReservationIdFactory.from_retrieve_hotel_reservation_id( retrieve_reservation_id ) ) # All should be equal assert original_data == notif_extracted assert original_data == retrieve_extracted assert notif_extracted == retrieve_extracted