diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index 4c36eda42e6..c34abc856b9 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -3,6 +3,7 @@ from ipaddress import ip_network import logging import os import ssl +from traceback import extract_stack from typing import Optional, cast from aiohttp import web @@ -119,14 +120,66 @@ class ApiConfig: host = host.rstrip("/") if host.startswith(("http://", "https://")): - self.base_url = host + self.deprecated_base_url = host elif use_ssl: - self.base_url = f"https://{host}" + self.deprecated_base_url = f"https://{host}" else: - self.base_url = f"http://{host}" + self.deprecated_base_url = f"http://{host}" if port is not None: - self.base_url += f":{port}" + self.deprecated_base_url += f":{port}" + + @property + def base_url(self) -> str: + """Proxy property to find caller of this deprecated property.""" + found_frame = None + for frame in reversed(extract_stack()): + for path in ("custom_components/", "homeassistant/components/"): + try: + index = frame.filename.index(path) + + # Skip webhook from the stack + if frame.filename[index:].startswith( + "homeassistant/components/webhook/" + ): + continue + + found_frame = frame + break + except ValueError: + continue + + if found_frame is not None: + break + + # Did not source from an integration? Hard error. + if found_frame is None: + raise RuntimeError( + "Detected use of deprecated `base_url` property in the Home Assistant core. Please report this issue." + ) + + # If a frame was found, it originated from an integration + if found_frame: + start = index + len(path) + end = found_frame.filename.index("/", start) + + integration = found_frame.filename[start:end] + + if path == "custom_components/": + extra = " to the custom component author" + else: + extra = "" + + _LOGGER.warning( + "Detected use of deprecated `base_url` property, use `homeassistant.helpers.network.async_get_url` method instead. Please report issue%s for %s using this method at %s, line %s: %s", + extra, + integration, + found_frame.filename[index:], + found_frame.lineno, + found_frame.line.strip(), + ) + + return self.deprecated_base_url async def async_setup(hass, config): diff --git a/homeassistant/core.py b/homeassistant/core.py index 64d99c30838..929d8c74da4 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -1463,7 +1463,7 @@ class Config: if self.hass.config.api is None: return - base_url = yarl.URL(self.hass.config.api.base_url) + base_url = yarl.URL(self.hass.config.api.deprecated_base_url) # Check if this is an internal URL if str(base_url.host).endswith(".local") or ( diff --git a/homeassistant/helpers/network.py b/homeassistant/helpers/network.py index 6658b92070d..3107c76946b 100644 --- a/homeassistant/helpers/network.py +++ b/homeassistant/helpers/network.py @@ -195,10 +195,10 @@ def _async_get_deprecated_base_url( require_standard_port: bool = False, ) -> str: """Work with the deprecated `base_url`, used as fallback.""" - if hass.config.api is None or not hass.config.api.base_url: + if hass.config.api is None or not hass.config.api.deprecated_base_url: raise NoURLAvailableError - base_url = yarl.URL(hass.config.api.base_url) + base_url = yarl.URL(hass.config.api.deprecated_base_url) # Rules that apply to both internal and external if ( (allow_ip or not is_ip_address(str(base_url.host))) diff --git a/tests/components/cast/test_home_assistant_cast.py b/tests/components/cast/test_home_assistant_cast.py index da6edbebc26..8a3a935429f 100644 --- a/tests/components/cast/test_home_assistant_cast.py +++ b/tests/components/cast/test_home_assistant_cast.py @@ -1,13 +1,16 @@ """Test Home Assistant Cast.""" from homeassistant.components.cast import home_assistant_cast +from homeassistant.config import async_process_ha_core_config -from tests.async_mock import Mock, patch +from tests.async_mock import patch from tests.common import MockConfigEntry, async_mock_signal async def test_service_show_view(hass): """Test we don't set app id in prod.""" - hass.config.api = Mock(base_url="https://example.com") + await async_process_ha_core_config( + hass, {"external_url": "https://example.com"}, + ) await home_assistant_cast.async_setup_ha_cast(hass, MockConfigEntry()) calls = async_mock_signal(hass, home_assistant_cast.SIGNAL_HASS_CAST_SHOW_VIEW) @@ -31,7 +34,9 @@ async def test_service_show_view(hass): async def test_service_show_view_dashboard(hass): """Test casting a specific dashboard.""" - hass.config.api = Mock(base_url="https://example.com") + await async_process_ha_core_config( + hass, {"external_url": "https://example.com"}, + ) await home_assistant_cast.async_setup_ha_cast(hass, MockConfigEntry()) calls = async_mock_signal(hass, home_assistant_cast.SIGNAL_HASS_CAST_SHOW_VIEW) @@ -55,7 +60,9 @@ async def test_service_show_view_dashboard(hass): async def test_use_cloud_url(hass): """Test that we fall back to cloud url.""" - hass.config.api = Mock(base_url="http://example.com") + await async_process_ha_core_config( + hass, {"internal_url": "http://example.local:8123"}, + ) hass.config.components.add("cloud") await home_assistant_cast.async_setup_ha_cast(hass, MockConfigEntry()) diff --git a/tests/components/dialogflow/test_init.py b/tests/components/dialogflow/test_init.py index c8c508a0d88..6df59873bd1 100644 --- a/tests/components/dialogflow/test_init.py +++ b/tests/components/dialogflow/test_init.py @@ -6,11 +6,10 @@ import pytest from homeassistant import data_entry_flow from homeassistant.components import dialogflow, intent_script +from homeassistant.config import async_process_ha_core_config from homeassistant.core import callback from homeassistant.setup import async_setup_component -from tests.async_mock import Mock - SESSION_ID = "a9b84cec-46b6-484e-8f31-f65dba03ae6d" INTENT_ID = "c6a74079-a8f0-46cd-b372-5a934d23591c" INTENT_NAME = "tests" @@ -79,7 +78,10 @@ async def fixture(hass, aiohttp_client): }, ) - hass.config.api = Mock(base_url="http://example.com") + await async_process_ha_core_config( + hass, {"internal_url": "http://example.local:8123"}, + ) + result = await hass.config_entries.flow.async_init( "dialogflow", context={"source": "user"} ) diff --git a/tests/components/geofency/test_init.py b/tests/components/geofency/test_init.py index bdb5b49d11b..3a9bdf1fe73 100644 --- a/tests/components/geofency/test_init.py +++ b/tests/components/geofency/test_init.py @@ -4,6 +4,7 @@ import pytest from homeassistant import data_entry_flow from homeassistant.components import zone from homeassistant.components.geofency import CONF_MOBILE_BEACONS, DOMAIN +from homeassistant.config import async_process_ha_core_config from homeassistant.const import ( HTTP_OK, HTTP_UNPROCESSABLE_ENTITY, @@ -14,7 +15,7 @@ from homeassistant.setup import async_setup_component from homeassistant.util import slugify # pylint: disable=redefined-outer-name -from tests.async_mock import Mock, patch +from tests.async_mock import patch HOME_LATITUDE = 37.239622 HOME_LONGITUDE = -115.815811 @@ -148,7 +149,9 @@ async def setup_zones(loop, hass): @pytest.fixture async def webhook_id(hass, geofency_client): """Initialize the Geofency component and get the webhook_id.""" - hass.config.api = Mock(base_url="http://example.com") + await async_process_ha_core_config( + hass, {"internal_url": "http://example.local:8123"}, + ) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": "user"} ) diff --git a/tests/components/google_assistant/test_helpers.py b/tests/components/google_assistant/test_helpers.py index 4943212bd5a..05afd29a5bd 100644 --- a/tests/components/google_assistant/test_helpers.py +++ b/tests/components/google_assistant/test_helpers.py @@ -8,6 +8,7 @@ from homeassistant.components.google_assistant.const import ( # noqa: F401 EVENT_COMMAND_RECEIVED, NOT_EXPOSE_LOCAL, ) +from homeassistant.config import async_process_ha_core_config from homeassistant.setup import async_setup_component from homeassistant.util import dt @@ -24,7 +25,11 @@ from tests.common import ( async def test_google_entity_sync_serialize_with_local_sdk(hass): """Test sync serialize attributes of a GoogleEntity.""" hass.states.async_set("light.ceiling_lights", "off") - hass.config.api = Mock(port=1234, use_ssl=True, base_url="https://hostname:1234") + hass.config.api = Mock(port=1234, use_ssl=True) + await async_process_ha_core_config( + hass, {"external_url": "https://hostname:1234"}, + ) + hass.http = Mock(server_port=1234) config = MockConfig( hass=hass, diff --git a/tests/components/gpslogger/test_init.py b/tests/components/gpslogger/test_init.py index bfdc087193e..18e80647fe7 100644 --- a/tests/components/gpslogger/test_init.py +++ b/tests/components/gpslogger/test_init.py @@ -5,6 +5,7 @@ from homeassistant import data_entry_flow from homeassistant.components import gpslogger, zone from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER_DOMAIN from homeassistant.components.gpslogger import DOMAIN, TRACKER_UPDATE +from homeassistant.config import async_process_ha_core_config from homeassistant.const import ( HTTP_OK, HTTP_UNPROCESSABLE_ENTITY, @@ -14,7 +15,7 @@ from homeassistant.const import ( from homeassistant.helpers.dispatcher import DATA_DISPATCHER from homeassistant.setup import async_setup_component -from tests.async_mock import Mock, patch +from tests.async_mock import patch HOME_LATITUDE = 37.239622 HOME_LONGITUDE = -115.815811 @@ -62,7 +63,9 @@ async def setup_zones(loop, hass): @pytest.fixture async def webhook_id(hass, gpslogger_client): """Initialize the GPSLogger component and get the webhook_id.""" - hass.config.api = Mock(base_url="http://example.com") + await async_process_ha_core_config( + hass, {"internal_url": "http://example.local:8123"}, + ) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": "user"} ) diff --git a/tests/components/http/test_init.py b/tests/components/http/test_init.py index 557ccbb4aa5..c929be45a29 100644 --- a/tests/components/http/test_init.py +++ b/tests/components/http/test_init.py @@ -3,11 +3,13 @@ from ipaddress import ip_network import logging import unittest +import pytest + import homeassistant.components.http as http from homeassistant.setup import async_setup_component from homeassistant.util.ssl import server_context_intermediate, server_context_modern -from tests.async_mock import patch +from tests.async_mock import Mock, patch class TestView(http.HomeAssistantView): @@ -271,3 +273,127 @@ async def test_storing_config(hass, aiohttp_client, aiohttp_unused_port): restored["trusted_proxies"][0] = ip_network(restored["trusted_proxies"][0]) assert restored == http.HTTP_SCHEMA(config) + + +async def test_use_of_base_url(hass): + """Test detection base_url usage when called without integration context.""" + await async_setup_component(hass, "http", {"http": {}}) + with patch( + "homeassistant.components.http.extract_stack", + return_value=[ + Mock( + filename="/home/frenck/homeassistant/core.py", + lineno="21", + line="do_something()", + ), + Mock( + filename="/home/frenck/homeassistant/core.py", + lineno="42", + line="url = hass.config.api.base_url", + ), + Mock( + filename="/home/frenck/example/client.py", + lineno="21", + line="something()", + ), + ], + ), pytest.raises(RuntimeError): + hass.config.api.base_url + + +async def test_use_of_base_url_integration(hass, caplog): + """Test detection base_url usage when called with integration context.""" + await async_setup_component(hass, "http", {"http": {}}) + with patch( + "homeassistant.components.http.extract_stack", + return_value=[ + Mock( + filename="/home/frenck/homeassistant/core.py", + lineno="21", + line="do_something()", + ), + Mock( + filename="/home/frenck/homeassistant/components/example/__init__.py", + lineno="42", + line="url = hass.config.api.base_url", + ), + Mock( + filename="/home/frenck/example/client.py", + lineno="21", + line="something()", + ), + ], + ): + assert hass.config.api.base_url == "http://127.0.0.1:8123" + + assert ( + "Detected use of deprecated `base_url` property, use `homeassistant.helpers.network.async_get_url` method instead. Please report issue for example using this method at homeassistant/components/example/__init__.py, line 42: url = hass.config.api.base_url" + in caplog.text + ) + + +async def test_use_of_base_url_integration_webhook(hass, caplog): + """Test detection base_url usage when called with integration context.""" + await async_setup_component(hass, "http", {"http": {}}) + with patch( + "homeassistant.components.http.extract_stack", + return_value=[ + Mock( + filename="/home/frenck/homeassistant/core.py", + lineno="21", + line="do_something()", + ), + Mock( + filename="/home/frenck/homeassistant/components/example/__init__.py", + lineno="42", + line="url = hass.config.api.base_url", + ), + Mock( + filename="/home/frenck/homeassistant/components/webhook/__init__.py", + lineno="42", + line="return async_get_url(hass)", + ), + Mock( + filename="/home/frenck/example/client.py", + lineno="21", + line="something()", + ), + ], + ): + assert hass.config.api.base_url == "http://127.0.0.1:8123" + + assert ( + "Detected use of deprecated `base_url` property, use `homeassistant.helpers.network.async_get_url` method instead. Please report issue for example using this method at homeassistant/components/example/__init__.py, line 42: url = hass.config.api.base_url" + in caplog.text + ) + + +async def test_use_of_base_url_custom_component(hass, caplog): + """Test detection base_url usage when called with custom component context.""" + await async_setup_component(hass, "http", {"http": {}}) + with patch( + "homeassistant.components.http.extract_stack", + return_value=[ + Mock( + filename="/home/frenck/homeassistant/core.py", + lineno="21", + line="do_something()", + ), + Mock( + filename="/home/frenck/.homeassistant/custom_components/example/__init__.py", + lineno="42", + line="url = hass.config.api.base_url", + ), + Mock( + filename="/home/frenck/example/client.py", + lineno="21", + line="something()", + ), + ], + ): + assert hass.config.api.base_url == "http://127.0.0.1:8123" + + assert ( + "Detected use of deprecated `base_url` property, use `homeassistant.helpers.network.async_get_url` method instead. Please report issue to the custom component author for example using this method at custom_components/example/__init__.py, line 42: url = hass.config.api.base_url" + in caplog.text + ) diff --git a/tests/components/locative/test_init.py b/tests/components/locative/test_init.py index 05b6a84cc0c..0f3d17c9b59 100644 --- a/tests/components/locative/test_init.py +++ b/tests/components/locative/test_init.py @@ -5,11 +5,12 @@ from homeassistant import data_entry_flow from homeassistant.components import locative from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER_DOMAIN from homeassistant.components.locative import DOMAIN, TRACKER_UPDATE +from homeassistant.config import async_process_ha_core_config from homeassistant.const import HTTP_OK, HTTP_UNPROCESSABLE_ENTITY from homeassistant.helpers.dispatcher import DATA_DISPATCHER from homeassistant.setup import async_setup_component -from tests.async_mock import Mock, patch +from tests.async_mock import patch # pylint: disable=redefined-outer-name @@ -33,7 +34,9 @@ async def locative_client(loop, hass, hass_client): @pytest.fixture async def webhook_id(hass, locative_client): """Initialize the Geofency component and get the webhook_id.""" - hass.config.api = Mock(base_url="http://example.com") + await async_process_ha_core_config( + hass, {"internal_url": "http://example.local:8123"}, + ) result = await hass.config_entries.flow.async_init( "locative", context={"source": "user"} ) diff --git a/tests/components/mailgun/test_init.py b/tests/components/mailgun/test_init.py index 8b98772dfcc..5d6cec844f2 100644 --- a/tests/components/mailgun/test_init.py +++ b/tests/components/mailgun/test_init.py @@ -6,12 +6,11 @@ import pytest from homeassistant import data_entry_flow from homeassistant.components import mailgun, webhook +from homeassistant.config import async_process_ha_core_config from homeassistant.const import CONF_API_KEY, CONF_DOMAIN from homeassistant.core import callback from homeassistant.setup import async_setup_component -from tests.async_mock import Mock - API_KEY = "abc123" @@ -31,7 +30,9 @@ async def webhook_id_with_api_key(hass): {mailgun.DOMAIN: {CONF_API_KEY: API_KEY, CONF_DOMAIN: "example.com"}}, ) - hass.config.api = Mock(base_url="http://example.com") + await async_process_ha_core_config( + hass, {"internal_url": "http://example.local:8123"}, + ) result = await hass.config_entries.flow.async_init( "mailgun", context={"source": "user"} ) @@ -48,7 +49,9 @@ async def webhook_id_without_api_key(hass): """Initialize the Mailgun component and get the webhook_id w/o API key.""" await async_setup_component(hass, mailgun.DOMAIN, {}) - hass.config.api = Mock(base_url="http://example.com") + await async_process_ha_core_config( + hass, {"internal_url": "http://example.local:8123"}, + ) result = await hass.config_entries.flow.async_init( "mailgun", context={"source": "user"} ) diff --git a/tests/components/owntracks/test_config_flow.py b/tests/components/owntracks/test_config_flow.py index a4a530b9083..3e6101e1989 100644 --- a/tests/components/owntracks/test_config_flow.py +++ b/tests/components/owntracks/test_config_flow.py @@ -9,7 +9,7 @@ from homeassistant.config import async_process_ha_core_config from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.setup import async_setup_component -from tests.async_mock import Mock, patch +from tests.async_mock import patch from tests.common import MockConfigEntry CONF_WEBHOOK_URL = "webhook_url" @@ -47,9 +47,11 @@ def mock_not_supports_encryption(): yield -def init_config_flow(hass): +async def init_config_flow(hass): """Init a configuration flow.""" - hass.config.api = Mock(base_url=BASE_URL) + await async_process_ha_core_config( + hass, {"external_url": BASE_URL}, + ) flow = config_flow.OwnTracksFlow() flow.hass = hass return flow @@ -57,7 +59,7 @@ def init_config_flow(hass): async def test_user(hass, webhook_id, secret): """Test user step.""" - flow = init_config_flow(hass) + flow = await init_config_flow(hass) result = await flow.async_step_user() assert result["type"] == data_entry_flow.RESULT_TYPE_FORM @@ -74,7 +76,7 @@ async def test_user(hass, webhook_id, secret): async def test_import(hass, webhook_id, secret): """Test import step.""" - flow = init_config_flow(hass) + flow = await init_config_flow(hass) result = await flow.async_step_import({}) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY @@ -99,7 +101,7 @@ async def test_import_setup(hass): async def test_abort_if_already_setup(hass): """Test that we can't add more than one instance.""" - flow = init_config_flow(hass) + flow = await init_config_flow(hass) MockConfigEntry(domain=DOMAIN, data={}).add_to_hass(hass) assert hass.config_entries.async_entries(DOMAIN) @@ -117,7 +119,7 @@ async def test_abort_if_already_setup(hass): async def test_user_not_supports_encryption(hass, not_supports_encryption): """Test user step.""" - flow = init_config_flow(hass) + flow = await init_config_flow(hass) result = await flow.async_step_user({}) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY diff --git a/tests/components/smartthings/conftest.py b/tests/components/smartthings/conftest.py index a47b06ee637..83f8e4dfca1 100644 --- a/tests/components/smartthings/conftest.py +++ b/tests/components/smartthings/conftest.py @@ -37,6 +37,7 @@ from homeassistant.components.smartthings.const import ( STORAGE_KEY, STORAGE_VERSION, ) +from homeassistant.config import async_process_ha_core_config from homeassistant.config_entries import CONN_CLASS_CLOUD_PUSH, SOURCE_USER, ConfigEntry from homeassistant.const import CONF_ACCESS_TOKEN, CONF_WEBHOOK_ID from homeassistant.setup import async_setup_component @@ -73,8 +74,10 @@ async def setup_platform(hass, platform: str, *, devices=None, scenes=None): async def setup_component(hass, config_file, hass_storage): """Load the SmartThing component.""" hass_storage[STORAGE_KEY] = {"data": config_file, "version": STORAGE_VERSION} + await async_process_ha_core_config( + hass, {"external_url": "https://test.local"}, + ) await async_setup_component(hass, "smartthings", {}) - hass.config.api.base_url = "https://test.local" def _create_location(): @@ -97,7 +100,7 @@ def locations_fixture(location): @pytest.fixture(name="app") -def app_fixture(hass, config_file): +async def app_fixture(hass, config_file): """Fixture for a single app.""" app = Mock(AppEntity) app.app_name = APP_NAME_PREFIX + str(uuid4()) @@ -105,7 +108,7 @@ def app_fixture(hass, config_file): app.app_type = "WEBHOOK_SMART_APP" app.classifications = [CLASSIFICATION_AUTOMATION] app.display_name = "Home Assistant" - app.description = f"{hass.config.location_name} at {hass.config.api.base_url}" + app.description = f"{hass.config.location_name} at https://test.local" app.single_instance = True app.webhook_target_url = webhook.async_generate_url( hass, hass.data[DOMAIN][CONF_WEBHOOK_ID] diff --git a/tests/components/smartthings/test_config_flow.py b/tests/components/smartthings/test_config_flow.py index ca964b9d1da..47726bfe270 100644 --- a/tests/components/smartthings/test_config_flow.py +++ b/tests/components/smartthings/test_config_flow.py @@ -15,6 +15,7 @@ from homeassistant.components.smartthings.const import ( CONF_OAUTH_CLIENT_SECRET, DOMAIN, ) +from homeassistant.config import async_process_ha_core_config from homeassistant.const import ( CONF_ACCESS_TOKEN, HTTP_FORBIDDEN, @@ -417,9 +418,10 @@ async def test_entry_created_with_cloudhook( async def test_invalid_webhook_aborts(hass): """Test flow aborts if webhook is invalid.""" - hass.config.api.base_url = "http://0.0.0.0" - # Webhook confirmation shown + await async_process_ha_core_config( + hass, {"external_url": "http://example.local:8123"}, + ) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": "user"} ) diff --git a/tests/components/smartthings/test_init.py b/tests/components/smartthings/test_init.py index 0fdbf5c255a..014b9a6673c 100644 --- a/tests/components/smartthings/test_init.py +++ b/tests/components/smartthings/test_init.py @@ -16,6 +16,7 @@ from homeassistant.components.smartthings.const import ( SIGNAL_SMARTTHINGS_UPDATE, SUPPORTED_PLATFORMS, ) +from homeassistant.config import async_process_ha_core_config from homeassistant.const import HTTP_FORBIDDEN, HTTP_INTERNAL_SERVER_ERROR from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -116,7 +117,9 @@ async def test_base_url_no_longer_https_does_not_load( hass, config_entry, app, smartthings_mock ): """Test base_url no longer valid creates a new flow.""" - hass.config.api.base_url = "http://0.0.0.0" + await async_process_ha_core_config( + hass, {"external_url": "http://example.local:8123"}, + ) config_entry.add_to_hass(hass) smartthings_mock.app.return_value = app @@ -218,7 +221,6 @@ async def test_config_entry_loads_unconnected_cloud( """Test entry loads during startup when cloud isn't connected.""" config_entry.add_to_hass(hass) hass.data[DOMAIN][CONF_CLOUDHOOK_URL] = "https://test.cloud" - hass.config.api.base_url = "http://0.0.0.0" smartthings_mock.app.return_value = app smartthings_mock.installed_app.return_value = installed_app smartthings_mock.devices.return_value = [device] diff --git a/tests/components/traccar/test_init.py b/tests/components/traccar/test_init.py index 2804d90ac33..942fd4b01d5 100644 --- a/tests/components/traccar/test_init.py +++ b/tests/components/traccar/test_init.py @@ -5,6 +5,7 @@ from homeassistant import data_entry_flow from homeassistant.components import traccar, zone from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER_DOMAIN from homeassistant.components.traccar import DOMAIN, TRACKER_UPDATE +from homeassistant.config import async_process_ha_core_config from homeassistant.const import ( HTTP_OK, HTTP_UNPROCESSABLE_ENTITY, @@ -14,7 +15,7 @@ from homeassistant.const import ( from homeassistant.helpers.dispatcher import DATA_DISPATCHER from homeassistant.setup import async_setup_component -from tests.async_mock import Mock, patch +from tests.async_mock import patch HOME_LATITUDE = 37.239622 HOME_LONGITUDE = -115.815811 @@ -23,7 +24,6 @@ HOME_LONGITUDE = -115.815811 @pytest.fixture(autouse=True) def mock_dev_track(mock_device_tracker_conf): """Mock device tracker config loading.""" - pass @pytest.fixture(name="client") @@ -60,7 +60,9 @@ async def setup_zones(loop, hass): @pytest.fixture(name="webhook_id") async def webhook_id_fixture(hass, client): """Initialize the Traccar component and get the webhook_id.""" - hass.config.api = Mock(base_url="http://example.com") + await async_process_ha_core_config( + hass, {"external_url": "http://example.com"}, + ) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": "user"} ) diff --git a/tests/components/webhook/test_init.py b/tests/components/webhook/test_init.py index 7d4ce563b03..9051b6325bf 100644 --- a/tests/components/webhook/test_init.py +++ b/tests/components/webhook/test_init.py @@ -1,10 +1,9 @@ """Test the webhook component.""" import pytest +from homeassistant.config import async_process_ha_core_config from homeassistant.setup import async_setup_component -from tests.async_mock import Mock - @pytest.fixture def mock_client(hass, hass_client): @@ -37,7 +36,9 @@ async def test_unregistering_webhook(hass, mock_client): async def test_generate_webhook_url(hass): """Test we generate a webhook url correctly.""" - hass.config.api = Mock(base_url="https://example.com") + await async_process_ha_core_config( + hass, {"external_url": "https://example.com"}, + ) url = hass.components.webhook.async_generate_url("some_id") assert url == "https://example.com/api/webhook/some_id" diff --git a/tests/helpers/test_config_entry_flow.py b/tests/helpers/test_config_entry_flow.py index 1331cf615d1..7130514f47f 100644 --- a/tests/helpers/test_config_entry_flow.py +++ b/tests/helpers/test_config_entry_flow.py @@ -2,6 +2,7 @@ import pytest from homeassistant import config_entries, data_entry_flow, setup +from homeassistant.config import async_process_ha_core_config from homeassistant.helpers import config_entry_flow from tests.async_mock import Mock, patch @@ -232,7 +233,9 @@ async def test_webhook_config_flow_registers_webhook(hass, webhook_flow_conf): flow = config_entries.HANDLERS["test_single"]() flow.hass = hass - hass.config.api = Mock(base_url="http://example.com") + await async_process_ha_core_config( + hass, {"external_url": "https://example.com"}, + ) result = await flow.async_step_user(user_input={}) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY diff --git a/tests/helpers/test_config_entry_oauth2_flow.py b/tests/helpers/test_config_entry_oauth2_flow.py index 9826d60025f..801ea49bfbb 100644 --- a/tests/helpers/test_config_entry_oauth2_flow.py +++ b/tests/helpers/test_config_entry_oauth2_flow.py @@ -6,6 +6,7 @@ import time import pytest from homeassistant import config_entries, data_entry_flow, setup +from homeassistant.config import async_process_ha_core_config from homeassistant.helpers import config_entry_oauth2_flow from tests.async_mock import patch @@ -124,7 +125,9 @@ async def test_abort_if_authorization_timeout(hass, flow_handler, local_impl): async def test_step_discovery(hass, flow_handler, local_impl): """Check flow triggers from discovery.""" - hass.config.api.base_url = "https://example.com" + await async_process_ha_core_config( + hass, {"external_url": "https://example.com"}, + ) flow_handler.async_register_implementation(hass, local_impl) config_entry_oauth2_flow.async_register_implementation( hass, TEST_DOMAIN, MockOAuth2Implementation() @@ -140,7 +143,10 @@ async def test_step_discovery(hass, flow_handler, local_impl): async def test_abort_discovered_multiple(hass, flow_handler, local_impl): """Test if aborts when discovered multiple times.""" - hass.config.api.base_url = "https://example.com" + await async_process_ha_core_config( + hass, {"external_url": "https://example.com"}, + ) + flow_handler.async_register_implementation(hass, local_impl) config_entry_oauth2_flow.async_register_implementation( hass, TEST_DOMAIN, MockOAuth2Implementation() @@ -163,7 +169,9 @@ async def test_abort_discovered_multiple(hass, flow_handler, local_impl): async def test_abort_discovered_existing_entries(hass, flow_handler, local_impl): """Test if abort discovery when entries exists.""" - hass.config.api.base_url = "https://example.com" + await async_process_ha_core_config( + hass, {"external_url": "https://example.com"}, + ) flow_handler.async_register_implementation(hass, local_impl) config_entry_oauth2_flow.async_register_implementation( hass, TEST_DOMAIN, MockOAuth2Implementation() @@ -184,7 +192,10 @@ async def test_full_flow( hass, flow_handler, local_impl, aiohttp_client, aioclient_mock ): """Check full flow.""" - hass.config.api.base_url = "https://example.com" + await async_process_ha_core_config( + hass, {"external_url": "https://example.com"}, + ) + flow_handler.async_register_implementation(hass, local_impl) config_entry_oauth2_flow.async_register_implementation( hass, TEST_DOMAIN, MockOAuth2Implementation() diff --git a/tests/helpers/test_network.py b/tests/helpers/test_network.py index 492a11b5f57..c996d2a3041 100644 --- a/tests/helpers/test_network.py +++ b/tests/helpers/test_network.py @@ -119,7 +119,7 @@ async def test_get_url_internal_fallback(hass: HomeAssistant): assert hass.config.internal_url is None hass.config.api = Mock( - use_ssl=False, port=8123, base_url=None, local_ip="192.168.123.123" + use_ssl=False, port=8123, deprecated_base_url=None, local_ip="192.168.123.123" ) assert _async_get_internal_url(hass) == "http://192.168.123.123:8123" @@ -133,7 +133,7 @@ async def test_get_url_internal_fallback(hass: HomeAssistant): _async_get_internal_url(hass, require_ssl=True) hass.config.api = Mock( - use_ssl=False, port=80, base_url=None, local_ip="192.168.123.123" + use_ssl=False, port=80, deprecated_base_url=None, local_ip="192.168.123.123" ) assert _async_get_internal_url(hass) == "http://192.168.123.123" assert ( @@ -147,7 +147,7 @@ async def test_get_url_internal_fallback(hass: HomeAssistant): with pytest.raises(NoURLAvailableError): _async_get_internal_url(hass, require_ssl=True) - hass.config.api = Mock(use_ssl=True, port=443, base_url=None) + hass.config.api = Mock(use_ssl=True, port=443, deprecated_base_url=None) with pytest.raises(NoURLAvailableError): _async_get_internal_url(hass) @@ -161,7 +161,9 @@ async def test_get_url_internal_fallback(hass: HomeAssistant): _async_get_internal_url(hass, require_ssl=True) # Do no accept any local loopback address as fallback - hass.config.api = Mock(use_ssl=False, port=80, base_url=None, local_ip="127.0.0.1") + hass.config.api = Mock( + use_ssl=False, port=80, deprecated_base_url=None, local_ip="127.0.0.1" + ) with pytest.raises(NoURLAvailableError): _async_get_internal_url(hass) @@ -367,7 +369,7 @@ async def test_get_url(hass: HomeAssistant): async_get_url(hass) hass.config.api = Mock( - use_ssl=False, port=8123, base_url=None, local_ip="192.168.123.123" + use_ssl=False, port=8123, deprecated_base_url=None, local_ip="192.168.123.123" ) assert async_get_url(hass) == "http://192.168.123.123:8123" assert async_get_url(hass, prefer_external=True) == "http://192.168.123.123:8123" @@ -409,7 +411,7 @@ async def test_get_url(hass: HomeAssistant): async def test_get_deprecated_base_url_internal(hass: HomeAssistant): """Test getting an internal instance URL from the deprecated base_url.""" # Test with SSL local URL - hass.config.api = Mock(base_url="https://example.local") + hass.config.api = Mock(deprecated_base_url="https://example.local") assert ( _async_get_deprecated_base_url(hass, internal=True) == "https://example.local" ) @@ -427,7 +429,7 @@ async def test_get_deprecated_base_url_internal(hass: HomeAssistant): ) # Test with no SSL, local IP URL - hass.config.api = Mock(base_url="http://10.10.10.10:8123") + hass.config.api = Mock(deprecated_base_url="http://10.10.10.10:8123") assert ( _async_get_deprecated_base_url(hass, internal=True) == "http://10.10.10.10:8123" ) @@ -442,7 +444,7 @@ async def test_get_deprecated_base_url_internal(hass: HomeAssistant): _async_get_deprecated_base_url(hass, internal=True, require_standard_port=True) # Test with SSL, local IP URL - hass.config.api = Mock(base_url="https://10.10.10.10") + hass.config.api = Mock(deprecated_base_url="https://10.10.10.10") assert _async_get_deprecated_base_url(hass, internal=True) == "https://10.10.10.10" assert ( _async_get_deprecated_base_url(hass, internal=True, require_ssl=True) @@ -454,7 +456,7 @@ async def test_get_deprecated_base_url_internal(hass: HomeAssistant): ) # Test external URL - hass.config.api = Mock(base_url="https://example.com") + hass.config.api = Mock(deprecated_base_url="https://example.com") with pytest.raises(NoURLAvailableError): _async_get_deprecated_base_url(hass, internal=True) @@ -468,7 +470,7 @@ async def test_get_deprecated_base_url_internal(hass: HomeAssistant): _async_get_deprecated_base_url(hass, internal=True, allow_ip=False) # Test with loopback - hass.config.api = Mock(base_url="https://127.0.0.42") + hass.config.api = Mock(deprecated_base_url="https://127.0.0.42") with pytest.raises(NoURLAvailableError): assert _async_get_deprecated_base_url(hass, internal=True) @@ -485,7 +487,7 @@ async def test_get_deprecated_base_url_internal(hass: HomeAssistant): async def test_get_deprecated_base_url_external(hass: HomeAssistant): """Test getting an external instance URL from the deprecated base_url.""" # Test with SSL and external domain on standard port - hass.config.api = Mock(base_url="https://example.com:443/") + hass.config.api = Mock(deprecated_base_url="https://example.com:443/") assert _async_get_deprecated_base_url(hass) == "https://example.com" assert ( _async_get_deprecated_base_url(hass, require_ssl=True) == "https://example.com" @@ -496,7 +498,7 @@ async def test_get_deprecated_base_url_external(hass: HomeAssistant): ) # Test without SSL and external domain on non-standard port - hass.config.api = Mock(base_url="http://example.com:8123/") + hass.config.api = Mock(deprecated_base_url="http://example.com:8123/") assert _async_get_deprecated_base_url(hass) == "http://example.com:8123" with pytest.raises(NoURLAvailableError): @@ -506,7 +508,7 @@ async def test_get_deprecated_base_url_external(hass: HomeAssistant): _async_get_deprecated_base_url(hass, require_standard_port=True) # Test SSL on external IP - hass.config.api = Mock(base_url="https://1.1.1.1") + hass.config.api = Mock(deprecated_base_url="https://1.1.1.1") assert _async_get_deprecated_base_url(hass) == "https://1.1.1.1" assert _async_get_deprecated_base_url(hass, require_ssl=True) == "https://1.1.1.1" assert ( @@ -518,7 +520,7 @@ async def test_get_deprecated_base_url_external(hass: HomeAssistant): _async_get_deprecated_base_url(hass, allow_ip=False) # Test with private IP - hass.config.api = Mock(base_url="https://10.10.10.10") + hass.config.api = Mock(deprecated_base_url="https://10.10.10.10") with pytest.raises(NoURLAvailableError): assert _async_get_deprecated_base_url(hass) @@ -532,7 +534,7 @@ async def test_get_deprecated_base_url_external(hass: HomeAssistant): _async_get_deprecated_base_url(hass, require_standard_port=True) # Test with local domain - hass.config.api = Mock(base_url="https://example.local") + hass.config.api = Mock(deprecated_base_url="https://example.local") with pytest.raises(NoURLAvailableError): assert _async_get_deprecated_base_url(hass) @@ -546,7 +548,7 @@ async def test_get_deprecated_base_url_external(hass: HomeAssistant): _async_get_deprecated_base_url(hass, require_standard_port=True) # Test with loopback - hass.config.api = Mock(base_url="https://127.0.0.42") + hass.config.api = Mock(deprecated_base_url="https://127.0.0.42") with pytest.raises(NoURLAvailableError): assert _async_get_deprecated_base_url(hass) @@ -563,7 +565,7 @@ async def test_get_deprecated_base_url_external(hass: HomeAssistant): async def test_get_internal_url_with_base_url_fallback(hass: HomeAssistant): """Test getting an internal instance URL with the deprecated base_url fallback.""" hass.config.api = Mock( - use_ssl=False, port=8123, base_url=None, local_ip="192.168.123.123" + use_ssl=False, port=8123, deprecated_base_url=None, local_ip="192.168.123.123" ) assert hass.config.internal_url is None assert _async_get_internal_url(hass) == "http://192.168.123.123:8123" @@ -578,7 +580,9 @@ async def test_get_internal_url_with_base_url_fallback(hass: HomeAssistant): _async_get_internal_url(hass, require_ssl=True) # Add base_url - hass.config.api = Mock(use_ssl=False, port=8123, base_url="https://example.local") + hass.config.api = Mock( + use_ssl=False, port=8123, deprecated_base_url="https://example.local" + ) assert _async_get_internal_url(hass) == "https://example.local" assert _async_get_internal_url(hass, allow_ip=False) == "https://example.local" assert ( @@ -626,14 +630,14 @@ async def test_get_internal_url_with_base_url_fallback(hass: HomeAssistant): async def test_get_external_url_with_base_url_fallback(hass: HomeAssistant): """Test getting an external instance URL with the deprecated base_url fallback.""" - hass.config.api = Mock(use_ssl=False, port=8123, base_url=None) + hass.config.api = Mock(use_ssl=False, port=8123, deprecated_base_url=None) assert hass.config.internal_url is None with pytest.raises(NoURLAvailableError): _async_get_external_url(hass) # Test with SSL and external domain on standard port - hass.config.api = Mock(base_url="https://example.com:443/") + hass.config.api = Mock(deprecated_base_url="https://example.com:443/") assert _async_get_external_url(hass) == "https://example.com" assert _async_get_external_url(hass, allow_ip=False) == "https://example.com" assert _async_get_external_url(hass, require_ssl=True) == "https://example.com" diff --git a/tests/test_core.py b/tests/test_core.py index bac17479c87..ced0b96fbed 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1310,12 +1310,12 @@ async def test_migration_base_url(hass, hass_storage): assert mock_listen.mock_calls[0][1][0] == EVENT_HOMEASSISTANT_START # External - hass.config.api = Mock(base_url="https://loaded-example.com") + hass.config.api = Mock(deprecated_base_url="https://loaded-example.com") await mock_listen.mock_calls[0][1][1](None) assert config.external_url == "https://loaded-example.com" # Internal for internal in ("http://hass.local", "http://192.168.1.100:8123"): - hass.config.api = Mock(base_url=internal) + hass.config.api = Mock(deprecated_base_url=internal) await mock_listen.mock_calls[0][1][1](None) assert config.internal_url == internal