"""The tests for the lock component.""" from __future__ import annotations from typing import Any from unittest.mock import MagicMock import pytest from homeassistant.components.lock import ( ATTR_CODE, CONF_DEFAULT_CODE, DOMAIN, SERVICE_LOCK, SERVICE_OPEN, SERVICE_UNLOCK, STATE_JAMMED, STATE_LOCKED, STATE_LOCKING, STATE_UNLOCKED, STATE_UNLOCKING, LockEntity, LockEntityFeature, _async_lock, _async_open, _async_unlock, ) from homeassistant.core import HomeAssistant, ServiceCall import homeassistant.helpers.entity_registry as er from homeassistant.setup import async_setup_component from tests.testing_config.custom_components.test.lock import MockLock class MockLockEntity(LockEntity): """Mock lock to use in tests.""" def __init__( self, code_format: str | None = None, lock_option_default_code: str = "", supported_features: LockEntityFeature = LockEntityFeature(0), ) -> None: """Initialize mock lock entity.""" self._attr_supported_features = supported_features self.calls_open = MagicMock() if code_format is not None: self._attr_code_format = code_format self._lock_option_default_code = lock_option_default_code async def async_lock(self, **kwargs: Any) -> None: """Lock the lock.""" self._attr_is_locking = False self._attr_is_locked = True async def async_unlock(self, **kwargs: Any) -> None: """Unlock the lock.""" self._attr_is_unlocking = False self._attr_is_locked = False async def async_open(self, **kwargs: Any) -> None: """Open the door latch.""" self.calls_open(kwargs) async def test_lock_default(hass: HomeAssistant) -> None: """Test lock entity with defaults.""" lock = MockLockEntity() lock.hass = hass assert lock.code_format is None assert lock.state is None async def test_lock_states(hass: HomeAssistant) -> None: """Test lock entity states.""" lock = MockLockEntity() lock.hass = hass assert lock.state is None lock._attr_is_locking = True assert lock.is_locking assert lock.state == STATE_LOCKING await _async_lock(lock, ServiceCall(DOMAIN, SERVICE_LOCK, {})) assert lock.is_locked assert lock.state == STATE_LOCKED lock._attr_is_unlocking = True assert lock.is_unlocking assert lock.state == STATE_UNLOCKING await _async_unlock(lock, ServiceCall(DOMAIN, SERVICE_UNLOCK, {})) assert not lock.is_locked assert lock.state == STATE_UNLOCKED lock._attr_is_jammed = True assert lock.is_jammed assert lock.state == STATE_JAMMED assert not lock.is_locked async def test_set_default_code_option( hass: HomeAssistant, enable_custom_integrations: None, ) -> None: """Test default code stored in the registry.""" entity_registry = er.async_get(hass) entry = entity_registry.async_get_or_create("lock", "test", "very_unique") await hass.async_block_till_done() platform = getattr(hass.components, "test.lock") platform.init(empty=True) platform.ENTITIES["lock1"] = platform.MockLock( name="Test", code_format=r"^\d{4}$", supported_features=LockEntityFeature.OPEN, unique_id="very_unique", ) assert await async_setup_component(hass, "lock", {"lock": {"platform": "test"}}) await hass.async_block_till_done() entity0: MockLock = platform.ENTITIES["lock1"] entity_registry.async_update_entity_options( entry.entity_id, "lock", {CONF_DEFAULT_CODE: "1234"} ) await hass.async_block_till_done() assert entity0._lock_option_default_code == "1234" async def test_default_code_option_update( hass: HomeAssistant, enable_custom_integrations: None, ) -> None: """Test default code stored in the registry is updated.""" entity_registry = er.async_get(hass) entry = entity_registry.async_get_or_create("lock", "test", "very_unique") await hass.async_block_till_done() platform = getattr(hass.components, "test.lock") platform.init(empty=True) # Pre-register entities entry = entity_registry.async_get_or_create("lock", "test", "very_unique") entity_registry.async_update_entity_options( entry.entity_id, "lock", { "default_code": "5432", }, ) platform.ENTITIES["lock1"] = platform.MockLock( name="Test", code_format=r"^\d{4}$", supported_features=LockEntityFeature.OPEN, unique_id="very_unique", ) assert await async_setup_component(hass, "lock", {"lock": {"platform": "test"}}) await hass.async_block_till_done() entity0: MockLock = platform.ENTITIES["lock1"] assert entity0._lock_option_default_code == "5432" entity_registry.async_update_entity_options( entry.entity_id, "lock", {CONF_DEFAULT_CODE: "1234"} ) await hass.async_block_till_done() assert entity0._lock_option_default_code == "1234" async def test_lock_open_with_code(hass: HomeAssistant) -> None: """Test lock entity with open service.""" lock = MockLockEntity( code_format=r"^\d{4}$", supported_features=LockEntityFeature.OPEN ) lock.hass = hass assert lock.state_attributes == {"code_format": r"^\d{4}$"} with pytest.raises(ValueError): await _async_open(lock, ServiceCall(DOMAIN, SERVICE_OPEN, {})) with pytest.raises(ValueError): await _async_open(lock, ServiceCall(DOMAIN, SERVICE_OPEN, {ATTR_CODE: ""})) with pytest.raises(ValueError): await _async_open(lock, ServiceCall(DOMAIN, SERVICE_OPEN, {ATTR_CODE: "HELLO"})) await _async_open(lock, ServiceCall(DOMAIN, SERVICE_OPEN, {ATTR_CODE: "1234"})) assert lock.calls_open.call_count == 1 async def test_lock_lock_with_code(hass: HomeAssistant) -> None: """Test lock entity with open service.""" lock = MockLockEntity(code_format=r"^\d{4}$") lock.hass = hass await _async_unlock(lock, ServiceCall(DOMAIN, SERVICE_UNLOCK, {ATTR_CODE: "1234"})) assert not lock.is_locked with pytest.raises(ValueError): await _async_lock(lock, ServiceCall(DOMAIN, SERVICE_LOCK, {})) with pytest.raises(ValueError): await _async_lock(lock, ServiceCall(DOMAIN, SERVICE_LOCK, {ATTR_CODE: ""})) with pytest.raises(ValueError): await _async_lock(lock, ServiceCall(DOMAIN, SERVICE_LOCK, {ATTR_CODE: "HELLO"})) await _async_lock(lock, ServiceCall(DOMAIN, SERVICE_LOCK, {ATTR_CODE: "1234"})) assert lock.is_locked async def test_lock_unlock_with_code(hass: HomeAssistant) -> None: """Test unlock entity with open service.""" lock = MockLockEntity(code_format=r"^\d{4}$") lock.hass = hass await _async_lock(lock, ServiceCall(DOMAIN, SERVICE_UNLOCK, {ATTR_CODE: "1234"})) assert lock.is_locked with pytest.raises(ValueError): await _async_unlock(lock, ServiceCall(DOMAIN, SERVICE_UNLOCK, {})) with pytest.raises(ValueError): await _async_unlock(lock, ServiceCall(DOMAIN, SERVICE_UNLOCK, {ATTR_CODE: ""})) with pytest.raises(ValueError): await _async_unlock( lock, ServiceCall(DOMAIN, SERVICE_UNLOCK, {ATTR_CODE: "HELLO"}) ) await _async_unlock(lock, ServiceCall(DOMAIN, SERVICE_UNLOCK, {ATTR_CODE: "1234"})) assert not lock.is_locked async def test_lock_with_default_code(hass: HomeAssistant) -> None: """Test lock entity with default code.""" lock = MockLockEntity( code_format=r"^\d{4}$", supported_features=LockEntityFeature.OPEN, lock_option_default_code="1234", ) lock.hass = hass assert lock.state_attributes == {"code_format": r"^\d{4}$"} assert lock._lock_option_default_code == "1234" await _async_open(lock, ServiceCall(DOMAIN, SERVICE_OPEN, {})) await _async_lock(lock, ServiceCall(DOMAIN, SERVICE_LOCK, {})) await _async_unlock(lock, ServiceCall(DOMAIN, SERVICE_UNLOCK, {}))