moved tests

This commit is contained in:
Jonas Linter
2025-10-07 10:24:01 +02:00
parent f0945ed431
commit 6102194712
13 changed files with 149 additions and 2 deletions

View File

@@ -0,0 +1,753 @@
import pytest
from alpine_bits_python.alpine_bits_helpers import (
AlpineBitsFactory,
CustomerData,
CustomerFactory,
HotelReservationIdData,
HotelReservationIdFactory,
NotifCustomer,
NotifHotelReservationId,
NotifResGuests,
OtaMessageType,
PhoneTechType,
ResGuestFactory,
RetrieveCustomer,
RetrieveHotelReservationId,
RetrieveResGuests,
)
@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

View File

View File

@@ -0,0 +1,2 @@

View File

@@ -0,0 +1,104 @@
import json
import pytest
from xsdata_pydantic.bindings import XmlParser
from alpine_bits_python.alpinebits_server import AlpineBitsClientInfo, AlpineBitsServer
from alpine_bits_python.generated.alpinebits import OtaPingRs
def extract_relevant_sections(xml_string):
# Remove version attribute value, keep only presence
# Use the same XmlParser as AlpineBitsServer
parser = XmlParser()
obj = parser.from_string(xml_string, OtaPingRs)
return obj
@pytest.mark.asyncio
async def test_ping_action_response_matches_expected():
with open("test/test_data/Handshake-OTA_PingRQ.xml", encoding="utf-8") as f:
server = AlpineBitsServer()
with open(
"test/test_data/Handshake-OTA_PingRQ.xml", encoding="utf-8"
) as f:
request_xml = f.read()
with open(
"test/test_data/Handshake-OTA_PingRS.xml", encoding="utf-8"
) as f:
expected_xml = f.read()
client_info = AlpineBitsClientInfo(username="irrelevant", password="irrelevant")
response = await server.handle_request(
request_action_name="OTA_Ping:Handshaking",
request_xml=request_xml,
client_info=client_info,
version="2024-10",
)
actual_obj = extract_relevant_sections(response.xml_content)
expected_obj = extract_relevant_sections(expected_xml)
actual_matches = json.loads(actual_obj.warnings.warning[0].content[0])
expected_matches = json.loads(expected_obj.warnings.warning[0].content[0])
assert actual_matches == expected_matches, (
f"Expected warnings {expected_matches}, got {actual_matches}"
)
actual_capabilities = json.loads(actual_obj.echo_data)
expected_capabilities = json.loads(expected_obj.echo_data)
assert actual_capabilities == expected_capabilities, (
f"Expected echo data {expected_capabilities}, got {actual_capabilities}"
)
@pytest.mark.asyncio
async def test_ping_action_response_success():
server = AlpineBitsServer()
with open("test/test_data/Handshake-OTA_PingRQ.xml", encoding="utf-8") as f:
request_xml = f.read()
client_info = AlpineBitsClientInfo(username="irrelevant", password="irrelevant")
response = await server.handle_request(
request_action_name="OTA_Ping:Handshaking",
request_xml=request_xml,
client_info=client_info,
version="2024-10",
)
assert response.status_code == 200
assert "<OTA_PingRS" in response.xml_content
assert "<Success" in response.xml_content
assert "Version=" in response.xml_content
@pytest.mark.asyncio
async def test_ping_action_response_version_arbitrary():
server = AlpineBitsServer()
with open("test/test_data/Handshake-OTA_PingRQ.xml", encoding="utf-8") as f:
request_xml = f.read()
client_info = AlpineBitsClientInfo(username="irrelevant", password="irrelevant")
response = await server.handle_request(
request_action_name="OTA_Ping:Handshaking",
request_xml=request_xml,
client_info=client_info,
version="2022-10",
)
assert response.status_code == 200
assert "<OTA_PingRS" in response.xml_content
assert "Version=" in response.xml_content
@pytest.mark.asyncio
async def test_ping_action_response_invalid_action():
server = AlpineBitsServer()
with open("test/test_data/Handshake-OTA_PingRQ.xml", encoding="utf-8") as f:
request_xml = f.read()
client_info = AlpineBitsClientInfo(username="irrelevant", password="irrelevant")
response = await server.handle_request(
request_action_name="InvalidAction",
request_xml=request_xml,
client_info=client_info,
version="2024-10",
)
assert response.status_code == 400
assert "Error" in response.xml_content

View File

@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
AlpineBits 2024-10
https://www.alpinebits.org/
Sample message file for a Handshake request
Changelog:
v. 2024-10 1.2 Example extended with all capabilities and two supported releases
v. 2024-10 1.1 Removed the OTA_Ping action
v. 2024-10 1.0 added supported version 2024-10 in the example
v. 2018-10 1.0 initial example
-->
<OTA_PingRQ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.opentravel.org/OTA/2003/05"
xsi:schemaLocation="http://www.opentravel.org/OTA/2003/05 OTA_PingRQ.xsd"
Version="8.000">
<EchoData>
{
"versions": [
{
"version": "2024-10",
"actions": [
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests_StatusUpdate"
},
{
"action": "action_OTA_HotelInvCountNotif",
"supports": [
"OTA_HotelInvCountNotif_accept_rooms",
"OTA_HotelInvCountNotif_accept_categories",
"OTA_HotelInvCountNotif_accept_deltas",
"OTA_HotelInvCountNotif_accept_out_of_market",
"OTA_HotelInvCountNotif_accept_out_of_order",
"OTA_HotelInvCountNotif_accept_complete_set",
"OTA_HotelInvCountNotif_accept_closing_seasons"
]
},
{
"action": "action_OTA_HotelDescriptiveContentNotif_Inventory",
"supports": [
"OTA_HotelDescriptiveContentNotif_Inventory_use_rooms",
"OTA_HotelDescriptiveContentNotif_Inventory_occupancy_children"
]
},
{
"action": "action_OTA_HotelDescriptiveContentNotif_Info"
},
{
"action": "action_OTA_HotelDescriptiveInfo_Inventory"
},
{
"action": "action_OTA_HotelDescriptiveInfo_Info"
},
{
"action": "action_OTA_HotelRatePlanNotif_RatePlans",
"supports": [
"OTA_HotelRatePlanNotif_accept_ArrivalDOW",
"OTA_HotelRatePlanNotif_accept_DepartureDOW",
"OTA_HotelRatePlanNotif_accept_RatePlan_BookingRule",
"OTA_HotelRatePlanNotif_accept_RatePlan_RoomType_BookingRule",
"OTA_HotelRatePlanNotif_accept_RatePlan_mixed_BookingRule",
"OTA_HotelRatePlanNotif_accept_Supplements",
"OTA_HotelRatePlanNotif_accept_FreeNightsOffers",
"OTA_HotelRatePlanNotif_accept_FamilyOffers",
"OTA_HotelRatePlanNotif_accept_full",
"OTA_HotelRatePlanNotif_accept_overlay",
"OTA_HotelRatePlanNotif_accept_RatePlanJoin",
"OTA_HotelRatePlanNotif_accept_OfferRule_BookingOffset",
"OTA_HotelRatePlanNotif_accept_OfferRule_DOWLOS"
]
},
{
"action": "action_OTA_HotelRatePlan_BaseRates",
"supports": [
"OTA_HotelRatePlan_BaseRates_deltas"
]
},
{
"action": "action_OTA_HotelPostEventNotif_EventReports"
}
]
},
{
"version": "2022-10",
"actions": [
{
"action": "action_OTA_Ping"
},
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests_StatusUpdate"
},
{
"action": "action_OTA_HotelInvCountNotif",
"supports": [
"OTA_HotelInvCountNotif_accept_rooms",
"OTA_HotelInvCountNotif_accept_categories",
"OTA_HotelInvCountNotif_accept_deltas",
"OTA_HotelInvCountNotif_accept_out_of_market",
"OTA_HotelInvCountNotif_accept_out_of_order",
"OTA_HotelInvCountNotif_accept_complete_set",
"OTA_HotelInvCountNotif_accept_closing_seasons"
]
},
{
"action": "action_OTA_HotelDescriptiveContentNotif_Inventory",
"supports": [
"OTA_HotelDescriptiveContentNotif_Inventory_use_rooms",
"OTA_HotelDescriptiveContentNotif_Inventory_occupancy_children"
]
},
{
"action": "action_OTA_HotelDescriptiveContentNotif_Info"
},
{
"action": "action_OTA_HotelDescriptiveInfo_Inventory"
},
{
"action": "action_OTA_HotelDescriptiveInfo_Info"
},
{
"action": "action_OTA_HotelRatePlanNotif_RatePlans",
"supports": [
"OTA_HotelRatePlanNotif_accept_ArrivalDOW",
"OTA_HotelRatePlanNotif_accept_DepartureDOW",
"OTA_HotelRatePlanNotif_accept_RatePlan_BookingRule",
"OTA_HotelRatePlanNotif_accept_RatePlan_RoomType_BookingRule",
"OTA_HotelRatePlanNotif_accept_RatePlan_mixed_BookingRule",
"OTA_HotelRatePlanNotif_accept_Supplements",
"OTA_HotelRatePlanNotif_accept_FreeNightsOffers",
"OTA_HotelRatePlanNotif_accept_FamilyOffers",
"OTA_HotelRatePlanNotif_accept_overlay",
"OTA_HotelRatePlanNotif_accept_RatePlanJoin",
"OTA_HotelRatePlanNotif_accept_OfferRule_BookingOffset",
"OTA_HotelRatePlanNotif_accept_OfferRule_DOWLOS"
]
}
]
}
]
}
</EchoData>
</OTA_PingRQ>

View File

@@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
AlpineBits 2024-10
https://www.alpinebits.org/
Sample message file for a Handshake response
Changelog:
v. 2024-10 1.2 Example extended with all capabilities and two supported releases
v. 2024-10 1.1 Removed the OTA_Ping action
v. 2024-10 1.0 added supported version 2024-10 in the example
v. 2018-10 1.0 initial example
-->
<OTA_PingRS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.opentravel.org/OTA/2003/05"
xsi:schemaLocation="http://www.opentravel.org/OTA/2003/05 OTA_PingRS.xsd"
Version="8.000">
<Success/>
<Warnings>
<Warning Type="11" Status="ALPINEBITS_HANDSHAKE">{
"versions": [
{
"version": "2024-10",
"actions": [
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
}
]
},
{
"version": "2022-10",
"actions": [
{
"action": "action_OTA_Ping"
},
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
}
]
}
]
}</Warning>
</Warnings>
<EchoData>
{
"versions": [
{
"version": "2024-10",
"actions": [
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests_StatusUpdate"
},
{
"action": "action_OTA_HotelInvCountNotif",
"supports": [
"OTA_HotelInvCountNotif_accept_rooms",
"OTA_HotelInvCountNotif_accept_categories",
"OTA_HotelInvCountNotif_accept_deltas",
"OTA_HotelInvCountNotif_accept_out_of_market",
"OTA_HotelInvCountNotif_accept_out_of_order",
"OTA_HotelInvCountNotif_accept_complete_set",
"OTA_HotelInvCountNotif_accept_closing_seasons"
]
},
{
"action": "action_OTA_HotelDescriptiveContentNotif_Inventory",
"supports": [
"OTA_HotelDescriptiveContentNotif_Inventory_use_rooms",
"OTA_HotelDescriptiveContentNotif_Inventory_occupancy_children"
]
},
{
"action": "action_OTA_HotelDescriptiveContentNotif_Info"
},
{
"action": "action_OTA_HotelDescriptiveInfo_Inventory"
},
{
"action": "action_OTA_HotelDescriptiveInfo_Info"
},
{
"action": "action_OTA_HotelRatePlanNotif_RatePlans",
"supports": [
"OTA_HotelRatePlanNotif_accept_ArrivalDOW",
"OTA_HotelRatePlanNotif_accept_DepartureDOW",
"OTA_HotelRatePlanNotif_accept_RatePlan_BookingRule",
"OTA_HotelRatePlanNotif_accept_RatePlan_RoomType_BookingRule",
"OTA_HotelRatePlanNotif_accept_RatePlan_mixed_BookingRule",
"OTA_HotelRatePlanNotif_accept_Supplements",
"OTA_HotelRatePlanNotif_accept_FreeNightsOffers",
"OTA_HotelRatePlanNotif_accept_FamilyOffers",
"OTA_HotelRatePlanNotif_accept_full",
"OTA_HotelRatePlanNotif_accept_overlay",
"OTA_HotelRatePlanNotif_accept_RatePlanJoin",
"OTA_HotelRatePlanNotif_accept_OfferRule_BookingOffset",
"OTA_HotelRatePlanNotif_accept_OfferRule_DOWLOS"
]
},
{
"action": "action_OTA_HotelRatePlan_BaseRates",
"supports": [
"OTA_HotelRatePlan_BaseRates_deltas"
]
},
{
"action": "action_OTA_HotelPostEventNotif_EventReports"
}
]
},
{
"version": "2022-10",
"actions": [
{
"action": "action_OTA_Ping"
},
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests_StatusUpdate"
},
{
"action": "action_OTA_HotelInvCountNotif",
"supports": [
"OTA_HotelInvCountNotif_accept_rooms",
"OTA_HotelInvCountNotif_accept_categories",
"OTA_HotelInvCountNotif_accept_deltas",
"OTA_HotelInvCountNotif_accept_out_of_market",
"OTA_HotelInvCountNotif_accept_out_of_order",
"OTA_HotelInvCountNotif_accept_complete_set",
"OTA_HotelInvCountNotif_accept_closing_seasons"
]
},
{
"action": "action_OTA_HotelDescriptiveContentNotif_Inventory",
"supports": [
"OTA_HotelDescriptiveContentNotif_Inventory_use_rooms",
"OTA_HotelDescriptiveContentNotif_Inventory_occupancy_children"
]
},
{
"action": "action_OTA_HotelDescriptiveContentNotif_Info"
},
{
"action": "action_OTA_HotelDescriptiveInfo_Inventory"
},
{
"action": "action_OTA_HotelDescriptiveInfo_Info"
},
{
"action": "action_OTA_HotelRatePlanNotif_RatePlans",
"supports": [
"OTA_HotelRatePlanNotif_accept_ArrivalDOW",
"OTA_HotelRatePlanNotif_accept_DepartureDOW",
"OTA_HotelRatePlanNotif_accept_RatePlan_BookingRule",
"OTA_HotelRatePlanNotif_accept_RatePlan_RoomType_BookingRule",
"OTA_HotelRatePlanNotif_accept_RatePlan_mixed_BookingRule",
"OTA_HotelRatePlanNotif_accept_Supplements",
"OTA_HotelRatePlanNotif_accept_FreeNightsOffers",
"OTA_HotelRatePlanNotif_accept_FamilyOffers",
"OTA_HotelRatePlanNotif_accept_overlay",
"OTA_HotelRatePlanNotif_accept_RatePlanJoin",
"OTA_HotelRatePlanNotif_accept_OfferRule_BookingOffset",
"OTA_HotelRatePlanNotif_accept_OfferRule_DOWLOS"
]
}
]
}
]
}
</EchoData>
</OTA_PingRS>

View File

@@ -0,0 +1,241 @@
{"data": {
"formName": "Contact us",
"submissions": [
{
"label": "Angebot auswählen",
"value": "Zimmer: Doppelzimmer"
},
{
"label": "Anreisedatum",
"value": "2025-10-21"
},
{
"label": "Abreisedatum",
"value": "2025-12-28"
},
{
"label": "Anzahl Erwachsene",
"value": "4"
},
{
"label": "Anzahl Kinder",
"value": "0"
},
{
"label": "Anrede",
"value": "Herr"
},
{
"label": "Vorname",
"value": "Jonas"
},
{
"label": "Nachname",
"value": "Linter"
},
{
"label": "Email",
"value": "jonas@vaius.ai"
},
{
"label": "Phone",
"value": "+39 392 007 6982"
},
{
"label": "Message",
"value": "Hallo nachricht in der Kommentarsection"
},
{
"label": "Einwilligung Marketing",
"value": "Angekreuzt"
},
{
"label": "utm_Source",
"value": ""
},
{
"label": "utm_Medium",
"value": ""
},
{
"label": "utm_Campaign",
"value": ""
},
{
"label": "utm_Term",
"value": ""
},
{
"label": "utm_Content",
"value": ""
},
{
"label": "utm_term_id",
"value": ""
},
{
"label": "utm_content_id",
"value": ""
},
{
"label": "gad_source",
"value": "5"
},
{
"label": "gad_campaignid",
"value": "23065043477"
},
{
"label": "gbraid",
"value": ""
},
{
"label": "gclid",
"value": "EAIaIQobChMI-d7Bn_-OkAMVuZJQBh09uD0vEAAYASAAEgKR8_D_BwE"
},
{
"label": "fbclid",
"value": ""
},
{
"label": "hotelid",
"value": "12345"
},
{
"label": "hotelname",
"value": "Bemelmans Post"
}
],
"field:date_picker_7e65": "2025-10-28",
"field:number_7cf5": "2",
"field:utm_source": "",
"submissionTime": "2025-10-06T07:05:34.001Z",
"field:gad_source": "5",
"field:form_field_5a7b": "Angekreuzt",
"field:gad_campaignid": "23065043477",
"field:utm_medium": "",
"field:utm_term_id": "",
"context": {
"metaSiteId": "1dea821c-8168-4736-96e4-4b92e8b364cf",
"activationId": "fd8e9c90-0335-4fd2-976d-985f065f3f80"
},
"field:email_5139": "jonas@vaius.ai",
"field:phone_4c77": "+39 392 007 6982",
"_context": {
"activation": {
"id": "fd8e9c90-0335-4fd2-976d-985f065f3f80"
},
"configuration": {
"id": "a976f18c-fa86-495d-be1e-676df188eeae"
},
"app": {
"id": "225dd912-7dea-4738-8688-4b8c6955ffc2"
},
"action": {
"id": "152db4d7-5263-40c4-be2b-1c81476318b7"
},
"trigger": {
"key": "wix_form_app-form_submitted"
}
},
"field:gclid": "nduaitreuditaor",
"formFieldMask": [
"field:",
"field:",
"field:angebot_auswaehlen",
"field:date_picker_a7c8",
"field:date_picker_7e65",
"field:",
"field:number_7cf5",
"field:anzahl_kinder",
"field:alter_kind_3",
"field:alter_kind_25",
"field:alter_kind_4",
"field:alter_kind_5",
"field:alter_kind_6",
"field:alter_kind_7",
"field:alter_kind_8",
"field:alter_kind_9",
"field:alter_kind_10",
"field:alter_kind_11",
"field:",
"field:anrede",
"field:first_name_abae",
"field:last_name_d97c",
"field:email_5139",
"field:phone_4c77",
"field:long_answer_3524",
"field:form_field_5a7b",
"field:",
"field:utm_source",
"field:utm_medium",
"field:utm_campaign",
"field:utm_term",
"field:utm_content",
"field:utm_term_id",
"field:utm_content_id",
"field:gad_source",
"field:gad_campaignid",
"field:gbraid",
"field:gclid",
"field:fbclid",
"field:hotelid",
"field:hotelname",
"field:",
"metaSiteId"
],
"contact": {
"name": {
"first": "Jonas",
"last": "Linter"
},
"email": "jonas@vaius.ai",
"locale": "de-de",
"phones": [
{
"tag": "UNTAGGED",
"formattedPhone": "+393920076982",
"id": "a3bf4-6dbe-4611-8963-a50df805785d",
"countryCode": "DE",
"e164Phone": "+393920076982",
"primary": true,
"phone": "392 0076982"
}
],
"contactId": "66659da8-4035-47fe-a66b-6ce461ad290f",
"emails": [
{
"id": "e1d2168e-ca3c-4844-8f93-f2e1b0ae70e3",
"tag": "UNTAGGED",
"email": "koepper-ed@t-online.de",
"primary": true
}
],
"updatedDate": "2025-10-06T07:05:35.675Z",
"phone": "+491758555456",
"createdDate": "2025-10-06T07:05:35.675Z"
},
"submissionId": "666247dc-9d5a-4eb7-87a7-677bf64645ad",
"field:anzahl_kinder": "0",
"field:first_name_abae": "Ernst-Dieter",
"field:utm_content_id": "",
"field:utm_campaign": "",
"field:utm_term": "",
"contactId": "66659da8-4035-47fe-a66b-6ce461ad290f",
"field:date_picker_a7c8": "2025-12-21",
"field:hotelname": "Testhotel",
"field:angebot_auswaehlen": "Zimmer: Doppelzimmer",
"field:utm_content": "",
"field:last_name_d97c": "Linter",
"field:hotelid": "135",
"submissionsLink": "https://manage.wix.app/forms/submissions/1dea821c-8168-4736-96e4-4b92e8b364cf/e084006b-ae83-4e4d-b2f5-074118cdb3b1?d=https%3A%2F%2Fmanage.wix.com%2Fdashboard%2F1dea821c-8168-4736-96e4-4b92e8b364cf%2Fwix-forms%2Fform%2Fe084006b-ae83-4e4d-b2f5-074118cdb3b1%2Fsubmissions&s=true",
"field:gbraid": "",
"field:fbclid": "",
"submissionPdf": {
"fileName": "86d247dc-9d5a-4eb7-87a7-677bf64645ad.pdf",
"downloadUrl": "https://manage.wix.com/_api/form-submission-service/v4/submissions/86d247dc-9d5a-4eb7-87a7-677bf64645ad/download?accessToken=JWS.eyJraWQiOiJWLVNuLWhwZSIsImFsZyI6IkhTMjU2In0.eyJkYXRhIjoie1wibWV0YVNpdGVJZFwiOlwiMWRlYTgyMWMtODE2OC00NzM2LTk2ZTQtNGI5MmU4YjM2NGNmXCJ9IiwiaWF0IjoxNzU5NzM0MzM1LCJleHAiOjE3NTk3MzQ5MzV9.9koy-O_ptm0dRspjh01Yefkt2rCHiUlRCFtE_S3auYw"
},
"field:anrede": "Herr",
"field:long_answer_3524": "Kommentarsektion vermutlich",
"formId": "e084006b-ae83-4e4d-b2f5-074118cdb3b1"
}}

View File

@@ -0,0 +1,242 @@
{"data": {
"formName": "Reservation Request",
"submissions": [
{
"label": "Angebot auswählen",
"value": "Zimmer: Einzelzimmer"
},
{
"label": "Anreisedatum",
"value": "2025-11-15"
},
{
"label": "Abreisedatum",
"value": "2025-11-18"
},
{
"label": "Anzahl Erwachsene",
"value": "1"
},
{
"label": "Anzahl Kinder",
"value": "1"
},
{
"label": "Anrede",
"value": "Frau"
},
{
"label": "Vorname",
"value": "Maria"
},
{
"label": "Nachname",
"value": "Schmidt"
},
{
"label": "Email",
"value": "maria.schmidt@gmail.com"
},
{
"label": "Phone",
"value": "+49 173 555 1234"
},
{
"label": "Message",
"value": "Benötige ein ruhiges Zimmer, bitte."
},
{
"label": "Einwilligung Marketing",
"value": "Selezionato"
},
{
"label": "utm_Source",
"value": "google"
},
{
"label": "utm_Medium",
"value": "cpc"
},
{
"label": "utm_Campaign",
"value": "winter_2025"
},
{
"label": "utm_Term",
"value": "hotel_booking"
},
{
"label": "utm_Content",
"value": "ad_variant_a"
},
{
"label": "utm_term_id",
"value": "12345"
},
{
"label": "utm_content_id",
"value": "67890"
},
{
"label": "gad_source",
"value": "1"
},
{
"label": "gad_campaignid",
"value": "98765432"
},
{
"label": "gbraid",
"value": "1.2.abc123def456"
},
{
"label": "gclid",
"value": "CjwKCAjw9eWYBhB3EiwAA5J8_xyz123abc"
},
{
"label": "fbclid",
"value": "IwAR123fbclid456"
},
{
"label": "hotelid",
"value": "135"
},
{
"label": "hotelname",
"value": "Frangart Inn"
}
],
"field:date_picker_7e65": "2025-11-18",
"field:number_7cf5": "1",
"field:utm_source": "google",
"submissionTime": "2025-10-06T14:22:15.001Z",
"field:gad_source": "1",
"field:form_field_5a7b": "Selezionato",
"field:gad_campaignid": "98765432",
"field:utm_medium": "cpc",
"field:utm_term_id": "12345",
"context": {
"metaSiteId": "2ebc832d-9279-5847-a7f5-5c03f9c475d0",
"activationId": "0e9a0d91-1446-5fe3-a87e-a96b17f720c1"
},
"field:email_5139": "maria.schmidt@gmail.com",
"field:phone_4c77": "+49 173 555 1234",
"_context": {
"activation": {
"id": "0e9a0d91-1446-5fe3-a87e-a96b17f720c1"
},
"configuration": {
"id": "b087029d-0b97-506e-cf2f-787e0299ffbf"
},
"app": {
"id": "336ee023-8efa-5849-9799-5c9d7066aac3"
},
"action": {
"id": "263ec5e8-6374-51d5-df3c-2d92587429c8"
},
"trigger": {
"key": "wix_form_app-form_submitted"
}
},
"field:gclid": "CjwKCAjw9eWYBhB3EiwAA5J8_xyz123abc",
"formFieldMask": [
"field:",
"field:",
"field:angebot_auswaehlen",
"field:date_picker_a7c8",
"field:date_picker_7e65",
"field:",
"field:number_7cf5",
"field:anzahl_kinder",
"field:alter_kind_3",
"field:alter_kind_25",
"field:alter_kind_4",
"field:alter_kind_5",
"field:alter_kind_6",
"field:alter_kind_7",
"field:alter_kind_8",
"field:alter_kind_9",
"field:alter_kind_10",
"field:alter_kind_11",
"field:",
"field:anrede",
"field:first_name_abae",
"field:last_name_d97c",
"field:email_5139",
"field:phone_4c77",
"field:long_answer_3524",
"field:form_field_5a7b",
"field:",
"field:utm_source",
"field:utm_medium",
"field:utm_campaign",
"field:utm_term",
"field:utm_content",
"field:utm_term_id",
"field:utm_content_id",
"field:gad_source",
"field:gad_campaignid",
"field:gbraid",
"field:gclid",
"field:fbclid",
"field:hotelid",
"field:hotelname",
"field:",
"metaSiteId"
],
"contact": {
"name": {
"first": "Maria",
"last": "Schmidt"
},
"email": "maria.schmidt@gmail.com",
"locale": "de-de",
"phones": [
{
"tag": "UNTAGGED",
"formattedPhone": "+49 173 555 1234",
"id": "641b4cf5-7ecf-5722-9a74-b61ea916391e",
"countryCode": "DE",
"e164Phone": "+393920076982",
"primary": true,
"phone": "173 5551234"
}
],
"contactId": "24760eb9-5146-58f0-b77c-7df572be401f",
"emails": [
{
"id": "f2e3279f-db4d-5955-90a4-03f2c1bf81f4",
"tag": "UNTAGGED",
"email": "maria.schmidt@gmail.com",
"primary": true
}
],
"updatedDate": "2025-10-06T14:22:16.675Z",
"phone": "+393920076982",
"createdDate": "2025-10-06T14:22:16.675Z"
},
"submissionId": "97e358ed-ae6b-5fc8-98c8-788cf75756be",
"field:anzahl_kinder": "1",
"field:first_name_abae": "Maria",
"field:utm_content_id": "67890",
"field:utm_campaign": "winter_2025",
"field:utm_term": "hotel_booking",
"contactId": "24760eb9-5146-58f0-b77c-7df572be401f",
"field:date_picker_a7c8": "2025-11-15",
"field:hotelname": "Frangart Inn",
"field:angebot_auswaehlen": "Zimmer: Einzelzimmer",
"field:utm_content": "ad_variant_a",
"field:last_name_d97c": "Schmidt",
"field:hotelid": "135",
"field:alter_kind_3": "8",
"submissionsLink": "https://manage.wix.app/forms/submissions/2ebc832d-9279-5847-a7f5-5c03f9c475d0/f195117c-bf94-5f5e-c6g6-185229dde4c2?d=https%3A%2F%2Fmanage.wix.com%2Fdashboard%2F2ebc832d-9279-5847-a7f5-5c03f9c475d0%2Fwix-forms%2Fform%2Ff195117c-bf94-5f5e-c6g6-185229dde4c2%2Fsubmissions&s=true",
"field:gbraid": "1.2.abc123def456",
"field:fbclid": "IwAR123fbclid456",
"submissionPdf": {
"fileName": "97e358ed-ae6b-5fc8-98c8-788cf75756be.pdf",
"downloadUrl": "https://manage.wix.com/_api/form-submission-service/v4/submissions/97e358ed-ae6b-5fc8-98c8-788cf75756be/download?accessToken=JWS.eyJraWQiOiJWLVNuLWhwZSIsImFsZyI6IkhTMjU2In0.eyJkYXRhIjoie1wibWV0YVNpdGVJZFwiOlwiMmViYzgzMmQtOTI3OS01ODQ3LWE3ZjUtNWMwM2Y5YzQ3NWQwXCJ9IiwiaWF0IjoxNzU5ODQ1MzM2LCJleHAiOjE3NTk4NDU5MzZ9.abc123_different_token_here"
},
"field:anrede": "Frau",
"field:long_answer_3524": "Benötige ein ruhiges Zimmer, bitte.",
"formId": "f195117c-bf94-5f5e-c6g6-185229dde4c2"
}}

View File

@@ -0,0 +1,244 @@
{"data": {
"formName": "Family Vacation Inquiry",
"submissions": [
{
"label": "Angebot auswählen",
"value": "Suite: Familiensuite"
},
{
"label": "Anreisedatum",
"value": "2025-12-20"
},
{
"label": "Abreisedatum",
"value": "2025-12-27"
},
{
"label": "Anzahl Erwachsene",
"value": "2"
},
{
"label": "Anzahl Kinder",
"value": "3"
},
{
"label": "Anrede",
"value": "Herr"
},
{
"label": "Vorname",
"value": "Alessandro"
},
{
"label": "Nachname",
"value": "Rossi"
},
{
"label": "Email",
"value": "alessandro.rossi@example.it"
},
{
"label": "Phone",
"value": "+39 348 123 4567"
},
{
"label": "Message",
"value": "Wir planen unseren Weihnachtsurlaub mit drei Kindern. Brauchen Kinderbetten und Nähe zum Spielplatz."
},
{
"label": "Einwilligung Marketing",
"value": "Angekreuzt"
},
{
"label": "utm_Source",
"value": "facebook"
},
{
"label": "utm_Medium",
"value": "social"
},
{
"label": "utm_Campaign",
"value": "christmas_special"
},
{
"label": "utm_Term",
"value": "family_hotel"
},
{
"label": "utm_Content",
"value": "carousel_ad"
},
{
"label": "utm_term_id",
"value": "54321"
},
{
"label": "utm_content_id",
"value": "09876"
},
{
"label": "gad_source",
"value": ""
},
{
"label": "gad_campaignid",
"value": ""
},
{
"label": "gbraid",
"value": ""
},
{
"label": "gclid",
"value": ""
},
{
"label": "fbclid",
"value": "IwAR3xHcVb6eJbMqQ_fbsocial789"
},
{
"label": "hotelid",
"value": "135"
},
{
"label": "hotelname",
"value": "Bemelmans"
}
],
"field:date_picker_7e65": "2025-12-27",
"field:number_7cf5": "2",
"field:utm_source": "facebook",
"submissionTime": "2025-10-06T16:45:22.001Z",
"field:gad_source": "",
"field:form_field_5a7b": "Angekreuzt",
"field:gad_campaignid": "",
"field:utm_medium": "social",
"field:utm_term_id": "54321",
"context": {
"metaSiteId": "3fcd943e-a38a-6958-b8g6-6d14gad586e1",
"activationId": "1f0b1e02-2557-6gf4-b98f-ba7c28g831d2"
},
"field:email_5139": "alessandro.rossi@example.it",
"field:phone_4c77": "+39 348 123 4567",
"_context": {
"activation": {
"id": "1f0b1e02-2557-6gf4-b98f-ba7c28g831d2"
},
"configuration": {
"id": "c198130e-1ca8-617f-dg3g-898f1300ggcg"
},
"app": {
"id": "447ff134-9g0b-6950-a8aa-6d0e8177bbdc"
},
"action": {
"id": "374fd6f9-7485-62e6-eg4d-3e03698540d9"
},
"trigger": {
"key": "wix_form_app-form_submitted"
}
},
"field:gclid": "",
"formFieldMask": [
"field:",
"field:",
"field:angebot_auswaehlen",
"field:date_picker_a7c8",
"field:date_picker_7e65",
"field:",
"field:number_7cf5",
"field:anzahl_kinder",
"field:alter_kind_3",
"field:alter_kind_25",
"field:alter_kind_4",
"field:alter_kind_5",
"field:alter_kind_6",
"field:alter_kind_7",
"field:alter_kind_8",
"field:alter_kind_9",
"field:alter_kind_10",
"field:alter_kind_11",
"field:",
"field:anrede",
"field:first_name_abae",
"field:last_name_d97c",
"field:email_5139",
"field:phone_4c77",
"field:long_answer_3524",
"field:form_field_5a7b",
"field:",
"field:utm_source",
"field:utm_medium",
"field:utm_campaign",
"field:utm_term",
"field:utm_content",
"field:utm_term_id",
"field:utm_content_id",
"field:gad_source",
"field:gad_campaignid",
"field:gbraid",
"field:gclid",
"field:fbclid",
"field:hotelid",
"field:hotelname",
"field:",
"metaSiteId"
],
"contact": {
"name": {
"first": "Alessandro",
"last": "Rossi"
},
"email": "alessandro.rossi@example.it",
"locale": "it-it",
"phones": [
{
"tag": "UNTAGGED",
"formattedPhone": "+39 348 123 4567",
"id": "752c5dg6-8fdf-6833-ab85-c72fb027402f",
"countryCode": "IT",
"e164Phone": "+393481234567",
"primary": true,
"phone": "348 1234567"
}
],
"contactId": "35871fca-6257-69g1-c88d-8eg683cf512g",
"emails": [
{
"id": "g3f4380g-ec5e-6a66-a1b5-14g3d2cg92g5",
"tag": "UNTAGGED",
"email": "alessandro.rossi@example.it",
"primary": true
}
],
"updatedDate": "2025-10-06T16:45:23.675Z",
"phone": "+393481234567",
"createdDate": "2025-10-06T16:45:23.675Z"
},
"submissionId": "a8g469fe-bf7c-6gd9-a9d9-899dg86867cf",
"field:anzahl_kinder": "3",
"field:first_name_abae": "Alessandro",
"field:utm_content_id": "09876",
"field:utm_campaign": "christmas_special",
"field:utm_term": "family_hotel",
"contactId": "35871fca-6257-69g1-c88d-8eg683cf512g",
"field:date_picker_a7c8": "2025-12-20",
"field:hotelname": "Bemelmans",
"field:angebot_auswaehlen": "Suite: Familiensuite",
"field:utm_content": "carousel_ad",
"field:last_name_d97c": "Rossi",
"field:hotelid": "135",
"field:alter_kind_3": "12",
"field:alter_kind_4": "9",
"field:alter_kind_5": "6",
"submissionsLink": "https://manage.wix.app/forms/submissions/3fcd943e-a38a-6958-b8g6-6d14gad586e1/g206228d-ch05-6g6f-d7h7-296330eef5d3?d=https%3A%2F%2Fmanage.wix.com%2Fdashboard%2F3fcd943e-a38a-6958-b8g6-6d14gad586e1%2Fwix-forms%2Fform%2Fg206228d-ch05-6g6f-d7h7-296330eef5d3%2Fsubmissions&s=true",
"field:gbraid": "",
"field:fbclid": "IwAR3xHcVb6eJbMqQ_fbsocial789",
"submissionPdf": {
"fileName": "a8g469fe-bf7c-6gd9-a9d9-899dg86867cf.pdf",
"downloadUrl": "https://manage.wix.com/_api/form-submission-service/v4/submissions/a8g469fe-bf7c-6gd9-a9d9-899dg86867cf/download?accessToken=JWS.eyJraWQiOiJWLVNuLWhwZSIsImFsZyI6IkhTMjU2In0.eyJkYXRhIjoie1wibWV0YVNpdGVJZFwiOlwiM2ZjZDk0M2UtYTM4YS02OTU4LWI4ZzYtNmQxNGdhZDU4NmUxXCJ9IiwiaWF0IjoxNzU5ODUyMDQ3LCJleHAiOjE3NTk4NTI2NDd9.xyz789_another_token_here"
},
"field:anrede": "Herr",
"field:long_answer_3524": "Wir planen unseren Weihnachtsurlaub mit drei Kindern. Brauchen Kinderbetten und Nähe zum Spielplatz.",
"formId": "g206228d-ch05-6g6f-d7h7-296330eef5d3"
}}

View File

@@ -0,0 +1,241 @@
{"data": {
"formName": "Business Travel Request",
"submissions": [
{
"label": "Angebot auswählen",
"value": "Zimmer: Business Suite"
},
{
"label": "Anreisedatum",
"value": "2025-11-08"
},
{
"label": "Abreisedatum",
"value": "2025-11-10"
},
{
"label": "Anzahl Erwachsene",
"value": "1"
},
{
"label": "Anzahl Kinder",
"value": "0"
},
{
"label": "Anrede",
"value": "Frau"
},
{
"label": "Vorname",
"value": "Sarah"
},
{
"label": "Nachname",
"value": "Johnson"
},
{
"label": "Email",
"value": "sarah.johnson@businesscorp.com"
},
{
"label": "Phone",
"value": "+1 555 987 6543"
},
{
"label": "Message",
"value": "Business trip for conference. Need WiFi and workspace. Will arrive late on Monday."
},
{
"label": "Einwilligung Marketing",
"value": ""
},
{
"label": "utm_Source",
"value": "direct"
},
{
"label": "utm_Medium",
"value": "none"
},
{
"label": "utm_Campaign",
"value": ""
},
{
"label": "utm_Term",
"value": ""
},
{
"label": "utm_Content",
"value": ""
},
{
"label": "utm_term_id",
"value": ""
},
{
"label": "utm_content_id",
"value": ""
},
{
"label": "gad_source",
"value": ""
},
{
"label": "gad_campaignid",
"value": ""
},
{
"label": "gbraid",
"value": ""
},
{
"label": "gclid",
"value": ""
},
{
"label": "fbclid",
"value": ""
},
{
"label": "hotelid",
"value": "135"
},
{
"label": "hotelname",
"value": "Business Hotel Alpine"
}
],
"field:date_picker_7e65": "2025-11-10",
"field:number_7cf5": "1",
"field:utm_source": "direct",
"submissionTime": "2025-10-06T09:15:45.001Z",
"field:gad_source": "",
"field:form_field_5a7b": "",
"field:gad_campaignid": "",
"field:utm_medium": "none",
"field:utm_term_id": "",
"context": {
"metaSiteId": "4hde054f-b49b-7a69-c9h7-7e25hbe697f2",
"activationId": "2g1c2f13-3668-7hg5-ca9g-cb8d39h942e3"
},
"field:email_5139": "sarah.johnson@businesscorp.com",
"field:phone_4c77": "+1 555 987 6543",
"_context": {
"activation": {
"id": "2g1c2f13-3668-7hg5-ca9g-cb8d39h942e3"
},
"configuration": {
"id": "d2a9241f-2db9-728g-eh4h-9a9g2411hhd0"
},
"app": {
"id": "558gg245-ah1c-7a61-b9bb-7e1f9288ccede"
},
"action": {
"id": "485ge7ga-8596-73f7-fh5e-4f146a9651ea"
},
"trigger": {
"key": "wix_form_app-form_submitted"
}
},
"field:gclid": "",
"formFieldMask": [
"field:",
"field:",
"field:angebot_auswaehlen",
"field:date_picker_a7c8",
"field:date_picker_7e65",
"field:",
"field:number_7cf5",
"field:anzahl_kinder",
"field:alter_kind_3",
"field:alter_kind_25",
"field:alter_kind_4",
"field:alter_kind_5",
"field:alter_kind_6",
"field:alter_kind_7",
"field:alter_kind_8",
"field:alter_kind_9",
"field:alter_kind_10",
"field:alter_kind_11",
"field:",
"field:anrede",
"field:first_name_abae",
"field:last_name_d97c",
"field:email_5139",
"field:phone_4c77",
"field:long_answer_3524",
"field:form_field_5a7b",
"field:",
"field:utm_source",
"field:utm_medium",
"field:utm_campaign",
"field:utm_term",
"field:utm_content",
"field:utm_term_id",
"field:utm_content_id",
"field:gad_source",
"field:gad_campaignid",
"field:gbraid",
"field:gclid",
"field:fbclid",
"field:hotelid",
"field:hotelname",
"field:",
"metaSiteId"
],
"contact": {
"name": {
"first": "Sarah",
"last": "Johnson"
},
"email": "sarah.johnson@businesscorp.com",
"locale": "en-us",
"phones": [
{
"tag": "UNTAGGED",
"formattedPhone": "+1 555 987 6543",
"id": "863d6eh7-9geg-7944-bc96-d83gc138513g",
"countryCode": "US",
"e164Phone": "+15559876543",
"primary": true,
"phone": "555 9876543"
}
],
"contactId": "46982gdb-7368-7ah2-d99e-9fh794dg623h",
"emails": [
{
"id": "h4g5491h-fd6f-7b77-b2c6-25h4e3dh03h6",
"tag": "UNTAGGED",
"email": "sarah.johnson@businesscorp.com",
"primary": true
}
],
"updatedDate": "2025-10-06T09:15:46.675Z",
"phone": "+15559876543",
"createdDate": "2025-10-06T09:15:46.675Z"
},
"submissionId": "b9h57agf-ch8d-7hea-baeb-9aaeth97978dg",
"field:anzahl_kinder": "0",
"field:first_name_abae": "Sarah",
"field:utm_content_id": "",
"field:utm_campaign": "",
"field:utm_term": "",
"contactId": "46982gdb-7368-7ah2-d99e-9fh794dg623h",
"field:date_picker_a7c8": "2025-11-08",
"field:hotelname": "Business Hotel Alpine",
"field:angebot_auswaehlen": "Zimmer: Business Suite",
"field:utm_content": "",
"field:last_name_d97c": "Johnson",
"field:hotelid": "135",
"submissionsLink": "https://manage.wix.app/forms/submissions/4hde054f-b49b-7a69-c9h7-7e25hbe697f2/h317339e-di16-7h7g-e8i8-3a7441ffg6e4?d=https%3A%2F%2Fmanage.wix.com%2Fdashboard%2F4hde054f-b49b-7a69-c9h7-7e25hbe697f2%2Fwix-forms%2Fform%2Fh317339e-di16-7h7g-e8i8-3a7441ffg6e4%2Fsubmissions&s=true",
"field:gbraid": "",
"field:fbclid": "",
"submissionPdf": {
"fileName": "b9h57agf-ch8d-7hea-baeb-9aaeth97978dg.pdf",
"downloadUrl": "https://manage.wix.com/_api/form-submission-service/v4/submissions/b9h57agf-ch8d-7hea-baeb-9aaeth97978dg/download?accessToken=JWS.eyJraWQiOiJWLVNuLWhwZSIsImFsZyI6IkhTMjU2In0.eyJkYXRhIjoie1wibWV0YVNpdGVJZFwiOlwiNGhkZTA1NGYtYjQ5Yi03YTY5LWM5aDctN2UyNWhiZTY5N2YyXCJ9IiwiaWF0IjoxNzU5ODI5MzQ2LCJleHAiOjE3NTk4Mjk5NDZ9.business_token_987654"
},
"field:anrede": "Frau",
"field:long_answer_3524": "Business trip for conference. Need WiFi and workspace. Will arrive late on Monday.",
"formId": "h317339e-di16-7h7g-e8i8-3a7441ffg6e4"
}}

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<OTA_PingRS xmlns="http://www.opentravel.org/OTA/2003/05" Version="7.000">
<Success/>
<Warnings>
<Warning Type="11" Status="ALPINEBITS_HANDSHAKE">{
"versions": [
{
"version": "2024-10",
"actions": [
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
}
]
},
{
"version": "2022-10",
"actions": [
{
"action": "action_OTA_Ping"
},
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
}
]
}
]
}</Warning>
</Warnings>
<EchoData>{
"versions": [
{
"version": "2024-10",
"actions": [
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
}
]
},
{
"version": "2022-10",
"actions": [
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_Ping"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
}
]
}
]
}</EchoData>
</OTA_PingRS>

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
AlpineBits 2024-10
https://www.alpinebits.org/
Sample message file for a Handshake response
Changelog:
v. 2024-10 1.2 Example extended with all capabilities and two supported releases
v. 2024-10 1.1 Removed the OTA_Ping action
v. 2024-10 1.0 added supported version 2024-10 in the example
v. 2018-10 1.0 initial example
-->
<OTA_PingRS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.opentravel.org/OTA/2003/05"
xsi:schemaLocation="http://www.opentravel.org/OTA/2003/05 OTA_PingRS.xsd"
Version="8.000">
<Success/>
<Warnings>
<Warning Type="11" Status="ALPINEBITS_HANDSHAKE">{
"versions": [
{
"version": "2024-10",
"actions": [
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
}
]
},
{
"version": "2022-10",
"actions": [
{
"action": "action_OTA_Ping"
},
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
}
]
}
]
}</Warning>
</Warnings>
<EchoData>{
"versions": [
{
"version": "2024-10",
"actions": [
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
}
]
},
{
"version": "2022-10",
"actions": [
{
"action": "action_OTA_Read"
},
{
"action": "action_OTA_Ping"
},
{
"action": "action_OTA_HotelResNotif_GuestRequests"
}
]
}
]
}</EchoData>
</OTA_PingRS>