Free rooms doesn't cause errors but further data verification is necessary

This commit is contained in:
Jonas Linter
2025-12-04 16:14:40 +01:00
parent ea3d886b87
commit 16d12f5b62
2 changed files with 194 additions and 0 deletions

View File

@@ -560,3 +560,166 @@ async def test_closing_season_with_rooms_is_allowed(db_session: AsyncSession):
db_session,
)
assert response.status_code == HttpStatusCode.OK
@pytest.mark.asyncio
async def test_complete_set_with_single_empty_inventory_resets_all_availability(
db_session: AsyncSession,
):
"""Test the special case: CompleteSet with one empty Inventory element to reset all availability.
According to AlpineBits spec, to completely reset all room availability information for a hotel,
a client can send a CompleteSet request with just one empty Inventory element without any
attributes. This is the only exception to the rule that StatusApplicationControl is required.
"""
await insert_test_hotel(db_session)
action = make_action()
# First, add some availability data
initial_xml = build_complete_set_xml(
daily_inventory("2025-01-01", "2025-01-05", inv_type="DBL", count=10)
)
await action.handle(
"OTA_HotelInvCountNotif:FreeRooms",
initial_xml,
Version.V2024_10,
make_client_info(),
db_session,
)
# Verify data was created
rows_before = (await db_session.execute(select(RoomAvailability))).scalars().all()
assert len(rows_before) == 5
inventory_before = (await db_session.execute(select(HotelInventory))).scalars().all()
assert len(inventory_before) == 1
assert inventory_before[0].source == "FreeRooms"
# Now send the special reset request with empty Inventory element
reset_xml = build_complete_set_xml("<Inventory/>")
response = await action.handle(
"OTA_HotelInvCountNotif:FreeRooms",
reset_xml,
Version.V2024_10,
make_client_info(),
db_session,
)
# Should succeed
assert response.status_code == HttpStatusCode.OK
# All availability and FreeRooms-sourced inventory should be cleared
rows_after = (await db_session.execute(select(RoomAvailability))).scalars().all()
assert len(rows_after) == 0
inventory_after = (await db_session.execute(select(HotelInventory))).scalars().all()
assert len(inventory_after) == 0
@pytest.mark.asyncio
async def test_delta_with_empty_inventory_is_rejected(db_session: AsyncSession):
"""Test that empty Inventory is only allowed for CompleteSet, not Delta."""
await insert_test_hotel(db_session)
action = make_action()
xml = build_delta_xml("<Inventory/>")
response = await action.handle(
"OTA_HotelInvCountNotif:FreeRooms",
xml,
Version.V2024_10,
make_client_info(),
db_session,
)
# Delta requests cannot use empty Inventory
assert response.status_code == HttpStatusCode.BAD_REQUEST
assert "StatusApplicationControl element is required" in response.xml_content
@pytest.mark.asyncio
async def test_complete_set_with_multiple_empty_inventories_is_rejected(
db_session: AsyncSession,
):
"""Test that the empty Inventory exception only applies to a single empty Inventory."""
await insert_test_hotel(db_session)
action = make_action()
# Multiple empty Inventory elements should not be allowed
xml = build_complete_set_xml("<Inventory/><Inventory/>")
response = await action.handle(
"OTA_HotelInvCountNotif:FreeRooms",
xml,
Version.V2024_10,
make_client_info(),
db_session,
)
# Should fail because the special case only applies to a single empty Inventory
assert response.status_code == HttpStatusCode.BAD_REQUEST
assert "StatusApplicationControl element is required" in response.xml_content
@pytest.mark.asyncio
async def test_complete_set_preserves_inventory_from_other_sources(db_session: AsyncSession):
"""Test that CompleteSet only deletes FreeRooms-sourced inventory, not inventory from other sources."""
await insert_test_hotel(db_session)
action = make_action()
# First, add some FreeRooms inventory
freerooms_xml = build_complete_set_xml(
daily_inventory("2025-01-01", "2025-01-05", inv_type="DBL", count=10)
)
await action.handle(
"OTA_HotelInvCountNotif:FreeRooms",
freerooms_xml,
Version.V2024_10,
make_client_info(),
db_session,
)
# Manually add inventory from another source (simulating HotelInventory endpoint)
other_inventory = HotelInventory(
hotel_id="TESTHOTEL",
inv_type_code="SGL",
inv_code=None,
source="HotelInventory",
first_seen=datetime.now(UTC),
last_updated=datetime.now(UTC),
)
db_session.add(other_inventory)
await db_session.commit()
# Verify both inventory items exist
inventory_before = (
await db_session.execute(select(HotelInventory).order_by(HotelInventory.source))
).scalars().all()
assert len(inventory_before) == 2
assert inventory_before[0].source == "FreeRooms"
assert inventory_before[1].source == "HotelInventory"
# Send a new CompleteSet with different data
new_xml = build_complete_set_xml(
daily_inventory("2025-01-01", "2025-01-03", inv_type="TRIPLE", count=5)
)
response = await action.handle(
"OTA_HotelInvCountNotif:FreeRooms",
new_xml,
Version.V2024_10,
make_client_info(),
db_session,
)
assert response.status_code == HttpStatusCode.OK
# Check inventory: FreeRooms inventory should be replaced, but HotelInventory source should remain
inventory_after = (
await db_session.execute(select(HotelInventory).order_by(HotelInventory.source))
).scalars().all()
assert len(inventory_after) == 2
# New FreeRooms inventory
assert inventory_after[0].source == "FreeRooms"
assert inventory_after[0].inv_type_code == "TRIPLE"
# Preserved HotelInventory source
assert inventory_after[1].source == "HotelInventory"
assert inventory_after[1].inv_type_code == "SGL"