Fixed room upsert logic
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user