Refresh Home Connect token during config entry setup (#140233)

* Refresh token during config entry setup

* Test 500 error

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
J. Diego Rodríguez Royo 2025-03-09 21:59:09 +01:00 committed by Franck Nijhof
parent bbbb5cadd4
commit 06188b8fbd
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
2 changed files with 71 additions and 6 deletions

View File

@ -16,11 +16,17 @@ from aiohomeconnect.model import (
SettingKey, SettingKey,
) )
from aiohomeconnect.model.error import HomeConnectError from aiohomeconnect.model.error import HomeConnectError
import aiohttp
import voluptuous as vol import voluptuous as vol
from homeassistant.const import ATTR_DEVICE_ID, Platform from homeassistant.const import ATTR_DEVICE_ID, Platform
from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.exceptions import (
ConfigEntryAuthFailed,
ConfigEntryNotReady,
HomeAssistantError,
ServiceValidationError,
)
from homeassistant.helpers import ( from homeassistant.helpers import (
config_entry_oauth2_flow, config_entry_oauth2_flow,
config_validation as cv, config_validation as cv,
@ -611,6 +617,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: HomeConnectConfigEntry)
session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)
config_entry_auth = AsyncConfigEntryAuth(hass, session) config_entry_auth = AsyncConfigEntryAuth(hass, session)
try:
await config_entry_auth.async_get_access_token()
except aiohttp.ClientResponseError as err:
if 400 <= err.status < 500:
raise ConfigEntryAuthFailed from err
raise ConfigEntryNotReady from err
except aiohttp.ClientError as err:
raise ConfigEntryNotReady from err
home_connect_client = HomeConnectClient(config_entry_auth) home_connect_client = HomeConnectClient(config_entry_auth)

View File

@ -8,9 +8,8 @@ from unittest.mock import MagicMock, patch
from aiohomeconnect.const import OAUTH2_TOKEN from aiohomeconnect.const import OAUTH2_TOKEN
from aiohomeconnect.model import OptionKey, ProgramKey, SettingKey, StatusKey from aiohomeconnect.model import OptionKey, ProgramKey, SettingKey, StatusKey
from aiohomeconnect.model.error import HomeConnectError, UnauthorizedError from aiohomeconnect.model.error import HomeConnectError, UnauthorizedError
import aiohttp
import pytest import pytest
import requests_mock
import respx
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
@ -221,14 +220,12 @@ async def test_exception_handling(
@pytest.mark.parametrize("token_expiration_time", [12345]) @pytest.mark.parametrize("token_expiration_time", [12345])
@respx.mock
async def test_token_refresh_success( async def test_token_refresh_success(
hass: HomeAssistant, hass: HomeAssistant,
platforms: list[Platform], platforms: list[Platform],
integration_setup: Callable[[MagicMock], Awaitable[bool]], integration_setup: Callable[[MagicMock], Awaitable[bool]],
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
requests_mock: requests_mock.Mocker,
setup_credentials: None, setup_credentials: None,
client: MagicMock, client: MagicMock,
) -> None: ) -> None:
@ -236,7 +233,6 @@ async def test_token_refresh_success(
assert config_entry.data["token"]["access_token"] == FAKE_ACCESS_TOKEN assert config_entry.data["token"]["access_token"] == FAKE_ACCESS_TOKEN
requests_mock.post(OAUTH2_TOKEN, json=SERVER_ACCESS_TOKEN)
aioclient_mock.post( aioclient_mock.post(
OAUTH2_TOKEN, OAUTH2_TOKEN,
json=SERVER_ACCESS_TOKEN, json=SERVER_ACCESS_TOKEN,
@ -280,6 +276,61 @@ async def test_token_refresh_success(
) )
@pytest.mark.parametrize("token_expiration_time", [12345])
@pytest.mark.parametrize(
("aioclient_mock_args", "expected_config_entry_state"),
[
(
{
"status": 400,
"json": {"error": "invalid_grant"},
},
ConfigEntryState.SETUP_ERROR,
),
(
{
"status": 500,
},
ConfigEntryState.SETUP_RETRY,
),
(
{
"exc": aiohttp.ClientError,
},
ConfigEntryState.SETUP_RETRY,
),
],
)
async def test_token_refresh_error(
aioclient_mock_args: dict[str, Any],
expected_config_entry_state: ConfigEntryState,
hass: HomeAssistant,
platforms: list[Platform],
integration_setup: Callable[[MagicMock], Awaitable[bool]],
config_entry: MockConfigEntry,
aioclient_mock: AiohttpClientMocker,
setup_credentials: None,
client: MagicMock,
) -> None:
"""Test where token is expired and the refresh attempt fails."""
config_entry.data["token"]["access_token"] = FAKE_ACCESS_TOKEN
aioclient_mock.post(
OAUTH2_TOKEN,
**aioclient_mock_args,
)
assert config_entry.state == ConfigEntryState.NOT_LOADED
with patch(
"homeassistant.components.home_connect.HomeConnectClient", return_value=client
):
assert not await integration_setup(client)
await hass.async_block_till_done()
assert config_entry.state == expected_config_entry_state
@pytest.mark.parametrize( @pytest.mark.parametrize(
("exception", "expected_state"), ("exception", "expected_state"),
[ [