1
0
mirror of https://github.com/home-assistant/core.git synced 2026-03-04 00:30:26 +00:00
Files
core/tests/components/hegel/test_config_flow.py
2026-02-10 22:56:48 +01:00

380 lines
12 KiB
Python

"""Test the Hegel config flow."""
from unittest.mock import MagicMock
from homeassistant.components.hegel.const import CONF_MODEL, DOMAIN
from homeassistant.config_entries import SOURCE_SSDP, SOURCE_USER
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.ssdp import SsdpServiceInfo
from .const import TEST_HOST, TEST_MODEL, TEST_UDN
from tests.common import MockConfigEntry
TEST_NAME = "Hegel H190"
TEST_SSDP_LOCATION = f"http://{TEST_HOST}:8080/description.xml"
async def test_user_flow_success(
hass: HomeAssistant,
mock_setup_entry: MagicMock,
mock_hegel_client: MagicMock,
) -> None:
"""Test successful user flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_HOST: TEST_HOST,
CONF_MODEL: TEST_MODEL,
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == f"Hegel {TEST_MODEL}"
assert result["data"] == {
CONF_HOST: TEST_HOST,
CONF_MODEL: TEST_MODEL,
}
async def test_user_flow_exception(
hass: HomeAssistant,
mock_setup_entry: MagicMock,
mock_hegel_client: MagicMock,
) -> None:
"""Test user flow when connection fails."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
mock_hegel_client.ensure_connected.side_effect = OSError("Connection refused")
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_HOST: TEST_HOST,
CONF_MODEL: TEST_MODEL,
},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "cannot_connect"}
mock_hegel_client.ensure_connected.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_HOST: TEST_HOST,
CONF_MODEL: TEST_MODEL,
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
async def test_user_flow_already_configured(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test user flow aborts when device is already configured."""
mock_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_HOST: TEST_HOST,
CONF_MODEL: TEST_MODEL,
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_ssdp_discovery_success(
hass: HomeAssistant,
mock_setup_entry: MagicMock,
mock_hegel_client: MagicMock,
) -> None:
"""Test successful SSDP discovery."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_SSDP},
data=SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_udn=TEST_UDN,
ssdp_location=TEST_SSDP_LOCATION,
upnp={
"presentationURL": f"http://{TEST_HOST}/",
"friendlyName": TEST_NAME,
"modelName": TEST_MODEL,
},
),
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "discovery_confirm"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_MODEL: TEST_MODEL}
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == TEST_NAME
assert result["data"] == {CONF_HOST: TEST_HOST, CONF_MODEL: TEST_MODEL}
assert result["result"].unique_id == TEST_UDN
async def test_ssdp_discovery_from_ssdp_location(
hass: HomeAssistant,
mock_setup_entry: MagicMock,
mock_hegel_client: MagicMock,
) -> None:
"""Test SSDP discovery extracts host from ssdp_location when presentationURL is not available."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_SSDP},
data=SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_udn=TEST_UDN,
ssdp_location=TEST_SSDP_LOCATION,
upnp={"friendlyName": TEST_NAME, "modelName": TEST_MODEL},
),
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "discovery_confirm"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_MODEL: TEST_MODEL}
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == TEST_NAME
assert result["data"] == {CONF_HOST: TEST_HOST, CONF_MODEL: TEST_MODEL}
async def test_ssdp_discovery_no_host(hass: HomeAssistant) -> None:
"""Test SSDP discovery aborts when no host can be determined."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_SSDP},
data=SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_udn=TEST_UDN,
ssdp_location="",
upnp={},
),
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "no_host_found"
async def test_ssdp_discovery_no_udn(hass: HomeAssistant) -> None:
"""Test SSDP discovery aborts when no UDN is available."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_SSDP},
data=SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location=TEST_SSDP_LOCATION,
upnp={
"presentationURL": f"http://{TEST_HOST}/",
"friendlyName": TEST_NAME,
},
),
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "no_host_found"
async def test_ssdp_discovery_already_configured(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:
"""Test SSDP discovery aborts when device is already configured."""
mock_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_SSDP},
data=SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_udn=TEST_UDN,
ssdp_location=TEST_SSDP_LOCATION,
upnp={
"presentationURL": f"http://{TEST_HOST}/",
"friendlyName": TEST_NAME,
"modelName": TEST_MODEL,
},
),
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_ssdp_discovery_already_configured_updates_host(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_hegel_client: MagicMock,
) -> None:
"""Test SSDP discovery updates host when device is already configured with different IP."""
new_host = "192.168.1.50"
mock_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_SSDP},
data=SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_udn=TEST_UDN,
ssdp_location=f"http://{new_host}:8080/description.xml",
upnp={
"presentationURL": f"http://{new_host}/",
"friendlyName": TEST_NAME,
"modelName": TEST_MODEL,
},
),
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
# Verify the host was updated
assert mock_config_entry.data[CONF_HOST] == new_host
async def test_ssdp_discovery_cannot_connect(
hass: HomeAssistant,
mock_hegel_client: MagicMock,
) -> None:
"""Test SSDP discovery aborts when connection fails."""
mock_hegel_client.ensure_connected.side_effect = OSError("Connection refused")
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_SSDP},
data=SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_udn=TEST_UDN,
ssdp_location=TEST_SSDP_LOCATION,
upnp={
"presentationURL": f"http://{TEST_HOST}/",
"friendlyName": TEST_NAME,
"modelName": TEST_MODEL,
},
),
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "cannot_connect"
async def test_ssdp_discovery_unknown_model(
hass: HomeAssistant,
mock_hegel_client: MagicMock,
) -> None:
"""Test SSDP discovery with unknown model falls back to first model in list."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_SSDP},
data=SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_udn=TEST_UDN,
ssdp_location=TEST_SSDP_LOCATION,
upnp={
"presentationURL": f"http://{TEST_HOST}/",
"friendlyName": "Hegel Unknown",
"modelName": "UnknownModel",
},
),
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "discovery_confirm"
async def test_ssdp_discovery_multiple_services_same_device(
hass: HomeAssistant,
mock_hegel_client: MagicMock,
) -> None:
"""Test that multiple SSDP discoveries from same device result in single discovery.
A MediaRenderer device advertises multiple SSDP services (RenderingControl,
AVTransport, ConnectionManager) each with different USN but same UDN.
This test verifies that only one discovery entry is created.
"""
# First discovery - RenderingControl service
result1 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_SSDP},
data=SsdpServiceInfo(
ssdp_usn=f"{TEST_UDN}::urn:schemas-upnp-org:service:RenderingControl:1",
ssdp_st="urn:schemas-upnp-org:service:RenderingControl:1",
ssdp_udn=TEST_UDN,
ssdp_location=TEST_SSDP_LOCATION,
upnp={
"presentationURL": f"http://{TEST_HOST}/",
"friendlyName": TEST_NAME,
"modelName": TEST_MODEL,
},
),
)
assert result1["type"] is FlowResultType.FORM
assert result1["step_id"] == "discovery_confirm"
flow_id = result1["flow_id"]
# Second discovery - AVTransport service (different USN, same UDN)
result2 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_SSDP},
data=SsdpServiceInfo(
ssdp_usn=f"{TEST_UDN}::urn:schemas-upnp-org:service:AVTransport:1",
ssdp_st="urn:schemas-upnp-org:service:AVTransport:1",
ssdp_udn=TEST_UDN,
ssdp_location=TEST_SSDP_LOCATION,
upnp={
"presentationURL": f"http://{TEST_HOST}/",
"friendlyName": TEST_NAME,
"modelName": TEST_MODEL,
},
),
)
# Second discovery should abort since same UDN is already in progress
assert result2["type"] is FlowResultType.ABORT
assert result2["reason"] == "already_in_progress"
# Original flow should still be available
flows = hass.config_entries.flow.async_progress()
assert len([f for f in flows if f["flow_id"] == flow_id]) == 1