From 437e54511620de7dc5b44a69d7f8682f8e6ae769 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Fri, 28 Feb 2025 20:45:47 +0100 Subject: [PATCH] Rework Comelit tests (#139475) * Rework Comelit tests * allign * restore coverage --- tests/components/comelit/__init__.py | 12 + tests/components/comelit/conftest.py | 104 +++++++ tests/components/comelit/const.py | 38 +-- .../comelit/snapshots/test_diagnostics.ambr | 3 +- tests/components/comelit/test_config_flow.py | 264 +++++++++++------- tests/components/comelit/test_diagnostics.py | 51 +--- 6 files changed, 304 insertions(+), 168 deletions(-) create mode 100644 tests/components/comelit/conftest.py diff --git a/tests/components/comelit/__init__.py b/tests/components/comelit/__init__.py index 916a684de4b..6475f500f01 100644 --- a/tests/components/comelit/__init__.py +++ b/tests/components/comelit/__init__.py @@ -1 +1,13 @@ """Tests for the Comelit SimpleHome integration.""" + +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + + +async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None: + """Fixture for setting up the component.""" + config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() diff --git a/tests/components/comelit/conftest.py b/tests/components/comelit/conftest.py new file mode 100644 index 00000000000..d2d450ccb8d --- /dev/null +++ b/tests/components/comelit/conftest.py @@ -0,0 +1,104 @@ +"""Configure tests for Comelit SimpleHome.""" + +import pytest + +from homeassistant.components.comelit.const import ( + BRIDGE, + DOMAIN as COMELIT_DOMAIN, + VEDO, +) +from homeassistant.const import CONF_HOST, CONF_PIN, CONF_PORT, CONF_TYPE + +from .const import ( + BRIDGE_DEVICE_QUERY, + BRIDGE_HOST, + BRIDGE_PIN, + BRIDGE_PORT, + VEDO_DEVICE_QUERY, + VEDO_HOST, + VEDO_PIN, + VEDO_PORT, +) + +from tests.common import AsyncMock, Generator, MockConfigEntry, patch + + +@pytest.fixture +def mock_setup_entry() -> Generator[AsyncMock]: + """Override async_setup_entry.""" + with patch( + "homeassistant.components.comelit.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + yield mock_setup_entry + + +@pytest.fixture +def mock_serial_bridge() -> Generator[AsyncMock]: + """Mock a Comelit serial bridge.""" + with ( + patch( + "homeassistant.components.comelit.coordinator.ComeliteSerialBridgeApi", + autospec=True, + ) as mock_comelit_serial_bridge, + patch( + "homeassistant.components.comelit.config_flow.ComeliteSerialBridgeApi", + new=mock_comelit_serial_bridge, + ), + ): + bridge = mock_comelit_serial_bridge.return_value + bridge.get_all_devices.return_value = BRIDGE_DEVICE_QUERY + bridge.host = BRIDGE_HOST + bridge.port = BRIDGE_PORT + bridge.pin = BRIDGE_PIN + yield bridge + + +@pytest.fixture +def mock_serial_bridge_config_entry() -> Generator[MockConfigEntry]: + """Mock a Comelit config entry for Comelit bridge.""" + return MockConfigEntry( + domain=COMELIT_DOMAIN, + data={ + CONF_HOST: BRIDGE_HOST, + CONF_PORT: BRIDGE_PORT, + CONF_PIN: BRIDGE_PIN, + CONF_TYPE: BRIDGE, + }, + ) + + +@pytest.fixture +def mock_vedo() -> Generator[AsyncMock]: + """Mock a Comelit vedo.""" + with ( + patch( + "homeassistant.components.comelit.coordinator.ComelitVedoApi", + autospec=True, + ) as mock_comelit_vedo, + patch( + "homeassistant.components.comelit.config_flow.ComelitVedoApi", + new=mock_comelit_vedo, + ), + ): + vedo = mock_comelit_vedo.return_value + vedo.get_all_areas_and_zones.return_value = VEDO_DEVICE_QUERY + vedo.host = VEDO_HOST + vedo.port = VEDO_PORT + vedo.pin = VEDO_PIN + vedo.type = VEDO + yield vedo + + +@pytest.fixture +def mock_vedo_config_entry() -> Generator[MockConfigEntry]: + """Mock a Comelit config entry for Comelit vedo.""" + return MockConfigEntry( + domain=COMELIT_DOMAIN, + data={ + CONF_HOST: VEDO_HOST, + CONF_PORT: VEDO_PORT, + CONF_PIN: VEDO_PIN, + CONF_TYPE: VEDO, + }, + ) diff --git a/tests/components/comelit/const.py b/tests/components/comelit/const.py index 92fdfebfa1d..3151b83d175 100644 --- a/tests/components/comelit/const.py +++ b/tests/components/comelit/const.py @@ -1,7 +1,10 @@ """Common stuff for Comelit SimpleHome tests.""" -from aiocomelit import ComelitVedoAreaObject, ComelitVedoZoneObject -from aiocomelit.api import ComelitSerialBridgeObject +from aiocomelit import ( + ComelitSerialBridgeObject, + ComelitVedoAreaObject, + ComelitVedoZoneObject, +) from aiocomelit.const import ( CLIMATE, COVER, @@ -9,37 +12,20 @@ from aiocomelit.const import ( LIGHT, OTHER, SCENARIO, - VEDO, WATT, AlarmAreaState, AlarmZoneState, ) -from homeassistant.components.comelit.const import DOMAIN -from homeassistant.const import CONF_DEVICES, CONF_HOST, CONF_PIN, CONF_PORT, CONF_TYPE +BRIDGE_HOST = "fake_bridge_host" +BRIDGE_PORT = 80 +BRIDGE_PIN = 1234 -MOCK_CONFIG = { - DOMAIN: { - CONF_DEVICES: [ - { - CONF_HOST: "fake_host", - CONF_PORT: 80, - CONF_PIN: 1234, - }, - { - CONF_HOST: "fake_vedo_host", - CONF_PORT: 8080, - CONF_PIN: 1234, - CONF_TYPE: VEDO, - }, - ] - } -} +VEDO_HOST = "fake_vedo_host" +VEDO_PORT = 8080 +VEDO_PIN = 5678 -MOCK_USER_BRIDGE_DATA = MOCK_CONFIG[DOMAIN][CONF_DEVICES][0] -MOCK_USER_VEDO_DATA = MOCK_CONFIG[DOMAIN][CONF_DEVICES][1] - -FAKE_PIN = 5678 +FAKE_PIN = 0000 BRIDGE_DEVICE_QUERY = { CLIMATE: {}, diff --git a/tests/components/comelit/snapshots/test_diagnostics.ambr b/tests/components/comelit/snapshots/test_diagnostics.ambr index 877f48a4611..b9891eb3209 100644 --- a/tests/components/comelit/snapshots/test_diagnostics.ambr +++ b/tests/components/comelit/snapshots/test_diagnostics.ambr @@ -57,9 +57,10 @@ }), 'entry': dict({ 'data': dict({ - 'host': 'fake_host', + 'host': 'fake_bridge_host', 'pin': '**REDACTED**', 'port': 80, + 'type': 'Serial bridge', }), 'disabled_by': None, 'discovery_keys': dict({ diff --git a/tests/components/comelit/test_config_flow.py b/tests/components/comelit/test_config_flow.py index eeaea0e41e9..dd1d1fb3836 100644 --- a/tests/components/comelit/test_config_flow.py +++ b/tests/components/comelit/test_config_flow.py @@ -1,59 +1,93 @@ """Tests for Comelit SimpleHome config flow.""" -from typing import Any -from unittest.mock import patch +from unittest.mock import AsyncMock from aiocomelit import CannotAuthenticate, CannotConnect +from aiocomelit.const import BRIDGE, VEDO import pytest from homeassistant.components.comelit.const import DOMAIN from homeassistant.config_entries import SOURCE_USER -from homeassistant.const import CONF_HOST, CONF_PIN, CONF_PORT +from homeassistant.const import CONF_HOST, CONF_PIN, CONF_PORT, CONF_TYPE from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType -from .const import FAKE_PIN, MOCK_USER_BRIDGE_DATA, MOCK_USER_VEDO_DATA +from .const import ( + BRIDGE_HOST, + BRIDGE_PIN, + BRIDGE_PORT, + FAKE_PIN, + VEDO_HOST, + VEDO_PIN, + VEDO_PORT, +) from tests.common import MockConfigEntry -@pytest.mark.parametrize( - ("class_api", "user_input"), - [ - ("ComeliteSerialBridgeApi", MOCK_USER_BRIDGE_DATA), - ("ComelitVedoApi", MOCK_USER_VEDO_DATA), - ], -) -async def test_full_flow( - hass: HomeAssistant, class_api: str, user_input: dict[str, Any] +async def test_flow_serial_bridge( + hass: HomeAssistant, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, ) -> None: """Test starting a flow by user.""" - with ( - patch( - f"aiocomelit.api.{class_api}.login", - ), - patch( - f"aiocomelit.api.{class_api}.logout", - ), - patch("homeassistant.components.comelit.async_setup_entry") as mock_setup_entry, - ): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER} - ) - assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "user" - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=user_input - ) - assert result["type"] is FlowResultType.CREATE_ENTRY - assert result["data"][CONF_HOST] == user_input[CONF_HOST] - assert result["data"][CONF_PORT] == user_input[CONF_PORT] - assert result["data"][CONF_PIN] == user_input[CONF_PIN] - assert not result["result"].unique_id - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" - assert mock_setup_entry.called + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: BRIDGE_HOST, + CONF_PORT: BRIDGE_PORT, + CONF_PIN: BRIDGE_PIN, + }, + ) + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_HOST: BRIDGE_HOST, + CONF_PORT: BRIDGE_PORT, + CONF_PIN: BRIDGE_PIN, + CONF_TYPE: BRIDGE, + } + assert not result["result"].unique_id + await hass.async_block_till_done() + + +async def test_flow_vedo( + hass: HomeAssistant, + mock_vedo: AsyncMock, + mock_vedo_config_entry: MockConfigEntry, +) -> None: + """Test starting a flow by user.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: VEDO_HOST, + CONF_PORT: VEDO_PORT, + CONF_PIN: VEDO_PIN, + CONF_TYPE: VEDO, + }, + ) + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_HOST: VEDO_HOST, + CONF_PORT: VEDO_PORT, + CONF_PIN: VEDO_PIN, + CONF_TYPE: VEDO, + } + assert not result["result"].unique_id + await hass.async_block_till_done() @pytest.mark.parametrize( @@ -64,7 +98,13 @@ async def test_full_flow( (ConnectionResetError, "unknown"), ], ) -async def test_exception_connection(hass: HomeAssistant, side_effect, error) -> None: +async def test_exception_connection( + hass: HomeAssistant, + mock_vedo: AsyncMock, + mock_vedo_config_entry: MockConfigEntry, + side_effect, + error, +) -> None: """Test starting a flow by user with a connection error.""" result = await hass.config_entries.flow.async_init( @@ -73,59 +113,65 @@ async def test_exception_connection(hass: HomeAssistant, side_effect, error) -> assert result.get("type") is FlowResultType.FORM assert result.get("step_id") == "user" - with ( - patch( - "aiocomelit.api.ComeliteSerialBridgeApi.login", - side_effect=side_effect, - ), - patch( - "aiocomelit.api.ComeliteSerialBridgeApi.logout", - ), - patch( - "homeassistant.components.comelit.async_setup_entry", - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=MOCK_USER_BRIDGE_DATA - ) + mock_vedo.login.side_effect = side_effect - assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "user" - assert result["errors"] is not None - assert result["errors"]["base"] == error + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: VEDO_HOST, + CONF_PORT: VEDO_PORT, + CONF_PIN: VEDO_PIN, + CONF_TYPE: VEDO, + }, + ) + + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {"base": error} + + mock_vedo.login.side_effect = None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: VEDO_HOST, + CONF_PORT: VEDO_PORT, + CONF_PIN: VEDO_PIN, + CONF_TYPE: VEDO, + }, + ) + + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == VEDO_HOST + assert result["data"] == { + CONF_HOST: VEDO_HOST, + CONF_PORT: VEDO_PORT, + CONF_PIN: VEDO_PIN, + CONF_TYPE: VEDO, + } -async def test_reauth_successful(hass: HomeAssistant) -> None: +async def test_reauth_successful( + hass: HomeAssistant, + mock_vedo: AsyncMock, + mock_vedo_config_entry: MockConfigEntry, +) -> None: """Test starting a reauthentication flow.""" - mock_config = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_BRIDGE_DATA) - mock_config.add_to_hass(hass) - result = await mock_config.start_reauth_flow(hass) + mock_vedo_config_entry.add_to_hass(hass) + result = await mock_vedo_config_entry.start_reauth_flow(hass) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "reauth_confirm" - with ( - patch( - "aiocomelit.api.ComeliteSerialBridgeApi.login", - ), - patch( - "aiocomelit.api.ComeliteSerialBridgeApi.logout", - ), - patch("homeassistant.components.comelit.async_setup_entry"), - patch("requests.get") as mock_request_get, - ): - mock_request_get.return_value.status_code = 200 + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_PIN: FAKE_PIN, + }, + ) - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={ - CONF_PIN: FAKE_PIN, - }, - ) - await hass.async_block_till_done() - - assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "reauth_successful" + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reauth_successful" @pytest.mark.parametrize( @@ -136,30 +182,40 @@ async def test_reauth_successful(hass: HomeAssistant) -> None: (ConnectionResetError, "unknown"), ], ) -async def test_reauth_not_successful(hass: HomeAssistant, side_effect, error) -> None: +async def test_reauth_not_successful( + hass: HomeAssistant, + mock_vedo: AsyncMock, + mock_vedo_config_entry: MockConfigEntry, + side_effect: Exception, + error: str, +) -> None: """Test starting a reauthentication flow but no connection found.""" - - mock_config = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_BRIDGE_DATA) - mock_config.add_to_hass(hass) - result = await mock_config.start_reauth_flow(hass) + mock_vedo_config_entry.add_to_hass(hass) + result = await mock_vedo_config_entry.start_reauth_flow(hass) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "reauth_confirm" - with ( - patch("aiocomelit.api.ComeliteSerialBridgeApi.login", side_effect=side_effect), - patch( - "aiocomelit.api.ComeliteSerialBridgeApi.logout", - ), - patch("homeassistant.components.comelit.async_setup_entry"), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={ - CONF_PIN: FAKE_PIN, - }, - ) + mock_vedo.login.side_effect = side_effect + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_PIN: FAKE_PIN, + }, + ) - assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "reauth_confirm" - assert result["errors"] is not None - assert result["errors"]["base"] == error + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "reauth_confirm" + assert result["errors"] == {"base": error} + + mock_vedo.login.side_effect = None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_PIN: VEDO_PIN, + }, + ) + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + assert mock_vedo_config_entry.data[CONF_PIN] == VEDO_PIN diff --git a/tests/components/comelit/test_diagnostics.py b/tests/components/comelit/test_diagnostics.py index 39d75af1152..cabcd0f4cac 100644 --- a/tests/components/comelit/test_diagnostics.py +++ b/tests/components/comelit/test_diagnostics.py @@ -2,21 +2,14 @@ from __future__ import annotations -from unittest.mock import patch +from unittest.mock import AsyncMock from syrupy import SnapshotAssertion from syrupy.filters import props -from homeassistant.components.comelit.const import DOMAIN -from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant -from .const import ( - BRIDGE_DEVICE_QUERY, - MOCK_USER_BRIDGE_DATA, - MOCK_USER_VEDO_DATA, - VEDO_DEVICE_QUERY, -) +from . import setup_integration from tests.common import MockConfigEntry from tests.components.diagnostics import get_diagnostics_for_config_entry @@ -25,25 +18,17 @@ from tests.typing import ClientSessionGenerator async def test_entry_diagnostics_bridge( hass: HomeAssistant, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, hass_client: ClientSessionGenerator, snapshot: SnapshotAssertion, ) -> None: """Test Bridge config entry diagnostics.""" - entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_BRIDGE_DATA) - entry.add_to_hass(hass) + await setup_integration(hass, mock_serial_bridge_config_entry) - with ( - patch("aiocomelit.api.ComeliteSerialBridgeApi.login"), - patch( - "aiocomelit.api.ComeliteSerialBridgeApi.get_all_devices", - return_value=BRIDGE_DEVICE_QUERY, - ), - ): - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - assert entry.state == ConfigEntryState.LOADED - assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot( + assert await get_diagnostics_for_config_entry( + hass, hass_client, mock_serial_bridge_config_entry + ) == snapshot( exclude=props( "entry_id", "created_at", @@ -54,25 +39,17 @@ async def test_entry_diagnostics_bridge( async def test_entry_diagnostics_vedo( hass: HomeAssistant, + mock_vedo: AsyncMock, + mock_vedo_config_entry: MockConfigEntry, hass_client: ClientSessionGenerator, snapshot: SnapshotAssertion, ) -> None: """Test Vedo System config entry diagnostics.""" - entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_VEDO_DATA) - entry.add_to_hass(hass) + await setup_integration(hass, mock_vedo_config_entry) - with ( - patch("aiocomelit.api.ComelitVedoApi.login"), - patch( - "aiocomelit.api.ComelitVedoApi.get_all_areas_and_zones", - return_value=VEDO_DEVICE_QUERY, - ), - ): - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - assert entry.state == ConfigEntryState.LOADED - assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot( + assert await get_diagnostics_for_config_entry( + hass, hass_client, mock_vedo_config_entry + ) == snapshot( exclude=props( "entry_id", "created_at",