1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-19 18:38:58 +00:00

Store unique user configurations for HomeLink integration (#159111)

This commit is contained in:
ryanjones-gentex
2025-12-16 11:14:49 -05:00
committed by GitHub
parent fe34da19e2
commit 36aefce9e1
5 changed files with 53 additions and 13 deletions

View File

@@ -5,6 +5,7 @@ from typing import Any
import botocore.exceptions
from homelink.auth.srp_auth import SRPAuth
import jwt
import voluptuous as vol
from homeassistant.config_entries import ConfigFlowResult
@@ -38,8 +39,6 @@ class SRPFlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN):
"""Ask for username and password."""
errors: dict[str, str] = {}
if user_input is not None:
self._async_abort_entries_match({CONF_EMAIL: user_input[CONF_EMAIL]})
srp_auth = SRPAuth()
try:
tokens = await self.hass.async_add_executor_job(
@@ -48,12 +47,17 @@ class SRPFlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN):
user_input[CONF_PASSWORD],
)
except botocore.exceptions.ClientError:
_LOGGER.exception("Error authenticating homelink account")
errors["base"] = "srp_auth_failed"
except Exception:
_LOGGER.exception("An unexpected error occurred")
errors["base"] = "unknown"
else:
access_token = jwt.decode(
tokens["AuthenticationResult"]["AccessToken"],
options={"verify_signature": False},
)
await self.async_set_unique_id(access_token["sub"])
self._abort_if_unique_id_configured()
self.external_data = {"tokens": tokens}
return await self.async_step_creation()

View File

@@ -1,10 +1,9 @@
"""Makes requests to the state server and stores the resulting data so that the buttons can access it."""
"""Establish MQTT connection and listen for event data."""
from __future__ import annotations
from collections.abc import Callable
from functools import partial
import logging
from typing import TypedDict
from homelink.model.device import Device
@@ -14,8 +13,6 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.util.ssl import get_default_context
_LOGGER = logging.getLogger(__name__)
type HomeLinkConfigEntry = ConfigEntry[HomeLinkCoordinator]
type EventCallback = Callable[[HomeLinkEventData], None]

View File

@@ -3,10 +3,17 @@
from typing import Any
from unittest.mock import AsyncMock
import jwt
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
TEST_CREDENTIALS = {CONF_EMAIL: "test@test.com", CONF_PASSWORD: "SomePassword"}
TEST_ACCESS_JWT = jwt.encode({"sub": "some-uuid"}, key="secret")
async def setup_integration(hass: HomeAssistant, entry: MockConfigEntry) -> None:
"""Set up the homelink integration for testing."""

View File

@@ -9,6 +9,8 @@ import pytest
from homeassistant.components.gentex_homelink import DOMAIN
from . import TEST_ACCESS_JWT
from tests.common import MockConfigEntry
@@ -21,7 +23,7 @@ def mock_srp_auth() -> Generator[AsyncMock]:
instance = mock_srp_auth.return_value
instance.async_get_access_token.return_value = {
"AuthenticationResult": {
"AccessToken": "access",
"AccessToken": TEST_ACCESS_JWT,
"RefreshToken": "refresh",
"TokenType": "bearer",
"ExpiresIn": 3600,
@@ -60,6 +62,8 @@ def mock_device() -> AsyncMock:
def mock_config_entry() -> MockConfigEntry:
"""Mock setup entry."""
return MockConfigEntry(
unique_id="some-uuid",
version=1,
domain=DOMAIN,
data={
"auth_implementation": "gentex_homelink",

View File

@@ -7,10 +7,13 @@ import pytest
from homeassistant.components.gentex_homelink.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from . import TEST_ACCESS_JWT, TEST_CREDENTIALS, setup_integration
from tests.common import MockConfigEntry
async def test_full_flow(
hass: HomeAssistant, mock_srp_auth: AsyncMock, mock_setup_entry: AsyncMock
@@ -26,13 +29,13 @@ async def test_full_flow(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_EMAIL: "test@test.com", CONF_PASSWORD: "SomePassword"},
user_input=TEST_CREDENTIALS,
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {
"auth_implementation": "gentex_homelink",
"token": {
"access_token": "access",
"access_token": TEST_ACCESS_JWT,
"refresh_token": "refresh",
"expires_in": 3600,
"token_type": "bearer",
@@ -40,6 +43,31 @@ async def test_full_flow(
},
}
assert result["title"] == "SRPAuth"
assert result["result"].unique_id == "some-uuid"
async def test_unique_configurations(
hass: HomeAssistant,
mock_srp_auth: AsyncMock,
mock_setup_entry: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Check full flow."""
await setup_integration(hass, mock_config_entry)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert not result["errors"]
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=TEST_CREDENTIALS,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
@pytest.mark.parametrize(
@@ -69,7 +97,7 @@ async def test_exceptions(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_EMAIL: "test@test.com", CONF_PASSWORD: "SomePassword"},
user_input=TEST_CREDENTIALS,
)
assert result["type"] is FlowResultType.FORM
@@ -79,6 +107,6 @@ async def test_exceptions(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_EMAIL: "test@test.com", CONF_PASSWORD: "SomePassword"},
user_input=TEST_CREDENTIALS,
)
assert result["type"] is FlowResultType.CREATE_ENTRY