Skip to content

Commit

Permalink
Catch Hydrawise authorization errors in the correct place (#132727)
Browse files Browse the repository at this point in the history
  • Loading branch information
dknowles2 authored and frenck committed Dec 10, 2024
1 parent e4765c4 commit 60e8a38
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 14 deletions.
15 changes: 10 additions & 5 deletions homeassistant/components/hydrawise/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Any

from aiohttp import ClientError
from pydrawise import auth, client
from pydrawise import auth as pydrawise_auth, client
from pydrawise.exceptions import NotAuthorizedError
import voluptuous as vol

Expand All @@ -29,16 +29,21 @@ async def _create_or_update_entry(
on_failure: Callable[[str], ConfigFlowResult],
) -> ConfigFlowResult:
"""Create the config entry."""

# Verify that the provided credentials work."""
api = client.Hydrawise(auth.Auth(username, password))
auth = pydrawise_auth.Auth(username, password)
try:
# Don't fetch zones because we don't need them yet.
user = await api.get_user(fetch_zones=False)
await auth.token()
except NotAuthorizedError:
return on_failure("invalid_auth")
except TimeoutError:
return on_failure("timeout_connect")

try:
api = client.Hydrawise(auth)
# Don't fetch zones because we don't need them yet.
user = await api.get_user(fetch_zones=False)
except TimeoutError:
return on_failure("timeout_connect")
except ClientError as ex:
LOGGER.error("Unable to connect to Hydrawise cloud service: %s", ex)
return on_failure("cannot_connect")
Expand Down
1 change: 0 additions & 1 deletion tests/components/hydrawise/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ def mock_legacy_pydrawise(

@pytest.fixture
def mock_pydrawise(
mock_auth: AsyncMock,
user: User,
controller: Controller,
zones: list[Zone],
Expand Down
39 changes: 31 additions & 8 deletions tests/components/hydrawise/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
async def test_form(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_auth: AsyncMock,
mock_pydrawise: AsyncMock,
user: User,
) -> None:
Expand All @@ -46,11 +47,12 @@ async def test_form(
CONF_PASSWORD: "__password__",
}
assert len(mock_setup_entry.mock_calls) == 1
mock_pydrawise.get_user.assert_called_once_with(fetch_zones=False)
mock_auth.token.assert_awaited_once_with()
mock_pydrawise.get_user.assert_awaited_once_with(fetch_zones=False)


async def test_form_api_error(
hass: HomeAssistant, mock_pydrawise: AsyncMock, user: User
hass: HomeAssistant, mock_auth: AsyncMock, mock_pydrawise: AsyncMock, user: User
) -> None:
"""Test we handle API errors."""
mock_pydrawise.get_user.side_effect = ClientError("XXX")
Expand All @@ -71,8 +73,29 @@ async def test_form_api_error(
assert result2["type"] is FlowResultType.CREATE_ENTRY


async def test_form_connect_timeout(
hass: HomeAssistant, mock_pydrawise: AsyncMock, user: User
async def test_form_auth_connect_timeout(
hass: HomeAssistant, mock_auth: AsyncMock, mock_pydrawise: AsyncMock
) -> None:
"""Test we handle API errors."""
mock_auth.token.side_effect = TimeoutError
init_result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
data = {CONF_USERNAME: "[email protected]", CONF_PASSWORD: "__password__"}
result = await hass.config_entries.flow.async_configure(
init_result["flow_id"], data
)

assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "timeout_connect"}

mock_auth.token.reset_mock(side_effect=True)
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data)
assert result2["type"] is FlowResultType.CREATE_ENTRY


async def test_form_client_connect_timeout(
hass: HomeAssistant, mock_auth: AsyncMock, mock_pydrawise: AsyncMock, user: User
) -> None:
"""Test we handle API errors."""
mock_pydrawise.get_user.side_effect = TimeoutError
Expand All @@ -94,10 +117,10 @@ async def test_form_connect_timeout(


async def test_form_not_authorized_error(
hass: HomeAssistant, mock_pydrawise: AsyncMock, user: User
hass: HomeAssistant, mock_auth: AsyncMock, mock_pydrawise: AsyncMock
) -> None:
"""Test we handle API errors."""
mock_pydrawise.get_user.side_effect = NotAuthorizedError
mock_auth.token.side_effect = NotAuthorizedError

init_result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
Expand All @@ -109,15 +132,15 @@ async def test_form_not_authorized_error(
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "invalid_auth"}

mock_pydrawise.get_user.reset_mock(side_effect=True)
mock_pydrawise.get_user.return_value = user
mock_auth.token.reset_mock(side_effect=True)
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data)
assert result2["type"] is FlowResultType.CREATE_ENTRY


async def test_reauth(
hass: HomeAssistant,
user: User,
mock_auth: AsyncMock,
mock_pydrawise: AsyncMock,
) -> None:
"""Test that re-authorization works."""
Expand Down

0 comments on commit 60e8a38

Please sign in to comment.