Refactor sharkiq tests (#39564)

* Refactor sharkiq tests

* Fix linting

* Remove unussed logger

* Test one more code branch

* Don't patch integration files

* Remove legacy calls

* Linting fixes

* Refactor coordinator update tests

* Reformat test params

* Refector config flow tests

* Minor code cleanup

* Fix spelling error

* Address review

* Minor formatting change

* Remove vacuum.py from .coveragerc
This commit is contained in:
Andrew Marks 2020-09-04 16:13:11 -04:00 committed by GitHub
parent 1cb60dd5c7
commit 01bac9f433
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 287 additions and 344 deletions

View file

@ -752,7 +752,6 @@ omit =
homeassistant/components/sesame/lock.py
homeassistant/components/seven_segments/image_processing.py
homeassistant/components/seventeentrack/sensor.py
homeassistant/components/sharkiq/vacuum.py
homeassistant/components/shiftr/*
homeassistant/components/shodan/sensor.py
homeassistant/components/shelly/__init__.py

View file

@ -60,7 +60,7 @@ async def async_setup_entry(hass, config_entry):
shark_vacs = await ayla_api.async_get_devices(False)
device_names = ", ".join([d.name for d in shark_vacs])
LOGGER.debug("Found %d Shark IQ device(s): %s", len(device_names), device_names)
LOGGER.debug("Found %d Shark IQ device(s): %s", len(shark_vacs), device_names)
coordinator = SharkIqUpdateCoordinator(hass, config_entry, ayla_api, shark_vacs)
await coordinator.async_refresh()

View file

@ -71,3 +71,4 @@ TEST_USERNAME = "test-username"
TEST_PASSWORD = "test-password"
UNIQUE_ID = "foo@bar.com"
CONFIG = {CONF_USERNAME: TEST_USERNAME, CONF_PASSWORD: TEST_PASSWORD}
ENTRY_ID = "0123456789abcdef0123456789abcdef"

View file

@ -1,24 +1,18 @@
"""Test the Shark IQ config flow."""
import aiohttp
from sharkiqpy import SharkIqAuthError
import pytest
from sharkiqpy import AylaApi, SharkIqAuthError
from homeassistant import config_entries, setup
from homeassistant.components.sharkiq.const import DOMAIN
from homeassistant.core import HomeAssistant
from .const import CONFIG, TEST_PASSWORD, TEST_USERNAME, UNIQUE_ID
from tests.async_mock import MagicMock, PropertyMock, patch
from tests.async_mock import patch
from tests.common import MockConfigEntry
def _create_mocked_ayla(connect=None):
"""Create a mocked AylaApi object."""
mocked_ayla = MagicMock()
type(mocked_ayla).sign_in = PropertyMock(side_effect=connect)
type(mocked_ayla).async_sign_in = PropertyMock(side_effect=connect)
return mocked_ayla
async def test_form(hass):
"""Test we get the form."""
await setup.async_setup_component(hass, "persistent_notification", {})
@ -46,75 +40,37 @@ async def test_form(hass):
"password": TEST_PASSWORD,
}
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
mock_setup.assert_called_once()
mock_setup_entry.assert_called_once()
async def test_form_invalid_auth(hass):
"""Test we handle invalid auth."""
@pytest.mark.parametrize(
"exc,base_error",
[
(SharkIqAuthError, "invalid_auth"),
(aiohttp.ClientError, "cannot_connect"),
(TypeError, "unknown"),
],
)
async def test_form_error(hass: HomeAssistant, exc: Exception, base_error: str):
"""Test form errors."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
mocked_ayla = _create_mocked_ayla(connect=SharkIqAuthError)
with patch(
"homeassistant.components.sharkiq.config_flow.get_ayla_api",
return_value=mocked_ayla,
):
with patch.object(AylaApi, "async_sign_in", side_effect=exc):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
CONFIG,
)
assert result2["type"] == "form"
assert result2["errors"] == {"base": "invalid_auth"}
assert result2["errors"].get("base") == base_error
async def test_form_cannot_connect(hass):
"""Test we handle cannot connect error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
mocked_ayla = _create_mocked_ayla(connect=aiohttp.ClientError)
with patch(
"homeassistant.components.sharkiq.config_flow.get_ayla_api",
return_value=mocked_ayla,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
CONFIG,
)
assert result2["type"] == "form"
assert result2["errors"] == {"base": "cannot_connect"}
async def test_form_other_error(hass):
"""Test we handle other errors."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
mocked_ayla = _create_mocked_ayla(connect=TypeError)
with patch(
"homeassistant.components.sharkiq.config_flow.get_ayla_api",
return_value=mocked_ayla,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], CONFIG
)
assert result2["type"] == "form"
assert result2["errors"] == {"base": "unknown"}
async def test_reauth(hass):
async def test_reauth_success(hass: HomeAssistant):
"""Test reauth flow."""
with patch(
"homeassistant.components.sharkiq.vacuum.async_setup_entry",
return_value=True,
), patch("sharkiqpy.AylaApi.async_sign_in", return_value=True):
with patch("sharkiqpy.AylaApi.async_sign_in", return_value=True):
mock_config = MockConfigEntry(domain=DOMAIN, unique_id=UNIQUE_ID, data=CONFIG)
mock_config.add_to_hass(hass)
@ -125,21 +81,33 @@ async def test_reauth(hass):
assert result["type"] == "abort"
assert result["reason"] == "reauth_successful"
with patch("sharkiqpy.AylaApi.async_sign_in", side_effect=SharkIqAuthError):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": "reauth", "unique_id": UNIQUE_ID},
data=CONFIG,
)
assert result["type"] == "form"
assert result["errors"] == {"base": "invalid_auth"}
with patch("sharkiqpy.AylaApi.async_sign_in", side_effect=RuntimeError):
@pytest.mark.parametrize(
"side_effect,result_type,msg_field,msg",
[
(SharkIqAuthError, "form", "errors", "invalid_auth"),
(aiohttp.ClientError, "abort", "reason", "cannot_connect"),
(TypeError, "abort", "reason", "unknown"),
],
)
async def test_reauth(
hass: HomeAssistant,
side_effect: Exception,
result_type: str,
msg_field: str,
msg: str,
):
"""Test reauth failures."""
with patch("sharkiqpy.AylaApi.async_sign_in", side_effect=side_effect):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": "reauth", "unique_id": UNIQUE_ID},
data=CONFIG,
)
assert result["type"] == "abort"
assert result["reason"] == "unknown"
msg_value = result[msg_field]
if msg_field == "errors":
msg_value = msg_value.get("base")
assert result["type"] == result_type
assert msg_value == msg

View file

@ -1,267 +0,0 @@
"""Test the Shark IQ vacuum entity."""
from copy import deepcopy
import enum
import json
from typing import Dict, List
from sharkiqpy import AylaApi, Properties, SharkIqAuthError, SharkIqVacuum, get_ayla_api
from homeassistant.components.sharkiq import SharkIqUpdateCoordinator
from homeassistant.components.sharkiq.vacuum import (
ATTR_ERROR_CODE,
ATTR_ERROR_MSG,
ATTR_LOW_LIGHT,
ATTR_RECHARGE_RESUME,
SharkVacuumEntity,
)
from homeassistant.components.vacuum import (
STATE_CLEANING,
STATE_DOCKED,
STATE_IDLE,
STATE_PAUSED,
STATE_RETURNING,
SUPPORT_BATTERY,
SUPPORT_FAN_SPEED,
SUPPORT_LOCATE,
SUPPORT_PAUSE,
SUPPORT_RETURN_HOME,
SUPPORT_START,
SUPPORT_STATE,
SUPPORT_STATUS,
SUPPORT_STOP,
)
from homeassistant.config_entries import ConfigEntriesFlowManager, ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import UpdateFailed
from .const import (
SHARK_DEVICE_DICT,
SHARK_METADATA_DICT,
SHARK_PROPERTIES_DICT,
TEST_PASSWORD,
TEST_USERNAME,
)
from tests.async_mock import MagicMock, patch
MockAyla = MagicMock(spec=AylaApi) # pylint: disable=invalid-name
def _set_property(self, property_name, value):
"""Set a property locally without hitting the API."""
if isinstance(property_name, enum.Enum):
property_name = property_name.value
if isinstance(value, enum.Enum):
value = value.value
self.properties_full[property_name]["value"] = value
async def _async_set_property(self, property_name, value):
"""Set a property locally without hitting the API."""
_set_property(self, property_name, value)
def _get_mock_shark_vac(ayla_api: AylaApi) -> SharkIqVacuum:
"""Create a crude sharkiq vacuum with mocked properties."""
shark = SharkIqVacuum(ayla_api, SHARK_DEVICE_DICT)
shark.properties_full = deepcopy(SHARK_PROPERTIES_DICT)
return shark
async def _async_list_devices(_) -> List[Dict]:
"""Generate a dummy of async_list_devices output."""
return [SHARK_DEVICE_DICT]
@patch.object(SharkIqVacuum, "set_property_value", new=_set_property)
@patch.object(SharkIqVacuum, "async_set_property_value", new=_async_set_property)
async def test_shark_operation_modes(hass: HomeAssistant) -> None:
"""Test all of the shark vacuum operation modes."""
ayla_api = MockAyla()
shark_vac = _get_mock_shark_vac(ayla_api)
coordinator = SharkIqUpdateCoordinator(hass, None, ayla_api, [shark_vac])
shark = SharkVacuumEntity(shark_vac, coordinator)
# These come from the setup
assert isinstance(shark.is_docked, bool) and not shark.is_docked
assert (
isinstance(shark.recharging_to_resume, bool) and not shark.recharging_to_resume
)
# Go through the operation modes while it's "off the dock"
await shark.async_start()
assert shark.operating_mode == shark.state == STATE_CLEANING
await shark.async_pause()
assert shark.operating_mode == shark.state == STATE_PAUSED
await shark.async_stop()
assert shark.operating_mode == shark.state == STATE_IDLE
await shark.async_return_to_base()
assert shark.operating_mode == shark.state == STATE_RETURNING
# Test the docked modes
await shark.async_stop()
shark.sharkiq.set_property_value(Properties.RECHARGING_TO_RESUME, 1)
shark.sharkiq.set_property_value(Properties.DOCKED_STATUS, 1)
assert isinstance(shark.is_docked, bool) and shark.is_docked
assert isinstance(shark.recharging_to_resume, bool) and shark.recharging_to_resume
assert shark.state == STATE_DOCKED
shark.sharkiq.set_property_value(Properties.RECHARGING_TO_RESUME, 0)
assert shark.state == STATE_DOCKED
await shark.async_set_fan_speed("Eco")
assert shark.fan_speed == "Eco"
await shark.async_set_fan_speed("Max")
assert shark.fan_speed == "Max"
await shark.async_set_fan_speed("Normal")
assert shark.fan_speed == "Normal"
assert set(shark.fan_speed_list) == {"Normal", "Max", "Eco"}
@patch.object(SharkIqVacuum, "set_property_value", new=_set_property)
async def test_shark_vac_properties(hass: HomeAssistant) -> None:
"""Test all of the shark vacuum property accessors."""
ayla_api = MockAyla()
shark_vac = _get_mock_shark_vac(ayla_api)
coordinator = SharkIqUpdateCoordinator(hass, None, ayla_api, [shark_vac])
shark = SharkVacuumEntity(shark_vac, coordinator)
assert shark.name == "Sharknado"
assert shark.serial_number == "AC000Wxxxxxxxxx"
assert shark.model == "RV1000A"
assert shark.battery_level == 50
assert shark.fan_speed == "Eco"
shark.sharkiq.set_property_value(Properties.POWER_MODE, 0)
assert shark.fan_speed == "Normal"
assert isinstance(shark.recharge_resume, bool) and shark.recharge_resume
assert isinstance(shark.low_light, bool) and not shark.low_light
target_state_attributes = {
ATTR_ERROR_CODE: 7,
ATTR_ERROR_MSG: "Cliff sensor is blocked",
ATTR_RECHARGE_RESUME: True,
ATTR_LOW_LIGHT: False,
}
state_json = json.dumps(shark.device_state_attributes, sort_keys=True)
target_json = json.dumps(target_state_attributes, sort_keys=True)
assert state_json == target_json
assert not shark.should_poll
@patch.object(SharkIqVacuum, "set_property_value", new=_set_property)
@patch.object(SharkIqVacuum, "async_set_property_value", new=_async_set_property)
async def test_shark_metadata(hass: HomeAssistant) -> None:
"""Test shark properties coming from metadata."""
ayla_api = MockAyla()
shark_vac = _get_mock_shark_vac(ayla_api)
coordinator = SharkIqUpdateCoordinator(hass, None, ayla_api, [shark_vac])
shark = SharkVacuumEntity(shark_vac, coordinator)
shark.sharkiq._update_metadata( # pylint: disable=protected-access
SHARK_METADATA_DICT
)
target_device_info = {
"identifiers": {("sharkiq", "AC000Wxxxxxxxxx")},
"name": "Sharknado",
"manufacturer": "Shark",
"model": "RV1001AE",
"sw_version": "Dummy Firmware 1.0",
}
assert shark.device_info == target_device_info
def _get_async_update(err=None):
async def _async_update(_) -> bool:
if err is not None:
raise err
return True
return _async_update
@patch.object(AylaApi, "async_list_devices", new=_async_list_devices)
async def test_updates(hass: HomeAssistant) -> None:
"""Test the update coordinator update functions."""
ayla_api = get_ayla_api(TEST_USERNAME, TEST_PASSWORD)
shark_vac = _get_mock_shark_vac(ayla_api)
mock_config = MagicMock(spec=ConfigEntry)
coordinator = SharkIqUpdateCoordinator(hass, mock_config, ayla_api, [shark_vac])
with patch.object(SharkIqVacuum, "async_update", new=_get_async_update()):
update_called = (
await coordinator._async_update_data() # pylint: disable=protected-access
)
assert update_called
update_failed = False
with patch.object(
SharkIqVacuum, "async_update", new=_get_async_update(SharkIqAuthError)
), patch.object(HomeAssistant, "async_create_task"), patch.object(
ConfigEntriesFlowManager, "async_init"
):
try:
await coordinator._async_update_data() # pylint: disable=protected-access
except UpdateFailed:
update_failed = True
assert update_failed
async def test_coordinator_match(hass: HomeAssistant):
"""Test that sharkiq-coordinator references work."""
ayla_api = get_ayla_api(TEST_PASSWORD, TEST_USERNAME)
shark_vac1 = _get_mock_shark_vac(ayla_api)
shark_vac2 = _get_mock_shark_vac(ayla_api)
shark_vac2._dsn = "FOOBAR!" # pylint: disable=protected-access
coordinator = SharkIqUpdateCoordinator(hass, None, ayla_api, [shark_vac1])
api = SharkVacuumEntity(shark_vac1, coordinator)
coordinator.last_update_success = True
coordinator._online_dsns = set() # pylint: disable=protected-access
assert not api.is_online
assert not api.available
coordinator._online_dsns = { # pylint: disable=protected-access
shark_vac1.serial_number
}
assert api.is_online
assert api.available
coordinator.last_update_success = False
assert not api.available
async def test_simple_properties(hass: HomeAssistant):
"""Test that simple properties work as intended."""
ayla_api = get_ayla_api(TEST_PASSWORD, TEST_USERNAME)
shark_vac1 = _get_mock_shark_vac(ayla_api)
coordinator = SharkIqUpdateCoordinator(hass, None, ayla_api, [shark_vac1])
entity = SharkVacuumEntity(shark_vac1, coordinator)
assert entity.unique_id == "AC000Wxxxxxxxxx"
assert entity.supported_features == (
SUPPORT_BATTERY
| SUPPORT_FAN_SPEED
| SUPPORT_PAUSE
| SUPPORT_RETURN_HOME
| SUPPORT_START
| SUPPORT_STATE
| SUPPORT_STATUS
| SUPPORT_STOP
| SUPPORT_LOCATE
)
assert entity.error_code == 7
assert entity.error_message == "Cliff sensor is blocked"
shark_vac1.properties_full[Properties.ERROR_CODE.value]["value"] = 0
assert entity.error_code == 0
assert entity.error_message is None
assert (
coordinator.online_dsns
is coordinator._online_dsns # pylint: disable=protected-access
)

View file

@ -0,0 +1,242 @@
"""Test the Shark IQ vacuum entity."""
from copy import deepcopy
import enum
from typing import Any, Iterable, List, Optional
import pytest
from sharkiqpy import AylaApi, SharkIqAuthError, SharkIqVacuum
from homeassistant.components.homeassistant import SERVICE_UPDATE_ENTITY
from homeassistant.components.sharkiq import DOMAIN
from homeassistant.components.sharkiq.vacuum import (
ATTR_ERROR_CODE,
ATTR_ERROR_MSG,
ATTR_LOW_LIGHT,
ATTR_RECHARGE_RESUME,
FAN_SPEEDS_MAP,
)
from homeassistant.components.vacuum import (
ATTR_BATTERY_LEVEL,
ATTR_FAN_SPEED,
ATTR_FAN_SPEED_LIST,
SERVICE_LOCATE,
SERVICE_PAUSE,
SERVICE_RETURN_TO_BASE,
SERVICE_SET_FAN_SPEED,
SERVICE_START,
SERVICE_STOP,
STATE_CLEANING,
STATE_IDLE,
STATE_PAUSED,
STATE_RETURNING,
SUPPORT_BATTERY,
SUPPORT_FAN_SPEED,
SUPPORT_LOCATE,
SUPPORT_PAUSE,
SUPPORT_RETURN_HOME,
SUPPORT_START,
SUPPORT_STATE,
SUPPORT_STATUS,
SUPPORT_STOP,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .const import (
CONFIG,
ENTRY_ID,
SHARK_DEVICE_DICT,
SHARK_METADATA_DICT,
SHARK_PROPERTIES_DICT,
TEST_USERNAME,
)
from tests.async_mock import patch
from tests.common import MockConfigEntry
VAC_ENTITY_ID = f"vacuum.{SHARK_DEVICE_DICT['product_name'].lower()}"
EXPECTED_FEATURES = (
SUPPORT_BATTERY
| SUPPORT_FAN_SPEED
| SUPPORT_PAUSE
| SUPPORT_RETURN_HOME
| SUPPORT_START
| SUPPORT_STATE
| SUPPORT_STATUS
| SUPPORT_STOP
| SUPPORT_LOCATE
)
class MockAyla(AylaApi):
"""Mocked AylaApi that doesn't do anything."""
async def async_sign_in(self):
"""Instead of signing in, just return."""
async def async_list_devices(self) -> List[dict]:
"""Return the device list."""
return [SHARK_DEVICE_DICT]
async def async_get_devices(self, update: bool = True) -> List[SharkIqVacuum]:
"""Get the list of devices."""
shark = MockShark(self, SHARK_DEVICE_DICT)
shark.properties_full = deepcopy(SHARK_PROPERTIES_DICT)
shark._update_metadata(SHARK_METADATA_DICT) # pylint: disable=protected-access
return [shark]
async def async_request(self, http_method: str, url: str, **kwargs):
"""Don't make an HTTP request."""
class MockShark(SharkIqVacuum):
"""Mocked SharkIqVacuum that won't hit the API."""
async def async_update(self, property_list: Optional[Iterable[str]] = None):
"""Don't do anything."""
def set_property_value(self, property_name, value):
"""Set a property locally without hitting the API."""
if isinstance(property_name, enum.Enum):
property_name = property_name.value
if isinstance(value, enum.Enum):
value = value.value
self.properties_full[property_name]["value"] = value
async def async_set_property_value(self, property_name, value):
"""Set a property locally without hitting the API."""
self.set_property_value(property_name, value)
@pytest.fixture(autouse=True)
@patch("sharkiqpy.ayla_api.AylaApi", MockAyla)
async def setup_integration(hass):
"""Build the mock integration."""
entry = MockConfigEntry(
domain=DOMAIN, unique_id=TEST_USERNAME, data=CONFIG, entry_id=ENTRY_ID
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
async def test_simple_properties(hass: HomeAssistant):
"""Test that simple properties work as intended."""
state = hass.states.get(VAC_ENTITY_ID)
registry = await hass.helpers.entity_registry.async_get_registry()
entity = registry.async_get(VAC_ENTITY_ID)
assert entity
assert state
assert state.state == STATE_CLEANING
assert entity.unique_id == "AC000Wxxxxxxxxx"
@pytest.mark.parametrize(
"attribute,target_value",
[
(ATTR_SUPPORTED_FEATURES, EXPECTED_FEATURES),
(ATTR_BATTERY_LEVEL, 50),
(ATTR_FAN_SPEED, "Eco"),
(ATTR_FAN_SPEED_LIST, list(FAN_SPEEDS_MAP)),
(ATTR_ERROR_CODE, 7),
(ATTR_ERROR_MSG, "Cliff sensor is blocked"),
(ATTR_LOW_LIGHT, False),
(ATTR_RECHARGE_RESUME, True),
],
)
async def test_initial_attributes(
hass: HomeAssistant, attribute: str, target_value: Any
):
"""Test initial config attributes."""
state = hass.states.get(VAC_ENTITY_ID)
assert state.attributes.get(attribute) == target_value
@pytest.mark.parametrize(
"service,target_state",
[
(SERVICE_STOP, STATE_IDLE),
(SERVICE_PAUSE, STATE_PAUSED),
(SERVICE_RETURN_TO_BASE, STATE_RETURNING),
(SERVICE_START, STATE_CLEANING),
],
)
async def test_cleaning_states(hass: HomeAssistant, service: str, target_state: str):
"""Test cleaning states."""
service_data = {ATTR_ENTITY_ID: VAC_ENTITY_ID}
await hass.services.async_call("vacuum", service, service_data, blocking=True)
state = hass.states.get(VAC_ENTITY_ID)
assert state.state == target_state
@pytest.mark.parametrize("fan_speed", list(FAN_SPEEDS_MAP))
async def test_fan_speed(hass: HomeAssistant, fan_speed: str) -> None:
"""Test setting fan speeds."""
service_data = {ATTR_ENTITY_ID: VAC_ENTITY_ID, ATTR_FAN_SPEED: fan_speed}
await hass.services.async_call(
"vacuum", SERVICE_SET_FAN_SPEED, service_data, blocking=True
)
state = hass.states.get(VAC_ENTITY_ID)
assert state.attributes.get(ATTR_FAN_SPEED) == fan_speed
@pytest.mark.parametrize(
"device_property,target_value",
[
("manufacturer", "Shark"),
("model", "RV1001AE"),
("name", "Sharknado"),
("sw_version", "Dummy Firmware 1.0"),
],
)
async def test_device_properties(
hass: HomeAssistant, device_property: str, target_value: str
):
"""Test device properties."""
registry = await hass.helpers.device_registry.async_get_registry()
device = registry.async_get_device({(DOMAIN, "AC000Wxxxxxxxxx")}, [])
assert getattr(device, device_property) == target_value
async def test_locate(hass):
"""Test that the locate command works."""
with patch.object(SharkIqVacuum, "async_find_device") as mock_locate:
data = {ATTR_ENTITY_ID: VAC_ENTITY_ID}
await hass.services.async_call("vacuum", SERVICE_LOCATE, data, blocking=True)
mock_locate.assert_called_once()
@pytest.mark.parametrize(
"side_effect,success",
[
(None, True),
(SharkIqAuthError, False),
(RuntimeError, False),
],
)
async def test_coordinator_updates(
hass: HomeAssistant, side_effect: Optional[Exception], success: bool
) -> None:
"""Test the update coordinator update functions."""
coordinator = hass.data[DOMAIN][ENTRY_ID]
await async_setup_component(hass, "homeassistant", {})
with patch.object(
MockShark, "async_update", side_effect=side_effect
) as mock_update:
data = {ATTR_ENTITY_ID: [VAC_ENTITY_ID]}
await hass.services.async_call(
"homeassistant", SERVICE_UPDATE_ENTITY, data, blocking=True
)
assert coordinator.last_update_success == success
mock_update.assert_called_once()
state = hass.states.get(VAC_ENTITY_ID)
assert (state.state == STATE_UNAVAILABLE) != success