| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- """Tests for zlock_slots service handlers."""
- import pytest
- from unittest.mock import patch, AsyncMock, call
- from homeassistant.exceptions import ServiceValidationError
- from custom_components.zlock_slots.coordinator import ZLockCoordinator
- from custom_components.zlock_slots.services import register_services, unregister_services
- from custom_components.zlock_slots.const import DOMAIN, CONF_LOCK_ENTITY_IDS
- from tests.conftest import LOCK_ENTITY_ID, FakeUserData, FakeNode
- _ZWAVE_HELPER = "homeassistant.components.zwave_js.helpers.async_get_node_from_entity_id"
- @pytest.fixture
- async def service_env(hass, config_entry, mock_node):
- """Coordinator wired into hass.data with services registered and mock zwave_js services ready."""
- config_entry.add_to_hass(hass)
- with patch(_ZWAVE_HELPER, return_value=mock_node):
- coord = ZLockCoordinator(hass, config_entry)
- await coord.async_setup()
- hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = coord
- set_lock_usercode = AsyncMock()
- clear_lock_usercode = AsyncMock()
- hass.services.async_register("zwave_js", "set_lock_usercode", set_lock_usercode)
- hass.services.async_register("zwave_js", "clear_lock_usercode", clear_lock_usercode)
- register_services(hass)
- yield coord, set_lock_usercode, clear_lock_usercode
- unregister_services(hass)
- coord.async_teardown()
- # ---------------------------------------------------------------------------
- # set_code
- # ---------------------------------------------------------------------------
- async def test_set_code_calls_zwave_service(hass, service_env, mock_node):
- coord, set_lock_usercode, _ = service_env
- with patch(_ZWAVE_HELPER, return_value=mock_node):
- await hass.services.async_call(
- DOMAIN, "set_code",
- {"lock_entity_id": LOCK_ENTITY_ID, "slot": 5, "code": "1234"},
- blocking=True,
- )
- set_lock_usercode.assert_awaited_once()
- service_data = set_lock_usercode.call_args[0][0].data
- assert service_data["code_slot"] == 5
- assert service_data["usercode"] == "1234"
- assert service_data["entity_id"] == LOCK_ENTITY_ID
- async def test_set_code_auto_assigns_lowest_free_slot(hass, service_env, mock_node):
- """Slots 1 and 2 occupied → auto-assign picks 3."""
- coord, set_lock_usercode, _ = service_env
- with patch(_ZWAVE_HELPER, return_value=mock_node):
- await hass.services.async_call(
- DOMAIN, "set_code",
- {"lock_entity_id": LOCK_ENTITY_ID, "code": "5678"},
- blocking=True,
- )
- service_data = set_lock_usercode.call_args[0][0].data
- assert service_data["code_slot"] == 3
- async def test_set_code_auto_assign_skips_occupied(hass, service_env, mock_node):
- """If slot 3 is also occupied, auto-assign picks 4."""
- node_with_3 = FakeNode(users=[
- FakeUserData(1), FakeUserData(2), FakeUserData(3),
- ])
- coord, set_lock_usercode, _ = service_env
- # Refresh to get node_with_3 slots into coordinator state.
- with patch(_ZWAVE_HELPER, return_value=node_with_3):
- await coord.async_refresh_lock(LOCK_ENTITY_ID)
- await hass.services.async_call(
- DOMAIN, "set_code",
- {"lock_entity_id": LOCK_ENTITY_ID, "code": "9999"},
- blocking=True,
- )
- service_data = set_lock_usercode.call_args[0][0].data
- assert service_data["code_slot"] == 4
- async def test_set_code_with_name_saves_name(hass, service_env, mock_node):
- coord, _, _ = service_env
- with patch(_ZWAVE_HELPER, return_value=mock_node):
- await hass.services.async_call(
- DOMAIN, "set_code",
- {"lock_entity_id": LOCK_ENTITY_ID, "slot": 5, "code": "4321", "name": "Charlie"},
- blocking=True,
- )
- assert coord.get_name(LOCK_ENTITY_ID, 5) == "Charlie"
- async def test_set_code_unknown_lock_raises(hass, service_env):
- with pytest.raises(ServiceValidationError):
- await hass.services.async_call(
- DOMAIN, "set_code",
- {"lock_entity_id": "lock.unknown", "slot": 1, "code": "1234"},
- blocking=True,
- )
- # ---------------------------------------------------------------------------
- # clear_code
- # ---------------------------------------------------------------------------
- async def test_clear_code_calls_zwave_service(hass, service_env, mock_node):
- _, _, clear_lock_usercode = service_env
- with patch(_ZWAVE_HELPER, return_value=mock_node):
- await hass.services.async_call(
- DOMAIN, "clear_code",
- {"lock_entity_id": LOCK_ENTITY_ID, "slot": 2},
- blocking=True,
- )
- clear_lock_usercode.assert_awaited_once()
- service_data = clear_lock_usercode.call_args[0][0].data
- assert service_data["code_slot"] == 2
- assert service_data["entity_id"] == LOCK_ENTITY_ID
- # ---------------------------------------------------------------------------
- # set_name
- # ---------------------------------------------------------------------------
- async def test_set_name_updates_coordinator(hass, service_env):
- coord, _, _ = service_env
- await hass.services.async_call(
- DOMAIN, "set_name",
- {"lock_entity_id": LOCK_ENTITY_ID, "slot": 1, "name": "Diana"},
- blocking=True,
- )
- assert coord.get_name(LOCK_ENTITY_ID, 1) == "Diana"
- # ---------------------------------------------------------------------------
- # set_slot_enabled
- # ---------------------------------------------------------------------------
- async def test_set_slot_enabled_calls_node(hass, service_env, mock_node):
- with patch(_ZWAVE_HELPER, return_value=mock_node):
- await hass.services.async_call(
- DOMAIN, "set_slot_enabled",
- {"lock_entity_id": LOCK_ENTITY_ID, "slot": 1, "enabled": False},
- blocking=True,
- )
- mock_node.access_control.set_user.assert_awaited_once()
- slot_arg, opts_arg = mock_node.access_control.set_user.call_args[0]
- assert slot_arg == 1
- assert opts_arg.active is False
- async def test_set_slot_enabled_true(hass, service_env, mock_node):
- with patch(_ZWAVE_HELPER, return_value=mock_node):
- await hass.services.async_call(
- DOMAIN, "set_slot_enabled",
- {"lock_entity_id": LOCK_ENTITY_ID, "slot": 2, "enabled": True},
- blocking=True,
- )
- _, opts_arg = mock_node.access_control.set_user.call_args[0]
- assert opts_arg.active is True
|