test_switches.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. """Unit tests for GuestModeSwitch, ModeSyncSwitch, and OverrideReservationSwitch.
  2. Uses object.__new__ to bypass HA entity setup. async_write_ha_state is
  3. replaced with a MagicMock to avoid needing a real HA instance.
  4. """
  5. from unittest.mock import AsyncMock, MagicMock, patch
  6. import pytest
  7. from custom_components.room_cal_ha.switch import GuestModeSwitch, ModeSyncSwitch, OverrideReservationSwitch, UseUnifiSwitch
  8. def _guest_mode():
  9. s = object.__new__(GuestModeSwitch)
  10. s._state = False
  11. s._deferred = True
  12. s._coordinator = MagicMock()
  13. s.async_write_ha_state = MagicMock()
  14. return s
  15. def _mode_sync():
  16. s = object.__new__(ModeSyncSwitch)
  17. s._state = False
  18. s._coordinator = MagicMock()
  19. s._coordinator.cancel_mode_sync = MagicMock()
  20. s.async_write_ha_state = MagicMock()
  21. return s
  22. # ---------------------------------------------------------------------------
  23. # GuestModeSwitch — basic state
  24. # ---------------------------------------------------------------------------
  25. async def test_guest_mode_initial_state_is_false():
  26. s = _guest_mode()
  27. assert s.is_on is False
  28. async def test_guest_mode_turn_on_sets_state():
  29. s = _guest_mode()
  30. s._deferred = False
  31. await s.async_turn_on()
  32. assert s.is_on is True
  33. async def test_guest_mode_turn_off_sets_state():
  34. s = _guest_mode()
  35. s._state = True
  36. s._deferred = False
  37. await s.async_turn_off()
  38. assert s.is_on is False
  39. # ---------------------------------------------------------------------------
  40. # GuestModeSwitch — deferred write behavior
  41. # ---------------------------------------------------------------------------
  42. async def test_guest_mode_deferred_suppresses_write():
  43. s = _guest_mode()
  44. assert s._deferred is True
  45. s.async_write_ha_state() # should be a no-op when deferred
  46. # The real async_write_ha_state is replaced; calling it when deferred returns early.
  47. # We verify by re-implementing the check:
  48. with patch.object(type(s).__bases__[0], "async_write_ha_state") as mock_parent:
  49. s.async_write_ha_state = GuestModeSwitch.async_write_ha_state.__get__(s, type(s))
  50. s.async_write_ha_state()
  51. mock_parent.assert_not_called()
  52. def test_guest_mode_commit_state_unblocks_and_writes():
  53. s = _guest_mode()
  54. s._state = True
  55. real_write = MagicMock()
  56. with patch(
  57. "homeassistant.helpers.entity.Entity.async_write_ha_state",
  58. real_write,
  59. ):
  60. s.commit_state()
  61. assert s._deferred is False
  62. real_write.assert_called_once()
  63. # ---------------------------------------------------------------------------
  64. # ModeSyncSwitch — turn off cancels mode sync
  65. # ---------------------------------------------------------------------------
  66. async def test_mode_sync_turn_off_cancels_sync():
  67. s = _mode_sync()
  68. s._state = True
  69. await s.async_turn_off()
  70. assert s.is_on is False
  71. s._coordinator.cancel_mode_sync.assert_called_once()
  72. async def test_mode_sync_turn_on_does_not_cancel_sync():
  73. s = _mode_sync()
  74. await s.async_turn_on()
  75. s._coordinator.cancel_mode_sync.assert_not_called()
  76. async def test_mode_sync_initial_state_is_false():
  77. s = _mode_sync()
  78. assert s.is_on is False
  79. # ---------------------------------------------------------------------------
  80. # OverrideReservationSwitch
  81. # ---------------------------------------------------------------------------
  82. def _override_switch(override_active: bool = False):
  83. s = object.__new__(OverrideReservationSwitch)
  84. s.coordinator = MagicMock()
  85. s.coordinator.async_end_override = AsyncMock()
  86. data = MagicMock()
  87. data.override_active = override_active
  88. s.coordinator.data = data
  89. return s
  90. def test_override_switch_is_off_when_not_active():
  91. assert _override_switch(override_active=False).is_on is False
  92. def test_override_switch_is_on_when_active():
  93. assert _override_switch(override_active=True).is_on is True
  94. def test_override_switch_none_data_is_off():
  95. s = _override_switch()
  96. s.coordinator.data = None
  97. assert s.is_on is False
  98. async def test_override_switch_turn_off_ends_override():
  99. s = _override_switch(override_active=True)
  100. await s.async_turn_off()
  101. s.coordinator.async_end_override.assert_awaited_once()
  102. async def test_override_switch_turn_on_is_noop():
  103. s = _override_switch()
  104. await s.async_turn_on()
  105. s.coordinator.async_end_override.assert_not_awaited()
  106. # ---------------------------------------------------------------------------
  107. # UseUnifiSwitch
  108. # ---------------------------------------------------------------------------
  109. def _use_unifi():
  110. s = object.__new__(UseUnifiSwitch)
  111. s._coordinator = MagicMock()
  112. s.async_write_ha_state = MagicMock()
  113. return s
  114. def test_use_unifi_default_state_is_true():
  115. s = _use_unifi()
  116. assert s.is_on is True
  117. async def test_use_unifi_turn_off_sets_false():
  118. s = _use_unifi()
  119. await s.async_turn_off()
  120. assert s.is_on is False
  121. async def test_use_unifi_turn_on_restores_true():
  122. s = _use_unifi()
  123. s._state = False
  124. await s.async_turn_on()
  125. assert s.is_on is True