Elmax - fix issue 136877 (#138419)

* Fix IPv6 zero-conf discovery not handling hostname correctly.

* Aligned tests.

* Remove redundant !s notation.

* Add IPv6 discovery tests

* Parametrize input_uri to avoid duplicated code

* Update tests/components/elmax/conftest.py

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
pull/138699/head^2
Alberto Geniola 2025-02-17 10:01:27 +01:00 committed by GitHub
parent e77193fa2e
commit cd13eff8ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 73 additions and 11 deletions

View File

@ -498,7 +498,11 @@ class ElmaxConfigFlow(ConfigFlow, domain=DOMAIN):
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle device found via zeroconf."""
host = discovery_info.host
host = (
f"[{discovery_info.ip_address}]"
if discovery_info.ip_address.version == 6
else str(discovery_info.ip_address)
)
https_port = (
int(discovery_info.port)
if discovery_info.port is not None

View File

@ -30,6 +30,7 @@ MOCK_PANEL_PIN = "000000"
MOCK_WRONG_PANEL_PIN = "000000"
MOCK_PASSWORD = "password"
MOCK_DIRECT_HOST = "1.1.1.1"
MOCK_DIRECT_HOST_V6 = "fd00::be2:54:34:2"
MOCK_DIRECT_HOST_CHANGED = "2.2.2.2"
MOCK_DIRECT_PORT = 443
MOCK_DIRECT_SSL = True

View File

@ -18,6 +18,7 @@ import respx
from . import (
MOCK_DIRECT_HOST,
MOCK_DIRECT_HOST_V6,
MOCK_DIRECT_PORT,
MOCK_DIRECT_SSL,
MOCK_PANEL_ID,
@ -29,6 +30,7 @@ from tests.common import load_fixture
MOCK_DIRECT_BASE_URI = (
f"{'https' if MOCK_DIRECT_SSL else 'http'}://{MOCK_DIRECT_HOST}:{MOCK_DIRECT_PORT}"
)
MOCK_DIRECT_BASE_URI_V6 = f"{'https' if MOCK_DIRECT_SSL else 'http'}://[{MOCK_DIRECT_HOST_V6}]:{MOCK_DIRECT_PORT}"
@pytest.fixture(autouse=True)
@ -58,12 +60,16 @@ def httpx_mock_cloud_fixture() -> Generator[respx.MockRouter]:
yield respx_mock
@pytest.fixture
def base_uri() -> str:
"""Configure the base-uri for the respx mock fixtures."""
return MOCK_DIRECT_BASE_URI
@pytest.fixture(autouse=True)
def httpx_mock_direct_fixture() -> Generator[respx.MockRouter]:
def httpx_mock_direct_fixture(base_uri: str) -> Generator[respx.MockRouter]:
"""Configure httpx fixture for direct Panel-API communication."""
with respx.mock(
base_url=MOCK_DIRECT_BASE_URI, assert_all_called=False
) as respx_mock:
with respx.mock(base_url=base_uri, assert_all_called=False) as respx_mock:
# Mock Login POST.
login_route = respx_mock.post(f"/api/v2/{ENDPOINT_LOGIN}", name="login")

View File

@ -1,8 +1,10 @@
"""Tests for the Elmax config flow."""
from ipaddress import IPv4Address, IPv6Address
from unittest.mock import patch
from elmax_api.exceptions import ElmaxBadLoginError, ElmaxBadPinError, ElmaxNetworkError
import pytest
from homeassistant import config_entries
from homeassistant.components.elmax.const import (
@ -28,6 +30,7 @@ from . import (
MOCK_DIRECT_CERT,
MOCK_DIRECT_HOST,
MOCK_DIRECT_HOST_CHANGED,
MOCK_DIRECT_HOST_V6,
MOCK_DIRECT_PORT,
MOCK_DIRECT_SSL,
MOCK_PANEL_ID,
@ -37,12 +40,27 @@ from . import (
MOCK_USERNAME,
MOCK_WRONG_PANEL_PIN,
)
from .conftest import MOCK_DIRECT_BASE_URI_V6
from tests.common import MockConfigEntry
MOCK_ZEROCONF_DISCOVERY_INFO = ZeroconfServiceInfo(
ip_address=MOCK_DIRECT_HOST,
ip_addresses=[MOCK_DIRECT_HOST],
ip_address=IPv4Address(address=MOCK_DIRECT_HOST),
ip_addresses=[IPv4Address(address=MOCK_DIRECT_HOST)],
hostname="VideoBox.local",
name="VideoBox",
port=443,
properties={
"idl": MOCK_PANEL_ID,
"idr": MOCK_PANEL_ID,
"v1": "PHANTOM64PRO_GSM 11.9.844",
"v2": "4.9.13",
},
type="_elmax-ssl._tcp",
)
MOCK_ZEROCONF_DISCOVERY_INFO_V6 = ZeroconfServiceInfo(
ip_address=IPv6Address(address=MOCK_DIRECT_HOST_V6),
ip_addresses=[IPv6Address(address=MOCK_DIRECT_HOST_V6)],
hostname="VideoBox.local",
name="VideoBox",
port=443,
@ -55,8 +73,8 @@ MOCK_ZEROCONF_DISCOVERY_INFO = ZeroconfServiceInfo(
type="_elmax-ssl._tcp",
)
MOCK_ZEROCONF_DISCOVERY_CHANGED_INFO = ZeroconfServiceInfo(
ip_address=MOCK_DIRECT_HOST_CHANGED,
ip_addresses=[MOCK_DIRECT_HOST_CHANGED],
ip_address=IPv4Address(address=MOCK_DIRECT_HOST_CHANGED),
ip_addresses=[IPv4Address(address=MOCK_DIRECT_HOST_CHANGED)],
hostname="VideoBox.local",
name="VideoBox",
port=443,
@ -69,8 +87,8 @@ MOCK_ZEROCONF_DISCOVERY_CHANGED_INFO = ZeroconfServiceInfo(
type="_elmax-ssl._tcp",
)
MOCK_ZEROCONF_DISCOVERY_INFO_NOT_SUPPORTED = ZeroconfServiceInfo(
ip_address=MOCK_DIRECT_HOST,
ip_addresses=[MOCK_DIRECT_HOST],
ip_address=IPv4Address(MOCK_DIRECT_HOST),
ip_addresses=[IPv4Address(MOCK_DIRECT_HOST)],
hostname="VideoBox.local",
name="VideoBox",
port=443,
@ -194,6 +212,18 @@ async def test_zeroconf_discovery(hass: HomeAssistant) -> None:
assert result["errors"] is None
async def test_zeroconf_discovery_ipv6(hass: HomeAssistant) -> None:
"""Test discovery of Elmax local api panel."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_ZEROCONF},
data=MOCK_ZEROCONF_DISCOVERY_INFO_V6,
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zeroconf_setup"
assert result["errors"] is None
async def test_zeroconf_setup_show_form(hass: HomeAssistant) -> None:
"""Test discovery shows a form when activated."""
result = await hass.config_entries.flow.async_init(
@ -230,6 +260,27 @@ async def test_zeroconf_setup(hass: HomeAssistant) -> None:
assert result["type"] is FlowResultType.CREATE_ENTRY
@pytest.mark.parametrize("base_uri", [MOCK_DIRECT_BASE_URI_V6])
async def test_zeroconf_ipv6_setup(hass: HomeAssistant) -> None:
"""Test the successful creation of config entry via discovery flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_ZEROCONF},
data=MOCK_ZEROCONF_DISCOVERY_INFO_V6,
)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN,
CONF_ELMAX_MODE_DIRECT_SSL: MOCK_DIRECT_SSL,
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
async def test_zeroconf_already_configured(hass: HomeAssistant) -> None:
"""Ensure local discovery aborts when same panel is already added to ha."""
MockConfigEntry(