mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 12:59:34 +00:00
clean up velux test fixtures (#156554)
This commit is contained in:
@@ -4,10 +4,10 @@ from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from pyvlx.lightening_device import LighteningDevice
|
||||
from pyvlx.opening_device import Blind, Window
|
||||
|
||||
from homeassistant.components.velux import DOMAIN
|
||||
from homeassistant.components.velux.binary_sensor import Window
|
||||
from homeassistant.components.velux.light import LighteningDevice
|
||||
from homeassistant.components.velux.scene import PyVLXScene as Scene
|
||||
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -26,22 +26,10 @@ def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_velux_client() -> Generator[AsyncMock]:
|
||||
"""Mock a Velux client."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.velux.config_flow.PyVLX",
|
||||
autospec=True,
|
||||
) as mock_client,
|
||||
):
|
||||
client = mock_client.return_value
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_user_config_entry() -> MockConfigEntry:
|
||||
"""Return the user config entry."""
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Return a mock config entry (unified fixture for all tests)."""
|
||||
return MockConfigEntry(
|
||||
entry_id="test_entry_id",
|
||||
domain=DOMAIN,
|
||||
title="127.0.0.1",
|
||||
data={
|
||||
@@ -66,7 +54,8 @@ def mock_discovered_config_entry() -> MockConfigEntry:
|
||||
)
|
||||
|
||||
|
||||
# fixtures for the binary sensor tests
|
||||
# various types of fixtures for specific node types
|
||||
# first the window
|
||||
@pytest.fixture
|
||||
def mock_window() -> AsyncMock:
|
||||
"""Create a mock Velux window with a rain sensor."""
|
||||
@@ -81,6 +70,27 @@ def mock_window() -> AsyncMock:
|
||||
return window
|
||||
|
||||
|
||||
# a blind
|
||||
@pytest.fixture
|
||||
def mock_blind() -> AsyncMock:
|
||||
"""Create a mock Velux blind (cover with tilt)."""
|
||||
blind = AsyncMock(spec=Blind, autospec=True)
|
||||
blind.name = "Test Blind"
|
||||
blind.serial_number = "4711"
|
||||
# Standard cover position (used by current_cover_position)
|
||||
blind.position = MagicMock(position_percent=40, closed=False)
|
||||
blind.is_opening = False
|
||||
blind.is_closing = False
|
||||
# Orientation/tilt-related attributes and methods
|
||||
blind.orientation = MagicMock(position_percent=25)
|
||||
blind.open_orientation = AsyncMock()
|
||||
blind.close_orientation = AsyncMock()
|
||||
blind.stop_orientation = AsyncMock()
|
||||
blind.set_orientation = AsyncMock()
|
||||
return blind
|
||||
|
||||
|
||||
# a light
|
||||
@pytest.fixture
|
||||
def mock_light() -> AsyncMock:
|
||||
"""Create a mock Velux light."""
|
||||
@@ -91,6 +101,56 @@ def mock_light() -> AsyncMock:
|
||||
return light
|
||||
|
||||
|
||||
# fixture to create all other cover types via parameterization
|
||||
@pytest.fixture
|
||||
def mock_cover_type(request: pytest.FixtureRequest) -> AsyncMock:
|
||||
"""Create a mock Velux cover of specified type."""
|
||||
cover = AsyncMock(spec=request.param, autospec=True)
|
||||
cover.name = f"Test {request.param.__name__}"
|
||||
cover.serial_number = f"serial_{request.param.__name__}"
|
||||
cover.is_opening = False
|
||||
cover.is_closing = False
|
||||
cover.position = MagicMock(position_percent=30, closed=False)
|
||||
return cover
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_pyvlx(
|
||||
mock_scene: AsyncMock,
|
||||
mock_light: AsyncMock,
|
||||
mock_window: AsyncMock,
|
||||
mock_blind: AsyncMock,
|
||||
request: pytest.FixtureRequest,
|
||||
) -> Generator[MagicMock]:
|
||||
"""Create the library mock and patch PyVLX in both component and config_flow.
|
||||
|
||||
Tests can parameterize this fixture with the name of a node fixture to include
|
||||
(e.g., "mock_window", "mock_blind", "mock_light", or "mock_cover_type").
|
||||
If no parameter is provided, an empty node list is used.
|
||||
"""
|
||||
|
||||
pyvlx = MagicMock()
|
||||
|
||||
if hasattr(request, "param"):
|
||||
pyvlx.nodes = [request.getfixturevalue(request.param)]
|
||||
else:
|
||||
pyvlx.nodes = [mock_light, mock_blind, mock_window, mock_cover_type]
|
||||
|
||||
pyvlx.scenes = [mock_scene]
|
||||
|
||||
# Async methods invoked by the integration/config flow
|
||||
pyvlx.load_scenes = AsyncMock()
|
||||
pyvlx.load_nodes = AsyncMock()
|
||||
pyvlx.connect = AsyncMock()
|
||||
pyvlx.disconnect = AsyncMock()
|
||||
|
||||
with (
|
||||
patch("homeassistant.components.velux.PyVLX", return_value=pyvlx),
|
||||
patch("homeassistant.components.velux.config_flow.PyVLX", return_value=pyvlx),
|
||||
):
|
||||
yield pyvlx
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_scene() -> AsyncMock:
|
||||
"""Create a mock Velux scene."""
|
||||
@@ -101,40 +161,12 @@ def mock_scene() -> AsyncMock:
|
||||
return scene
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_pyvlx(
|
||||
mock_window: MagicMock, mock_light: MagicMock, mock_scene: AsyncMock
|
||||
) -> Generator[MagicMock]:
|
||||
"""Create the library mock and patch PyVLX."""
|
||||
pyvlx = MagicMock()
|
||||
pyvlx.nodes = [mock_window, mock_light]
|
||||
pyvlx.scenes = [mock_scene]
|
||||
pyvlx.load_scenes = AsyncMock()
|
||||
pyvlx.load_nodes = AsyncMock()
|
||||
pyvlx.disconnect = AsyncMock()
|
||||
|
||||
with patch("homeassistant.components.velux.PyVLX", return_value=pyvlx):
|
||||
yield pyvlx
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Return a mock config entry."""
|
||||
return MockConfigEntry(
|
||||
entry_id="test_entry_id",
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "testhost",
|
||||
CONF_PASSWORD: "testpw",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Fixture to set up the integration for testing, needs platform fixture, to be defined in each test file
|
||||
@pytest.fixture
|
||||
async def setup_integration(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_pyvlx: MagicMock,
|
||||
mock_pyvlx: AsyncMock,
|
||||
platform: Platform,
|
||||
) -> None:
|
||||
"""Set up the integration for testing."""
|
||||
|
||||
@@ -1,5 +1,261 @@
|
||||
# serializer version: 1
|
||||
# name: test_cover_setup[cover.test_window-entry]
|
||||
# name: test_blind_entity_setup[mock_blind][cover.test_blind-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'cover',
|
||||
'entity_category': None,
|
||||
'entity_id': 'cover.test_blind',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <CoverDeviceClass.BLIND: 'blind'>,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'velux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <CoverEntityFeature: 255>,
|
||||
'translation_key': None,
|
||||
'unique_id': '4711',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_blind_entity_setup[mock_blind][cover.test_blind-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_position': 60,
|
||||
'current_tilt_position': 75,
|
||||
'device_class': 'blind',
|
||||
'friendly_name': 'Test Blind',
|
||||
'supported_features': <CoverEntityFeature: 255>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'cover.test_blind',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'open',
|
||||
})
|
||||
# ---
|
||||
# name: test_cover_entity_setup[mock_cover_type-Awning][cover.test_awning-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'cover',
|
||||
'entity_category': None,
|
||||
'entity_id': 'cover.test_awning',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <CoverDeviceClass.AWNING: 'awning'>,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'velux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
'translation_key': None,
|
||||
'unique_id': 'serial_Awning',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_cover_entity_setup[mock_cover_type-Awning][cover.test_awning-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_position': 70,
|
||||
'device_class': 'awning',
|
||||
'friendly_name': 'Test Awning',
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'cover.test_awning',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'open',
|
||||
})
|
||||
# ---
|
||||
# name: test_cover_entity_setup[mock_cover_type-GarageDoor][cover.test_garagedoor-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'cover',
|
||||
'entity_category': None,
|
||||
'entity_id': 'cover.test_garagedoor',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <CoverDeviceClass.GARAGE: 'garage'>,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'velux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
'translation_key': None,
|
||||
'unique_id': 'serial_GarageDoor',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_cover_entity_setup[mock_cover_type-GarageDoor][cover.test_garagedoor-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_position': 70,
|
||||
'device_class': 'garage',
|
||||
'friendly_name': 'Test GarageDoor',
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'cover.test_garagedoor',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'open',
|
||||
})
|
||||
# ---
|
||||
# name: test_cover_entity_setup[mock_cover_type-Gate][cover.test_gate-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'cover',
|
||||
'entity_category': None,
|
||||
'entity_id': 'cover.test_gate',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <CoverDeviceClass.GATE: 'gate'>,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'velux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
'translation_key': None,
|
||||
'unique_id': 'serial_Gate',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_cover_entity_setup[mock_cover_type-Gate][cover.test_gate-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_position': 70,
|
||||
'device_class': 'gate',
|
||||
'friendly_name': 'Test Gate',
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'cover.test_gate',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'open',
|
||||
})
|
||||
# ---
|
||||
# name: test_cover_entity_setup[mock_cover_type-RollerShutter][cover.test_rollershutter-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'cover',
|
||||
'entity_category': None,
|
||||
'entity_id': 'cover.test_rollershutter',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <CoverDeviceClass.SHUTTER: 'shutter'>,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'velux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
'translation_key': None,
|
||||
'unique_id': 'serial_RollerShutter',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_cover_entity_setup[mock_cover_type-RollerShutter][cover.test_rollershutter-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_position': 70,
|
||||
'device_class': 'shutter',
|
||||
'friendly_name': 'Test RollerShutter',
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'cover.test_rollershutter',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'open',
|
||||
})
|
||||
# ---
|
||||
# name: test_cover_entity_setup[mock_cover_type-Window][cover.test_window-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -30,11 +286,11 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
'translation_key': None,
|
||||
'unique_id': '123456789',
|
||||
'unique_id': 'serial_Window',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_cover_setup[cover.test_window-state]
|
||||
# name: test_cover_entity_setup[mock_cover_type-Window][cover.test_window-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_position': 70,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Tests for the Velux binary sensor platform."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
@@ -16,22 +16,21 @@ from . import update_polled_entities
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def platform() -> Platform:
|
||||
"""Fixture to specify platform to test."""
|
||||
return Platform.BINARY_SENSOR
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_integration")
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
@pytest.mark.usefixtures("mock_pyvlx")
|
||||
async def test_rain_sensor_state(
|
||||
hass: HomeAssistant,
|
||||
mock_window: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test the rain sensor."""
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
with patch("homeassistant.components.velux.PLATFORMS", [Platform.BINARY_SENSOR]):
|
||||
# setup config entry
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
test_entity_id = "binary_sensor.test_window_rain_sensor"
|
||||
|
||||
# simulate no rain detected
|
||||
@@ -62,8 +61,8 @@ async def test_rain_sensor_state(
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_integration")
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
@pytest.mark.usefixtures("mock_pyvlx")
|
||||
async def test_rain_sensor_device_association(
|
||||
hass: HomeAssistant,
|
||||
mock_window: MagicMock,
|
||||
@@ -73,11 +72,6 @@ async def test_rain_sensor_device_association(
|
||||
) -> None:
|
||||
"""Test the rain sensor is properly associated with its device."""
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
with patch("homeassistant.components.velux.PLATFORMS", [Platform.BINARY_SENSOR]):
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
test_entity_id = "binary_sensor.test_window_rain_sensor"
|
||||
|
||||
# Verify entity exists
|
||||
|
||||
@@ -26,7 +26,7 @@ DHCP_DISCOVERY = DhcpServiceInfo(
|
||||
async def test_user_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_velux_client: AsyncMock,
|
||||
mock_pyvlx: AsyncMock,
|
||||
) -> None:
|
||||
"""Test starting a flow by user with valid values."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@@ -53,8 +53,8 @@ async def test_user_flow(
|
||||
}
|
||||
assert not result["result"].unique_id
|
||||
|
||||
mock_velux_client.disconnect.assert_called_once()
|
||||
mock_velux_client.connect.assert_called_once()
|
||||
mock_pyvlx.disconnect.assert_called_once()
|
||||
mock_pyvlx.connect.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -66,14 +66,14 @@ async def test_user_flow(
|
||||
)
|
||||
async def test_user_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_velux_client: AsyncMock,
|
||||
mock_pyvlx: AsyncMock,
|
||||
exception: Exception,
|
||||
error: str,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test starting a flow by user but with exceptions."""
|
||||
|
||||
mock_velux_client.connect.side_effect = exception
|
||||
mock_pyvlx.connect.side_effect = exception
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
@@ -95,9 +95,9 @@ async def test_user_errors(
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": error}
|
||||
|
||||
mock_velux_client.connect.assert_called_once()
|
||||
mock_pyvlx.connect.assert_called_once()
|
||||
|
||||
mock_velux_client.connect.side_effect = None
|
||||
mock_pyvlx.connect.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -112,11 +112,11 @@ async def test_user_errors(
|
||||
|
||||
async def test_user_flow_duplicate_entry(
|
||||
hass: HomeAssistant,
|
||||
mock_user_config_entry: MockConfigEntry,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test initialized flow with a duplicate entry."""
|
||||
mock_user_config_entry.add_to_hass(hass)
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
@@ -140,7 +140,7 @@ async def test_user_flow_duplicate_entry(
|
||||
|
||||
async def test_dhcp_discovery(
|
||||
hass: HomeAssistant,
|
||||
mock_velux_client: AsyncMock,
|
||||
mock_pyvlx: AsyncMock,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test we can setup from dhcp discovery."""
|
||||
@@ -168,8 +168,8 @@ async def test_dhcp_discovery(
|
||||
}
|
||||
assert result["result"].unique_id == "VELUX_KLF_ABCD"
|
||||
|
||||
mock_velux_client.disconnect.assert_called()
|
||||
mock_velux_client.connect.assert_called()
|
||||
mock_pyvlx.disconnect.assert_called()
|
||||
mock_pyvlx.connect.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -181,7 +181,7 @@ async def test_dhcp_discovery(
|
||||
)
|
||||
async def test_dhcp_discovery_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_velux_client: AsyncMock,
|
||||
mock_pyvlx: AsyncMock,
|
||||
exception: Exception,
|
||||
error: str,
|
||||
mock_setup_entry: AsyncMock,
|
||||
@@ -196,7 +196,7 @@ async def test_dhcp_discovery_errors(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "discovery_confirm"
|
||||
|
||||
mock_velux_client.connect.side_effect = exception
|
||||
mock_pyvlx.connect.side_effect = exception
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -207,7 +207,7 @@ async def test_dhcp_discovery_errors(
|
||||
assert result["step_id"] == "discovery_confirm"
|
||||
assert result["errors"] == {"base": error}
|
||||
|
||||
mock_velux_client.connect.side_effect = None
|
||||
mock_pyvlx.connect.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -226,7 +226,7 @@ async def test_dhcp_discovery_errors(
|
||||
|
||||
async def test_dhcp_discovery_already_configured(
|
||||
hass: HomeAssistant,
|
||||
mock_velux_client: AsyncMock,
|
||||
mock_pyvlx: AsyncMock,
|
||||
mock_discovered_config_entry: MockConfigEntry,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
@@ -245,15 +245,15 @@ async def test_dhcp_discovery_already_configured(
|
||||
async def test_dhcp_discover_unique_id(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_velux_client: AsyncMock,
|
||||
mock_user_config_entry: MockConfigEntry,
|
||||
mock_pyvlx: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test dhcp discovery when already configured."""
|
||||
mock_user_config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(mock_user_config_entry.entry_id)
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
|
||||
assert mock_user_config_entry.state is ConfigEntryState.LOADED
|
||||
assert mock_user_config_entry.unique_id is None
|
||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||
assert mock_config_entry.unique_id is None
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
@@ -263,20 +263,20 @@ async def test_dhcp_discover_unique_id(
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
assert mock_user_config_entry.unique_id == "VELUX_KLF_ABCD"
|
||||
assert mock_config_entry.unique_id == "VELUX_KLF_ABCD"
|
||||
|
||||
|
||||
async def test_dhcp_discovery_not_loaded(
|
||||
hass: HomeAssistant,
|
||||
mock_velux_client: AsyncMock,
|
||||
mock_user_config_entry: MockConfigEntry,
|
||||
mock_pyvlx: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test dhcp discovery when entry with same host not loaded."""
|
||||
mock_user_config_entry.add_to_hass(hass)
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
assert mock_user_config_entry.state is not ConfigEntryState.LOADED
|
||||
assert mock_user_config_entry.unique_id is None
|
||||
assert mock_config_entry.state is not ConfigEntryState.LOADED
|
||||
assert mock_config_entry.unique_id is None
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
@@ -286,4 +286,4 @@ async def test_dhcp_discovery_not_loaded(
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
assert mock_user_config_entry.unique_id is None
|
||||
assert mock_config_entry.unique_id is None
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from pyvlx.opening_device import Awning, GarageDoor, Gate, RollerShutter, Window
|
||||
|
||||
from homeassistant.components.velux import DOMAIN
|
||||
from homeassistant.const import STATE_CLOSED, STATE_OPEN, Platform
|
||||
@@ -21,14 +22,14 @@ def platform() -> Platform:
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_integration")
|
||||
async def test_cover_setup(
|
||||
@pytest.mark.parametrize("mock_pyvlx", ["mock_blind"], indirect=True)
|
||||
async def test_blind_entity_setup(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Snapshot the cover entity (registry + state)."""
|
||||
"""Snapshot the entity and validate registry metadata."""
|
||||
await snapshot_platform(
|
||||
hass,
|
||||
entity_registry,
|
||||
@@ -36,31 +37,64 @@ async def test_cover_setup(
|
||||
mock_config_entry.entry_id,
|
||||
)
|
||||
|
||||
# Get the cover entity setup and test device association
|
||||
|
||||
@pytest.mark.usefixtures("setup_integration")
|
||||
@pytest.mark.usefixtures("mock_cover_type")
|
||||
@pytest.mark.parametrize(
|
||||
"mock_cover_type", [Awning, GarageDoor, Gate, RollerShutter, Window], indirect=True
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"mock_pyvlx",
|
||||
["mock_cover_type"],
|
||||
indirect=True,
|
||||
)
|
||||
async def test_cover_entity_setup(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Snapshot the entity and validate entity metadata."""
|
||||
await snapshot_platform(
|
||||
hass,
|
||||
entity_registry,
|
||||
snapshot,
|
||||
mock_config_entry.entry_id,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_integration")
|
||||
async def test_cover_device_association(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
"""Test the cover entity device association."""
|
||||
|
||||
entity_entries = er.async_entries_for_config_entry(
|
||||
entity_registry, mock_config_entry.entry_id
|
||||
)
|
||||
assert len(entity_entries) == 1
|
||||
entry = entity_entries[0]
|
||||
assert len(entity_entries) >= 1
|
||||
|
||||
assert entry.device_id is not None
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry is not None
|
||||
assert (DOMAIN, f"{123456789}") in device_entry.identifiers
|
||||
assert device_entry.via_device_id is not None
|
||||
via_device_entry = device_registry.async_get(device_entry.via_device_id)
|
||||
assert via_device_entry is not None
|
||||
assert (
|
||||
DOMAIN,
|
||||
f"gateway_{mock_config_entry.entry_id}",
|
||||
) in via_device_entry.identifiers
|
||||
for entry in entity_entries:
|
||||
assert entry.device_id is not None
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry is not None
|
||||
assert (DOMAIN, entry.unique_id) in device_entry.identifiers
|
||||
assert device_entry.via_device_id is not None
|
||||
via_device_entry = device_registry.async_get(device_entry.via_device_id)
|
||||
assert via_device_entry is not None
|
||||
assert (
|
||||
DOMAIN,
|
||||
f"gateway_{mock_config_entry.entry_id}",
|
||||
) in via_device_entry.identifiers
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_integration")
|
||||
async def test_cover_closed(
|
||||
hass: HomeAssistant,
|
||||
mock_window: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the cover closed state."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user