Files
core/tests/components/switchbot/test_services.py
2026-01-29 12:23:38 +01:00

251 lines
8.2 KiB
Python

"""Test the switchbot services."""
from collections.abc import Callable
from unittest.mock import AsyncMock, patch
import pytest
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from homeassistant.components.switchbot.const import DOMAIN
from homeassistant.components.switchbot.services import (
SERVICE_ADD_PASSWORD,
async_setup_services,
)
from homeassistant.const import ATTR_DEVICE_ID
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import device_registry as dr
from . import (
KEYPAD_VISION_INFO,
KEYPAD_VISION_PRO_INFO,
SMART_THERMOSTAT_RADIATOR_SERVICE_INFO,
)
from tests.common import MockConfigEntry
from tests.components.bluetooth import inject_bluetooth_service_info
@pytest.mark.parametrize(
("ble_service_info", "sensor_type"),
[
(KEYPAD_VISION_INFO, "keypad_vision"),
(KEYPAD_VISION_PRO_INFO, "keypad_vision_pro"),
],
)
async def test_add_password_service(
hass: HomeAssistant,
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
ble_service_info: BluetoothServiceInfoBleak,
sensor_type: str,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test the add_password service."""
inject_bluetooth_service_info(hass, ble_service_info)
entry = mock_entry_encrypted_factory(sensor_type=sensor_type)
entry.add_to_hass(hass)
mocked_instance = AsyncMock(return_value=True)
with patch.multiple(
"homeassistant.components.switchbot.switchbot.SwitchbotKeypadVision",
update=AsyncMock(return_value=None),
add_password=mocked_instance,
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
device_entry = dr.async_entries_for_config_entry(
device_registry, entry.entry_id
)[0]
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_PASSWORD,
{
ATTR_DEVICE_ID: device_entry.id,
"password": "123456",
},
blocking=True,
)
mocked_instance.assert_called_once_with("123456")
async def test_device_not_found(
hass: HomeAssistant,
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
) -> None:
"""Test the add_password service with non-existent device."""
inject_bluetooth_service_info(hass, KEYPAD_VISION_INFO)
entry = mock_entry_encrypted_factory(sensor_type="keypad_vision")
entry.add_to_hass(hass)
with patch.multiple(
"homeassistant.components.switchbot.switchbot.SwitchbotKeypadVision",
update=AsyncMock(return_value=None),
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
with pytest.raises(ServiceValidationError) as err:
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_PASSWORD,
{
ATTR_DEVICE_ID: "nonexistent_device",
"password": "123456",
},
blocking=True,
)
assert err.value.translation_domain == DOMAIN
assert err.value.translation_key == "invalid_device_id"
assert err.value.translation_placeholders == {"device_id": "nonexistent_device"}
async def test_device_not_belonging(
hass: HomeAssistant,
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
device_registry: dr.DeviceRegistry,
) -> None:
"""Test service errors when device belongs to a different integration."""
inject_bluetooth_service_info(hass, KEYPAD_VISION_INFO)
entry = mock_entry_encrypted_factory(sensor_type="keypad_vision")
entry.add_to_hass(hass)
with patch.multiple(
"homeassistant.components.switchbot.switchbot.SwitchbotKeypadVision",
update=AsyncMock(return_value=None),
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
other_entry = MockConfigEntry(domain="not_switchbot", data={}, title="Other")
other_entry.add_to_hass(hass)
device_entry = device_registry.async_get_or_create(
config_entry_id=other_entry.entry_id,
identifiers={("not_switchbot", "other_unique_id")},
name="Other device",
)
with pytest.raises(ServiceValidationError) as err:
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_PASSWORD,
{
ATTR_DEVICE_ID: device_entry.id,
"password": "123456",
},
blocking=True,
)
assert err.value.translation_domain == DOMAIN
assert err.value.translation_key == "device_not_belonging"
assert err.value.translation_placeholders == {"device_id": device_entry.id}
async def test_device_entry_not_loaded(
hass: HomeAssistant,
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
device_registry: dr.DeviceRegistry,
) -> None:
"""Test service errors when the config entry is not loaded."""
inject_bluetooth_service_info(hass, KEYPAD_VISION_INFO)
entry = mock_entry_encrypted_factory(sensor_type="keypad_vision")
entry.add_to_hass(hass)
with patch.multiple(
"homeassistant.components.switchbot.switchbot.SwitchbotKeypadVision",
update=AsyncMock(return_value=None),
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
second_entry = mock_entry_encrypted_factory(sensor_type="keypad_vision")
second_entry.add_to_hass(hass)
device_entry = device_registry.async_get_or_create(
config_entry_id=second_entry.entry_id,
identifiers={(DOMAIN, "not_loaded_unique_id")},
name="Not loaded device",
)
with pytest.raises(ServiceValidationError) as err:
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_PASSWORD,
{
ATTR_DEVICE_ID: device_entry.id,
"password": "123456",
},
blocking=True,
)
assert err.value.translation_domain == DOMAIN
assert err.value.translation_key == "device_entry_not_loaded"
assert err.value.translation_placeholders == {"device_id": device_entry.id}
async def test_service_unsupported_device(
hass: HomeAssistant,
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
device_registry: dr.DeviceRegistry,
) -> None:
"""Test service errors when the device does not support the service."""
inject_bluetooth_service_info(hass, SMART_THERMOSTAT_RADIATOR_SERVICE_INFO)
entry = mock_entry_encrypted_factory(sensor_type="smart_thermostat_radiator")
entry.add_to_hass(hass)
with patch.multiple(
"homeassistant.components.switchbot.switchbot.SwitchbotSmartThermostatRadiator",
update=AsyncMock(return_value=None),
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
device_entry = dr.async_entries_for_config_entry(device_registry, entry.entry_id)[0]
with pytest.raises(ServiceValidationError) as err:
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_PASSWORD,
{
ATTR_DEVICE_ID: device_entry.id,
"password": "123456",
},
blocking=True,
)
assert err.value.translation_domain == DOMAIN
assert err.value.translation_key == "not_keypad_vision_device"
async def test_device_without_config_entry_id(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test service errors when device has no config entry id."""
async_setup_services(hass)
entry = MockConfigEntry(domain=DOMAIN, data={}, title="No entry device")
entry.add_to_hass(hass)
with pytest.raises(ServiceValidationError) as err:
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_PASSWORD,
{
ATTR_DEVICE_ID: "abc",
"password": "123456",
},
blocking=True,
)
assert err.value.translation_domain == DOMAIN
assert err.value.translation_key == "invalid_device_id"
assert err.value.translation_placeholders == {"device_id": "abc"}