Add tests for BMW binary_sensor and lock (#118436)

* BMW: Add tests for binary_sensor & lock

* Use entity_registry_enabled_by_default fixture

* Update tests/components/bmw_connected_drive/test_binary_sensor.py

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* Move fixtures to decorator

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* Use fixture decorators if possible

* Fix rebase

* Spelling adjustments

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* Use snapshot_platform helper

* Spelling

* Remove comment

---------

Co-authored-by: Richard <rikroe@users.noreply.github.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
This commit is contained in:
Richard Kroegel 2024-06-04 15:08:15 +02:00 committed by GitHub
parent 67e9e90346
commit 1eb13b48a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 1902 additions and 5 deletions

View file

@ -149,12 +149,7 @@ omit =
homeassistant/components/bloomsky/*
homeassistant/components/bluesound/*
homeassistant/components/bluetooth_tracker/*
homeassistant/components/bmw_connected_drive/__init__.py
homeassistant/components/bmw_connected_drive/binary_sensor.py
homeassistant/components/bmw_connected_drive/coordinator.py
homeassistant/components/bmw_connected_drive/lock.py
homeassistant/components/bmw_connected_drive/notify.py
homeassistant/components/bmw_connected_drive/sensor.py
homeassistant/components/bosch_shc/__init__.py
homeassistant/components/bosch_shc/binary_sensor.py
homeassistant/components/bosch_shc/cover.py

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,205 @@
# serializer version: 1
# name: test_entity_state_attrs[lock.i3_rex_lock-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'lock',
'entity_category': None,
'entity_id': 'lock.i3_rex_lock',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Lock',
'platform': 'bmw_connected_drive',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'lock',
'unique_id': 'WBY00000000REXI01-lock',
'unit_of_measurement': None,
})
# ---
# name: test_entity_state_attrs[lock.i3_rex_lock-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by MyBMW',
'car': 'i3 (+ REX)',
'door_lock_state': 'UNLOCKED',
'friendly_name': 'i3 (+ REX) Lock',
'supported_features': <LockEntityFeature: 0>,
'vin': 'WBY00000000REXI01',
}),
'context': <ANY>,
'entity_id': 'lock.i3_rex_lock',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unlocked',
})
# ---
# name: test_entity_state_attrs[lock.i4_edrive40_lock-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'lock',
'entity_category': None,
'entity_id': 'lock.i4_edrive40_lock',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Lock',
'platform': 'bmw_connected_drive',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'lock',
'unique_id': 'WBA00000000DEMO02-lock',
'unit_of_measurement': None,
})
# ---
# name: test_entity_state_attrs[lock.i4_edrive40_lock-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by MyBMW',
'car': 'i4 eDrive40',
'door_lock_state': 'LOCKED',
'friendly_name': 'i4 eDrive40 Lock',
'supported_features': <LockEntityFeature: 0>,
'vin': 'WBA00000000DEMO02',
}),
'context': <ANY>,
'entity_id': 'lock.i4_edrive40_lock',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'locked',
})
# ---
# name: test_entity_state_attrs[lock.ix_xdrive50_lock-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'lock',
'entity_category': None,
'entity_id': 'lock.ix_xdrive50_lock',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Lock',
'platform': 'bmw_connected_drive',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'lock',
'unique_id': 'WBA00000000DEMO01-lock',
'unit_of_measurement': None,
})
# ---
# name: test_entity_state_attrs[lock.ix_xdrive50_lock-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by MyBMW',
'car': 'iX xDrive50',
'door_lock_state': 'LOCKED',
'friendly_name': 'iX xDrive50 Lock',
'supported_features': <LockEntityFeature: 0>,
'vin': 'WBA00000000DEMO01',
}),
'context': <ANY>,
'entity_id': 'lock.ix_xdrive50_lock',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'locked',
})
# ---
# name: test_entity_state_attrs[lock.m340i_xdrive_lock-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'lock',
'entity_category': None,
'entity_id': 'lock.m340i_xdrive_lock',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Lock',
'platform': 'bmw_connected_drive',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'lock',
'unique_id': 'WBA00000000DEMO03-lock',
'unit_of_measurement': None,
})
# ---
# name: test_entity_state_attrs[lock.m340i_xdrive_lock-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by MyBMW',
'car': 'M340i xDrive',
'door_lock_state': 'LOCKED',
'friendly_name': 'M340i xDrive Lock',
'supported_features': <LockEntityFeature: 0>,
'vin': 'WBA00000000DEMO03',
}),
'context': <ANY>,
'entity_id': 'lock.m340i_xdrive_lock',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'locked',
})
# ---

View file

@ -0,0 +1,35 @@
"""Test BMW binary sensors."""
from unittest.mock import patch
from freezegun import freeze_time
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_mocked_integration
from tests.common import snapshot_platform
@freeze_time("2023-06-22 10:30:00+00:00")
@pytest.mark.usefixtures("bmw_fixture")
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_entity_state_attrs(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
) -> None:
"""Test binary sensor states and attributes."""
# Setup component
with patch(
"homeassistant.components.bmw_connected_drive.PLATFORMS",
[Platform.BINARY_SENSOR],
):
mock_config_entry = await setup_mocked_integration(hass)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)

View file

@ -0,0 +1,139 @@
"""Test BMW locks."""
from unittest.mock import AsyncMock, patch
from bimmer_connected.models import MyBMWRemoteServiceError
from bimmer_connected.vehicle.remote_services import RemoteServices
from freezegun import freeze_time
import pytest
import respx
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.recorder.history import get_significant_states
from homeassistant.const import STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from homeassistant.util import dt as dt_util
from . import check_remote_service_call, setup_mocked_integration
from tests.common import snapshot_platform
from tests.components.recorder.common import async_wait_recording_done
@freeze_time("2023-06-22 10:30:00+00:00")
@pytest.mark.usefixtures("bmw_fixture")
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_entity_state_attrs(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
) -> None:
"""Test lock states and attributes."""
# Setup component
with patch(
"homeassistant.components.bmw_connected_drive.PLATFORMS", [Platform.LOCK]
):
mock_config_entry = await setup_mocked_integration(hass)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
@pytest.mark.usefixtures("recorder_mock")
@pytest.mark.parametrize(
("entity_id", "new_value", "old_value", "service", "remote_service"),
[
(
"lock.m340i_xdrive_lock",
"locked",
"unlocked",
"lock",
"door-lock",
),
("lock.m340i_xdrive_lock", "unlocked", "locked", "unlock", "door-unlock"),
],
)
async def test_service_call_success(
hass: HomeAssistant,
entity_id: str,
new_value: str,
old_value: str,
service: str,
remote_service: str,
bmw_fixture: respx.Router,
) -> None:
"""Test successful service call."""
# Setup component
assert await setup_mocked_integration(hass)
hass.states.async_set(entity_id, old_value)
assert hass.states.get(entity_id).state == old_value
now = dt_util.utcnow()
# Test
await hass.services.async_call(
"lock",
service,
blocking=True,
target={"entity_id": entity_id},
)
check_remote_service_call(bmw_fixture, remote_service)
assert hass.states.get(entity_id).state == new_value
# wait for the recorder to really store the data
await async_wait_recording_done(hass)
states = await hass.async_add_executor_job(
get_significant_states, hass, now, None, [entity_id]
)
assert any(s for s in states[entity_id] if s.state == STATE_UNKNOWN) is False
@pytest.mark.usefixtures("bmw_fixture")
@pytest.mark.usefixtures("recorder_mock")
@pytest.mark.parametrize(
("entity_id", "service"),
[
("lock.m340i_xdrive_lock", "lock"),
("lock.m340i_xdrive_lock", "unlock"),
],
)
async def test_service_call_fail(
hass: HomeAssistant,
entity_id: str,
service: str,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test failed service call."""
# Setup component
assert await setup_mocked_integration(hass)
old_value = hass.states.get(entity_id).state
now = dt_util.utcnow()
# Setup exception
monkeypatch.setattr(
RemoteServices,
"trigger_remote_service",
AsyncMock(side_effect=MyBMWRemoteServiceError),
)
# Test
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
"lock",
service,
blocking=True,
target={"entity_id": entity_id},
)
assert hass.states.get(entity_id).state == old_value
# wait for the recorder to really store the data
await async_wait_recording_done(hass)
states = await hass.async_add_executor_job(
get_significant_states, hass, now, None, [entity_id]
)
assert states[entity_id][-2].state == STATE_UNKNOWN