|
|
@@ -830,13 +830,13 @@ async def test_start_override_empty_current_triggers_checkout_not_checkin(
|
|
|
assert coordinator.data.override_active is True
|
|
|
|
|
|
|
|
|
-async def test_override_active_checks_out_on_schedule_while_override_stays_active(
|
|
|
+async def test_override_active_schedules_no_mode_sync_handles(
|
|
|
hass: HomeAssistant, coordinator: RoomCalCoordinator
|
|
|
):
|
|
|
- """With Mode Sync on, an override's own active record schedules a real check-out
|
|
|
- handle; firing it clears the override's active (not the store's), runs checkout
|
|
|
- actions, override_active stays True, and a subsequent Next check-in keeps writing
|
|
|
- into the override rather than the store."""
|
|
|
+ """While an override is active, no Store-driven check-in/check-out handles are
|
|
|
+ scheduled — an override parks Active until it's ended manually or its own end_at
|
|
|
+ timer fires. Next's check-in time elapsing (Store empty -> occupied) must not
|
|
|
+ overwrite the override."""
|
|
|
override_guest = ReservationData(
|
|
|
guest_name="Override Guest",
|
|
|
check_in_date=date(2026, 7, 1), check_in_time=time(14, 0),
|
|
|
@@ -847,39 +847,52 @@ async def test_override_active_checks_out_on_schedule_while_override_stays_activ
|
|
|
mock_switch.async_turn_off = AsyncMock()
|
|
|
coordinator._room_state_switch = mock_switch
|
|
|
|
|
|
+ await coordinator.store.async_set(STORE_NEXT, ReservationData(
|
|
|
+ guest_name="Next Guest",
|
|
|
+ check_in_date=date(2026, 7, 1), check_in_time=time(13, 0),
|
|
|
+ ))
|
|
|
+
|
|
|
with (
|
|
|
patch.object(coordinator, "_is_mode_sync_on", return_value=True),
|
|
|
patch("custom_components.room_cal_ha.coordinator.dt_util.now", return_value=_AT_CHECK_IN),
|
|
|
):
|
|
|
await coordinator.async_start_override(OverrideData(active=override_guest))
|
|
|
|
|
|
- assert len(coordinator._mode_sync_handles) >= 1
|
|
|
+ assert coordinator._mode_sync_handles == []
|
|
|
assert coordinator.data.override_active is True
|
|
|
+ assert coordinator.data.active.guest_name == "Override Guest"
|
|
|
|
|
|
- with (
|
|
|
- patch.object(coordinator, "_is_mode_sync_on", return_value=True),
|
|
|
- patch("custom_components.room_cal_ha.coordinator.dt_util.now", return_value=_AT_CHECK_IN),
|
|
|
- ):
|
|
|
- await coordinator._async_do_check_out()
|
|
|
- coordinator.cancel_mode_sync()
|
|
|
|
|
|
- mock_switch.async_turn_off.assert_awaited_once()
|
|
|
- assert coordinator.data.override_active is True
|
|
|
- assert coordinator.data.active.guest_name is None
|
|
|
- assert coordinator.store.get(STORE_CURRENT) is None
|
|
|
+async def test_ending_override_reschedules_mode_sync_from_store(
|
|
|
+ hass: HomeAssistant, coordinator: RoomCalCoordinator
|
|
|
+):
|
|
|
+ """Ending an override re-arms the normal check-in/check-out handles from whatever
|
|
|
+ the Store currently holds — the same reconcile-and-reschedule path used at startup."""
|
|
|
+ override_guest = ReservationData(
|
|
|
+ guest_name="Override Guest",
|
|
|
+ check_in_date=date(2026, 7, 1), check_in_time=time(14, 0),
|
|
|
+ check_out_date=date(2026, 7, 5), check_out_time=time(12, 0),
|
|
|
+ )
|
|
|
+ coordinator._room_state_switch = None
|
|
|
+ await coordinator.store.async_set(STORE_NEXT, ReservationData(
|
|
|
+ guest_name="Next Guest",
|
|
|
+ check_in_date=date(2026, 7, 1), check_in_time=time(15, 0),
|
|
|
+ ))
|
|
|
|
|
|
- await coordinator.store.async_set(STORE_NEXT, ReservationData(guest_name="Next Guest"))
|
|
|
with (
|
|
|
patch.object(coordinator, "_is_mode_sync_on", return_value=True),
|
|
|
patch("custom_components.room_cal_ha.coordinator.dt_util.now", return_value=_AT_CHECK_IN),
|
|
|
):
|
|
|
- await coordinator._async_do_check_in()
|
|
|
- coordinator.cancel_mode_sync()
|
|
|
+ await coordinator.async_start_override(OverrideData(active=override_guest))
|
|
|
+ assert coordinator._mode_sync_handles == []
|
|
|
|
|
|
- assert coordinator.data.override_active is True
|
|
|
- assert coordinator.data.active.guest_name == "Next Guest"
|
|
|
- # The real store's own Current is untouched — the promoted value went into the override.
|
|
|
- assert coordinator.store.get(STORE_CURRENT) is None
|
|
|
+ await coordinator.async_end_override()
|
|
|
+
|
|
|
+ assert coordinator.data.override_active is False
|
|
|
+ # The check-in handle for Next Guest (15:00, still in the future relative to
|
|
|
+ # _AT_CHECK_IN) is re-armed now that the override no longer owns Active.
|
|
|
+ assert len(coordinator._mode_sync_handles) >= 1
|
|
|
+ coordinator.cancel_mode_sync()
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|