hass-core/tests/components/lock/test_init.py
2023-12-04 14:59:51 +01:00

329 lines
10 KiB
Python

"""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,
STATE_JAMMED,
STATE_LOCKED,
STATE_LOCKING,
STATE_UNLOCKED,
STATE_UNLOCKING,
LockEntity,
LockEntityFeature,
)
from homeassistant.core import HomeAssistant
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_lock = MagicMock()
self.calls_unlock = MagicMock()
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.calls_lock(kwargs)
self._attr_is_locking = False
self._attr_is_locked = True
async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock the lock."""
self.calls_unlock(kwargs)
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 lock.async_handle_lock_service()
assert lock.is_locked
assert lock.state == STATE_LOCKED
lock._attr_is_unlocking = True
assert lock.is_unlocking
assert lock.state == STATE_UNLOCKING
await lock.async_handle_unlock_service()
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,
entity_registry: er.EntityRegistry,
enable_custom_integrations: None,
) -> None:
"""Test default code stored in the registry."""
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,
entity_registry: er.EntityRegistry,
enable_custom_integrations: None,
) -> None:
"""Test default code stored in the registry is updated."""
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 lock.async_handle_open_service()
with pytest.raises(ValueError):
await lock.async_handle_open_service(code="")
with pytest.raises(ValueError):
await lock.async_handle_open_service(code="HELLO")
await lock.async_handle_open_service(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 lock.async_handle_unlock_service(code="1234")
assert not lock.is_locked
with pytest.raises(ValueError):
await lock.async_handle_lock_service()
with pytest.raises(ValueError):
await lock.async_handle_lock_service(code="")
with pytest.raises(ValueError):
await lock.async_handle_lock_service(code="HELLO")
await lock.async_handle_lock_service(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 lock.async_handle_lock_service(code="1234")
assert lock.is_locked
with pytest.raises(ValueError):
await lock.async_handle_unlock_service()
with pytest.raises(ValueError):
await lock.async_handle_unlock_service(code="")
with pytest.raises(ValueError):
await lock.async_handle_unlock_service(code="HELLO")
await lock.async_handle_unlock_service(code="1234")
assert not lock.is_locked
async def test_lock_with_illegal_code(hass: HomeAssistant) -> None:
"""Test lock entity with default code that does not match the code format."""
lock = MockLockEntity(
code_format=r"^\d{4}$",
supported_features=LockEntityFeature.OPEN,
)
lock.hass = hass
with pytest.raises(ValueError):
await lock.async_handle_open_service(code="123456")
with pytest.raises(ValueError):
await lock.async_handle_lock_service(code="123456")
with pytest.raises(ValueError):
await lock.async_handle_unlock_service(code="123456")
async def test_lock_with_no_code(hass: HomeAssistant) -> None:
"""Test lock entity with default code that does not match the code format."""
lock = MockLockEntity(
supported_features=LockEntityFeature.OPEN,
)
lock.hass = hass
await lock.async_handle_open_service()
lock.calls_open.assert_called_with({})
await lock.async_handle_lock_service()
lock.calls_lock.assert_called_with({})
await lock.async_handle_unlock_service()
lock.calls_unlock.assert_called_with({})
await lock.async_handle_open_service(code="")
lock.calls_open.assert_called_with({})
await lock.async_handle_lock_service(code="")
lock.calls_lock.assert_called_with({})
await lock.async_handle_unlock_service(code="")
lock.calls_unlock.assert_called_with({})
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 lock.async_handle_open_service()
lock.calls_open.assert_called_with({ATTR_CODE: "1234"})
await lock.async_handle_lock_service()
lock.calls_lock.assert_called_with({ATTR_CODE: "1234"})
await lock.async_handle_unlock_service()
lock.calls_unlock.assert_called_with({ATTR_CODE: "1234"})
await lock.async_handle_open_service(code="")
lock.calls_open.assert_called_with({ATTR_CODE: "1234"})
await lock.async_handle_lock_service(code="")
lock.calls_lock.assert_called_with({ATTR_CODE: "1234"})
await lock.async_handle_unlock_service(code="")
lock.calls_unlock.assert_called_with({ATTR_CODE: "1234"})
async def test_lock_with_provided_and_default_code(hass: HomeAssistant) -> None:
"""Test lock entity with provided code when default code is set."""
lock = MockLockEntity(
code_format=r"^\d{4}$",
supported_features=LockEntityFeature.OPEN,
lock_option_default_code="1234",
)
lock.hass = hass
await lock.async_handle_open_service(code="4321")
lock.calls_open.assert_called_with({ATTR_CODE: "4321"})
await lock.async_handle_lock_service(code="4321")
lock.calls_lock.assert_called_with({ATTR_CODE: "4321"})
await lock.async_handle_unlock_service(code="4321")
lock.calls_unlock.assert_called_with({ATTR_CODE: "4321"})
async def test_lock_with_illegal_default_code(hass: HomeAssistant) -> None:
"""Test lock entity with default code that does not match the code format."""
lock = MockLockEntity(
code_format=r"^\d{4}$",
supported_features=LockEntityFeature.OPEN,
lock_option_default_code="123456",
)
lock.hass = hass
assert lock.state_attributes == {"code_format": r"^\d{4}$"}
assert lock._lock_option_default_code == "123456"
with pytest.raises(ValueError):
await lock.async_handle_open_service()
with pytest.raises(ValueError):
await lock.async_handle_lock_service()
with pytest.raises(ValueError):
await lock.async_handle_unlock_service()