diff --git a/homeassistant/components/tado/config_flow.py b/homeassistant/components/tado/config_flow.py index ec195573203..a755622ea76 100644 --- a/homeassistant/components/tado/config_flow.py +++ b/homeassistant/components/tado/config_flow.py @@ -2,6 +2,7 @@ from __future__ import annotations import logging +from typing import Any from PyTado.interface import Tado import requests.exceptions @@ -31,7 +32,9 @@ DATA_SCHEMA = vol.Schema( ) -async def validate_input(hass: core.HomeAssistant, data): +async def validate_input( + hass: core.HomeAssistant, data: dict[str, Any] +) -> dict[str, Any]: """Validate the user input allows us to connect. Data has the keys from DATA_SCHEMA with values provided by the user. @@ -66,7 +69,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle the initial step.""" errors = {} if user_input is not None: @@ -105,13 +110,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self._abort_if_unique_id_configured() return await self.async_step_user() - def _username_already_configured(self, user_input): - """See if we already have a username matching user input configured.""" - existing_username = { - entry.data[CONF_USERNAME] for entry in self._async_current_entries() - } - return user_input[CONF_USERNAME] in existing_username - @staticmethod @callback def async_get_options_flow( @@ -122,16 +120,18 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): class OptionsFlowHandler(config_entries.OptionsFlow): - """Handle a option flow for tado.""" + """Handle an option flow for Tado.""" def __init__(self, config_entry: config_entries.ConfigEntry) -> None: """Initialize options flow.""" self.config_entry = config_entry - async def async_step_init(self, user_input=None): + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle options flow.""" if user_input is not None: - return self.async_create_entry(title="", data=user_input) + return self.async_create_entry(data=user_input) data_schema = vol.Schema( { diff --git a/tests/components/tado/test_config_flow.py b/tests/components/tado/test_config_flow.py index f0fef1dff5a..dcbb33b587e 100644 --- a/tests/components/tado/test_config_flow.py +++ b/tests/components/tado/test_config_flow.py @@ -2,18 +2,24 @@ from http import HTTPStatus from unittest.mock import MagicMock, patch +import pytest import requests from homeassistant import config_entries from homeassistant.components import zeroconf -from homeassistant.components.tado.const import DOMAIN +from homeassistant.components.tado.const import ( + CONF_FALLBACK, + CONST_OVERLAY_TADO_DEFAULT, + DOMAIN, +) from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry -def _get_mock_tado_api(getMe=None): +def _get_mock_tado_api(getMe=None) -> MagicMock: mock_tado = MagicMock() if isinstance(getMe, Exception): type(mock_tado).getMe = MagicMock(side_effect=getMe) @@ -22,13 +28,100 @@ def _get_mock_tado_api(getMe=None): return mock_tado -async def test_form(hass: HomeAssistant) -> None: +@pytest.mark.parametrize( + ("exception", "error"), + [ + (KeyError, "invalid_auth"), + (RuntimeError, "cannot_connect"), + (ValueError, "unknown"), + ], +) +async def test_form_exceptions( + hass: HomeAssistant, exception: Exception, error: str +) -> None: + """Test we handle Form Exceptions.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.tado.config_flow.Tado", + side_effect=exception, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"username": "test-username", "password": "test-password"}, + ) + + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {"base": error} + + # Test a retry to recover, upon failure + mock_tado_api = _get_mock_tado_api(getMe={"homes": [{"id": 1, "name": "myhome"}]}) + + with patch( + "homeassistant.components.tado.config_flow.Tado", + return_value=mock_tado_api, + ), patch( + "homeassistant.components.tado.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"username": "test-username", "password": "test-password"}, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "myhome" + assert result["data"] == { + "username": "test-username", + "password": "test-password", + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_options_flow(hass: HomeAssistant) -> None: + """Test config flow options.""" + entry = MockConfigEntry(domain=DOMAIN, data={"username": "test-username"}) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {} + + with patch( + "homeassistant.components.tado.async_setup_entry", + return_value=True, + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init( + entry.entry_id, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + {CONF_FALLBACK: CONST_OVERLAY_TADO_DEFAULT}, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["data"] == {CONF_FALLBACK: CONST_OVERLAY_TADO_DEFAULT} + + +async def test_create_entry(hass: HomeAssistant) -> None: """Test we can setup though the user path.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == "form" + assert result["type"] == FlowResultType.FORM assert result["errors"] == {} mock_tado_api = _get_mock_tado_api(getMe={"homes": [{"id": 1, "name": "myhome"}]}) @@ -40,15 +133,15 @@ async def test_form(hass: HomeAssistant) -> None: "homeassistant.components.tado.async_setup_entry", return_value=True, ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"username": "test-username", "password": "test-password"}, ) await hass.async_block_till_done() - assert result2["type"] == "create_entry" - assert result2["title"] == "myhome" - assert result2["data"] == { + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "myhome" + assert result["data"] == { "username": "test-username", "password": "test-password", } @@ -69,13 +162,13 @@ async def test_form_invalid_auth(hass: HomeAssistant) -> None: "homeassistant.components.tado.config_flow.Tado", return_value=mock_tado_api, ): - result2 = await hass.config_entries.flow.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"username": "test-username", "password": "test-password"}, ) - assert result2["type"] == "form" - assert result2["errors"] == {"base": "invalid_auth"} + assert result["type"] == "form" + assert result["errors"] == {"base": "invalid_auth"} async def test_form_cannot_connect(hass: HomeAssistant) -> None: @@ -92,13 +185,13 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None: "homeassistant.components.tado.config_flow.Tado", return_value=mock_tado_api, ): - result2 = await hass.config_entries.flow.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"username": "test-username", "password": "test-password"}, ) - assert result2["type"] == "form" - assert result2["errors"] == {"base": "cannot_connect"} + assert result["type"] == "form" + assert result["errors"] == {"base": "cannot_connect"} async def test_no_homes(hass: HomeAssistant) -> None: @@ -113,13 +206,13 @@ async def test_no_homes(hass: HomeAssistant) -> None: "homeassistant.components.tado.config_flow.Tado", return_value=mock_tado_api, ): - result2 = await hass.config_entries.flow.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"username": "test-username", "password": "test-password"}, ) - assert result2["type"] == "form" - assert result2["errors"] == {"base": "no_homes"} + assert result["type"] == "form" + assert result["errors"] == {"base": "no_homes"} async def test_form_homekit(hass: HomeAssistant) -> None: