mirror of
https://github.com/home-assistant/core.git
synced 2026-07-02 04:06:41 +01:00
05553090b9
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1155 lines
42 KiB
Python
1155 lines
42 KiB
Python
"""Test network helper."""
|
|
|
|
from unittest.mock import MagicMock, Mock, patch
|
|
|
|
from aiohttp import hdrs
|
|
from multidict import CIMultiDict, CIMultiDictProxy
|
|
import pytest
|
|
from yarl import URL
|
|
|
|
from homeassistant.components import cloud
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.core_config import async_process_ha_core_config
|
|
from homeassistant.helpers.network import (
|
|
NoURLAvailableError,
|
|
_get_cloud_url,
|
|
_get_external_url,
|
|
_get_internal_url,
|
|
_get_request_host_port,
|
|
get_supervisor_network_url,
|
|
get_url,
|
|
is_hass_url,
|
|
is_internal_request,
|
|
)
|
|
|
|
from tests.common import mock_component
|
|
|
|
|
|
@pytest.fixture(name="mock_current_request")
|
|
def mock_current_request_mock():
|
|
"""Mock the current request."""
|
|
mock_current_request = Mock(name="mock_request")
|
|
with patch(
|
|
"homeassistant.helpers.network.http.current_request",
|
|
Mock(get=mock_current_request),
|
|
):
|
|
yield mock_current_request
|
|
|
|
|
|
async def test_get_url_internal(hass: HomeAssistant) -> None:
|
|
"""Test getting an instance URL when the user has set an internal URL."""
|
|
assert hass.config.internal_url is None
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_current_request=True)
|
|
|
|
# Test with internal URL: http://example.local:8123
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "http://example.local:8123"},
|
|
)
|
|
|
|
assert hass.config.internal_url == "http://example.local:8123"
|
|
assert _get_internal_url(hass) == "http://example.local:8123"
|
|
assert _get_internal_url(hass, allow_ip=False) == "http://example.local:8123"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_standard_port=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_ssl=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_current_request=True)
|
|
|
|
with patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("example.local", 8123),
|
|
):
|
|
assert (
|
|
_get_internal_url(hass, require_current_request=True)
|
|
== "http://example.local:8123"
|
|
)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(
|
|
hass, require_current_request=True, require_standard_port=True
|
|
)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_current_request=True, require_ssl=True)
|
|
|
|
with (
|
|
patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("no_match.example.local", 8123),
|
|
),
|
|
pytest.raises(NoURLAvailableError),
|
|
):
|
|
_get_internal_url(hass, require_current_request=True)
|
|
|
|
# Test with internal URL: https://example.local:8123
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "https://example.local:8123"},
|
|
)
|
|
|
|
assert hass.config.internal_url == "https://example.local:8123"
|
|
assert _get_internal_url(hass) == "https://example.local:8123"
|
|
assert _get_internal_url(hass, allow_ip=False) == "https://example.local:8123"
|
|
assert _get_internal_url(hass, require_ssl=True) == "https://example.local:8123"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_standard_port=True)
|
|
|
|
# Test with internal URL: http://example.local:80/
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "http://example.local:80/"},
|
|
)
|
|
|
|
assert hass.config.internal_url == "http://example.local:80/"
|
|
assert _get_internal_url(hass) == "http://example.local"
|
|
assert _get_internal_url(hass, allow_ip=False) == "http://example.local"
|
|
assert _get_internal_url(hass, require_standard_port=True) == "http://example.local"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_ssl=True)
|
|
|
|
# Test with internal URL: https://example.local:443
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "https://example.local:443"},
|
|
)
|
|
|
|
assert hass.config.internal_url == "https://example.local:443"
|
|
assert _get_internal_url(hass) == "https://example.local"
|
|
assert _get_internal_url(hass, allow_ip=False) == "https://example.local"
|
|
assert (
|
|
_get_internal_url(hass, require_standard_port=True) == "https://example.local"
|
|
)
|
|
assert _get_internal_url(hass, require_ssl=True) == "https://example.local"
|
|
|
|
# Test with internal URL: https://192.168.0.1
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "https://192.168.0.1"},
|
|
)
|
|
|
|
assert hass.config.internal_url == "https://192.168.0.1"
|
|
assert _get_internal_url(hass) == "https://192.168.0.1"
|
|
assert _get_internal_url(hass, require_standard_port=True) == "https://192.168.0.1"
|
|
assert _get_internal_url(hass, require_ssl=True) == "https://192.168.0.1"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, allow_ip=False)
|
|
|
|
# Test with internal URL: http://192.168.0.1:8123
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "http://192.168.0.1:8123"},
|
|
)
|
|
|
|
assert hass.config.internal_url == "http://192.168.0.1:8123"
|
|
assert _get_internal_url(hass) == "http://192.168.0.1:8123"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_standard_port=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_ssl=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, allow_ip=False)
|
|
|
|
with patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("192.168.0.1", 8123),
|
|
):
|
|
assert (
|
|
_get_internal_url(hass, require_current_request=True)
|
|
== "http://192.168.0.1:8123"
|
|
)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_current_request=True, allow_ip=False)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(
|
|
hass, require_current_request=True, require_standard_port=True
|
|
)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_current_request=True, require_ssl=True)
|
|
|
|
|
|
async def test_get_url_internal_fallback(hass: HomeAssistant) -> None:
|
|
"""Test getting an instance URL when the user has not set an internal URL."""
|
|
assert hass.config.internal_url is None
|
|
|
|
hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123")
|
|
assert _get_internal_url(hass) == "http://192.168.123.123:8123"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, allow_ip=False)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_standard_port=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_ssl=True)
|
|
|
|
hass.config.api = Mock(use_ssl=False, port=80, local_ip="192.168.123.123")
|
|
assert _get_internal_url(hass) == "http://192.168.123.123"
|
|
assert (
|
|
_get_internal_url(hass, require_standard_port=True) == "http://192.168.123.123"
|
|
)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, allow_ip=False)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_ssl=True)
|
|
|
|
hass.config.api = Mock(use_ssl=True, port=443)
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_standard_port=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, allow_ip=False)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_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, local_ip="127.0.0.1")
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_standard_port=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, allow_ip=False)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_internal_url(hass, require_ssl=True)
|
|
|
|
|
|
async def test_get_url_external(hass: HomeAssistant) -> None:
|
|
"""Test getting an instance URL when the user has set an external URL."""
|
|
assert hass.config.external_url is None
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_current_request=True)
|
|
|
|
# Test with external URL: http://example.com:8123
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"external_url": "http://example.com:8123"},
|
|
)
|
|
|
|
assert hass.config.external_url == "http://example.com:8123"
|
|
assert _get_external_url(hass) == "http://example.com:8123"
|
|
assert _get_external_url(hass, allow_cloud=False) == "http://example.com:8123"
|
|
assert _get_external_url(hass, allow_ip=False) == "http://example.com:8123"
|
|
assert _get_external_url(hass, prefer_cloud=True) == "http://example.com:8123"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_standard_port=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_ssl=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_current_request=True)
|
|
|
|
with patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("example.com", 8123),
|
|
):
|
|
assert (
|
|
_get_external_url(hass, require_current_request=True)
|
|
== "http://example.com:8123"
|
|
)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(
|
|
hass, require_current_request=True, require_standard_port=True
|
|
)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_current_request=True, require_ssl=True)
|
|
|
|
with (
|
|
patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("no_match.example.com", 8123),
|
|
),
|
|
pytest.raises(NoURLAvailableError),
|
|
):
|
|
_get_external_url(hass, require_current_request=True)
|
|
|
|
# Test with external URL: http://example.com:80/
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"external_url": "http://example.com:80/"},
|
|
)
|
|
|
|
assert hass.config.external_url == "http://example.com:80/"
|
|
assert _get_external_url(hass) == "http://example.com"
|
|
assert _get_external_url(hass, allow_cloud=False) == "http://example.com"
|
|
assert _get_external_url(hass, allow_ip=False) == "http://example.com"
|
|
assert _get_external_url(hass, prefer_cloud=True) == "http://example.com"
|
|
assert _get_external_url(hass, require_standard_port=True) == "http://example.com"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_ssl=True)
|
|
|
|
# Test with external url: https://example.com:443/
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"external_url": "https://example.com:443/"},
|
|
)
|
|
assert hass.config.external_url == "https://example.com:443/"
|
|
assert _get_external_url(hass) == "https://example.com"
|
|
assert _get_external_url(hass, allow_cloud=False) == "https://example.com"
|
|
assert _get_external_url(hass, allow_ip=False) == "https://example.com"
|
|
assert _get_external_url(hass, prefer_cloud=True) == "https://example.com"
|
|
assert _get_external_url(hass, require_ssl=False) == "https://example.com"
|
|
assert _get_external_url(hass, require_standard_port=True) == "https://example.com"
|
|
|
|
# Test with external URL: https://example.com:80
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"external_url": "https://example.com:80"},
|
|
)
|
|
assert hass.config.external_url == "https://example.com:80"
|
|
assert _get_external_url(hass) == "https://example.com:80"
|
|
assert _get_external_url(hass, allow_cloud=False) == "https://example.com:80"
|
|
assert _get_external_url(hass, allow_ip=False) == "https://example.com:80"
|
|
assert _get_external_url(hass, prefer_cloud=True) == "https://example.com:80"
|
|
assert _get_external_url(hass, require_ssl=True) == "https://example.com:80"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_standard_port=True)
|
|
|
|
# Test with external URL: https://192.168.0.1
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"external_url": "https://192.168.0.1"},
|
|
)
|
|
assert hass.config.external_url == "https://192.168.0.1"
|
|
assert _get_external_url(hass) == "https://192.168.0.1"
|
|
assert _get_external_url(hass, allow_cloud=False) == "https://192.168.0.1"
|
|
assert _get_external_url(hass, prefer_cloud=True) == "https://192.168.0.1"
|
|
assert _get_external_url(hass, require_standard_port=True) == "https://192.168.0.1"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, allow_ip=False)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_ssl=True)
|
|
|
|
with patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("192.168.0.1", 443),
|
|
):
|
|
assert (
|
|
_get_external_url(hass, require_current_request=True)
|
|
== "https://192.168.0.1"
|
|
)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_current_request=True, allow_ip=False)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_current_request=True, require_ssl=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_external_url(hass, require_cloud=True)
|
|
|
|
with patch(
|
|
"homeassistant.components.cloud.async_remote_ui_url",
|
|
return_value="https://example.nabu.casa",
|
|
):
|
|
hass.config.components.add("cloud")
|
|
assert (
|
|
_get_external_url(hass, require_cloud=True) == "https://example.nabu.casa"
|
|
)
|
|
|
|
|
|
async def test_get_cloud_url(hass: HomeAssistant) -> None:
|
|
"""Test getting an instance URL when the user has set an external URL."""
|
|
assert hass.config.external_url is None
|
|
hass.config.components.add("cloud")
|
|
|
|
with patch(
|
|
"homeassistant.components.cloud.async_remote_ui_url",
|
|
return_value="https://example.nabu.casa",
|
|
):
|
|
assert _get_cloud_url(hass) == "https://example.nabu.casa"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_cloud_url(hass, require_current_request=True)
|
|
|
|
with patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("example.nabu.casa", 443),
|
|
):
|
|
assert (
|
|
_get_cloud_url(hass, require_current_request=True)
|
|
== "https://example.nabu.casa"
|
|
)
|
|
|
|
with (
|
|
patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("no_match.nabu.casa", 443),
|
|
),
|
|
pytest.raises(NoURLAvailableError),
|
|
):
|
|
_get_cloud_url(hass, require_current_request=True)
|
|
|
|
with (
|
|
patch(
|
|
"homeassistant.components.cloud.async_remote_ui_url",
|
|
side_effect=cloud.CloudNotAvailable,
|
|
),
|
|
pytest.raises(NoURLAvailableError),
|
|
):
|
|
_get_cloud_url(hass)
|
|
|
|
|
|
async def test_get_external_url_cloud_fallback(hass: HomeAssistant) -> None:
|
|
"""Test getting an external instance URL with cloud fallback."""
|
|
assert hass.config.external_url is None
|
|
|
|
# Test with external URL: http://1.1.1.1:8123
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"external_url": "http://1.1.1.1:8123"},
|
|
)
|
|
|
|
assert hass.config.external_url == "http://1.1.1.1:8123"
|
|
assert _get_external_url(hass, prefer_cloud=True) == "http://1.1.1.1:8123"
|
|
|
|
# Add Cloud to the previous test
|
|
hass.config.components.add("cloud")
|
|
with patch(
|
|
"homeassistant.components.cloud.async_remote_ui_url",
|
|
return_value="https://example.nabu.casa",
|
|
):
|
|
assert _get_external_url(hass, allow_cloud=False) == "http://1.1.1.1:8123"
|
|
assert _get_external_url(hass, allow_ip=False) == "https://example.nabu.casa"
|
|
assert _get_external_url(hass, prefer_cloud=False) == "http://1.1.1.1:8123"
|
|
assert _get_external_url(hass, prefer_cloud=True) == "https://example.nabu.casa"
|
|
assert _get_external_url(hass, require_ssl=True) == "https://example.nabu.casa"
|
|
assert (
|
|
_get_external_url(hass, require_standard_port=True)
|
|
== "https://example.nabu.casa"
|
|
)
|
|
|
|
# Test with external URL: https://example.com
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"external_url": "https://example.com"},
|
|
)
|
|
|
|
assert hass.config.external_url == "https://example.com"
|
|
assert _get_external_url(hass, prefer_cloud=True) == "https://example.com"
|
|
|
|
# Add Cloud to the previous test
|
|
hass.config.components.add("cloud")
|
|
with patch(
|
|
"homeassistant.components.cloud.async_remote_ui_url",
|
|
return_value="https://example.nabu.casa",
|
|
):
|
|
assert _get_external_url(hass, allow_cloud=False) == "https://example.com"
|
|
assert _get_external_url(hass, allow_ip=False) == "https://example.com"
|
|
assert _get_external_url(hass, prefer_cloud=False) == "https://example.com"
|
|
assert _get_external_url(hass, prefer_cloud=True) == "https://example.nabu.casa"
|
|
assert _get_external_url(hass, require_ssl=True) == "https://example.com"
|
|
assert (
|
|
_get_external_url(hass, require_standard_port=True) == "https://example.com"
|
|
)
|
|
assert (
|
|
_get_external_url(hass, prefer_cloud=True, allow_cloud=False)
|
|
== "https://example.com"
|
|
)
|
|
|
|
|
|
async def test_get_url(hass: HomeAssistant) -> None:
|
|
"""Test getting an instance URL."""
|
|
assert hass.config.external_url is None
|
|
assert hass.config.internal_url is None
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass)
|
|
|
|
hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123")
|
|
assert get_url(hass) == "http://192.168.123.123:8123"
|
|
assert get_url(hass, prefer_external=True) == "http://192.168.123.123:8123"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, allow_internal=False)
|
|
|
|
# Test only external
|
|
hass.config.api = None
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"external_url": "https://example.com"},
|
|
)
|
|
assert hass.config.external_url == "https://example.com"
|
|
assert hass.config.internal_url is None
|
|
assert get_url(hass) == "https://example.com"
|
|
|
|
# Test preference or allowance
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "http://example.local", "external_url": "https://example.com"},
|
|
)
|
|
assert hass.config.external_url == "https://example.com"
|
|
assert hass.config.internal_url == "http://example.local"
|
|
assert get_url(hass) == "http://example.local"
|
|
assert get_url(hass, prefer_external=True) == "https://example.com"
|
|
assert get_url(hass, allow_internal=False) == "https://example.com"
|
|
assert (
|
|
get_url(hass, prefer_external=True, allow_external=False)
|
|
== "http://example.local"
|
|
)
|
|
# Prefer external defaults to True if use_ssl=True
|
|
hass.config.api = Mock(use_ssl=True)
|
|
assert get_url(hass) == "https://example.com"
|
|
hass.config.api = Mock(use_ssl=False)
|
|
assert get_url(hass) == "http://example.local"
|
|
hass.config.api = None
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, allow_external=False, require_ssl=True)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, allow_external=False, allow_internal=False)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, require_current_request=True)
|
|
|
|
with (
|
|
patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("example.com", 443),
|
|
),
|
|
patch("homeassistant.helpers.http.current_request"),
|
|
):
|
|
assert get_url(hass, require_current_request=True) == "https://example.com"
|
|
assert (
|
|
get_url(hass, require_current_request=True, require_ssl=True)
|
|
== "https://example.com"
|
|
)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, require_current_request=True, allow_external=False)
|
|
|
|
with (
|
|
patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("example.local", 80),
|
|
),
|
|
patch("homeassistant.helpers.http.current_request"),
|
|
):
|
|
assert get_url(hass, require_current_request=True) == "http://example.local"
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, require_current_request=True, allow_internal=False)
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, require_current_request=True, require_ssl=True)
|
|
|
|
with (
|
|
patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("no_match.example.com", 80),
|
|
),
|
|
pytest.raises(NoURLAvailableError),
|
|
):
|
|
_get_internal_url(hass, require_current_request=True)
|
|
|
|
# Test allow_ip defaults when SSL specified
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"external_url": "https://1.1.1.1"},
|
|
)
|
|
assert hass.config.external_url == "https://1.1.1.1"
|
|
assert get_url(hass, allow_internal=False) == "https://1.1.1.1"
|
|
hass.config.api = Mock(use_ssl=False)
|
|
assert get_url(hass, allow_internal=False) == "https://1.1.1.1"
|
|
hass.config.api = Mock(use_ssl=True)
|
|
with pytest.raises(NoURLAvailableError):
|
|
assert get_url(hass, allow_internal=False)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
(
|
|
"internal_url",
|
|
"external_url",
|
|
"host_header",
|
|
"request_url",
|
|
"expected_url",
|
|
"prefer_external",
|
|
),
|
|
[
|
|
# Scenario 1: HTTP, internal is standard port (http://example.com), external is custom port (http://example.com:18123)
|
|
pytest.param(
|
|
"http://example.com",
|
|
"http://example.com:18123",
|
|
"example.com",
|
|
"http://example.com/test/request",
|
|
"http://example.com",
|
|
False,
|
|
id="http_internal_std_request_std_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"http://example.com",
|
|
"http://example.com:18123",
|
|
"example.com",
|
|
"http://example.com/test/request",
|
|
"http://example.com",
|
|
True,
|
|
id="http_internal_std_request_std_prefer_external",
|
|
),
|
|
pytest.param(
|
|
"http://example.com",
|
|
"http://example.com:18123",
|
|
"example.com:80",
|
|
"http://example.com:80/test/request",
|
|
"http://example.com",
|
|
False,
|
|
id="http_internal_std_request_80_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"http://example.com",
|
|
"http://example.com:18123",
|
|
"example.com:80",
|
|
"http://example.com:80/test/request",
|
|
"http://example.com",
|
|
True,
|
|
id="http_internal_std_request_80_prefer_external",
|
|
),
|
|
pytest.param(
|
|
"http://example.com",
|
|
"http://example.com:18123",
|
|
"example.com:18123",
|
|
"http://example.com:18123/test/request",
|
|
"http://example.com:18123",
|
|
False,
|
|
id="http_internal_std_request_custom_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"http://example.com",
|
|
"http://example.com:18123",
|
|
"example.com:18123",
|
|
"http://example.com:18123/test/request",
|
|
"http://example.com:18123",
|
|
True,
|
|
id="http_internal_std_request_custom_prefer_external",
|
|
),
|
|
# Scenario 2: HTTP, internal is custom port (http://example.com:18123), external is standard port (http://example.com)
|
|
pytest.param(
|
|
"http://example.com:18123",
|
|
"http://example.com",
|
|
"example.com",
|
|
"http://example.com/test/request",
|
|
"http://example.com",
|
|
False,
|
|
id="http_internal_custom_request_std_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"http://example.com:18123",
|
|
"http://example.com",
|
|
"example.com",
|
|
"http://example.com/test/request",
|
|
"http://example.com",
|
|
True,
|
|
id="http_internal_custom_request_std_prefer_external",
|
|
),
|
|
pytest.param(
|
|
"http://example.com:18123",
|
|
"http://example.com",
|
|
"example.com:80",
|
|
"http://example.com:80/test/request",
|
|
"http://example.com",
|
|
False,
|
|
id="http_internal_custom_request_80_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"http://example.com:18123",
|
|
"http://example.com",
|
|
"example.com:80",
|
|
"http://example.com:80/test/request",
|
|
"http://example.com",
|
|
True,
|
|
id="http_internal_custom_request_80_prefer_external",
|
|
),
|
|
pytest.param(
|
|
"http://example.com:18123",
|
|
"http://example.com",
|
|
"example.com:18123",
|
|
"http://example.com:18123/test/request",
|
|
"http://example.com:18123",
|
|
False,
|
|
id="http_internal_custom_request_custom_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"http://example.com:18123",
|
|
"http://example.com",
|
|
"example.com:18123",
|
|
"http://example.com:18123/test/request",
|
|
"http://example.com:18123",
|
|
True,
|
|
id="http_internal_custom_request_custom_prefer_external",
|
|
),
|
|
# Scenario 3: HTTPS, internal is standard port (https://example.com), external is custom port (https://example.com:18123)
|
|
pytest.param(
|
|
"https://example.com",
|
|
"https://example.com:18123",
|
|
"example.com",
|
|
"https://example.com/test/request",
|
|
"https://example.com",
|
|
False,
|
|
id="https_internal_std_request_std_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"https://example.com",
|
|
"https://example.com:18123",
|
|
"example.com",
|
|
"https://example.com/test/request",
|
|
"https://example.com",
|
|
True,
|
|
id="https_internal_std_request_std_prefer_external",
|
|
),
|
|
pytest.param(
|
|
"https://example.com",
|
|
"https://example.com:18123",
|
|
"example.com:443",
|
|
"https://example.com:443/test/request",
|
|
"https://example.com",
|
|
False,
|
|
id="https_internal_std_request_443_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"https://example.com",
|
|
"https://example.com:18123",
|
|
"example.com:443",
|
|
"https://example.com:443/test/request",
|
|
"https://example.com",
|
|
True,
|
|
id="https_internal_std_request_443_prefer_external",
|
|
),
|
|
pytest.param(
|
|
"https://example.com",
|
|
"https://example.com:18123",
|
|
"example.com:18123",
|
|
"https://example.com:18123/test/request",
|
|
"https://example.com:18123",
|
|
False,
|
|
id="https_internal_std_request_custom_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"https://example.com",
|
|
"https://example.com:18123",
|
|
"example.com:18123",
|
|
"https://example.com:18123/test/request",
|
|
"https://example.com:18123",
|
|
True,
|
|
id="https_internal_std_request_custom_prefer_external",
|
|
),
|
|
# Scenario 4: HTTPS, internal is custom port (https://example.com:18123), external is standard port (https://example.com)
|
|
pytest.param(
|
|
"https://example.com:18123",
|
|
"https://example.com",
|
|
"example.com",
|
|
"https://example.com/test/request",
|
|
"https://example.com",
|
|
False,
|
|
id="https_internal_custom_request_std_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"https://example.com:18123",
|
|
"https://example.com",
|
|
"example.com",
|
|
"https://example.com/test/request",
|
|
"https://example.com",
|
|
True,
|
|
id="https_internal_custom_request_std_prefer_external",
|
|
),
|
|
pytest.param(
|
|
"https://example.com:18123",
|
|
"https://example.com",
|
|
"example.com:443",
|
|
"https://example.com:443/test/request",
|
|
"https://example.com",
|
|
False,
|
|
id="https_internal_custom_request_443_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"https://example.com:18123",
|
|
"https://example.com",
|
|
"example.com:443",
|
|
"https://example.com:443/test/request",
|
|
"https://example.com",
|
|
True,
|
|
id="https_internal_custom_request_443_prefer_external",
|
|
),
|
|
pytest.param(
|
|
"https://example.com:18123",
|
|
"https://example.com",
|
|
"example.com:18123",
|
|
"https://example.com:18123/test/request",
|
|
"https://example.com:18123",
|
|
False,
|
|
id="https_internal_custom_request_custom_prefer_internal",
|
|
),
|
|
pytest.param(
|
|
"https://example.com:18123",
|
|
"https://example.com",
|
|
"example.com:18123",
|
|
"https://example.com:18123/test/request",
|
|
"https://example.com:18123",
|
|
True,
|
|
id="https_internal_custom_request_custom_prefer_external",
|
|
),
|
|
],
|
|
)
|
|
async def test_get_url_host_matching_respects_port(
|
|
hass: HomeAssistant,
|
|
internal_url: str,
|
|
external_url: str,
|
|
host_header: str,
|
|
request_url: str,
|
|
expected_url: str,
|
|
prefer_external: bool,
|
|
) -> None:
|
|
"""Test that get_url respects the port when matching the request host."""
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{
|
|
"internal_url": internal_url,
|
|
"external_url": external_url,
|
|
},
|
|
)
|
|
with patch("homeassistant.helpers.http.current_request") as mock_request_context:
|
|
mock_request = Mock()
|
|
mock_request.headers = {hdrs.HOST: host_header}
|
|
mock_request.url = URL(request_url)
|
|
mock_request_context.get.return_value = mock_request
|
|
|
|
assert (
|
|
get_url(hass, require_current_request=True, prefer_external=prefer_external)
|
|
== expected_url
|
|
)
|
|
|
|
|
|
async def test_get_request_host_port_with_port(hass: HomeAssistant) -> None:
|
|
"""Test getting the host and port of the current web request from the request context."""
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_request_host_port()
|
|
|
|
with patch("homeassistant.helpers.http.current_request") as mock_request_context:
|
|
mock_request = Mock()
|
|
mock_request.headers = CIMultiDictProxy(
|
|
CIMultiDict({hdrs.HOST: "example.com:8123"})
|
|
)
|
|
mock_request.url = URL("http://example.com:8123/test/request")
|
|
mock_request.host = "example.com:8123"
|
|
mock_request_context.get = Mock(return_value=mock_request)
|
|
|
|
assert _get_request_host_port() == ("example.com", 8123)
|
|
|
|
|
|
async def test_get_request_host_port_without_port(hass: HomeAssistant) -> None:
|
|
"""Test getting the host and port of the current web request from the request context."""
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_request_host_port()
|
|
|
|
with patch("homeassistant.helpers.http.current_request") as mock_request_context:
|
|
mock_request = Mock()
|
|
mock_request.headers = CIMultiDictProxy(CIMultiDict({hdrs.HOST: "example.com"}))
|
|
mock_request.url = URL("http://example.com/test/request")
|
|
mock_request.host = "example.com"
|
|
mock_request_context.get = Mock(return_value=mock_request)
|
|
|
|
assert _get_request_host_port() == ("example.com", 80)
|
|
|
|
|
|
async def test_get_request_ipv6_address(hass: HomeAssistant) -> None:
|
|
"""Test getting the ipv6 host and port of the current web request."""
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_request_host_port()
|
|
|
|
with patch("homeassistant.helpers.http.current_request") as mock_request_context:
|
|
mock_request = Mock()
|
|
mock_request.headers = CIMultiDictProxy(CIMultiDict({hdrs.HOST: "[::1]:8123"}))
|
|
mock_request.url = URL("http://[::1]:8123/test/request")
|
|
mock_request.host = "[::1]:8123"
|
|
mock_request_context.get = Mock(return_value=mock_request)
|
|
|
|
assert _get_request_host_port() == ("::1", 8123)
|
|
|
|
|
|
async def test_get_request_ipv6_address_without_port(hass: HomeAssistant) -> None:
|
|
"""Test getting the ipv6 host and port of the current web request."""
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_request_host_port()
|
|
|
|
with patch("homeassistant.helpers.http.current_request") as mock_request_context:
|
|
mock_request = Mock()
|
|
mock_request.headers = CIMultiDictProxy(CIMultiDict({hdrs.HOST: "[::1]"}))
|
|
mock_request.url = URL("http://[::1]/test/request")
|
|
mock_request.host = "[::1]"
|
|
mock_request_context.get = Mock(return_value=mock_request)
|
|
|
|
assert _get_request_host_port() == ("::1", 80)
|
|
|
|
|
|
async def test_get_request_host_port_no_host_header(hass: HomeAssistant) -> None:
|
|
"""Test getting the host and port of the current web request from the request context."""
|
|
with pytest.raises(NoURLAvailableError):
|
|
_get_request_host_port()
|
|
|
|
with patch("homeassistant.helpers.http.current_request") as mock_request_context:
|
|
mock_request = Mock()
|
|
mock_request.headers = CIMultiDictProxy(CIMultiDict())
|
|
mock_request.url = URL("/test/request")
|
|
mock_request_context.get = Mock(return_value=mock_request)
|
|
|
|
assert _get_request_host_port() == (None, None)
|
|
|
|
|
|
@patch("homeassistant.helpers.hassio.is_hassio", Mock(return_value=True))
|
|
@patch(
|
|
"homeassistant.components.hassio.get_host_info",
|
|
Mock(return_value={"hostname": "homeassistant"}),
|
|
)
|
|
async def test_get_current_request_url_with_known_host(
|
|
hass: HomeAssistant, current_request: MagicMock
|
|
) -> None:
|
|
"""Test getting current request URL with known hosts addresses."""
|
|
hass.config.api = Mock(use_ssl=False, port=8123, local_ip="127.0.0.1")
|
|
assert hass.config.internal_url is None
|
|
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, require_current_request=True)
|
|
|
|
# Ensure we accept localhost
|
|
with patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("localhost", 8123),
|
|
):
|
|
assert get_url(hass, require_current_request=True) == "http://localhost:8123"
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, require_current_request=True, require_ssl=True)
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, require_current_request=True, require_standard_port=True)
|
|
|
|
# Ensure we accept local loopback ip (e.g., 127.0.0.1)
|
|
with patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("127.0.0.8", 8123),
|
|
):
|
|
assert get_url(hass, require_current_request=True) == "http://127.0.0.8:8123"
|
|
with pytest.raises(NoURLAvailableError):
|
|
get_url(hass, require_current_request=True, allow_ip=False)
|
|
|
|
# Ensure hostname from Supervisor is accepted transparently
|
|
mock_component(hass, "hassio")
|
|
|
|
with patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("homeassistant.local", 8123),
|
|
):
|
|
assert (
|
|
get_url(hass, require_current_request=True)
|
|
== "http://homeassistant.local:8123"
|
|
)
|
|
|
|
with patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("homeassistant", 8123),
|
|
):
|
|
assert (
|
|
get_url(hass, require_current_request=True) == "http://homeassistant:8123"
|
|
)
|
|
|
|
with (
|
|
patch(
|
|
"homeassistant.helpers.network._get_request_host_port",
|
|
return_value=("unknown.local", 8123),
|
|
),
|
|
pytest.raises(NoURLAvailableError),
|
|
):
|
|
get_url(hass, require_current_request=True)
|
|
|
|
|
|
@patch(
|
|
"homeassistant.helpers.network.is_hassio",
|
|
Mock(return_value={"hostname": "homeassistant"}),
|
|
)
|
|
@patch(
|
|
"homeassistant.components.hassio.get_host_info",
|
|
Mock(return_value={"hostname": "hellohost"}),
|
|
)
|
|
async def test_is_internal_request(
|
|
hass: HomeAssistant,
|
|
mock_current_request: Mock,
|
|
) -> None:
|
|
"""Test if accessing an instance on its internal URL."""
|
|
# Test with internal URL: http://example.local:8123
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "http://example.local:8123"},
|
|
)
|
|
|
|
assert hass.config.internal_url == "http://example.local:8123"
|
|
|
|
# No request active
|
|
mock_current_request.return_value = None
|
|
assert not is_internal_request(hass)
|
|
|
|
mock_current_request.return_value = Mock(
|
|
headers=CIMultiDictProxy(CIMultiDict({hdrs.HOST: "example.local:8123"})),
|
|
host="example.local:8123",
|
|
url=URL("http://example.local:8123"),
|
|
)
|
|
assert is_internal_request(hass)
|
|
|
|
mock_current_request.return_value = Mock(
|
|
headers=CIMultiDictProxy(
|
|
CIMultiDict({hdrs.HOST: "no_match.example.local:8123"})
|
|
),
|
|
host="no_match.example.local:8123",
|
|
url=URL("http://no_match.example.local:8123"),
|
|
)
|
|
assert not is_internal_request(hass)
|
|
|
|
# Test with internal URL: http://192.168.0.1:8123
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "http://192.168.0.1:8123"},
|
|
)
|
|
|
|
assert hass.config.internal_url == "http://192.168.0.1:8123"
|
|
assert not is_internal_request(hass)
|
|
|
|
mock_current_request.return_value = Mock(
|
|
headers=CIMultiDictProxy(CIMultiDict({hdrs.HOST: "192.168.0.1:8123"})),
|
|
host="192.168.0.1:8123",
|
|
url=URL("http://192.168.0.1:8123"),
|
|
)
|
|
assert is_internal_request(hass)
|
|
|
|
# Test for matching against local IP
|
|
hass.config.api = Mock(use_ssl=False, local_ip="192.168.123.123", port=8123)
|
|
for allowed in ("127.0.0.1", "192.168.123.123"):
|
|
mock_current_request.return_value = Mock(
|
|
headers=CIMultiDictProxy(CIMultiDict({hdrs.HOST: f"{allowed}:8123"})),
|
|
host=f"{allowed}:8123",
|
|
url=URL(f"http://{allowed}:8123"),
|
|
)
|
|
assert is_internal_request(hass), mock_current_request.return_value.url
|
|
|
|
# Test for matching against HassOS hostname
|
|
for allowed in ("hellohost", "hellohost.local"):
|
|
mock_current_request.return_value = Mock(
|
|
headers=CIMultiDictProxy(CIMultiDict({hdrs.HOST: f"{allowed}:8123"})),
|
|
host=f"{allowed}:8123",
|
|
url=URL(f"http://{allowed}:8123"),
|
|
)
|
|
assert is_internal_request(hass), mock_current_request.return_value.url
|
|
|
|
|
|
async def test_is_hass_url(hass: HomeAssistant) -> None:
|
|
"""Test is_hass_url."""
|
|
assert hass.config.api is None
|
|
assert hass.config.internal_url is None
|
|
assert hass.config.external_url is None
|
|
|
|
assert is_hass_url(hass, "http://example.com") is False
|
|
assert is_hass_url(hass, "bad_url") is False
|
|
assert is_hass_url(hass, "bad_url.com") is False
|
|
assert is_hass_url(hass, "http:/bad_url.com") is False
|
|
|
|
hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123")
|
|
assert is_hass_url(hass, "http://192.168.123.123:8123") is True
|
|
assert is_hass_url(hass, "https://192.168.123.123:8123") is False
|
|
assert is_hass_url(hass, "http://192.168.123.123") is False
|
|
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "http://example.local:8123"},
|
|
)
|
|
assert is_hass_url(hass, "http://example.local:8123") is True
|
|
assert is_hass_url(hass, "https://example.local:8123") is False
|
|
assert is_hass_url(hass, "http://example.local") is False
|
|
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"external_url": "https://example.com:443"},
|
|
)
|
|
assert is_hass_url(hass, "https://example.com:443") is True
|
|
assert is_hass_url(hass, "https://example.com") is True
|
|
assert is_hass_url(hass, "http://example.com:443") is False
|
|
assert is_hass_url(hass, "http://example.com") is False
|
|
|
|
with patch(
|
|
"homeassistant.components.cloud.async_remote_ui_url",
|
|
return_value="https://example.nabu.casa",
|
|
):
|
|
assert is_hass_url(hass, "https://example.nabu.casa") is False
|
|
|
|
hass.config.components.add("cloud")
|
|
assert is_hass_url(hass, "https://example.nabu.casa:443") is True
|
|
assert is_hass_url(hass, "https://example.nabu.casa") is True
|
|
assert is_hass_url(hass, "http://example.nabu.casa:443") is False
|
|
assert is_hass_url(hass, "http://example.nabu.casa") is False
|
|
|
|
|
|
async def test_is_hass_url_addon_url(hass: HomeAssistant) -> None:
|
|
"""Test is_hass_url with a supervisor network URL."""
|
|
assert is_hass_url(hass, "http://homeassistant:8123") is False
|
|
|
|
hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123")
|
|
await async_process_ha_core_config(
|
|
hass,
|
|
{"internal_url": "http://example.local:8123"},
|
|
)
|
|
assert is_hass_url(hass, "http://homeassistant:8123") is False
|
|
|
|
mock_component(hass, "hassio")
|
|
assert is_hass_url(hass, "http://homeassistant:8123")
|
|
assert not is_hass_url(hass, "https://homeassistant:8123")
|
|
|
|
hass.config.api = Mock(use_ssl=True, port=8123, local_ip="192.168.123.123")
|
|
assert not is_hass_url(hass, "http://homeassistant:8123")
|
|
assert is_hass_url(hass, "https://homeassistant:8123")
|
|
|
|
|
|
async def test_get_supervisor_network_url(hass: HomeAssistant) -> None:
|
|
"""Test get_supervisor_network_url."""
|
|
assert get_supervisor_network_url(hass) is None
|
|
|
|
hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123")
|
|
await async_process_ha_core_config(hass, {})
|
|
assert get_supervisor_network_url(hass) is None
|
|
|
|
mock_component(hass, "hassio")
|
|
assert get_supervisor_network_url(hass) == "http://homeassistant:8123"
|
|
|
|
hass.config.api = Mock(use_ssl=True, port=8123, local_ip="192.168.123.123")
|
|
assert get_supervisor_network_url(hass) is None
|
|
assert (
|
|
get_supervisor_network_url(hass, allow_ssl=True) == "https://homeassistant:8123"
|
|
)
|