From 8a8be71f96f57cc67f43d9335d6e33af5bc9c96a Mon Sep 17 00:00:00 2001 From: dontinelli <73341522+dontinelli@users.noreply.github.com> Date: Wed, 18 Dec 2024 20:53:05 +0100 Subject: [PATCH] Add tests for cover and increase test coverage for slide_local (#133515) --- .../components/slide_local/quality_scale.yaml | 2 +- .../slide_local/snapshots/test_cover.ambr | 51 +++++ tests/components/slide_local/test_cover.py | 215 ++++++++++++++++++ tests/components/slide_local/test_init.py | 36 +++ 4 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 tests/components/slide_local/snapshots/test_cover.ambr create mode 100644 tests/components/slide_local/test_cover.py diff --git a/homeassistant/components/slide_local/quality_scale.yaml b/homeassistant/components/slide_local/quality_scale.yaml index 4833f19e2b2..c3ce12efd80 100644 --- a/homeassistant/components/slide_local/quality_scale.yaml +++ b/homeassistant/components/slide_local/quality_scale.yaml @@ -28,7 +28,7 @@ rules: action-exceptions: done reauthentication-flow: todo parallel-updates: done - test-coverage: todo + test-coverage: done integration-owner: done docs-installation-parameters: done docs-configuration-parameters: done diff --git a/tests/components/slide_local/snapshots/test_cover.ambr b/tests/components/slide_local/snapshots/test_cover.ambr new file mode 100644 index 00000000000..d9283618a47 --- /dev/null +++ b/tests/components/slide_local/snapshots/test_cover.ambr @@ -0,0 +1,51 @@ +# serializer version: 1 +# name: test_all_entities[cover.slide_bedroom-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'cover', + 'entity_category': None, + 'entity_id': 'cover.slide_bedroom', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'slide_local', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '1234567890ab', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[cover.slide_bedroom-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'assumed_state': True, + 'current_position': 100, + 'device_class': 'curtain', + 'friendly_name': 'slide bedroom', + 'supported_features': , + }), + 'context': , + 'entity_id': 'cover.slide_bedroom', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'open', + }) +# --- diff --git a/tests/components/slide_local/test_cover.py b/tests/components/slide_local/test_cover.py new file mode 100644 index 00000000000..e0e4a0741d8 --- /dev/null +++ b/tests/components/slide_local/test_cover.py @@ -0,0 +1,215 @@ +"""Tests for the Slide Local cover platform.""" + +from datetime import timedelta +from unittest.mock import AsyncMock + +from freezegun.api import FrozenDateTimeFactory +from goslideapi.goslideapi import ClientConnectionError +from syrupy import SnapshotAssertion + +from homeassistant.components.cover import ( + ATTR_POSITION, + DOMAIN as COVER_DOMAIN, + SERVICE_CLOSE_COVER, + SERVICE_OPEN_COVER, + SERVICE_SET_COVER_POSITION, + SERVICE_STOP_COVER, + CoverState, +) +from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import setup_platform +from .const import SLIDE_INFO_DATA + +from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform + + +async def test_all_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_slide_api: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test all entities.""" + await setup_platform(hass, mock_config_entry, [Platform.COVER]) + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +async def test_connection_error( + hass: HomeAssistant, + mock_slide_api: AsyncMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test connection error.""" + await setup_platform(hass, mock_config_entry, [Platform.COVER]) + + mock_slide_api.slide_info.side_effect = [ClientConnectionError, SLIDE_INFO_DATA] + + freezer.tick(delta=timedelta(minutes=1)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert hass.states.get("cover.slide_bedroom").state == STATE_UNAVAILABLE + + freezer.tick(delta=timedelta(minutes=2)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert hass.states.get("cover.slide_bedroom").state == CoverState.OPEN + + +async def test_state_change( + hass: HomeAssistant, + mock_slide_api: AsyncMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test connection error.""" + await setup_platform(hass, mock_config_entry, [Platform.COVER]) + + mock_slide_api.slide_info.side_effect = [ + dict(SLIDE_INFO_DATA, pos=0.0), + dict(SLIDE_INFO_DATA, pos=0.4), + dict(SLIDE_INFO_DATA, pos=1.0), + dict(SLIDE_INFO_DATA, pos=0.8), + ] + + freezer.tick(delta=timedelta(minutes=1)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert hass.states.get("cover.slide_bedroom").state == CoverState.OPEN + + freezer.tick(delta=timedelta(seconds=15)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert hass.states.get("cover.slide_bedroom").state == CoverState.CLOSING + + freezer.tick(delta=timedelta(seconds=15)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert hass.states.get("cover.slide_bedroom").state == CoverState.CLOSED + + freezer.tick(delta=timedelta(seconds=15)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert hass.states.get("cover.slide_bedroom").state == CoverState.OPENING + + +async def test_open_cover( + hass: HomeAssistant, + mock_slide_api: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test open cover.""" + await setup_platform(hass, mock_config_entry, [Platform.COVER]) + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_OPEN_COVER, + { + ATTR_ENTITY_ID: "cover.slide_bedroom", + }, + blocking=True, + ) + mock_slide_api.slide_open.assert_called_once() + + +async def test_close_cover( + hass: HomeAssistant, + mock_slide_api: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test close cover.""" + await setup_platform(hass, mock_config_entry, [Platform.COVER]) + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_CLOSE_COVER, + { + ATTR_ENTITY_ID: "cover.slide_bedroom", + }, + blocking=True, + ) + mock_slide_api.slide_close.assert_called_once() + + +async def test_stop_cover( + hass: HomeAssistant, + mock_slide_api: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test stop cover.""" + await setup_platform(hass, mock_config_entry, [Platform.COVER]) + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_STOP_COVER, + { + ATTR_ENTITY_ID: "cover.slide_bedroom", + }, + blocking=True, + ) + mock_slide_api.slide_stop.assert_called_once() + + +async def test_set_position( + hass: HomeAssistant, + mock_slide_api: AsyncMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test set cover position.""" + + await setup_platform(hass, mock_config_entry, [Platform.COVER]) + + mock_slide_api.slide_info.side_effect = [ + dict(SLIDE_INFO_DATA, pos=0.0), + dict(SLIDE_INFO_DATA, pos=1.0), + dict(SLIDE_INFO_DATA, pos=1.0), + dict(SLIDE_INFO_DATA, pos=0.0), + ] + + freezer.tick(delta=timedelta(seconds=15)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_POSITION, + {ATTR_ENTITY_ID: "cover.slide_bedroom", ATTR_POSITION: 1.0}, + blocking=True, + ) + + freezer.tick(delta=timedelta(seconds=15)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert hass.states.get("cover.slide_bedroom").state == CoverState.CLOSED + + freezer.tick(delta=timedelta(seconds=15)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_POSITION, + {ATTR_ENTITY_ID: "cover.slide_bedroom", ATTR_POSITION: 0.0}, + blocking=True, + ) + + freezer.tick(delta=timedelta(seconds=15)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert hass.states.get("cover.slide_bedroom").state == CoverState.OPEN + + assert len(mock_slide_api.slide_set_position.mock_calls) == 2 diff --git a/tests/components/slide_local/test_init.py b/tests/components/slide_local/test_init.py index 7b0a2d83164..ec9a12f9eeb 100644 --- a/tests/components/slide_local/test_init.py +++ b/tests/components/slide_local/test_init.py @@ -2,8 +2,10 @@ from unittest.mock import AsyncMock +from goslideapi.goslideapi import ClientConnectionError from syrupy import SnapshotAssertion +from homeassistant.config_entries import ConfigEntryState from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr @@ -27,3 +29,37 @@ async def test_device_info( ) assert device_entry is not None assert device_entry == snapshot + + +async def test_raise_config_entry_not_ready_when_offline( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_slide_api: AsyncMock, +) -> None: + """Config entry state is SETUP_RETRY when slide is offline.""" + + mock_slide_api.slide_info.side_effect = [ClientConnectionError, None] + + await setup_platform(hass, mock_config_entry, [Platform.COVER]) + await hass.async_block_till_done() + + assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + + assert len(hass.config_entries.flow.async_progress()) == 0 + + +async def test_raise_config_entry_not_ready_when_empty_data( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_slide_api: AsyncMock, +) -> None: + """Config entry state is SETUP_RETRY when slide is offline.""" + + mock_slide_api.slide_info.return_value = None + + await setup_platform(hass, mock_config_entry, [Platform.COVER]) + await hass.async_block_till_done() + + assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + + assert len(hass.config_entries.flow.async_progress()) == 0