From 8edf7f040740636419a7c92dab634a46865d7796 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 26 May 2021 11:32:26 -0400 Subject: [PATCH] Don't enforce uniqueness requirements for Waze and Google Travel Time (#50254) Co-authored-by: Paulus Schoutsen --- .../components/google_travel_time/__init__.py | 19 +- .../google_travel_time/config_flow.py | 63 +++- .../components/google_travel_time/sensor.py | 2 +- .../components/waze_travel_time/__init__.py | 15 + .../waze_travel_time/config_flow.py | 69 +++- .../components/waze_travel_time/sensor.py | 2 +- .../components/google_travel_time/conftest.py | 10 + .../google_travel_time/test_config_flow.py | 323 +++++++++++++++++- .../google_travel_time/test_init.py | 21 ++ tests/components/waze_travel_time/conftest.py | 10 + .../waze_travel_time/test_config_flow.py | 124 ++++++- .../components/waze_travel_time/test_init.py | 21 ++ 12 files changed, 651 insertions(+), 28 deletions(-) create mode 100644 tests/components/google_travel_time/test_init.py create mode 100644 tests/components/waze_travel_time/test_init.py diff --git a/homeassistant/components/google_travel_time/__init__.py b/homeassistant/components/google_travel_time/__init__.py index 5d4b3d1b74a..bad6edd119e 100644 --- a/homeassistant/components/google_travel_time/__init__.py +++ b/homeassistant/components/google_travel_time/__init__.py @@ -1,14 +1,29 @@ """The google_travel_time component.""" +import logging from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_registry import ( + async_entries_for_config_entry, + async_get, +) PLATFORMS = ["sensor"] +_LOGGER = logging.getLogger(__name__) -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): +async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry): """Set up Google Maps Travel Time from a config entry.""" - hass.config_entries.async_setup_platforms(entry, PLATFORMS) + if config_entry.unique_id is not None: + hass.config_entries.async_update_entry(config_entry, unique_id=None) + + ent_reg = async_get(hass) + for entity in async_entries_for_config_entry(ent_reg, config_entry.entry_id): + ent_reg.async_update_entity( + entity.entity_id, new_unique_id=config_entry.entry_id + ) + + hass.config_entries.async_setup_platforms(config_entry, PLATFORMS) return True diff --git a/homeassistant/components/google_travel_time/config_flow.py b/homeassistant/components/google_travel_time/config_flow.py index 603a0ec12f0..931e1f355aa 100644 --- a/homeassistant/components/google_travel_time/config_flow.py +++ b/homeassistant/components/google_travel_time/config_flow.py @@ -1,13 +1,15 @@ """Config flow for Google Maps Travel Time integration.""" +from __future__ import annotations + import logging +from typing import Any import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_NAME -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv -from homeassistant.util import slugify from .const import ( ALL_LANGUAGES, @@ -18,12 +20,14 @@ from .const import ( CONF_DEPARTURE_TIME, CONF_DESTINATION, CONF_LANGUAGE, + CONF_OPTIONS, CONF_ORIGIN, CONF_TIME, CONF_TIME_TYPE, CONF_TRAFFIC_MODEL, CONF_TRANSIT_MODE, CONF_TRANSIT_ROUTING_PREFERENCE, + CONF_TRAVEL_MODE, CONF_UNITS, DEFAULT_NAME, DEPARTURE_TIME, @@ -40,6 +44,47 @@ from .helpers import is_valid_config_entry _LOGGER = logging.getLogger(__name__) +def is_dupe_import( + hass: HomeAssistant, entry: config_entries.ConfigEntry, user_input: dict[str, Any] +) -> bool: + """Return whether imported config already exists.""" + # Check the main data keys + if any( + entry.data[key] != user_input[key] + for key in (CONF_API_KEY, CONF_DESTINATION, CONF_ORIGIN) + ): + return False + + options = user_input.get(CONF_OPTIONS, {}) + + # We have to check for units differently because there is a default + units = options.get(CONF_UNITS) or hass.config.units.name + if entry.options[CONF_UNITS] != units: + return False + + # We have to check for travel mode differently because of the default and because + # it can be provided in two different ways. We have to give mode preference over + # travel mode because that's the way that entry setup works. + mode = options.get(CONF_MODE) or user_input.get(CONF_TRAVEL_MODE) or "driving" + if entry.options[CONF_MODE] != mode: + return False + + # We have to check for options that don't have defaults + for key in ( + CONF_LANGUAGE, + CONF_AVOID, + CONF_ARRIVAL_TIME, + CONF_DEPARTURE_TIME, + CONF_TRAFFIC_MODEL, + CONF_TRANSIT_MODE, + CONF_TRANSIT_ROUTING_PREFERENCE, + ): + if options.get(key) != entry.options.get(key): + return False + + return True + + class GoogleOptionsFlow(config_entries.OptionsFlow): """Handle an options flow for Google Travel Time.""" @@ -126,12 +171,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors = {} user_input = user_input or {} if user_input: - await self.async_set_unique_id( - slugify( - f"{DOMAIN}_{user_input[CONF_ORIGIN]}_{user_input[CONF_DESTINATION]}" - ) - ) - self._abort_if_unique_id_configured() + # We need to prevent duplicate imports + if self.source == config_entries.SOURCE_IMPORT and any( + is_dupe_import(self.hass, entry, user_input) + for entry in self.hass.config_entries.async_entries(DOMAIN) + if entry.source == config_entries.SOURCE_IMPORT + ): + return self.async_abort(reason="already_configured") + if ( self.source == config_entries.SOURCE_IMPORT or await self.hass.async_add_executor_job( diff --git a/homeassistant/components/google_travel_time/sensor.py b/homeassistant/components/google_travel_time/sensor.py index b6019ba2991..6dbe6aa698b 100644 --- a/homeassistant/components/google_travel_time/sensor.py +++ b/homeassistant/components/google_travel_time/sensor.py @@ -172,7 +172,7 @@ class GoogleTravelTimeSensor(SensorEntity): self._unit_of_measurement = TIME_MINUTES self._matrix = None self._api_key = api_key - self._unique_id = config_entry.unique_id + self._unique_id = config_entry.entry_id self._client = client # Check if location is a trackable entity diff --git a/homeassistant/components/waze_travel_time/__init__.py b/homeassistant/components/waze_travel_time/__init__.py index 5800cfe94ab..57382689f61 100644 --- a/homeassistant/components/waze_travel_time/__init__.py +++ b/homeassistant/components/waze_travel_time/__init__.py @@ -1,13 +1,28 @@ """The waze_travel_time component.""" +import logging from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_registry import ( + async_entries_for_config_entry, + async_get, +) PLATFORMS = ["sensor"] +_LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Load the saved entities.""" + if config_entry.unique_id is not None: + hass.config_entries.async_update_entry(config_entry, unique_id=None) + + ent_reg = async_get(hass) + for entity in async_entries_for_config_entry(ent_reg, config_entry.entry_id): + ent_reg.async_update_entity( + entity.entity_id, new_unique_id=config_entry.entry_id + ) + hass.config_entries.async_setup_platforms(config_entry, PLATFORMS) return True diff --git a/homeassistant/components/waze_travel_time/config_flow.py b/homeassistant/components/waze_travel_time/config_flow.py index cdd83a35aa1..54097ad37bd 100644 --- a/homeassistant/components/waze_travel_time/config_flow.py +++ b/homeassistant/components/waze_travel_time/config_flow.py @@ -1,13 +1,15 @@ """Config flow for Waze Travel Time integration.""" +from __future__ import annotations + import logging +from typing import Any import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_NAME, CONF_REGION -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv -from homeassistant.util import slugify from .const import ( CONF_AVOID_FERRIES, @@ -20,7 +22,12 @@ from .const import ( CONF_REALTIME, CONF_UNITS, CONF_VEHICLE_TYPE, + DEFAULT_AVOID_FERRIES, + DEFAULT_AVOID_SUBSCRIPTION_ROADS, + DEFAULT_AVOID_TOLL_ROADS, DEFAULT_NAME, + DEFAULT_REALTIME, + DEFAULT_VEHICLE_TYPE, DOMAIN, REGIONS, UNITS, @@ -31,6 +38,50 @@ from .helpers import is_valid_config_entry _LOGGER = logging.getLogger(__name__) +def is_dupe_import( + hass: HomeAssistant, entry: config_entries.ConfigEntry, user_input: dict[str, Any] +) -> bool: + """Return whether imported config already exists.""" + entry_data = {**entry.data, **entry.options} + defaults = { + CONF_REALTIME: DEFAULT_REALTIME, + CONF_VEHICLE_TYPE: DEFAULT_VEHICLE_TYPE, + CONF_UNITS: hass.config.units.name, + CONF_AVOID_FERRIES: DEFAULT_AVOID_FERRIES, + CONF_AVOID_SUBSCRIPTION_ROADS: DEFAULT_AVOID_SUBSCRIPTION_ROADS, + CONF_AVOID_TOLL_ROADS: DEFAULT_AVOID_TOLL_ROADS, + } + + for key in ( + CONF_ORIGIN, + CONF_DESTINATION, + CONF_REGION, + CONF_INCL_FILTER, + CONF_EXCL_FILTER, + CONF_REALTIME, + CONF_VEHICLE_TYPE, + CONF_UNITS, + CONF_AVOID_FERRIES, + CONF_AVOID_SUBSCRIPTION_ROADS, + CONF_AVOID_TOLL_ROADS, + ): + # If the key is present the check is simple + if key in user_input and user_input[key] != entry_data[key]: + return False + + # If the key is not present, then we have to check if the key has a default and + # if the default is in the options. If it doesn't have a default, we have to check + # if the key is in the options + if key not in user_input: + if key in defaults and defaults[key] != entry_data[key]: + return False + + if key not in defaults and key in entry_data: + return False + + return True + + class WazeOptionsFlow(config_entries.OptionsFlow): """Handle an options flow for Waze Travel Time.""" @@ -108,12 +159,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): user_input = user_input or {} if user_input: - await self.async_set_unique_id( - slugify( - f"{DOMAIN}_{user_input[CONF_ORIGIN]}_{user_input[CONF_DESTINATION]}" - ) - ) - self._abort_if_unique_id_configured() + # We need to prevent duplicate imports + if self.source == config_entries.SOURCE_IMPORT and any( + is_dupe_import(self.hass, entry, user_input) + for entry in self.hass.config_entries.async_entries(DOMAIN) + if entry.source == config_entries.SOURCE_IMPORT + ): + return self.async_abort(reason="already_configured") + if ( self.source == config_entries.SOURCE_IMPORT or await self.hass.async_add_executor_job( diff --git a/homeassistant/components/waze_travel_time/sensor.py b/homeassistant/components/waze_travel_time/sensor.py index bd8e41dc31c..4fb56700f59 100644 --- a/homeassistant/components/waze_travel_time/sensor.py +++ b/homeassistant/components/waze_travel_time/sensor.py @@ -158,7 +158,7 @@ async def async_setup_entry( config_entry, ) - sensor = WazeTravelTime(config_entry.unique_id, name, origin, destination, data) + sensor = WazeTravelTime(config_entry.entry_id, name, origin, destination, data) async_add_entities([sensor], False) diff --git a/tests/components/google_travel_time/conftest.py b/tests/components/google_travel_time/conftest.py index 18e16a79e27..87d922ac22c 100644 --- a/tests/components/google_travel_time/conftest.py +++ b/tests/components/google_travel_time/conftest.py @@ -37,6 +37,16 @@ def bypass_setup_fixture(): yield +@pytest.fixture(name="bypass_platform_setup") +def bypass_platform_setup_fixture(): + """Bypass platform setup.""" + with patch( + "homeassistant.components.google_travel_time.sensor.async_setup_entry", + return_value=True, + ): + yield + + @pytest.fixture(name="bypass_update") def bypass_update_fixture(): """Bypass sensor update.""" diff --git a/tests/components/google_travel_time/test_config_flow.py b/tests/components/google_travel_time/test_config_flow.py index a0767246087..6118aac7cfa 100644 --- a/tests/components/google_travel_time/test_config_flow.py +++ b/tests/components/google_travel_time/test_config_flow.py @@ -14,6 +14,7 @@ from homeassistant.components.google_travel_time.const import ( CONF_TRAFFIC_MODEL, CONF_TRANSIT_MODE, CONF_TRANSIT_ROUTING_PREFERENCE, + CONF_TRAVEL_MODE, CONF_UNITS, DEFAULT_NAME, DEPARTURE_TIME, @@ -24,6 +25,7 @@ from homeassistant.const import ( CONF_MODE, CONF_NAME, CONF_UNIT_SYSTEM_IMPERIAL, + CONF_UNIT_SYSTEM_METRIC, ) from tests.common import MockConfigEntry @@ -197,8 +199,8 @@ async def test_options_flow_departure_time(hass, validate_config_entry, bypass_u } -async def test_dupe_id(hass, validate_config_entry, bypass_setup): - """Test setting up the same entry twice fails.""" +async def test_dupe(hass, validate_config_entry, bypass_setup): + """Test setting up the same entry data twice is OK.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -233,8 +235,7 @@ async def test_dupe_id(hass, validate_config_entry, bypass_setup): ) await hass.async_block_till_done() - assert result2["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result2["reason"] == "already_configured" + assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY async def test_import_flow(hass, validate_config_entry, bypass_update): @@ -297,3 +298,317 @@ async def test_import_flow(hass, validate_config_entry, bypass_update): CONF_TRANSIT_MODE: "train", CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", } + + +async def test_dupe_import_no_options(hass, bypass_update): + """Test duplicate import with no options.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def test_dupe_import_default_options(hass, bypass_update): + """Test duplicate import with default options.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_OPTIONS: { + CONF_LANGUAGE: "en", + CONF_AVOID: "tolls", + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_OPTIONS: { + CONF_LANGUAGE: "en", + CONF_AVOID: "tolls", + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def _setup_dupe_import(hass, bypass_update): + """Set up dupe import.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_OPTIONS: { + CONF_MODE: "walking", + CONF_LANGUAGE: "en", + CONF_AVOID: "tolls", + CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() + + +async def test_dupe_import(hass, bypass_update): + """Test duplicate import.""" + await _setup_dupe_import(hass, bypass_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_OPTIONS: { + CONF_MODE: "walking", + CONF_LANGUAGE: "en", + CONF_AVOID: "tolls", + CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def test_dupe_import_false_check_data_keys(hass, bypass_update): + """Test false duplicate import check when data keys differ.""" + await _setup_dupe_import(hass, bypass_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key2", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_OPTIONS: { + CONF_MODE: "walking", + CONF_LANGUAGE: "en", + CONF_AVOID: "tolls", + CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + + +async def test_dupe_import_false_check_no_units(hass, bypass_update): + """Test false duplicate import check when units aren't provided.""" + await _setup_dupe_import(hass, bypass_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_OPTIONS: { + CONF_MODE: "walking", + CONF_LANGUAGE: "en", + CONF_AVOID: "tolls", + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + + +async def test_dupe_import_false_check_units(hass, bypass_update): + """Test false duplicate import check when units are provided but different.""" + await _setup_dupe_import(hass, bypass_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_OPTIONS: { + CONF_MODE: "walking", + CONF_LANGUAGE: "en", + CONF_AVOID: "tolls", + CONF_UNITS: CONF_UNIT_SYSTEM_METRIC, + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + + +async def test_dupe_import_false_check_travel_mode(hass, bypass_update): + """Test false duplicate import check when travel mode differs.""" + await _setup_dupe_import(hass, bypass_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_TRAVEL_MODE: "driving", + CONF_OPTIONS: { + CONF_LANGUAGE: "en", + CONF_AVOID: "tolls", + CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + + +async def test_dupe_import_false_check_mode(hass, bypass_update): + """Test false duplicate import check when mode diiffers.""" + await _setup_dupe_import(hass, bypass_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_OPTIONS: { + CONF_MODE: "driving", + CONF_LANGUAGE: "en", + CONF_AVOID: "tolls", + CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + + +async def test_dupe_import_false_check_no_mode(hass, bypass_update): + """Test false duplicate import check when no mode is provided.""" + await _setup_dupe_import(hass, bypass_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_OPTIONS: { + CONF_LANGUAGE: "en", + CONF_AVOID: "tolls", + CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + + +async def test_dupe_import_false_check_options(hass, bypass_update): + """Test false duplicate import check when options differ.""" + await _setup_dupe_import(hass, bypass_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: "api_key", + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_NAME: "test_name", + CONF_OPTIONS: { + CONF_MODE: "walking", + CONF_AVOID: "tolls", + CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_ARRIVAL_TIME: "test", + CONF_TRAFFIC_MODEL: "best_guess", + CONF_TRANSIT_MODE: "train", + CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking", + }, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY diff --git a/tests/components/google_travel_time/test_init.py b/tests/components/google_travel_time/test_init.py new file mode 100644 index 00000000000..583cd4dc7ce --- /dev/null +++ b/tests/components/google_travel_time/test_init.py @@ -0,0 +1,21 @@ +"""Test Google Maps Travel Time initialization.""" +from homeassistant.components.google_travel_time.const import DOMAIN +from homeassistant.helpers.entity_registry import async_get + +from tests.common import MockConfigEntry + + +async def test_migration(hass, bypass_platform_setup): + """Test migration logic for unique id.""" + config_entry = MockConfigEntry( + domain=DOMAIN, version=1, entry_id="test", unique_id="test" + ) + ent_reg = async_get(hass) + ent_entry = ent_reg.async_get_or_create( + "sensor", DOMAIN, unique_id="replaceable_unique_id", config_entry=config_entry + ) + entity_id = ent_entry.entity_id + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + assert config_entry.unique_id is None + assert ent_reg.async_get(entity_id).unique_id == config_entry.entry_id diff --git a/tests/components/waze_travel_time/conftest.py b/tests/components/waze_travel_time/conftest.py index dd5b343cc16..237b476aa25 100644 --- a/tests/components/waze_travel_time/conftest.py +++ b/tests/components/waze_travel_time/conftest.py @@ -35,6 +35,16 @@ def bypass_setup_fixture(): yield +@pytest.fixture(name="bypass_platform_setup") +def bypass_platform_setup_fixture(): + """Bypass platform setup.""" + with patch( + "homeassistant.components.waze_travel_time.sensor.async_setup_entry", + return_value=True, + ): + yield + + @pytest.fixture(name="mock_update") def mock_update_fixture(): """Mock an update to the sensor.""" diff --git a/tests/components/waze_travel_time/test_config_flow.py b/tests/components/waze_travel_time/test_config_flow.py index b6690be4d86..f0f8a0f3bde 100644 --- a/tests/components/waze_travel_time/test_config_flow.py +++ b/tests/components/waze_travel_time/test_config_flow.py @@ -145,8 +145,125 @@ async def test_import(hass, validate_config_entry, mock_update): } -async def test_dupe_id(hass, validate_config_entry, bypass_setup): - """Test setting up the same entry twice fails.""" +async def _setup_dupe_import(hass, mock_update): + """Set up dupe import.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_REGION: "US", + CONF_AVOID_FERRIES: True, + CONF_AVOID_SUBSCRIPTION_ROADS: True, + CONF_AVOID_TOLL_ROADS: True, + CONF_EXCL_FILTER: "exclude", + CONF_INCL_FILTER: "include", + CONF_REALTIME: False, + CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_VEHICLE_TYPE: "taxi", + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() + + +async def test_dupe_import(hass, mock_update): + """Test duplicate import.""" + await _setup_dupe_import(hass, mock_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_REGION: "US", + CONF_AVOID_FERRIES: True, + CONF_AVOID_SUBSCRIPTION_ROADS: True, + CONF_AVOID_TOLL_ROADS: True, + CONF_EXCL_FILTER: "exclude", + CONF_INCL_FILTER: "include", + CONF_REALTIME: False, + CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_VEHICLE_TYPE: "taxi", + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def test_dupe_import_false_check_different_options_value(hass, mock_update): + """Test false duplicate import check when options value differs.""" + await _setup_dupe_import(hass, mock_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_REGION: "US", + CONF_AVOID_FERRIES: True, + CONF_AVOID_SUBSCRIPTION_ROADS: True, + CONF_AVOID_TOLL_ROADS: True, + CONF_EXCL_FILTER: "exclude", + CONF_INCL_FILTER: "include", + CONF_REALTIME: False, + CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_VEHICLE_TYPE: "car", + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + + +async def test_dupe_import_false_check_default_option(hass, mock_update): + """Test false duplicate import check when option with a default is missing.""" + await _setup_dupe_import(hass, mock_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_REGION: "US", + CONF_AVOID_FERRIES: True, + CONF_AVOID_SUBSCRIPTION_ROADS: True, + CONF_AVOID_TOLL_ROADS: True, + CONF_EXCL_FILTER: "exclude", + CONF_INCL_FILTER: "include", + CONF_REALTIME: False, + CONF_VEHICLE_TYPE: "taxi", + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + + +async def test_dupe_import_false_check_no_default_option(hass, mock_update): + """Test false duplicate import check option when option with no default is miissing.""" + await _setup_dupe_import(hass, mock_update) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_ORIGIN: "location1", + CONF_DESTINATION: "location2", + CONF_REGION: "US", + CONF_AVOID_FERRIES: True, + CONF_AVOID_SUBSCRIPTION_ROADS: True, + CONF_AVOID_TOLL_ROADS: True, + CONF_EXCL_FILTER: "exclude", + CONF_REALTIME: False, + CONF_VEHICLE_TYPE: "taxi", + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + + +async def test_dupe(hass, validate_config_entry, bypass_setup): + """Test setting up the same entry data twice is OK.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -182,8 +299,7 @@ async def test_dupe_id(hass, validate_config_entry, bypass_setup): ) await hass.async_block_till_done() - assert result2["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result2["reason"] == "already_configured" + assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY async def test_invalid_config_entry(hass, invalidate_config_entry): diff --git a/tests/components/waze_travel_time/test_init.py b/tests/components/waze_travel_time/test_init.py new file mode 100644 index 00000000000..bf8f6a95844 --- /dev/null +++ b/tests/components/waze_travel_time/test_init.py @@ -0,0 +1,21 @@ +"""Test Waze Travel Time initialization.""" +from homeassistant.components.waze_travel_time.const import DOMAIN +from homeassistant.helpers.entity_registry import async_get + +from tests.common import MockConfigEntry + + +async def test_migration(hass, bypass_platform_setup): + """Test migration logic for unique id.""" + config_entry = MockConfigEntry( + domain=DOMAIN, version=1, entry_id="test", unique_id="test" + ) + ent_reg = async_get(hass) + ent_entry = ent_reg.async_get_or_create( + "sensor", DOMAIN, unique_id="replaceable_unique_id", config_entry=config_entry + ) + entity_id = ent_entry.entity_id + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + assert config_entry.unique_id is None + assert ent_reg.async_get(entity_id).unique_id == config_entry.entry_id