Add Motionblinds Bluetooth full test coverage (#121878)
* Add tests * Fix entity test * Format * Fix sensor tests * Fix sensor tests * Fix sensor tests * Add init tests * Change service info * Rename test_sensor parameters * Removce ConfigEntryState.LOADED assertion * Remove platforms parameter from setup_platform * Rename setup_platform to setup_integration * Fixture for blind_type and mock_config_entry * Use mock for MotionDevice * Use mock for MotionDevice * Add type hint * Use Mock instead of patch * Use mock_config_entry fixture * Move constants to init * Fix entity_id name * Use fixture * Use fixtures instead of constants * Use display_name fixture * Rename mac to mac_code * Remove one patch * Use fixtures for mock_config_entry * Apply suggestion * Replace patch with mock * Replace patch with mock * Replace patch with mock * Fix * Use pytest.mark.usefixtures if parameter not used * Base mac code on address * Remove if statement from entity test --------- Co-authored-by: Joostlek <joostlek@outlook.com>pull/123613/head^2
parent
f72d9a2c02
commit
c674a25eba
|
@ -1 +1,16 @@
|
|||
"""Tests for the Motionblinds Bluetooth integration."""
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def setup_integration(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Mock a fully setup config entry."""
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
|
|
@ -3,21 +3,140 @@
|
|||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
from motionblindsble.const import MotionBlindType
|
||||
import pytest
|
||||
|
||||
TEST_MAC = "abcd"
|
||||
TEST_NAME = f"MOTION_{TEST_MAC.upper()}"
|
||||
TEST_ADDRESS = "test_adress"
|
||||
from homeassistant.components.bluetooth.models import BluetoothServiceInfoBleak
|
||||
from homeassistant.components.motionblinds_ble.const import (
|
||||
CONF_BLIND_TYPE,
|
||||
CONF_LOCAL_NAME,
|
||||
CONF_MAC_CODE,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.const import CONF_ADDRESS
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.bluetooth import generate_advertisement_data, generate_ble_device
|
||||
|
||||
|
||||
@pytest.fixture(name="motionblinds_ble_connect", autouse=True)
|
||||
def motion_blinds_connect_fixture(
|
||||
enable_bluetooth: None,
|
||||
@pytest.fixture
|
||||
def address() -> str:
|
||||
"""Address fixture."""
|
||||
return "cc:cc:cc:cc:cc:cc"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mac_code(address: str) -> str:
|
||||
"""MAC code fixture."""
|
||||
return "".join(address.split(":")[-3:-1]).upper()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def display_name(mac_code: str) -> str:
|
||||
"""Display name fixture."""
|
||||
return f"Motionblind {mac_code.upper()}"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def name(display_name: str) -> str:
|
||||
"""Name fixture."""
|
||||
return display_name.lower().replace(" ", "_")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def local_name(mac_code: str) -> str:
|
||||
"""Local name fixture."""
|
||||
return f"MOTION_{mac_code.upper()}"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def blind_type() -> MotionBlindType:
|
||||
"""Blind type fixture."""
|
||||
return MotionBlindType.ROLLER
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def service_info(local_name: str, address: str) -> BluetoothServiceInfoBleak:
|
||||
"""Service info fixture."""
|
||||
return BluetoothServiceInfoBleak(
|
||||
name=local_name,
|
||||
address=address,
|
||||
device=generate_ble_device(
|
||||
address=address,
|
||||
name=local_name,
|
||||
),
|
||||
rssi=-61,
|
||||
manufacturer_data={000: b"test"},
|
||||
service_data={
|
||||
"test": bytearray(b"0000"),
|
||||
},
|
||||
service_uuids=[
|
||||
"test",
|
||||
],
|
||||
source="local",
|
||||
advertisement=generate_advertisement_data(
|
||||
manufacturer_data={000: b"test"},
|
||||
service_uuids=["test"],
|
||||
),
|
||||
connectable=True,
|
||||
time=0,
|
||||
tx_power=-127,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_motion_device(
|
||||
blind_type: MotionBlindType, display_name: str
|
||||
) -> Generator[AsyncMock]:
|
||||
"""Mock a MotionDevice."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.motionblinds_ble.MotionDevice",
|
||||
autospec=True,
|
||||
) as mock_device:
|
||||
device = mock_device.return_value
|
||||
device.ble_device = Mock()
|
||||
device.display_name = display_name
|
||||
device.blind_type = blind_type
|
||||
yield device
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry(
|
||||
blind_type: MotionBlindType, address: str, display_name: str, mac_code: str
|
||||
) -> MockConfigEntry:
|
||||
"""Config entry fixture."""
|
||||
return MockConfigEntry(
|
||||
title="mock_title",
|
||||
domain=DOMAIN,
|
||||
unique_id=address,
|
||||
data={
|
||||
CONF_ADDRESS: address,
|
||||
CONF_LOCAL_NAME: display_name,
|
||||
CONF_MAC_CODE: mac_code,
|
||||
CONF_BLIND_TYPE: blind_type.name.lower(),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.motionblinds_ble.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def motionblinds_ble_connect(
|
||||
enable_bluetooth: None, local_name: str, address: str
|
||||
) -> Generator[tuple[AsyncMock, Mock]]:
|
||||
"""Mock motion blinds ble connection and entry setup."""
|
||||
device = Mock()
|
||||
device.name = TEST_NAME
|
||||
device.address = TEST_ADDRESS
|
||||
device.name = local_name
|
||||
device.address = address
|
||||
|
||||
bleak_scanner = AsyncMock()
|
||||
bleak_scanner.discover.return_value = [device]
|
||||
|
@ -31,9 +150,5 @@ def motion_blinds_connect_fixture(
|
|||
"homeassistant.components.motionblinds_ble.config_flow.bluetooth.async_get_scanner",
|
||||
return_value=bleak_scanner,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.motionblinds_ble.async_setup_entry",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
yield bleak_scanner, device
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
"""Tests for Motionblinds BLE buttons."""
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
|
||||
from homeassistant.components.motionblinds_ble.const import (
|
||||
ATTR_CONNECT,
|
||||
ATTR_DISCONNECT,
|
||||
ATTR_FAVORITE,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("motionblinds_ble_connect")
|
||||
@pytest.mark.parametrize(
|
||||
("button"),
|
||||
[
|
||||
ATTR_CONNECT,
|
||||
ATTR_DISCONNECT,
|
||||
ATTR_FAVORITE,
|
||||
],
|
||||
)
|
||||
async def test_button(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_motion_device: Mock,
|
||||
name: str,
|
||||
hass: HomeAssistant,
|
||||
button: str,
|
||||
) -> None:
|
||||
"""Test states of the button."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{ATTR_ENTITY_ID: f"button.{name}_{button}"},
|
||||
blocking=True,
|
||||
)
|
||||
getattr(mock_motion_device, button).assert_called_once()
|
|
@ -12,41 +12,19 @@ from homeassistant.const import CONF_ADDRESS
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from .conftest import TEST_ADDRESS, TEST_MAC, TEST_NAME
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.bluetooth import generate_advertisement_data, generate_ble_device
|
||||
|
||||
TEST_BLIND_TYPE = MotionBlindType.ROLLER.name.lower()
|
||||
|
||||
BLIND_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||
name=TEST_NAME,
|
||||
address=TEST_ADDRESS,
|
||||
device=generate_ble_device(
|
||||
address="cc:cc:cc:cc:cc:cc",
|
||||
name=TEST_NAME,
|
||||
),
|
||||
rssi=-61,
|
||||
manufacturer_data={000: b"test"},
|
||||
service_data={
|
||||
"test": bytearray(b"0000"),
|
||||
},
|
||||
service_uuids=[
|
||||
"test",
|
||||
],
|
||||
source="local",
|
||||
advertisement=generate_advertisement_data(
|
||||
manufacturer_data={000: b"test"},
|
||||
service_uuids=["test"],
|
||||
),
|
||||
connectable=True,
|
||||
time=0,
|
||||
tx_power=-127,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("motionblinds_ble_connect")
|
||||
async def test_config_flow_manual_success(hass: HomeAssistant) -> None:
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_config_flow_manual_success(
|
||||
hass: HomeAssistant,
|
||||
blind_type: MotionBlindType,
|
||||
mac_code: str,
|
||||
address: str,
|
||||
local_name: str,
|
||||
display_name: str,
|
||||
) -> None:
|
||||
"""Successful flow manually initialized by the user."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
const.DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
|
@ -57,28 +35,36 @@ async def test_config_flow_manual_success(hass: HomeAssistant) -> None:
|
|||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{const.CONF_MAC_CODE: TEST_MAC},
|
||||
{const.CONF_MAC_CODE: mac_code},
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{const.CONF_BLIND_TYPE: MotionBlindType.ROLLER.name.lower()},
|
||||
{const.CONF_BLIND_TYPE: blind_type.name.lower()},
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == f"Motionblind {TEST_MAC.upper()}"
|
||||
assert result["title"] == display_name
|
||||
assert result["data"] == {
|
||||
CONF_ADDRESS: TEST_ADDRESS,
|
||||
const.CONF_LOCAL_NAME: TEST_NAME,
|
||||
const.CONF_MAC_CODE: TEST_MAC.upper(),
|
||||
const.CONF_BLIND_TYPE: TEST_BLIND_TYPE,
|
||||
CONF_ADDRESS: address,
|
||||
const.CONF_LOCAL_NAME: local_name,
|
||||
const.CONF_MAC_CODE: mac_code,
|
||||
const.CONF_BLIND_TYPE: blind_type.name.lower(),
|
||||
}
|
||||
assert result["options"] == {}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("motionblinds_ble_connect")
|
||||
async def test_config_flow_manual_error_invalid_mac(hass: HomeAssistant) -> None:
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_config_flow_manual_error_invalid_mac(
|
||||
hass: HomeAssistant,
|
||||
mac_code: str,
|
||||
address: str,
|
||||
local_name: str,
|
||||
display_name: str,
|
||||
blind_type: MotionBlindType,
|
||||
) -> None:
|
||||
"""Invalid MAC code error flow manually initialized by the user."""
|
||||
|
||||
# Initialize
|
||||
|
@ -101,7 +87,7 @@ async def test_config_flow_manual_error_invalid_mac(hass: HomeAssistant) -> None
|
|||
# Recover
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{const.CONF_MAC_CODE: TEST_MAC},
|
||||
{const.CONF_MAC_CODE: mac_code},
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm"
|
||||
|
@ -109,15 +95,15 @@ async def test_config_flow_manual_error_invalid_mac(hass: HomeAssistant) -> None
|
|||
# Finish flow
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{const.CONF_BLIND_TYPE: MotionBlindType.ROLLER.name.lower()},
|
||||
{const.CONF_BLIND_TYPE: blind_type.name.lower()},
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == f"Motionblind {TEST_MAC.upper()}"
|
||||
assert result["title"] == display_name
|
||||
assert result["data"] == {
|
||||
CONF_ADDRESS: TEST_ADDRESS,
|
||||
const.CONF_LOCAL_NAME: TEST_NAME,
|
||||
const.CONF_MAC_CODE: TEST_MAC.upper(),
|
||||
const.CONF_BLIND_TYPE: TEST_BLIND_TYPE,
|
||||
CONF_ADDRESS: address,
|
||||
const.CONF_LOCAL_NAME: local_name,
|
||||
const.CONF_MAC_CODE: mac_code,
|
||||
const.CONF_BLIND_TYPE: blind_type.name.lower(),
|
||||
}
|
||||
assert result["options"] == {}
|
||||
|
||||
|
@ -125,6 +111,7 @@ async def test_config_flow_manual_error_invalid_mac(hass: HomeAssistant) -> None
|
|||
@pytest.mark.usefixtures("motionblinds_ble_connect")
|
||||
async def test_config_flow_manual_error_no_bluetooth_adapter(
|
||||
hass: HomeAssistant,
|
||||
mac_code: str,
|
||||
) -> None:
|
||||
"""No Bluetooth adapter error flow manually initialized by the user."""
|
||||
|
||||
|
@ -153,14 +140,21 @@ async def test_config_flow_manual_error_no_bluetooth_adapter(
|
|||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{const.CONF_MAC_CODE: TEST_MAC},
|
||||
{const.CONF_MAC_CODE: mac_code},
|
||||
)
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == const.ERROR_NO_BLUETOOTH_ADAPTER
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_config_flow_manual_error_could_not_find_motor(
|
||||
hass: HomeAssistant, motionblinds_ble_connect: tuple[AsyncMock, Mock]
|
||||
hass: HomeAssistant,
|
||||
motionblinds_ble_connect: tuple[AsyncMock, Mock],
|
||||
mac_code: str,
|
||||
local_name: str,
|
||||
display_name: str,
|
||||
address: str,
|
||||
blind_type: MotionBlindType,
|
||||
) -> None:
|
||||
"""Could not find motor error flow manually initialized by the user."""
|
||||
|
||||
|
@ -176,17 +170,17 @@ async def test_config_flow_manual_error_could_not_find_motor(
|
|||
motionblinds_ble_connect[1].name = "WRONG_NAME"
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{const.CONF_MAC_CODE: TEST_MAC},
|
||||
{const.CONF_MAC_CODE: mac_code},
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": const.ERROR_COULD_NOT_FIND_MOTOR}
|
||||
|
||||
# Recover
|
||||
motionblinds_ble_connect[1].name = TEST_NAME
|
||||
motionblinds_ble_connect[1].name = local_name
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{const.CONF_MAC_CODE: TEST_MAC},
|
||||
{const.CONF_MAC_CODE: mac_code},
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm"
|
||||
|
@ -194,21 +188,23 @@ async def test_config_flow_manual_error_could_not_find_motor(
|
|||
# Finish flow
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{const.CONF_BLIND_TYPE: MotionBlindType.ROLLER.name.lower()},
|
||||
{const.CONF_BLIND_TYPE: blind_type.name.lower()},
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == f"Motionblind {TEST_MAC.upper()}"
|
||||
assert result["title"] == display_name
|
||||
assert result["data"] == {
|
||||
CONF_ADDRESS: TEST_ADDRESS,
|
||||
const.CONF_LOCAL_NAME: TEST_NAME,
|
||||
const.CONF_MAC_CODE: TEST_MAC.upper(),
|
||||
const.CONF_BLIND_TYPE: TEST_BLIND_TYPE,
|
||||
CONF_ADDRESS: address,
|
||||
const.CONF_LOCAL_NAME: local_name,
|
||||
const.CONF_MAC_CODE: mac_code,
|
||||
const.CONF_BLIND_TYPE: blind_type.name.lower(),
|
||||
}
|
||||
assert result["options"] == {}
|
||||
|
||||
|
||||
async def test_config_flow_manual_error_no_devices_found(
|
||||
hass: HomeAssistant, motionblinds_ble_connect: tuple[AsyncMock, Mock]
|
||||
hass: HomeAssistant,
|
||||
motionblinds_ble_connect: tuple[AsyncMock, Mock],
|
||||
mac_code: str,
|
||||
) -> None:
|
||||
"""No devices found error flow manually initialized by the user."""
|
||||
|
||||
|
@ -224,19 +220,27 @@ async def test_config_flow_manual_error_no_devices_found(
|
|||
motionblinds_ble_connect[0].discover.return_value = []
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{const.CONF_MAC_CODE: TEST_MAC},
|
||||
{const.CONF_MAC_CODE: mac_code},
|
||||
)
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == const.ERROR_NO_DEVICES_FOUND
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("motionblinds_ble_connect")
|
||||
async def test_config_flow_bluetooth_success(hass: HomeAssistant) -> None:
|
||||
async def test_config_flow_bluetooth_success(
|
||||
hass: HomeAssistant,
|
||||
mac_code: str,
|
||||
service_info: BluetoothServiceInfoBleak,
|
||||
address: str,
|
||||
local_name: str,
|
||||
display_name: str,
|
||||
blind_type: MotionBlindType,
|
||||
) -> None:
|
||||
"""Successful bluetooth discovery flow."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
const.DOMAIN,
|
||||
context={"source": config_entries.SOURCE_BLUETOOTH},
|
||||
data=BLIND_SERVICE_INFO,
|
||||
data=service_info,
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
|
@ -244,36 +248,32 @@ async def test_config_flow_bluetooth_success(hass: HomeAssistant) -> None:
|
|||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{const.CONF_BLIND_TYPE: MotionBlindType.ROLLER.name.lower()},
|
||||
{const.CONF_BLIND_TYPE: blind_type.name.lower()},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == f"Motionblind {TEST_MAC.upper()}"
|
||||
assert result["title"] == display_name
|
||||
assert result["data"] == {
|
||||
CONF_ADDRESS: TEST_ADDRESS,
|
||||
const.CONF_LOCAL_NAME: TEST_NAME,
|
||||
const.CONF_MAC_CODE: TEST_MAC.upper(),
|
||||
const.CONF_BLIND_TYPE: TEST_BLIND_TYPE,
|
||||
CONF_ADDRESS: address,
|
||||
const.CONF_LOCAL_NAME: local_name,
|
||||
const.CONF_MAC_CODE: mac_code,
|
||||
const.CONF_BLIND_TYPE: blind_type.name.lower(),
|
||||
}
|
||||
assert result["options"] == {}
|
||||
|
||||
|
||||
async def test_options_flow(hass: HomeAssistant) -> None:
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_options_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the options flow."""
|
||||
entry = MockConfigEntry(
|
||||
domain=const.DOMAIN,
|
||||
unique_id="0123456789",
|
||||
data={
|
||||
const.CONF_BLIND_TYPE: MotionBlindType.ROLLER,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
"""Tests for Motionblinds BLE covers."""
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import Mock
|
||||
|
||||
from motionblindsble.const import MotionBlindType, MotionRunningType
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.cover import (
|
||||
ATTR_POSITION,
|
||||
ATTR_TILT_POSITION,
|
||||
DOMAIN as COVER_DOMAIN,
|
||||
SERVICE_CLOSE_COVER,
|
||||
SERVICE_CLOSE_COVER_TILT,
|
||||
SERVICE_OPEN_COVER,
|
||||
SERVICE_OPEN_COVER_TILT,
|
||||
SERVICE_SET_COVER_POSITION,
|
||||
SERVICE_SET_COVER_TILT_POSITION,
|
||||
SERVICE_STOP_COVER,
|
||||
SERVICE_STOP_COVER_TILT,
|
||||
STATE_CLOSED,
|
||||
STATE_CLOSING,
|
||||
STATE_OPEN,
|
||||
STATE_OPENING,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.parametrize("blind_type", [MotionBlindType.VENETIAN])
|
||||
@pytest.mark.parametrize(
|
||||
("service", "method", "kwargs"),
|
||||
[
|
||||
(SERVICE_OPEN_COVER, "open", {}),
|
||||
(SERVICE_CLOSE_COVER, "close", {}),
|
||||
(SERVICE_OPEN_COVER_TILT, "open_tilt", {}),
|
||||
(SERVICE_CLOSE_COVER_TILT, "close_tilt", {}),
|
||||
(SERVICE_SET_COVER_POSITION, "position", {ATTR_POSITION: 5}),
|
||||
(SERVICE_SET_COVER_TILT_POSITION, "tilt", {ATTR_TILT_POSITION: 10}),
|
||||
(SERVICE_STOP_COVER, "stop", {}),
|
||||
(SERVICE_STOP_COVER_TILT, "stop", {}),
|
||||
],
|
||||
)
|
||||
async def test_cover_service(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_motion_device: Mock,
|
||||
name: str,
|
||||
hass: HomeAssistant,
|
||||
service: str,
|
||||
method: str,
|
||||
kwargs: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test cover service."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: f"cover.{name}", **kwargs},
|
||||
blocking=True,
|
||||
)
|
||||
getattr(mock_motion_device, method).assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("running_type", "state"),
|
||||
[
|
||||
(None, "unknown"),
|
||||
(MotionRunningType.STILL, "unknown"),
|
||||
(MotionRunningType.OPENING, STATE_OPENING),
|
||||
(MotionRunningType.CLOSING, STATE_CLOSING),
|
||||
],
|
||||
)
|
||||
async def test_cover_update_running(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_motion_device: Mock,
|
||||
name: str,
|
||||
hass: HomeAssistant,
|
||||
running_type: str | None,
|
||||
state: str,
|
||||
) -> None:
|
||||
"""Test updating running status."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
async_update_running = mock_motion_device.register_running_callback.call_args[0][0]
|
||||
|
||||
async_update_running(running_type)
|
||||
assert hass.states.get(f"cover.{name}").state == state
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("position", "tilt", "state"),
|
||||
[
|
||||
(None, None, "unknown"),
|
||||
(0, 0, STATE_OPEN),
|
||||
(50, 90, STATE_OPEN),
|
||||
(100, 180, STATE_CLOSED),
|
||||
],
|
||||
)
|
||||
async def test_cover_update_position(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_motion_device: Mock,
|
||||
name: str,
|
||||
hass: HomeAssistant,
|
||||
position: int,
|
||||
tilt: int,
|
||||
state: str,
|
||||
) -> None:
|
||||
"""Test updating cover position and tilt."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
async_update_position = mock_motion_device.register_position_callback.call_args[0][
|
||||
0
|
||||
]
|
||||
|
||||
async_update_position(position, tilt)
|
||||
assert hass.states.get(f"cover.{name}").state == state
|
|
@ -0,0 +1,54 @@
|
|||
"""Tests for Motionblinds BLE entities."""
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.homeassistant import (
|
||||
DOMAIN as HA_DOMAIN,
|
||||
SERVICE_UPDATE_ENTITY,
|
||||
)
|
||||
from homeassistant.components.motionblinds_ble.const import (
|
||||
ATTR_CONNECT,
|
||||
ATTR_DISCONNECT,
|
||||
ATTR_FAVORITE,
|
||||
ATTR_SPEED,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("platform", "entity"),
|
||||
[
|
||||
(Platform.BUTTON, ATTR_CONNECT),
|
||||
(Platform.BUTTON, ATTR_DISCONNECT),
|
||||
(Platform.BUTTON, ATTR_FAVORITE),
|
||||
(Platform.SELECT, ATTR_SPEED),
|
||||
],
|
||||
)
|
||||
async def test_entity_update(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_motion_device: Mock,
|
||||
name: str,
|
||||
hass: HomeAssistant,
|
||||
platform: Platform,
|
||||
entity: str,
|
||||
) -> None:
|
||||
"""Test updating entity using homeassistant.update_entity."""
|
||||
|
||||
await async_setup_component(hass, HA_DOMAIN, {})
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
HA_DOMAIN,
|
||||
SERVICE_UPDATE_ENTITY,
|
||||
{ATTR_ENTITY_ID: f"{platform.name.lower()}.{name}_{entity}"},
|
||||
blocking=True,
|
||||
)
|
||||
getattr(mock_motion_device, "status_query").assert_called_once_with()
|
|
@ -0,0 +1,48 @@
|
|||
"""Tests for Motionblinds BLE init."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.bluetooth.models import BluetoothServiceInfoBleak
|
||||
from homeassistant.components.motionblinds_ble import options_update_listener
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||
|
||||
|
||||
async def test_options_update_listener(
|
||||
mock_config_entry: MockConfigEntry, hass: HomeAssistant
|
||||
) -> None:
|
||||
"""Test options_update_listener."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.motionblinds_ble.MotionDevice.set_custom_disconnect_time"
|
||||
) as mock_set_custom_disconnect_time,
|
||||
patch(
|
||||
"homeassistant.components.motionblinds_ble.MotionDevice.set_permanent_connection"
|
||||
) as set_permanent_connection,
|
||||
):
|
||||
await options_update_listener(hass, mock_config_entry)
|
||||
mock_set_custom_disconnect_time.assert_called_once()
|
||||
set_permanent_connection.assert_called_once()
|
||||
|
||||
|
||||
async def test_update_ble_device(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
hass: HomeAssistant,
|
||||
service_info: BluetoothServiceInfoBleak,
|
||||
) -> None:
|
||||
"""Test async_update_ble_device."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.motionblinds_ble.MotionDevice.set_ble_device"
|
||||
) as mock_set_ble_device:
|
||||
inject_bluetooth_service_info(hass, service_info)
|
||||
mock_set_ble_device.assert_called_once()
|
|
@ -0,0 +1,76 @@
|
|||
"""Tests for Motionblinds BLE selects."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
from unittest.mock import Mock
|
||||
|
||||
from motionblindsble.const import MotionSpeedLevel
|
||||
from motionblindsble.device import MotionDevice
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.motionblinds_ble.const import ATTR_SPEED
|
||||
from homeassistant.components.select import (
|
||||
DOMAIN as SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_OPTION
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("select", "args"), [(ATTR_SPEED, MotionSpeedLevel.HIGH)])
|
||||
async def test_select(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_motion_device: Mock,
|
||||
name: str,
|
||||
hass: HomeAssistant,
|
||||
select: str,
|
||||
args: Any,
|
||||
) -> None:
|
||||
"""Test select."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"select.{name}_{select}",
|
||||
ATTR_OPTION: MotionSpeedLevel.HIGH.value,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
getattr(mock_motion_device, select).assert_called_once_with(args)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("select", "register_callback", "value"),
|
||||
[
|
||||
(
|
||||
ATTR_SPEED,
|
||||
lambda device: device.register_speed_callback,
|
||||
MotionSpeedLevel.HIGH,
|
||||
)
|
||||
],
|
||||
)
|
||||
async def test_select_update(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_motion_device: Mock,
|
||||
name: str,
|
||||
hass: HomeAssistant,
|
||||
select: str,
|
||||
register_callback: Callable[[MotionDevice], Callable[..., None]],
|
||||
value: type[Enum],
|
||||
) -> None:
|
||||
"""Test select state update."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
update_func = register_callback(mock_motion_device).call_args[0][0]
|
||||
|
||||
update_func(value)
|
||||
assert hass.states.get(f"select.{name}_{select}").state == str(value.value)
|
|
@ -0,0 +1,107 @@
|
|||
"""Tests for Motionblinds BLE sensors."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
from unittest.mock import Mock
|
||||
|
||||
from motionblindsble.const import (
|
||||
MotionBlindType,
|
||||
MotionCalibrationType,
|
||||
MotionConnectionType,
|
||||
)
|
||||
from motionblindsble.device import MotionDevice
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.motionblinds_ble.const import (
|
||||
ATTR_BATTERY,
|
||||
ATTR_SIGNAL_STRENGTH,
|
||||
)
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.parametrize("blind_type", [MotionBlindType.CURTAIN])
|
||||
@pytest.mark.parametrize(
|
||||
("sensor", "register_callback", "initial_value", "args", "expected_value"),
|
||||
[
|
||||
(
|
||||
"connection_status",
|
||||
lambda device: device.register_connection_callback,
|
||||
MotionConnectionType.DISCONNECTED.value,
|
||||
[MotionConnectionType.CONNECTING],
|
||||
MotionConnectionType.CONNECTING.value,
|
||||
),
|
||||
(
|
||||
ATTR_BATTERY,
|
||||
lambda device: device.register_battery_callback,
|
||||
"unknown",
|
||||
[25, True, False],
|
||||
"25",
|
||||
),
|
||||
( # Battery unknown
|
||||
ATTR_BATTERY,
|
||||
lambda device: device.register_battery_callback,
|
||||
"unknown",
|
||||
[None, False, False],
|
||||
"unknown",
|
||||
),
|
||||
( # Wired
|
||||
ATTR_BATTERY,
|
||||
lambda device: device.register_battery_callback,
|
||||
"unknown",
|
||||
[255, False, True],
|
||||
"255",
|
||||
),
|
||||
( # Almost full
|
||||
ATTR_BATTERY,
|
||||
lambda device: device.register_battery_callback,
|
||||
"unknown",
|
||||
[99, False, False],
|
||||
"99",
|
||||
),
|
||||
( # Almost empty
|
||||
ATTR_BATTERY,
|
||||
lambda device: device.register_battery_callback,
|
||||
"unknown",
|
||||
[1, False, False],
|
||||
"1",
|
||||
),
|
||||
(
|
||||
"calibration_status",
|
||||
lambda device: device.register_calibration_callback,
|
||||
"unknown",
|
||||
[MotionCalibrationType.CALIBRATING],
|
||||
MotionCalibrationType.CALIBRATING.value,
|
||||
),
|
||||
(
|
||||
ATTR_SIGNAL_STRENGTH,
|
||||
lambda device: device.register_signal_strength_callback,
|
||||
"unknown",
|
||||
[-50],
|
||||
"-50",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_sensor(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_motion_device: Mock,
|
||||
name: str,
|
||||
hass: HomeAssistant,
|
||||
sensor: str,
|
||||
register_callback: Callable[[MotionDevice], Callable[..., None]],
|
||||
initial_value: str,
|
||||
args: list[Any],
|
||||
expected_value: str,
|
||||
) -> None:
|
||||
"""Test sensors."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get(f"{SENSOR_DOMAIN}.{name}_{sensor}").state == initial_value
|
||||
update_func = register_callback(mock_motion_device).call_args[0][0]
|
||||
update_func(*args)
|
||||
assert hass.states.get(f"{SENSOR_DOMAIN}.{name}_{sensor}").state == expected_value
|
Loading…
Reference in New Issue