Skip to content

Commit e591291

Browse files
authored
Add platform tests for aladdin_connect cover and sensor (home-assistant#164011)
1 parent cb99082 commit e591291

File tree

5 files changed

+302
-3
lines changed

5 files changed

+302
-3
lines changed

homeassistant/components/aladdin_connect/quality_scale.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ rules:
3737
log-when-unavailable: todo
3838
parallel-updates: todo
3939
reauthentication-flow: done
40-
test-coverage:
41-
status: todo
42-
comment: Platform tests for cover and sensor need to be implemented to reach 95% coverage.
40+
test-coverage: done
4341

4442
# Gold
4543
devices: done
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# serializer version: 1
2+
# name: test_cover_entities[cover.test_door-entry]
3+
EntityRegistryEntrySnapshot({
4+
'aliases': set({
5+
}),
6+
'area_id': None,
7+
'capabilities': None,
8+
'config_entry_id': <ANY>,
9+
'config_subentry_id': <ANY>,
10+
'device_class': None,
11+
'device_id': <ANY>,
12+
'disabled_by': None,
13+
'domain': 'cover',
14+
'entity_category': None,
15+
'entity_id': 'cover.test_door',
16+
'has_entity_name': True,
17+
'hidden_by': None,
18+
'icon': None,
19+
'id': <ANY>,
20+
'labels': set({
21+
}),
22+
'name': None,
23+
'object_id_base': None,
24+
'options': dict({
25+
}),
26+
'original_device_class': <CoverDeviceClass.GARAGE: 'garage'>,
27+
'original_icon': None,
28+
'original_name': None,
29+
'platform': 'aladdin_connect',
30+
'previous_unique_id': None,
31+
'suggested_object_id': None,
32+
'supported_features': <CoverEntityFeature: 3>,
33+
'translation_key': None,
34+
'unique_id': 'test_device_id-1',
35+
'unit_of_measurement': None,
36+
})
37+
# ---
38+
# name: test_cover_entities[cover.test_door-state]
39+
StateSnapshot({
40+
'attributes': ReadOnlyDict({
41+
'device_class': 'garage',
42+
'friendly_name': 'Test Door',
43+
'supported_features': <CoverEntityFeature: 3>,
44+
}),
45+
'context': <ANY>,
46+
'entity_id': 'cover.test_door',
47+
'last_changed': <ANY>,
48+
'last_reported': <ANY>,
49+
'last_updated': <ANY>,
50+
'state': 'closed',
51+
})
52+
# ---
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# serializer version: 1
2+
# name: test_sensor_entities[sensor.test_door_battery-entry]
3+
EntityRegistryEntrySnapshot({
4+
'aliases': set({
5+
}),
6+
'area_id': None,
7+
'capabilities': dict({
8+
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
9+
}),
10+
'config_entry_id': <ANY>,
11+
'config_subentry_id': <ANY>,
12+
'device_class': None,
13+
'device_id': <ANY>,
14+
'disabled_by': None,
15+
'domain': 'sensor',
16+
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
17+
'entity_id': 'sensor.test_door_battery',
18+
'has_entity_name': True,
19+
'hidden_by': None,
20+
'icon': None,
21+
'id': <ANY>,
22+
'labels': set({
23+
}),
24+
'name': None,
25+
'object_id_base': 'Battery',
26+
'options': dict({
27+
}),
28+
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
29+
'original_icon': None,
30+
'original_name': 'Battery',
31+
'platform': 'aladdin_connect',
32+
'previous_unique_id': None,
33+
'suggested_object_id': None,
34+
'supported_features': 0,
35+
'translation_key': None,
36+
'unique_id': 'test_device_id-1-battery_level',
37+
'unit_of_measurement': '%',
38+
})
39+
# ---
40+
# name: test_sensor_entities[sensor.test_door_battery-state]
41+
StateSnapshot({
42+
'attributes': ReadOnlyDict({
43+
'device_class': 'battery',
44+
'friendly_name': 'Test Door Battery',
45+
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
46+
'unit_of_measurement': '%',
47+
}),
48+
'context': <ANY>,
49+
'entity_id': 'sensor.test_door_battery',
50+
'last_changed': <ANY>,
51+
'last_reported': <ANY>,
52+
'last_updated': <ANY>,
53+
'state': '100',
54+
})
55+
# ---
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""Tests for the Aladdin Connect cover platform."""
2+
3+
from unittest.mock import AsyncMock, patch
4+
5+
import aiohttp
6+
from freezegun.api import FrozenDateTimeFactory
7+
import pytest
8+
from syrupy.assertion import SnapshotAssertion
9+
10+
from homeassistant.components.cover import (
11+
DOMAIN as COVER_DOMAIN,
12+
SERVICE_CLOSE_COVER,
13+
SERVICE_OPEN_COVER,
14+
)
15+
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform
16+
from homeassistant.core import HomeAssistant
17+
from homeassistant.helpers import entity_registry as er
18+
19+
from . import init_integration
20+
21+
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
22+
23+
ENTITY_ID = "cover.test_door"
24+
25+
26+
async def _setup(hass: HomeAssistant, entry: MockConfigEntry) -> None:
27+
"""Set up integration with only the cover platform."""
28+
with patch("homeassistant.components.aladdin_connect.PLATFORMS", [Platform.COVER]):
29+
await init_integration(hass, entry)
30+
31+
32+
async def test_cover_entities(
33+
hass: HomeAssistant,
34+
mock_config_entry: MockConfigEntry,
35+
entity_registry: er.EntityRegistry,
36+
snapshot: SnapshotAssertion,
37+
) -> None:
38+
"""Test the cover entity states and attributes."""
39+
await _setup(hass, mock_config_entry)
40+
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
41+
42+
43+
async def test_open_cover(
44+
hass: HomeAssistant,
45+
mock_config_entry: MockConfigEntry,
46+
mock_aladdin_connect_api: AsyncMock,
47+
) -> None:
48+
"""Test opening the cover."""
49+
await _setup(hass, mock_config_entry)
50+
await hass.services.async_call(
51+
COVER_DOMAIN,
52+
SERVICE_OPEN_COVER,
53+
{ATTR_ENTITY_ID: ENTITY_ID},
54+
blocking=True,
55+
)
56+
mock_aladdin_connect_api.open_door.assert_called_once_with("test_device_id", 1)
57+
58+
59+
async def test_close_cover(
60+
hass: HomeAssistant,
61+
mock_config_entry: MockConfigEntry,
62+
mock_aladdin_connect_api: AsyncMock,
63+
) -> None:
64+
"""Test closing the cover."""
65+
await _setup(hass, mock_config_entry)
66+
await hass.services.async_call(
67+
COVER_DOMAIN,
68+
SERVICE_CLOSE_COVER,
69+
{ATTR_ENTITY_ID: ENTITY_ID},
70+
blocking=True,
71+
)
72+
mock_aladdin_connect_api.close_door.assert_called_once_with("test_device_id", 1)
73+
74+
75+
@pytest.mark.parametrize(
76+
("status", "expected_closed", "expected_opening", "expected_closing"),
77+
[
78+
("closed", True, False, False),
79+
("open", False, False, False),
80+
("opening", False, True, False),
81+
("closing", False, False, True),
82+
],
83+
)
84+
async def test_cover_states(
85+
hass: HomeAssistant,
86+
mock_config_entry: MockConfigEntry,
87+
mock_aladdin_connect_api: AsyncMock,
88+
status: str,
89+
expected_closed: bool,
90+
expected_opening: bool,
91+
expected_closing: bool,
92+
) -> None:
93+
"""Test cover state properties."""
94+
mock_aladdin_connect_api.get_doors.return_value[0].status = status
95+
await _setup(hass, mock_config_entry)
96+
state = hass.states.get(ENTITY_ID)
97+
assert state is not None
98+
assert (state.state == "closed") == expected_closed
99+
assert (state.state == "opening") == expected_opening
100+
assert (state.state == "closing") == expected_closing
101+
102+
103+
async def test_cover_none_status(
104+
hass: HomeAssistant,
105+
mock_config_entry: MockConfigEntry,
106+
mock_aladdin_connect_api: AsyncMock,
107+
) -> None:
108+
"""Test cover state when status is None."""
109+
mock_aladdin_connect_api.get_doors.return_value[0].status = None
110+
await _setup(hass, mock_config_entry)
111+
state = hass.states.get(ENTITY_ID)
112+
assert state is not None
113+
assert state.state == "unknown"
114+
115+
116+
async def test_cover_unavailable(
117+
hass: HomeAssistant,
118+
mock_config_entry: MockConfigEntry,
119+
mock_aladdin_connect_api: AsyncMock,
120+
freezer: FrozenDateTimeFactory,
121+
) -> None:
122+
"""Test cover becomes unavailable when coordinator update fails."""
123+
await _setup(hass, mock_config_entry)
124+
state = hass.states.get(ENTITY_ID)
125+
assert state is not None
126+
assert state.state != STATE_UNAVAILABLE
127+
128+
mock_aladdin_connect_api.update_door.side_effect = aiohttp.ClientError()
129+
freezer.tick(15)
130+
async_fire_time_changed(hass)
131+
await hass.async_block_till_done()
132+
133+
state = hass.states.get(ENTITY_ID)
134+
assert state is not None
135+
assert state.state == STATE_UNAVAILABLE
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Tests for the Aladdin Connect sensor platform."""
2+
3+
from unittest.mock import AsyncMock, patch
4+
5+
import aiohttp
6+
from freezegun.api import FrozenDateTimeFactory
7+
import pytest
8+
from syrupy.assertion import SnapshotAssertion
9+
10+
from homeassistant.const import STATE_UNAVAILABLE, Platform
11+
from homeassistant.core import HomeAssistant
12+
from homeassistant.helpers import entity_registry as er
13+
14+
from . import init_integration
15+
16+
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
17+
18+
ENTITY_ID = "sensor.test_door_battery"
19+
20+
21+
async def _setup(hass: HomeAssistant, entry: MockConfigEntry) -> None:
22+
"""Set up integration with only the sensor platform."""
23+
with patch("homeassistant.components.aladdin_connect.PLATFORMS", [Platform.SENSOR]):
24+
await init_integration(hass, entry)
25+
26+
27+
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
28+
async def test_sensor_entities(
29+
hass: HomeAssistant,
30+
mock_config_entry: MockConfigEntry,
31+
entity_registry: er.EntityRegistry,
32+
snapshot: SnapshotAssertion,
33+
) -> None:
34+
"""Test the sensor entity states and attributes."""
35+
await _setup(hass, mock_config_entry)
36+
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
37+
38+
39+
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
40+
async def test_sensor_unavailable(
41+
hass: HomeAssistant,
42+
mock_config_entry: MockConfigEntry,
43+
mock_aladdin_connect_api: AsyncMock,
44+
freezer: FrozenDateTimeFactory,
45+
) -> None:
46+
"""Test sensor becomes unavailable when coordinator update fails."""
47+
await _setup(hass, mock_config_entry)
48+
state = hass.states.get(ENTITY_ID)
49+
assert state is not None
50+
assert state.state != STATE_UNAVAILABLE
51+
52+
mock_aladdin_connect_api.update_door.side_effect = aiohttp.ClientError()
53+
freezer.tick(15)
54+
async_fire_time_changed(hass)
55+
await hass.async_block_till_done()
56+
57+
state = hass.states.get(ENTITY_ID)
58+
assert state is not None
59+
assert state.state == STATE_UNAVAILABLE

0 commit comments

Comments
 (0)