mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
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>
This commit is contained in:
parent
e77193fa2e
commit
cd13eff8ae
@ -498,7 +498,11 @@ class ElmaxConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
self, discovery_info: ZeroconfServiceInfo
|
self, discovery_info: ZeroconfServiceInfo
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle device found via zeroconf."""
|
"""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 = (
|
https_port = (
|
||||||
int(discovery_info.port)
|
int(discovery_info.port)
|
||||||
if discovery_info.port is not None
|
if discovery_info.port is not None
|
||||||
|
@ -30,6 +30,7 @@ MOCK_PANEL_PIN = "000000"
|
|||||||
MOCK_WRONG_PANEL_PIN = "000000"
|
MOCK_WRONG_PANEL_PIN = "000000"
|
||||||
MOCK_PASSWORD = "password"
|
MOCK_PASSWORD = "password"
|
||||||
MOCK_DIRECT_HOST = "1.1.1.1"
|
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_HOST_CHANGED = "2.2.2.2"
|
||||||
MOCK_DIRECT_PORT = 443
|
MOCK_DIRECT_PORT = 443
|
||||||
MOCK_DIRECT_SSL = True
|
MOCK_DIRECT_SSL = True
|
||||||
|
@ -18,6 +18,7 @@ import respx
|
|||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
MOCK_DIRECT_HOST,
|
MOCK_DIRECT_HOST,
|
||||||
|
MOCK_DIRECT_HOST_V6,
|
||||||
MOCK_DIRECT_PORT,
|
MOCK_DIRECT_PORT,
|
||||||
MOCK_DIRECT_SSL,
|
MOCK_DIRECT_SSL,
|
||||||
MOCK_PANEL_ID,
|
MOCK_PANEL_ID,
|
||||||
@ -29,6 +30,7 @@ from tests.common import load_fixture
|
|||||||
MOCK_DIRECT_BASE_URI = (
|
MOCK_DIRECT_BASE_URI = (
|
||||||
f"{'https' if MOCK_DIRECT_SSL else 'http'}://{MOCK_DIRECT_HOST}:{MOCK_DIRECT_PORT}"
|
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)
|
@pytest.fixture(autouse=True)
|
||||||
@ -58,12 +60,16 @@ def httpx_mock_cloud_fixture() -> Generator[respx.MockRouter]:
|
|||||||
yield respx_mock
|
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)
|
@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."""
|
"""Configure httpx fixture for direct Panel-API communication."""
|
||||||
with respx.mock(
|
with respx.mock(base_url=base_uri, assert_all_called=False) as respx_mock:
|
||||||
base_url=MOCK_DIRECT_BASE_URI, assert_all_called=False
|
|
||||||
) as respx_mock:
|
|
||||||
# Mock Login POST.
|
# Mock Login POST.
|
||||||
login_route = respx_mock.post(f"/api/v2/{ENDPOINT_LOGIN}", name="login")
|
login_route = respx_mock.post(f"/api/v2/{ENDPOINT_LOGIN}", name="login")
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""Tests for the Elmax config flow."""
|
"""Tests for the Elmax config flow."""
|
||||||
|
|
||||||
|
from ipaddress import IPv4Address, IPv6Address
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from elmax_api.exceptions import ElmaxBadLoginError, ElmaxBadPinError, ElmaxNetworkError
|
from elmax_api.exceptions import ElmaxBadLoginError, ElmaxBadPinError, ElmaxNetworkError
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.elmax.const import (
|
from homeassistant.components.elmax.const import (
|
||||||
@ -28,6 +30,7 @@ from . import (
|
|||||||
MOCK_DIRECT_CERT,
|
MOCK_DIRECT_CERT,
|
||||||
MOCK_DIRECT_HOST,
|
MOCK_DIRECT_HOST,
|
||||||
MOCK_DIRECT_HOST_CHANGED,
|
MOCK_DIRECT_HOST_CHANGED,
|
||||||
|
MOCK_DIRECT_HOST_V6,
|
||||||
MOCK_DIRECT_PORT,
|
MOCK_DIRECT_PORT,
|
||||||
MOCK_DIRECT_SSL,
|
MOCK_DIRECT_SSL,
|
||||||
MOCK_PANEL_ID,
|
MOCK_PANEL_ID,
|
||||||
@ -37,12 +40,27 @@ from . import (
|
|||||||
MOCK_USERNAME,
|
MOCK_USERNAME,
|
||||||
MOCK_WRONG_PANEL_PIN,
|
MOCK_WRONG_PANEL_PIN,
|
||||||
)
|
)
|
||||||
|
from .conftest import MOCK_DIRECT_BASE_URI_V6
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
MOCK_ZEROCONF_DISCOVERY_INFO = ZeroconfServiceInfo(
|
MOCK_ZEROCONF_DISCOVERY_INFO = ZeroconfServiceInfo(
|
||||||
ip_address=MOCK_DIRECT_HOST,
|
ip_address=IPv4Address(address=MOCK_DIRECT_HOST),
|
||||||
ip_addresses=[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",
|
hostname="VideoBox.local",
|
||||||
name="VideoBox",
|
name="VideoBox",
|
||||||
port=443,
|
port=443,
|
||||||
@ -55,8 +73,8 @@ MOCK_ZEROCONF_DISCOVERY_INFO = ZeroconfServiceInfo(
|
|||||||
type="_elmax-ssl._tcp",
|
type="_elmax-ssl._tcp",
|
||||||
)
|
)
|
||||||
MOCK_ZEROCONF_DISCOVERY_CHANGED_INFO = ZeroconfServiceInfo(
|
MOCK_ZEROCONF_DISCOVERY_CHANGED_INFO = ZeroconfServiceInfo(
|
||||||
ip_address=MOCK_DIRECT_HOST_CHANGED,
|
ip_address=IPv4Address(address=MOCK_DIRECT_HOST_CHANGED),
|
||||||
ip_addresses=[MOCK_DIRECT_HOST_CHANGED],
|
ip_addresses=[IPv4Address(address=MOCK_DIRECT_HOST_CHANGED)],
|
||||||
hostname="VideoBox.local",
|
hostname="VideoBox.local",
|
||||||
name="VideoBox",
|
name="VideoBox",
|
||||||
port=443,
|
port=443,
|
||||||
@ -69,8 +87,8 @@ MOCK_ZEROCONF_DISCOVERY_CHANGED_INFO = ZeroconfServiceInfo(
|
|||||||
type="_elmax-ssl._tcp",
|
type="_elmax-ssl._tcp",
|
||||||
)
|
)
|
||||||
MOCK_ZEROCONF_DISCOVERY_INFO_NOT_SUPPORTED = ZeroconfServiceInfo(
|
MOCK_ZEROCONF_DISCOVERY_INFO_NOT_SUPPORTED = ZeroconfServiceInfo(
|
||||||
ip_address=MOCK_DIRECT_HOST,
|
ip_address=IPv4Address(MOCK_DIRECT_HOST),
|
||||||
ip_addresses=[MOCK_DIRECT_HOST],
|
ip_addresses=[IPv4Address(MOCK_DIRECT_HOST)],
|
||||||
hostname="VideoBox.local",
|
hostname="VideoBox.local",
|
||||||
name="VideoBox",
|
name="VideoBox",
|
||||||
port=443,
|
port=443,
|
||||||
@ -194,6 +212,18 @@ async def test_zeroconf_discovery(hass: HomeAssistant) -> None:
|
|||||||
assert result["errors"] is 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:
|
async def test_zeroconf_setup_show_form(hass: HomeAssistant) -> None:
|
||||||
"""Test discovery shows a form when activated."""
|
"""Test discovery shows a form when activated."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
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
|
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:
|
async def test_zeroconf_already_configured(hass: HomeAssistant) -> None:
|
||||||
"""Ensure local discovery aborts when same panel is already added to ha."""
|
"""Ensure local discovery aborts when same panel is already added to ha."""
|
||||||
MockConfigEntry(
|
MockConfigEntry(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user