mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 23:27:37 +00:00
Address late smarttub review (#46703)
* _config -> config * remove unused string * remove entity tests * replace unit tests with integration tests using the core * refactor polling to use asyncio.gather * remove redundant component init * remove gather in favor of simple loop * use async_fire_time_changed instead of async_update_entity * use hass.config_entries.async_setup instead of calling smarttub.async_setup_entry directly * replace stray smarttub.async_setup_entry call * async_unload_entry -> hass.config_entries.async_unload * remove broken test
This commit is contained in:
parent
d9ce7db554
commit
da51e23514
@ -10,7 +10,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
PLATFORMS = ["climate"]
|
PLATFORMS = ["climate"]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass, _config):
|
async def async_setup(hass, config):
|
||||||
"""Set up smarttub component."""
|
"""Set up smarttub component."""
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
@ -79,12 +79,15 @@ class SmartTubController:
|
|||||||
try:
|
try:
|
||||||
async with async_timeout.timeout(POLLING_TIMEOUT):
|
async with async_timeout.timeout(POLLING_TIMEOUT):
|
||||||
for spa in self.spas:
|
for spa in self.spas:
|
||||||
data[spa.id] = {"status": await spa.get_status()}
|
data[spa.id] = await self._get_spa_data(spa)
|
||||||
except APIError as err:
|
except APIError as err:
|
||||||
raise UpdateFailed(err) from err
|
raise UpdateFailed(err) from err
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
async def _get_spa_data(self, spa):
|
||||||
|
return {"status": await spa.get_status()}
|
||||||
|
|
||||||
async def async_register_devices(self, entry):
|
async def async_register_devices(self, entry):
|
||||||
"""Register devices with the device registry for all spas."""
|
"""Register devices with the device registry for all spas."""
|
||||||
device_registry = await dr.async_get_registry(self._hass)
|
device_registry = await dr.async_get_registry(self._hass)
|
||||||
|
@ -11,8 +11,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
|
@ -5,8 +5,7 @@
|
|||||||
"reauth_successful": "Re-authentication was successful"
|
"reauth_successful": "Re-authentication was successful"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_auth": "Invalid authentication",
|
"invalid_auth": "Invalid authentication"
|
||||||
"unknown": "Unexpected error"
|
|
||||||
},
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
|
@ -6,8 +6,8 @@ import pytest
|
|||||||
import smarttub
|
import smarttub
|
||||||
|
|
||||||
from homeassistant.components.smarttub.const import DOMAIN
|
from homeassistant.components.smarttub.const import DOMAIN
|
||||||
from homeassistant.components.smarttub.controller import SmartTubController
|
|
||||||
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
|
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
@ -28,6 +28,12 @@ def config_entry(config_data):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def setup_component(hass):
|
||||||
|
"""Set up the component."""
|
||||||
|
assert await async_setup_component(hass, DOMAIN, {}) is True
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="spa")
|
@pytest.fixture(name="spa")
|
||||||
def mock_spa():
|
def mock_spa():
|
||||||
"""Mock a SmartTub.Spa."""
|
"""Mock a SmartTub.Spa."""
|
||||||
@ -65,22 +71,3 @@ def mock_api(account, spa):
|
|||||||
api_mock = api_class_mock.return_value
|
api_mock = api_class_mock.return_value
|
||||||
api_mock.get_account.return_value = account
|
api_mock.get_account.return_value = account
|
||||||
yield api_mock
|
yield api_mock
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
async def controller(smarttub_api, hass, config_entry):
|
|
||||||
"""Instantiate controller for testing."""
|
|
||||||
|
|
||||||
controller = SmartTubController(hass)
|
|
||||||
assert len(controller.spas) == 0
|
|
||||||
assert await controller.async_setup_entry(config_entry)
|
|
||||||
|
|
||||||
assert len(controller.spas) > 0
|
|
||||||
|
|
||||||
return controller
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
async def coordinator(controller):
|
|
||||||
"""Provide convenient access to the coordinator via the controller."""
|
|
||||||
return controller.coordinator
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
"""Test the SmartTub climate platform."""
|
"""Test the SmartTub climate platform."""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
import smarttub
|
||||||
|
|
||||||
from homeassistant.components.climate.const import (
|
from homeassistant.components.climate.const import (
|
||||||
ATTR_CURRENT_TEMPERATURE,
|
ATTR_CURRENT_TEMPERATURE,
|
||||||
ATTR_HVAC_ACTION,
|
ATTR_HVAC_ACTION,
|
||||||
@ -15,15 +19,22 @@ from homeassistant.components.climate.const import (
|
|||||||
SERVICE_SET_TEMPERATURE,
|
SERVICE_SET_TEMPERATURE,
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
SUPPORT_TARGET_TEMPERATURE,
|
||||||
)
|
)
|
||||||
from homeassistant.components.smarttub.const import DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP
|
from homeassistant.components.smarttub.const import (
|
||||||
|
DEFAULT_MAX_TEMP,
|
||||||
|
DEFAULT_MIN_TEMP,
|
||||||
|
SCAN_INTERVAL,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
ATTR_SUPPORTED_FEATURES,
|
ATTR_SUPPORTED_FEATURES,
|
||||||
ATTR_TEMPERATURE,
|
ATTR_TEMPERATURE,
|
||||||
)
|
)
|
||||||
|
from homeassistant.util import dt
|
||||||
|
|
||||||
|
from tests.common import async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
async def test_thermostat(coordinator, spa, hass, config_entry):
|
async def test_thermostat_update(spa, hass, config_entry, smarttub_api):
|
||||||
"""Test the thermostat entity."""
|
"""Test the thermostat entity."""
|
||||||
|
|
||||||
spa.get_status.return_value = {
|
spa.get_status.return_value = {
|
||||||
@ -44,7 +55,7 @@ async def test_thermostat(coordinator, spa, hass, config_entry):
|
|||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
|
||||||
|
|
||||||
spa.get_status.return_value["heater"] = "OFF"
|
spa.get_status.return_value["heater"] = "OFF"
|
||||||
await hass.helpers.entity_component.async_update_entity(entity_id)
|
await trigger_update(hass)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
|
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
|
||||||
@ -72,3 +83,14 @@ async def test_thermostat(coordinator, spa, hass, config_entry):
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
# does nothing
|
# does nothing
|
||||||
|
|
||||||
|
spa.get_status.side_effect = smarttub.APIError
|
||||||
|
await trigger_update(hass)
|
||||||
|
# should not fail
|
||||||
|
|
||||||
|
|
||||||
|
async def trigger_update(hass):
|
||||||
|
"""Trigger a polling update by moving time forward."""
|
||||||
|
new_time = dt.utcnow() + timedelta(seconds=SCAN_INTERVAL + 1)
|
||||||
|
async_fire_time_changed(hass, new_time)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
"""Test the SmartTub controller."""
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
import smarttub
|
|
||||||
|
|
||||||
from homeassistant.components.smarttub.controller import SmartTubController
|
|
||||||
from homeassistant.helpers.update_coordinator import UpdateFailed
|
|
||||||
|
|
||||||
|
|
||||||
async def test_invalid_credentials(hass, controller, smarttub_api, config_entry):
|
|
||||||
"""Check that we return False if the configured credentials are invalid.
|
|
||||||
|
|
||||||
This should mean that the user changed their SmartTub password.
|
|
||||||
"""
|
|
||||||
|
|
||||||
smarttub_api.login.side_effect = smarttub.LoginFailed
|
|
||||||
controller = SmartTubController(hass)
|
|
||||||
ret = await controller.async_setup_entry(config_entry)
|
|
||||||
assert ret is False
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update(controller, spa):
|
|
||||||
"""Test data updates from API."""
|
|
||||||
data = await controller.async_update_data()
|
|
||||||
assert data[spa.id] == {"status": spa.get_status.return_value}
|
|
||||||
|
|
||||||
spa.get_status.side_effect = smarttub.APIError
|
|
||||||
with pytest.raises(UpdateFailed):
|
|
||||||
data = await controller.async_update_data()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_login(controller, smarttub_api, account):
|
|
||||||
"""Test SmartTubController.login."""
|
|
||||||
smarttub_api.get_account.return_value.id = "account-id1"
|
|
||||||
account = await controller.login("test-email1", "test-password1")
|
|
||||||
smarttub_api.login.assert_called()
|
|
||||||
assert account == account
|
|
@ -1,18 +0,0 @@
|
|||||||
"""Test SmartTubEntity."""
|
|
||||||
|
|
||||||
from homeassistant.components.smarttub.entity import SmartTubEntity
|
|
||||||
|
|
||||||
|
|
||||||
async def test_entity(coordinator, spa):
|
|
||||||
"""Test SmartTubEntity."""
|
|
||||||
|
|
||||||
entity = SmartTubEntity(coordinator, spa, "entity1")
|
|
||||||
|
|
||||||
assert entity.device_info
|
|
||||||
assert entity.name
|
|
||||||
|
|
||||||
coordinator.data[spa.id] = {}
|
|
||||||
assert entity.get_spa_status("foo") is None
|
|
||||||
coordinator.data[spa.id]["status"] = {"foo": "foo1", "bar": {"baz": "barbaz1"}}
|
|
||||||
assert entity.get_spa_status("foo") == "foo1"
|
|
||||||
assert entity.get_spa_status("bar.baz") == "barbaz1"
|
|
@ -1,48 +1,50 @@
|
|||||||
"""Test smarttub setup process."""
|
"""Test smarttub setup process."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from smarttub import LoginFailed
|
from smarttub import LoginFailed
|
||||||
|
|
||||||
from homeassistant.components import smarttub
|
from homeassistant.components import smarttub
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.config_entries import (
|
||||||
|
ENTRY_STATE_SETUP_ERROR,
|
||||||
|
ENTRY_STATE_SETUP_RETRY,
|
||||||
|
)
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_with_no_config(hass):
|
async def test_setup_with_no_config(setup_component, hass, smarttub_api):
|
||||||
"""Test that we do not discover anything."""
|
"""Test that we do not discover anything."""
|
||||||
assert await async_setup_component(hass, smarttub.DOMAIN, {}) is True
|
|
||||||
|
|
||||||
# No flows started
|
# No flows started
|
||||||
assert len(hass.config_entries.flow.async_progress()) == 0
|
assert len(hass.config_entries.flow.async_progress()) == 0
|
||||||
|
|
||||||
assert smarttub.const.SMARTTUB_CONTROLLER not in hass.data[smarttub.DOMAIN]
|
smarttub_api.login.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_entry_not_ready(hass, config_entry, smarttub_api):
|
async def test_setup_entry_not_ready(setup_component, hass, config_entry, smarttub_api):
|
||||||
"""Test setup when the entry is not ready."""
|
"""Test setup when the entry is not ready."""
|
||||||
assert await async_setup_component(hass, smarttub.DOMAIN, {}) is True
|
|
||||||
smarttub_api.login.side_effect = asyncio.TimeoutError
|
smarttub_api.login.side_effect = asyncio.TimeoutError
|
||||||
|
|
||||||
with pytest.raises(ConfigEntryNotReady):
|
config_entry.add_to_hass(hass)
|
||||||
await smarttub.async_setup_entry(hass, config_entry)
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
assert config_entry.state == ENTRY_STATE_SETUP_RETRY
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_auth_failed(hass, config_entry, smarttub_api):
|
async def test_setup_auth_failed(setup_component, hass, config_entry, smarttub_api):
|
||||||
"""Test setup when the credentials are invalid."""
|
"""Test setup when the credentials are invalid."""
|
||||||
assert await async_setup_component(hass, smarttub.DOMAIN, {}) is True
|
|
||||||
smarttub_api.login.side_effect = LoginFailed
|
smarttub_api.login.side_effect = LoginFailed
|
||||||
|
|
||||||
assert await smarttub.async_setup_entry(hass, config_entry) is False
|
config_entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
assert config_entry.state == ENTRY_STATE_SETUP_ERROR
|
||||||
|
|
||||||
|
|
||||||
async def test_config_passed_to_config_entry(hass, config_entry, config_data):
|
async def test_config_passed_to_config_entry(
|
||||||
|
hass, config_entry, config_data, smarttub_api
|
||||||
|
):
|
||||||
"""Test that configured options are loaded via config entry."""
|
"""Test that configured options are loaded via config entry."""
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
ret = await async_setup_component(hass, smarttub.DOMAIN, config_data)
|
assert await async_setup_component(hass, smarttub.DOMAIN, config_data)
|
||||||
assert ret is True
|
|
||||||
|
|
||||||
|
|
||||||
async def test_unload_entry(hass, config_entry, smarttub_api):
|
async def test_unload_entry(hass, config_entry, smarttub_api):
|
||||||
@ -51,10 +53,4 @@ async def test_unload_entry(hass, config_entry, smarttub_api):
|
|||||||
|
|
||||||
assert await async_setup_component(hass, smarttub.DOMAIN, {}) is True
|
assert await async_setup_component(hass, smarttub.DOMAIN, {}) is True
|
||||||
|
|
||||||
assert await smarttub.async_unload_entry(hass, config_entry)
|
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
|
|
||||||
# test failure of platform unload
|
|
||||||
assert await async_setup_component(hass, smarttub.DOMAIN, {}) is True
|
|
||||||
with patch.object(hass.config_entries, "async_forward_entry_unload") as mock:
|
|
||||||
mock.return_value = False
|
|
||||||
assert await smarttub.async_unload_entry(hass, config_entry) is False
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user