From 1b27acdde00764f96878a95bab7b71cd6e77cc20 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Tue, 9 Sep 2025 21:08:04 +0200 Subject: [PATCH] Improve config entry migration for edge cases in Alexa Devices (#151788) --- .../components/alexa_devices/__init__.py | 24 +++++- .../components/alexa_devices/config_flow.py | 2 +- .../components/alexa_devices/const.py | 1 + tests/components/alexa_devices/conftest.py | 13 +++- .../snapshots/test_diagnostics.ambr | 3 +- tests/components/alexa_devices/test_init.py | 74 +++++++++++++++++-- 6 files changed, 102 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/alexa_devices/__init__.py b/homeassistant/components/alexa_devices/__init__.py index 9407a2d8987f..af0a3d7818cc 100644 --- a/homeassistant/components/alexa_devices/__init__.py +++ b/homeassistant/components/alexa_devices/__init__.py @@ -5,7 +5,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import aiohttp_client, config_validation as cv from homeassistant.helpers.typing import ConfigType -from .const import _LOGGER, CONF_LOGIN_DATA, COUNTRY_DOMAINS, DOMAIN +from .const import _LOGGER, CONF_LOGIN_DATA, CONF_SITE, COUNTRY_DOMAINS, DOMAIN from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator from .services import async_setup_services @@ -42,7 +42,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bo async def async_migrate_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool: """Migrate old entry.""" - if entry.version == 1 and entry.minor_version == 1: + + if entry.version == 1 and entry.minor_version < 3: + if CONF_SITE in entry.data: + # Site in data (wrong place), just move to login data + new_data = entry.data.copy() + new_data[CONF_LOGIN_DATA][CONF_SITE] = new_data[CONF_SITE] + new_data.pop(CONF_SITE) + hass.config_entries.async_update_entry( + entry, data=new_data, version=1, minor_version=3 + ) + return True + + if CONF_SITE in entry.data[CONF_LOGIN_DATA]: + # Site is there, just update version to avoid future migrations + hass.config_entries.async_update_entry(entry, version=1, minor_version=3) + return True + _LOGGER.debug( "Migrating from version %s.%s", entry.version, entry.minor_version ) @@ -53,10 +69,10 @@ async def async_migrate_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> # Add site to login data new_data = entry.data.copy() - new_data[CONF_LOGIN_DATA]["site"] = f"https://www.amazon.{domain}" + new_data[CONF_LOGIN_DATA][CONF_SITE] = f"https://www.amazon.{domain}" hass.config_entries.async_update_entry( - entry, data=new_data, version=1, minor_version=2 + entry, data=new_data, version=1, minor_version=3 ) _LOGGER.info( diff --git a/homeassistant/components/alexa_devices/config_flow.py b/homeassistant/components/alexa_devices/config_flow.py index ccf18fd45585..f266a8688547 100644 --- a/homeassistant/components/alexa_devices/config_flow.py +++ b/homeassistant/components/alexa_devices/config_flow.py @@ -52,7 +52,7 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN): """Handle a config flow for Alexa Devices.""" VERSION = 1 - MINOR_VERSION = 2 + MINOR_VERSION = 3 async def async_step_user( self, user_input: dict[str, Any] | None = None diff --git a/homeassistant/components/alexa_devices/const.py b/homeassistant/components/alexa_devices/const.py index c60096bae571..e783f67f5034 100644 --- a/homeassistant/components/alexa_devices/const.py +++ b/homeassistant/components/alexa_devices/const.py @@ -6,6 +6,7 @@ _LOGGER = logging.getLogger(__package__) DOMAIN = "alexa_devices" CONF_LOGIN_DATA = "login_data" +CONF_SITE = "site" DEFAULT_DOMAIN = "com" COUNTRY_DOMAINS = { diff --git a/tests/components/alexa_devices/conftest.py b/tests/components/alexa_devices/conftest.py index 236f7b23dc48..2ef2c2431dc1 100644 --- a/tests/components/alexa_devices/conftest.py +++ b/tests/components/alexa_devices/conftest.py @@ -7,7 +7,11 @@ from aioamazondevices.api import AmazonDevice, AmazonDeviceSensor from aioamazondevices.const import DEVICE_TYPE_TO_MODEL import pytest -from homeassistant.components.alexa_devices.const import CONF_LOGIN_DATA, DOMAIN +from homeassistant.components.alexa_devices.const import ( + CONF_LOGIN_DATA, + CONF_SITE, + DOMAIN, +) from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from .const import TEST_PASSWORD, TEST_SERIAL_NUMBER, TEST_USERNAME @@ -81,9 +85,12 @@ def mock_config_entry() -> MockConfigEntry: data={ CONF_USERNAME: TEST_USERNAME, CONF_PASSWORD: TEST_PASSWORD, - CONF_LOGIN_DATA: {"session": "test-session"}, + CONF_LOGIN_DATA: { + "session": "test-session", + CONF_SITE: "https://www.amazon.com", + }, }, unique_id=TEST_USERNAME, version=1, - minor_version=2, + minor_version=3, ) diff --git a/tests/components/alexa_devices/snapshots/test_diagnostics.ambr b/tests/components/alexa_devices/snapshots/test_diagnostics.ambr index 6f9dc9a5cc3e..9ae5832ce334 100644 --- a/tests/components/alexa_devices/snapshots/test_diagnostics.ambr +++ b/tests/components/alexa_devices/snapshots/test_diagnostics.ambr @@ -49,6 +49,7 @@ 'data': dict({ 'login_data': dict({ 'session': 'test-session', + 'site': 'https://www.amazon.com', }), 'password': '**REDACTED**', 'username': '**REDACTED**', @@ -57,7 +58,7 @@ 'discovery_keys': dict({ }), 'domain': 'alexa_devices', - 'minor_version': 2, + 'minor_version': 3, 'options': dict({ }), 'pref_disable_new_entities': False, diff --git a/tests/components/alexa_devices/test_init.py b/tests/components/alexa_devices/test_init.py index 6c3faffd27b8..328654682e91 100644 --- a/tests/components/alexa_devices/test_init.py +++ b/tests/components/alexa_devices/test_init.py @@ -2,9 +2,14 @@ from unittest.mock import AsyncMock +import pytest from syrupy.assertion import SnapshotAssertion -from homeassistant.components.alexa_devices.const import CONF_LOGIN_DATA, DOMAIN +from homeassistant.components.alexa_devices.const import ( + CONF_LOGIN_DATA, + CONF_SITE, + DOMAIN, +) from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant @@ -32,24 +37,81 @@ async def test_device_info( assert device_entry == snapshot +@pytest.mark.parametrize( + ("minor_version", "extra_data"), + [ + # Standard migration case + ( + 1, + { + CONF_COUNTRY: "US", + CONF_LOGIN_DATA: { + "session": "test-session", + }, + }, + ), + # Edge case #1: no country, site already in login data, minor version 1 + ( + 1, + { + CONF_LOGIN_DATA: { + "session": "test-session", + CONF_SITE: "https://www.amazon.com", + }, + }, + ), + # Edge case #2: no country, site in data (wrong place), minor version 1 + ( + 1, + { + CONF_SITE: "https://www.amazon.com", + CONF_LOGIN_DATA: { + "session": "test-session", + }, + }, + ), + # Edge case #3: no country, site already in login data, minor version 2 + ( + 2, + { + CONF_LOGIN_DATA: { + "session": "test-session", + CONF_SITE: "https://www.amazon.com", + }, + }, + ), + # Edge case #4: no country, site in data (wrong place), minor version 2 + ( + 2, + { + CONF_SITE: "https://www.amazon.com", + CONF_LOGIN_DATA: { + "session": "test-session", + }, + }, + ), + ], +) async def test_migrate_entry( hass: HomeAssistant, mock_amazon_devices_client: AsyncMock, mock_config_entry: MockConfigEntry, + minor_version: int, + extra_data: dict[str, str], ) -> None: """Test successful migration of entry data.""" + config_entry = MockConfigEntry( domain=DOMAIN, title="Amazon Test Account", data={ - CONF_COUNTRY: "US", # country should be in COUNTRY_DOMAINS exceptions CONF_USERNAME: TEST_USERNAME, CONF_PASSWORD: TEST_PASSWORD, - CONF_LOGIN_DATA: {"session": "test-session"}, + **(extra_data), }, unique_id=TEST_USERNAME, version=1, - minor_version=1, + minor_version=minor_version, ) config_entry.add_to_hass(hass) await hass.config_entries.async_setup(config_entry.entry_id) @@ -57,5 +119,5 @@ async def test_migrate_entry( assert len(hass.config_entries.async_entries(DOMAIN)) == 1 assert config_entry.state is ConfigEntryState.LOADED - assert config_entry.minor_version == 2 - assert config_entry.data[CONF_LOGIN_DATA]["site"] == "https://www.amazon.com" + assert config_entry.minor_version == 3 + assert config_entry.data[CONF_LOGIN_DATA][CONF_SITE] == "https://www.amazon.com"