mirror of
https://github.com/home-assistant/core.git
synced 2026-04-02 00:20:30 +01:00
Clean up SmartTub integration and tests (#165517)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
@@ -19,8 +19,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: SmartTubConfigEntry) ->
|
||||
|
||||
controller = SmartTubController(hass)
|
||||
|
||||
if not await controller.async_setup_entry(entry):
|
||||
return False
|
||||
await controller.async_setup_entry(entry)
|
||||
|
||||
entry.runtime_data = controller
|
||||
|
||||
|
||||
@@ -21,12 +21,13 @@ def config_data() -> dict[str, Any]:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry(config_data: dict[str, Any]) -> MockConfigEntry:
|
||||
def config_entry(config_data: dict[str, Any], account) -> MockConfigEntry:
|
||||
"""Create a mock config entry."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=config_data,
|
||||
options={},
|
||||
unique_id=account.id,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import smarttub
|
||||
from homeassistant.components.climate import (
|
||||
ATTR_CURRENT_TEMPERATURE,
|
||||
ATTR_HVAC_ACTION,
|
||||
ATTR_HVAC_MODE,
|
||||
ATTR_HVAC_MODES,
|
||||
ATTR_MAX_TEMP,
|
||||
ATTR_MIN_TEMP,
|
||||
@@ -14,7 +13,6 @@ from homeassistant.components.climate import (
|
||||
DOMAIN as CLIMATE_DOMAIN,
|
||||
PRESET_ECO,
|
||||
PRESET_NONE,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
ClimateEntityFeature,
|
||||
@@ -32,25 +30,16 @@ from homeassistant.core import HomeAssistant
|
||||
from . import trigger_update
|
||||
|
||||
|
||||
async def test_thermostat_update(
|
||||
async def test_thermostat_state(
|
||||
spa, spa_state, setup_entry, hass: HomeAssistant
|
||||
) -> None:
|
||||
"""Test the thermostat entity."""
|
||||
|
||||
"""Test the thermostat entity initial state and attributes."""
|
||||
entity_id = f"climate.{spa.brand}_{spa.model}_thermostat"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.HEATING
|
||||
|
||||
spa_state.heater = "OFF"
|
||||
await trigger_update(hass)
|
||||
state = hass.states.get(entity_id)
|
||||
|
||||
assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
||||
|
||||
assert set(state.attributes[ATTR_HVAC_MODES]) == {HVACMode.HEAT}
|
||||
assert state.state == HVACMode.HEAT
|
||||
assert set(state.attributes[ATTR_HVAC_MODES]) == {HVACMode.HEAT}
|
||||
assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.HEATING
|
||||
assert (
|
||||
state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||
== ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
@@ -60,7 +49,28 @@ async def test_thermostat_update(
|
||||
assert state.attributes[ATTR_MAX_TEMP] == DEFAULT_MAX_TEMP
|
||||
assert state.attributes[ATTR_MIN_TEMP] == DEFAULT_MIN_TEMP
|
||||
assert state.attributes[ATTR_PRESET_MODES] == ["none", "eco", "day", "ready"]
|
||||
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_NONE
|
||||
|
||||
|
||||
async def test_thermostat_hvac_action_update(
|
||||
spa, spa_state, setup_entry, hass: HomeAssistant
|
||||
) -> None:
|
||||
"""Test the thermostat HVAC action transitions from heating to idle."""
|
||||
entity_id = f"climate.{spa.brand}_{spa.model}_thermostat"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.HEATING
|
||||
|
||||
spa_state.heater = "OFF"
|
||||
await trigger_update(hass)
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
||||
|
||||
|
||||
async def test_thermostat_set_temperature(
|
||||
spa, setup_entry, hass: HomeAssistant
|
||||
) -> None:
|
||||
"""Test setting the target temperature."""
|
||||
entity_id = f"climate.{spa.brand}_{spa.model}_thermostat"
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
@@ -69,15 +79,12 @@ async def test_thermostat_update(
|
||||
)
|
||||
spa.set_temperature.assert_called_with(37)
|
||||
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.HEAT},
|
||||
blocking=True,
|
||||
)
|
||||
# does nothing
|
||||
|
||||
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_NONE
|
||||
async def test_thermostat_set_preset_mode(
|
||||
spa, spa_state, setup_entry, hass: HomeAssistant
|
||||
) -> None:
|
||||
"""Test setting a preset mode updates state correctly."""
|
||||
entity_id = f"climate.{spa.brand}_{spa.model}_thermostat"
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
@@ -91,6 +98,9 @@ async def test_thermostat_update(
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_ECO
|
||||
|
||||
|
||||
async def test_thermostat_api_error(spa, setup_entry, hass: HomeAssistant) -> None:
|
||||
"""Test that an API error during update does not raise."""
|
||||
spa.get_status_full.side_effect = smarttub.APIError
|
||||
await trigger_update(hass)
|
||||
# should not fail
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from smarttub import LoginFailed
|
||||
|
||||
from homeassistant import config_entries
|
||||
@@ -13,35 +14,43 @@ from homeassistant.data_entry_flow import FlowResultType
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_form(hass: HomeAssistant) -> None:
|
||||
"""Test we get the form."""
|
||||
@pytest.fixture
|
||||
def mock_setup_entry():
|
||||
"""Mock the integration setup."""
|
||||
with patch(
|
||||
"homeassistant.components.smarttub.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
async def test_user_flow(hass: HomeAssistant, mock_setup_entry, account) -> None:
|
||||
"""Test the user config flow creates an entry with correct data."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.smarttub.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_EMAIL: "test-email", CONF_PASSWORD: "test-password"},
|
||||
)
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_EMAIL: "test-email", CONF_PASSWORD: "test-password"},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "test-email"
|
||||
assert result["data"] == {
|
||||
CONF_EMAIL: "test-email",
|
||||
CONF_PASSWORD: "test-password",
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
mock_setup_entry.assert_called_once()
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "test-email"
|
||||
assert result["data"] == {
|
||||
CONF_EMAIL: "test-email",
|
||||
CONF_PASSWORD: "test-password",
|
||||
}
|
||||
assert result["result"].unique_id == account.id
|
||||
mock_setup_entry.assert_called_once()
|
||||
|
||||
|
||||
async def test_form_invalid_auth(hass: HomeAssistant, smarttub_api) -> None:
|
||||
"""Test we handle invalid auth."""
|
||||
async def test_form_invalid_auth(
|
||||
hass: HomeAssistant, smarttub_api, mock_setup_entry
|
||||
) -> None:
|
||||
"""Test we handle invalid auth and can recover."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
@@ -56,17 +65,21 @@ async def test_form_invalid_auth(hass: HomeAssistant, smarttub_api) -> None:
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
smarttub_api.login.side_effect = None
|
||||
|
||||
async def test_reauth_success(hass: HomeAssistant, smarttub_api, account) -> None:
|
||||
"""Test reauthentication flow."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={CONF_EMAIL: "test-email", CONF_PASSWORD: "test-password"},
|
||||
unique_id=account.id,
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_EMAIL: "test-email", CONF_PASSWORD: "test-password"},
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
result = await mock_entry.start_reauth_flow(hass)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_reauth_success(hass: HomeAssistant, smarttub_api, config_entry) -> None:
|
||||
"""Test reauthentication flow."""
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
result = await config_entry.start_reauth_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
@@ -77,18 +90,15 @@ async def test_reauth_success(hass: HomeAssistant, smarttub_api, account) -> Non
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
assert mock_entry.data[CONF_EMAIL] == "test-email3"
|
||||
assert mock_entry.data[CONF_PASSWORD] == "test-password3"
|
||||
assert config_entry.data[CONF_EMAIL] == "test-email3"
|
||||
assert config_entry.data[CONF_PASSWORD] == "test-password3"
|
||||
|
||||
|
||||
async def test_reauth_wrong_account(hass: HomeAssistant, smarttub_api, account) -> None:
|
||||
async def test_reauth_wrong_account(
|
||||
hass: HomeAssistant, smarttub_api, account, config_entry
|
||||
) -> None:
|
||||
"""Test reauthentication flow if the user enters credentials for a different already-configured account."""
|
||||
mock_entry1 = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={CONF_EMAIL: "test-email1", CONF_PASSWORD: "test-password1"},
|
||||
unique_id=account.id,
|
||||
)
|
||||
mock_entry1.add_to_hass(hass)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
mock_entry2 = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
@@ -98,7 +108,7 @@ async def test_reauth_wrong_account(hass: HomeAssistant, smarttub_api, account)
|
||||
mock_entry2.add_to_hass(hass)
|
||||
|
||||
# we try to reauth account #2, and the user successfully authenticates to account #1
|
||||
account.id = mock_entry1.unique_id
|
||||
account.id = config_entry.unique_id
|
||||
result = await mock_entry2.start_reauth_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
"""Test smarttub setup process."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from smarttub import LoginFailed
|
||||
|
||||
from homeassistant.components.smarttub.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
async def test_setup_with_no_config(
|
||||
@@ -39,34 +36,23 @@ async def test_setup_auth_failed(
|
||||
smarttub_api.login.side_effect = LoginFailed
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
with patch.object(hass.config_entries.flow, "async_init") as mock_flow_init:
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert config_entry.state is ConfigEntryState.SETUP_ERROR
|
||||
mock_flow_init.assert_called_with(
|
||||
DOMAIN,
|
||||
context={
|
||||
"source": SOURCE_REAUTH,
|
||||
"entry_id": config_entry.entry_id,
|
||||
"unique_id": config_entry.unique_id,
|
||||
"title_placeholders": {"name": config_entry.title},
|
||||
},
|
||||
data=config_entry.data,
|
||||
)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state is ConfigEntryState.SETUP_ERROR
|
||||
|
||||
async def test_config_passed_to_config_entry(
|
||||
hass: HomeAssistant, config_entry, config_data
|
||||
) -> None:
|
||||
"""Test that configured options are loaded via config entry."""
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, config_data)
|
||||
flows = hass.config_entries.flow.async_progress_by_handler(DOMAIN)
|
||||
assert len(flows) == 1
|
||||
assert flows[0]["context"]["source"] == SOURCE_REAUTH
|
||||
|
||||
|
||||
async def test_unload_entry(hass: HomeAssistant, config_entry) -> None:
|
||||
"""Test being able to unload an entry."""
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await async_setup_component(hass, DOMAIN, {}) is True
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
@@ -7,49 +7,73 @@ from homeassistant.core import HomeAssistant
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("pump_id", "entity_suffix", "pump_state"),
|
||||
("pump_id", "entity_suffix", "expected_state"),
|
||||
[
|
||||
("CP", "circulation_pump", "off"),
|
||||
("P1", "jet_p1", "off"),
|
||||
("P2", "jet_p2", "on"),
|
||||
("CP", "circulation_pump", STATE_OFF),
|
||||
("P1", "jet_p1", STATE_OFF),
|
||||
("P2", "jet_p2", STATE_ON),
|
||||
],
|
||||
)
|
||||
async def test_pumps(
|
||||
spa, setup_entry, hass: HomeAssistant, pump_id, pump_state, entity_suffix
|
||||
async def test_pump_state(
|
||||
spa, setup_entry, hass: HomeAssistant, pump_id, entity_suffix, expected_state
|
||||
) -> None:
|
||||
"""Test pump entities."""
|
||||
|
||||
status = await spa.get_status_full()
|
||||
pump = next(pump for pump in status.pumps if pump.id == pump_id)
|
||||
|
||||
"""Test pump entity initial state."""
|
||||
entity_id = f"switch.{spa.brand}_{spa.model}_{entity_suffix}"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == pump_state
|
||||
assert state.state == expected_state
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("pump_id", "entity_suffix"),
|
||||
[
|
||||
("CP", "circulation_pump"),
|
||||
("P1", "jet_p1"),
|
||||
("P2", "jet_p2"),
|
||||
],
|
||||
)
|
||||
async def test_pump_toggle(
|
||||
spa, setup_entry, hass: HomeAssistant, pump_id, entity_suffix
|
||||
) -> None:
|
||||
"""Test toggling a pump."""
|
||||
status = await spa.get_status_full()
|
||||
pump = next(pump for pump in status.pumps if pump.id == pump_id)
|
||||
entity_id = f"switch.{spa.brand}_{spa.model}_{entity_suffix}"
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch",
|
||||
"toggle",
|
||||
{"entity_id": entity_id},
|
||||
blocking=True,
|
||||
"switch", "toggle", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
pump.toggle.assert_called()
|
||||
|
||||
if state.state == STATE_OFF:
|
||||
await hass.services.async_call(
|
||||
"switch",
|
||||
"turn_on",
|
||||
{"entity_id": entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
pump.toggle.assert_called()
|
||||
else:
|
||||
assert state.state == STATE_ON
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch",
|
||||
"turn_off",
|
||||
{"entity_id": entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
pump.toggle.assert_called()
|
||||
@pytest.mark.parametrize(
|
||||
("pump_id", "entity_suffix"),
|
||||
[
|
||||
("CP", "circulation_pump"),
|
||||
("P1", "jet_p1"),
|
||||
],
|
||||
)
|
||||
async def test_pump_turn_on(
|
||||
spa, setup_entry, hass: HomeAssistant, pump_id, entity_suffix
|
||||
) -> None:
|
||||
"""Test turning on an off pump toggles it."""
|
||||
status = await spa.get_status_full()
|
||||
pump = next(pump for pump in status.pumps if pump.id == pump_id)
|
||||
entity_id = f"switch.{spa.brand}_{spa.model}_{entity_suffix}"
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_on", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
pump.toggle.assert_called()
|
||||
|
||||
|
||||
async def test_pump_turn_off(spa, setup_entry, hass: HomeAssistant) -> None:
|
||||
"""Test turning off an on pump toggles it."""
|
||||
status = await spa.get_status_full()
|
||||
pump = next(pump for pump in status.pumps if pump.id == "P2")
|
||||
entity_id = f"switch.{spa.brand}_{spa.model}_jet_p2"
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_off", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
pump.toggle.assert_called()
|
||||
|
||||
Reference in New Issue
Block a user