Fixed room upsert logic

This commit is contained in:
Jonas Linter
2025-12-01 11:12:22 +01:00
parent 2be10ff899
commit 877b2909f2
6 changed files with 1293 additions and 12 deletions

View File

@@ -340,13 +340,348 @@ class TestConversionServiceWithImportedData:
assert stats["total_daily_sales"] == 0
assert stats["errors"] == 0
@pytest.mark.asyncio
async def test_duplicate_reservations(self, test_db_session):
"""Test that room entries are correctly updated when reservation status changes.
This test detects a bug where ConversionRoom records are not properly upserted
when the same reservation is processed multiple times with different room numbers.
Scenario:
1. Process reservation with status='request', no revenue, room_number='101'
2. Process reservation with status='reservation', with revenue, room_number='102'
3. Swap: Process same reservations but reversed - first one now has status='reservation'
with room_number='201', second has status='request' with room_number='202'
4. The old room entries (101, 102) should no longer exist in the database
"""
from tests.helpers import ReservationXMLBuilder, MultiReservationXMLBuilder
# First batch: Process two reservations
multi_builder1 = MultiReservationXMLBuilder()
# Reservation 1: Request status, no revenue, room 101
res1_v1 = (
ReservationXMLBuilder(
hotel_id="39054_001",
reservation_id="res_001",
reservation_number="RES-001",
reservation_date="2025-11-14",
reservation_type="request",
)
.set_guest(
guest_id="guest_001",
first_name="Alice",
last_name="Johnson",
email="alice@example.com",
)
.add_room(
arrival="2025-12-01",
departure="2025-12-03",
room_number="101",
status="request",
# No revenue
)
)
multi_builder1.add_reservation(res1_v1)
# Reservation 2: Reservation status, with revenue, room 102
res2_v1 = (
ReservationXMLBuilder(
hotel_id="39054_001",
reservation_id="res_002",
reservation_number="RES-002",
reservation_date="2025-11-15",
reservation_type="reservation",
)
.set_guest(
guest_id="guest_002",
first_name="Bob",
last_name="Smith",
email="bob@example.com",
)
.add_room(
arrival="2025-12-10",
departure="2025-12-12",
room_number="102",
status="reserved",
revenue_logis_per_day=150.0,
)
)
multi_builder1.add_reservation(res2_v1)
xml_content1 = multi_builder1.build_xml()
# Process first batch
service = ConversionService(test_db_session)
stats1 = await service.process_conversion_xml(xml_content1)
assert stats1["total_reservations"] == 2
# Verify rooms exist in database
result = await test_db_session.execute(
select(ConversionRoom).where(ConversionRoom.room_number == "101")
)
room_101 = result.scalar_one_or_none()
assert room_101 is not None, "Room 101 should exist after first processing"
result = await test_db_session.execute(
select(ConversionRoom).where(ConversionRoom.room_number == "102")
)
room_102 = result.scalar_one_or_none()
assert room_102 is not None, "Room 102 should exist after first processing"
# Second batch: Swap the reservations and change room numbers
multi_builder2 = MultiReservationXMLBuilder()
# Reservation 1: NOW has reservation status, with revenue, room 201 (changed from 101)
res1_v2 = (
ReservationXMLBuilder(
hotel_id="39054_001",
reservation_id="res_001", # Same ID
reservation_number="RES-001", # Same number
reservation_date="2025-11-14",
reservation_type="reservation", # Changed from request
)
.set_guest(
guest_id="guest_001",
first_name="Alice",
last_name="Johnson",
email="alice@example.com",
)
.add_room(
arrival="2025-12-01",
departure="2025-12-03",
room_number="201", # Changed from 101
status="reserved",
revenue_logis_per_day=200.0, # Now has revenue
)
)
multi_builder2.add_reservation(res1_v2)
# Reservation 2: NOW has request status, no revenue, room 202 (changed from 102)
res2_v2 = (
ReservationXMLBuilder(
hotel_id="39054_001",
reservation_id="res_002", # Same ID
reservation_number="RES-002", # Same number
reservation_date="2025-11-15",
reservation_type="request", # Changed from reservation
)
.set_guest(
guest_id="guest_002",
first_name="Bob",
last_name="Smith",
email="bob@example.com",
)
.add_room(
arrival="2025-12-10",
departure="2025-12-12",
room_number="202", # Changed from 102
status="request",
# No revenue anymore
)
)
multi_builder2.add_reservation(res2_v2)
xml_content2 = multi_builder2.build_xml()
# Process second batch
stats2 = await service.process_conversion_xml(xml_content2)
assert stats2["total_reservations"] == 2
# BUG DETECTION: Old room entries (101, 102) should NOT exist anymore
# They should have been replaced by new room entries (201, 202)
result = await test_db_session.execute(
select(ConversionRoom).where(ConversionRoom.room_number == "101")
)
room_101_after = result.scalar_one_or_none()
assert room_101_after is None, (
"BUG: Room 101 should no longer exist after reprocessing with room 201. "
"Old room entries are not being removed when reservation is updated."
)
result = await test_db_session.execute(
select(ConversionRoom).where(ConversionRoom.room_number == "102")
)
room_102_after = result.scalar_one_or_none()
assert room_102_after is None, (
"BUG: Room 102 should no longer exist after reprocessing with room 202. "
"Old room entries are not being removed when reservation is updated."
)
# New room entries should exist
result = await test_db_session.execute(
select(ConversionRoom).where(ConversionRoom.room_number == "201")
)
room_201 = result.scalar_one_or_none()
assert room_201 is not None, "Room 201 should exist after second processing"
result = await test_db_session.execute(
select(ConversionRoom).where(ConversionRoom.room_number == "202")
)
room_202 = result.scalar_one_or_none()
assert room_202 is not None, "Room 202 should exist after second processing"
# Verify we only have 2 conversion room records total (not 4)
result = await test_db_session.execute(select(ConversionRoom))
all_rooms = result.scalars().all()
assert len(all_rooms) == 2, (
f"BUG: Expected 2 conversion rooms total, but found {len(all_rooms)}. "
f"Old room entries are not being deleted. Room numbers: {[r.room_number for r in all_rooms]}"
)
class TestXMLBuilderUsage:
"""Demonstrate usage of XML builder helpers for creating test data."""
@pytest.mark.asyncio
async def test_using_xml_builder_for_simple_reservation(self, test_db_session):
"""Example: Create a simple reservation using the XML builder helper."""
from tests.helpers import ReservationXMLBuilder
# Build a reservation with convenient fluent API
xml_content = (
ReservationXMLBuilder(
hotel_id="39054_001",
reservation_id="test_123",
reservation_number="RES-123",
reservation_date="2025-11-14",
)
.set_guest(
guest_id="guest_001",
first_name="John",
last_name="Doe",
email="john@example.com",
country_code="US",
)
.add_room(
arrival="2025-12-01",
departure="2025-12-05",
room_type="DZV",
room_number="101",
revenue_logis_per_day=150.0,
adults=2
)
.build_xml()
)
# Process the XML
service = ConversionService(test_db_session)
stats = await service.process_conversion_xml(xml_content)
assert stats["total_reservations"] == 1
assert stats["total_daily_sales"] == 5 # 4 nights + departure day
@pytest.mark.asyncio
async def test_using_xml_builder_for_multi_room_reservation(
self, test_db_session
):
"""Example: Create a reservation with multiple rooms."""
from tests.helpers import ReservationXMLBuilder
xml_content = (
ReservationXMLBuilder(
hotel_id="39054_001",
reservation_id="test_456",
reservation_number="RES-456",
reservation_date="2025-11-14",
)
.set_guest(
guest_id="guest_002",
first_name="Jane",
last_name="Smith",
email="jane@example.com",
)
.add_room(
arrival="2025-12-01",
departure="2025-12-05",
room_number="101",
revenue_logis_per_day=150.0,
)
.add_room(
arrival="2025-12-01",
departure="2025-12-05",
room_number="102",
revenue_logis_per_day=200.0,
)
.build_xml()
)
service = ConversionService(test_db_session)
stats = await service.process_conversion_xml(xml_content)
assert stats["total_reservations"] == 1
# 2 rooms × 5 daily sales each = 10 total
assert stats["total_daily_sales"] == 10
@pytest.mark.asyncio
async def test_using_multi_reservation_builder(self, test_db_session):
"""Example: Create multiple reservations in one XML document."""
from tests.helpers import ReservationXMLBuilder, MultiReservationXMLBuilder
multi_builder = MultiReservationXMLBuilder()
# Add first reservation
res1 = (
ReservationXMLBuilder(
hotel_id="39054_001",
reservation_id="test_001",
reservation_number="RES-001",
reservation_date="2025-11-14",
)
.set_guest(
guest_id="guest_001",
first_name="Alice",
last_name="Johnson",
email="alice@example.com",
)
.add_room(
arrival="2025-12-01",
departure="2025-12-03",
revenue_logis_per_day=100.0,
)
)
multi_builder.add_reservation(res1)
# Add second reservation
res2 = (
ReservationXMLBuilder(
hotel_id="39054_001",
reservation_id="test_002",
reservation_number="RES-002",
reservation_date="2025-11-15",
)
.set_guest(
guest_id="guest_002",
first_name="Bob",
last_name="Williams",
email="bob@example.com",
)
.add_room(
arrival="2025-12-10",
departure="2025-12-12",
revenue_logis_per_day=150.0,
)
)
multi_builder.add_reservation(res2)
xml_content = multi_builder.build_xml()
# Process the XML
service = ConversionService(test_db_session)
stats = await service.process_conversion_xml(xml_content)
assert stats["total_reservations"] == 2
# Res1: 3 days (2 nights), Res2: 3 days (2 nights) = 6 total
assert stats["total_daily_sales"] == 6
class TestHashedMatchingLogic:
"""Test the hashed matching logic used in ConversionService."""
@pytest.mark.asyncio
async def test_conversion_guest_hashed_fields_are_populated(
self, test_db_session