|
@@ -322,3 +322,39 @@ async def test_diff_returns_complete_structure():
|
|
|
assert isinstance(diffs, list)
|
|
assert isinstance(diffs, list)
|
|
|
for row in diffs:
|
|
for row in diffs:
|
|
|
assert 'date' in row and 'local' in row and 'rule' in row
|
|
assert 'date' in row and 'local' in row and 'rule' in row
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@pytest.mark.asyncio
|
|
|
|
|
+async def test_diff_reservations_ignores_boundary_touching_checkout(monkeypatch):
|
|
|
|
|
+ """A reservation whose checkout lands exactly on start_date has zero real nights in
|
|
|
|
|
+ this window and belongs to the prior period's diff — it must not be reported as
|
|
|
|
|
+ 'added' just because the service's own range query includes boundary-touching stays
|
|
|
|
|
+ (e.g. Booking.com returning a checkout == date_from reservation)."""
|
|
|
|
|
+ import room_cal.actions.diff as diff_mod
|
|
|
|
|
+
|
|
|
|
|
+ class _FakeReservationCal:
|
|
|
|
|
+ async def list(self, start, end):
|
|
|
|
|
+ return []
|
|
|
|
|
+
|
|
|
|
|
+ class _ReservationsAPI(_MockAPI):
|
|
|
|
|
+ async def get_reservations(self, start, end, *, _out_errors=None):
|
|
|
|
|
+ return [
|
|
|
|
|
+ { # touches the boundary only — checkout == start_date
|
|
|
|
|
+ 'id': 'boundary', 'guest_name': 'Boundary Guest', 'service': 'booking',
|
|
|
|
|
+ 'start_date': start - timedelta(days=1), 'end_date': start,
|
|
|
|
|
+ },
|
|
|
|
|
+ { # genuinely starts inside the window
|
|
|
|
|
+ 'id': 'real', 'guest_name': 'Real Guest', 'service': 'booking',
|
|
|
|
|
+ 'start_date': start, 'end_date': start + timedelta(days=2),
|
|
|
|
|
+ },
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ monkeypatch.setattr(diff_mod, 'Reservation', _FakeReservationCal)
|
|
|
|
|
+
|
|
|
|
|
+ start = date(2026, 10, 1)
|
|
|
|
|
+ end = date(2026, 10, 31)
|
|
|
|
|
+ result = await diff_mod._diff_reservations(start, end, _ReservationsAPI())
|
|
|
|
|
+
|
|
|
|
|
+ ids = {row.get('reservation_id') for row in result}
|
|
|
|
|
+ assert 'boundary' not in ids
|
|
|
|
|
+ assert 'real' in ids
|