hass-core/tests/components/tado/test_config_flow.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

412 lines
13 KiB
Python
Raw Normal View History

"""Test the Tado config flow."""
from http import HTTPStatus
from ipaddress import ip_address
2021-01-01 22:31:56 +01:00
from unittest.mock import MagicMock, patch
import PyTado
import pytest
import requests
from homeassistant import config_entries
from homeassistant.components import zeroconf
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) -> MagicMock:
mock_tado = MagicMock()
if isinstance(getMe, Exception):
type(mock_tado).getMe = MagicMock(side_effect=getMe)
else:
type(mock_tado).getMe = MagicMock(return_value=getMe)
return mock_tado
@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"] == FlowResultType.FORM
assert result["errors"] == {}
mock_tado_api = _get_mock_tado_api(getMe={"homes": [{"id": 1, "name": "myhome"}]})
with (
patch(
2020-08-27 13:56:20 +02:00
"homeassistant.components.tado.config_flow.Tado",
return_value=mock_tado_api,
),
patch(
2020-08-27 13:56:20 +02:00
"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_form_invalid_auth(hass: HomeAssistant) -> None:
"""Test we handle invalid auth."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
response_mock = MagicMock()
type(response_mock).status_code = HTTPStatus.UNAUTHORIZED
mock_tado_api = _get_mock_tado_api(getMe=requests.HTTPError(response=response_mock))
with patch(
2020-08-27 13:56:20 +02:00
"homeassistant.components.tado.config_flow.Tado",
return_value=mock_tado_api,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"username": "test-username", "password": "test-password"},
)
assert result["type"] == "form"
assert result["errors"] == {"base": "invalid_auth"}
async def test_form_cannot_connect(hass: HomeAssistant) -> None:
"""Test we handle cannot connect error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
response_mock = MagicMock()
type(response_mock).status_code = HTTPStatus.INTERNAL_SERVER_ERROR
mock_tado_api = _get_mock_tado_api(getMe=requests.HTTPError(response=response_mock))
with patch(
2020-08-27 13:56:20 +02:00
"homeassistant.components.tado.config_flow.Tado",
return_value=mock_tado_api,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"username": "test-username", "password": "test-password"},
)
assert result["type"] == "form"
assert result["errors"] == {"base": "cannot_connect"}
async def test_no_homes(hass: HomeAssistant) -> None:
"""Test we handle no homes error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
mock_tado_api = _get_mock_tado_api(getMe={"homes": []})
with patch(
2020-08-27 13:56:20 +02:00
"homeassistant.components.tado.config_flow.Tado",
return_value=mock_tado_api,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"username": "test-username", "password": "test-password"},
)
assert result["type"] == "form"
assert result["errors"] == {"base": "no_homes"}
async def test_form_homekit(hass: HomeAssistant) -> None:
"""Test that we abort from homekit if tado is already setup."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_HOMEKIT},
data=zeroconf.ZeroconfServiceInfo(
ip_address=ip_address("127.0.0.1"),
ip_addresses=[ip_address("127.0.0.1")],
hostname="mock_hostname",
name="mock_name",
port=None,
properties={zeroconf.ATTR_PROPERTIES_ID: "AA:BB:CC:DD:EE:FF"},
type="mock_type",
),
)
assert result["type"] == "form"
assert result["errors"] == {}
flow = next(
flow
for flow in hass.config_entries.flow.async_progress()
if flow["flow_id"] == result["flow_id"]
)
assert flow["context"]["unique_id"] == "AA:BB:CC:DD:EE:FF"
entry = MockConfigEntry(
domain=DOMAIN, data={CONF_USERNAME: "mock", CONF_PASSWORD: "mock"}
)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_HOMEKIT},
data=zeroconf.ZeroconfServiceInfo(
ip_address=ip_address("127.0.0.1"),
ip_addresses=[ip_address("127.0.0.1")],
hostname="mock_hostname",
name="mock_name",
port=None,
properties={zeroconf.ATTR_PROPERTIES_ID: "AA:BB:CC:DD:EE:FF"},
type="mock_type",
),
)
assert result["type"] == "abort"
Refactor Tado to use OAuth in the DeviceTracker (#102610) * Refactor to use TadoConnector in the DeviceTracker * Proposing myself as code owner to be notified of issues * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Fixing method names * Current progress, switching machines * Updating DeviceTracker to working prototype * Removing unnecessary callback * Adding dispatcher logic * Minor fine-tuning the intervals * Removing unnecessary debug log * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Fix sorting * Retrieve devices from the Tado connector data * Asyncio feedback & dispatch generic mobile devices * Updating const * Fine-tuning unloading * Making add_tracked_entites callback * Adding unload over dispatcher_connect * Convert on_demand_update to callback * Removing now unused method * Merging method to on_demand_u * Adding create_issue to address repair * Updating with better translation * Converting to callback * Adding _attr_should_poll * Putting back the on_demand_update * Adding unique_id * Converting to TrackerEntity * Adding import step (review needed!) * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Typing and location_name * Changing to _attr_unique_id * Import improvement attempt * Property feedback * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Adding CONF_HOME_ID and task in get_scanner * Updating descriptions * Removing the create_task * Putting back PLATFORM_SCHEMA * Adding device_tracker * Adding get for HomeID * Get it better ;) * Retrieve HomeID from API * Add integration title in dialogs * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Fixing homeID and strings.json * Delete request in strings * Update deprecation date * Adding test cases for import flow * Update tests/components/tado/test_config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/tado/test_config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/tado/test_config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Removing none * Fixing test cases * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Removing from context manager * Removing code owner * Re-adding code owner * Fix get scanner return value * Fix device tracker interface --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-12-27 14:17:23 +01:00
async def test_import_step(hass: HomeAssistant) -> None:
"""Test import step."""
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_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={
"username": "test-username",
"password": "test-password",
"home_id": 1,
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["data"] == {
"username": "test-username",
"password": "test-password",
"home_id": "1",
}
assert mock_setup_entry.call_count == 1
async def test_import_step_existing_entry(hass: HomeAssistant) -> None:
"""Test import step with existing entry."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
"username": "test-username",
"password": "test-password",
"home_id": 1,
},
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.tado.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={
"username": "test-username",
"password": "test-password",
"home_id": 1,
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "already_configured"
assert mock_setup_entry.call_count == 0
async def test_import_step_validation_failed(hass: HomeAssistant) -> None:
"""Test import step with validation failed."""
with patch(
"homeassistant.components.tado.config_flow.Tado",
side_effect=RuntimeError,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={
"username": "test-username",
"password": "test-password",
"home_id": 1,
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "import_failed"
async def test_import_step_device_authentication_failed(hass: HomeAssistant) -> None:
"""Test import step with device tracker authentication failed."""
with patch(
"homeassistant.components.tado.config_flow.Tado",
side_effect=PyTado.exceptions.TadoWrongCredentialsException,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={
"username": "test-username",
"password": "test-password",
"home_id": 1,
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "import_failed_invalid_auth"
Refactor Tado to use OAuth in the DeviceTracker (#102610) * Refactor to use TadoConnector in the DeviceTracker * Proposing myself as code owner to be notified of issues * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Fixing method names * Current progress, switching machines * Updating DeviceTracker to working prototype * Removing unnecessary callback * Adding dispatcher logic * Minor fine-tuning the intervals * Removing unnecessary debug log * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Fix sorting * Retrieve devices from the Tado connector data * Asyncio feedback & dispatch generic mobile devices * Updating const * Fine-tuning unloading * Making add_tracked_entites callback * Adding unload over dispatcher_connect * Convert on_demand_update to callback * Removing now unused method * Merging method to on_demand_u * Adding create_issue to address repair * Updating with better translation * Converting to callback * Adding _attr_should_poll * Putting back the on_demand_update * Adding unique_id * Converting to TrackerEntity * Adding import step (review needed!) * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/tado/device_tracker.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Typing and location_name * Changing to _attr_unique_id * Import improvement attempt * Property feedback * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Adding CONF_HOME_ID and task in get_scanner * Updating descriptions * Removing the create_task * Putting back PLATFORM_SCHEMA * Adding device_tracker * Adding get for HomeID * Get it better ;) * Retrieve HomeID from API * Add integration title in dialogs * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Fixing homeID and strings.json * Delete request in strings * Update deprecation date * Adding test cases for import flow * Update tests/components/tado/test_config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/tado/test_config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/tado/test_config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Removing none * Fixing test cases * Update homeassistant/components/tado/config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Removing from context manager * Removing code owner * Re-adding code owner * Fix get scanner return value * Fix device tracker interface --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-12-27 14:17:23 +01:00
async def test_import_step_unique_id_configured(hass: HomeAssistant) -> None:
"""Test import step with unique ID already configured."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
"username": "test-username",
"password": "test-password",
"home_id": 1,
},
unique_id="unique_id",
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.tado.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={
"username": "test-username",
"password": "test-password",
"home_id": 1,
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "already_configured"
assert mock_setup_entry.call_count == 0