mirror of
https://github.com/home-assistant/core.git
synced 2026-04-19 00:10:21 +01:00
Co-authored-by: xiaozhouhhh <13588112144@163.com> Co-authored-by: Joostlek <joostlek@outlook.com>
253 lines
7.8 KiB
Python
253 lines
7.8 KiB
Python
"""Test the Solarman config flow."""
|
|
|
|
from ipaddress import ip_address
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
|
|
from homeassistant.components.solarman.const import CONF_SN, DOMAIN, MODEL_NAME_MAP
|
|
from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF
|
|
from homeassistant.const import CONF_HOST, CONF_MODEL
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.data_entry_flow import FlowResultType
|
|
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
# Configure test constants
|
|
TEST_HOST = "192.168.1.100"
|
|
TEST_DEVICE_SN = "SN1234567890"
|
|
TEST_MODEL = "SP-2W-EU"
|
|
|
|
|
|
@pytest.mark.parametrize("device_fixture", ["P1-2W"], indirect=True)
|
|
async def test_flow_success(hass: HomeAssistant, mock_solarman: AsyncMock) -> None:
|
|
"""Test successful configuration flow."""
|
|
|
|
# Initiate config flow
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": SOURCE_USER}
|
|
)
|
|
|
|
# Verify initial form
|
|
assert result["type"] is FlowResultType.FORM
|
|
assert result["step_id"] == "user"
|
|
|
|
# Submit valid data
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {CONF_HOST: TEST_HOST}
|
|
)
|
|
|
|
# Verify entry creation
|
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
|
assert result["title"] == f"P1 Meter Reader ({TEST_HOST})"
|
|
assert result["context"]["unique_id"] == "SN2345678901"
|
|
|
|
# Verify configuration data.
|
|
data = result["data"]
|
|
assert data[CONF_HOST] == TEST_HOST
|
|
assert data[CONF_SN] == "SN2345678901"
|
|
assert data[CONF_MODEL] == "P1-2W"
|
|
|
|
|
|
@pytest.mark.parametrize("device_fixture", ["SP-2W-EU"], indirect=True)
|
|
@pytest.mark.parametrize(
|
|
("exception", "expected_error"),
|
|
[
|
|
(TimeoutError, "timeout"),
|
|
(ConnectionError, "cannot_connect"),
|
|
(Exception("Some unknown error"), "unknown"),
|
|
],
|
|
)
|
|
async def test_flow_error(
|
|
hass: HomeAssistant,
|
|
mock_solarman: AsyncMock,
|
|
exception: Exception,
|
|
expected_error: str,
|
|
) -> None:
|
|
"""Test connection error handling."""
|
|
# Initiate and submit config flow
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": SOURCE_USER}
|
|
)
|
|
|
|
mock_solarman.get_config.side_effect = exception
|
|
|
|
# Submit config flow
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{CONF_HOST: TEST_HOST},
|
|
)
|
|
|
|
# Verify error form display.
|
|
assert result["type"] is FlowResultType.FORM
|
|
assert result["errors"]["base"] == expected_error
|
|
|
|
mock_solarman.get_config.side_effect = None
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{CONF_HOST: TEST_HOST},
|
|
)
|
|
|
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
|
|
|
|
|
@pytest.mark.parametrize("device_fixture", ["SP-2W-EU"], indirect=True)
|
|
async def test_flow_duplicate_entry(
|
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_solarman: AsyncMock
|
|
) -> None:
|
|
"""Test duplicate entry handling."""
|
|
# Create existing config entry.
|
|
mock_config_entry.add_to_hass(hass)
|
|
|
|
# Initiate and submit config flow
|
|
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}
|
|
)
|
|
|
|
# Verify flow abort
|
|
assert result["type"] is FlowResultType.ABORT
|
|
assert result["reason"] == "already_configured"
|
|
|
|
|
|
@pytest.mark.parametrize("device_fixture", ["SP-2W-EU"], indirect=True)
|
|
async def test_zeroconf(hass: HomeAssistant, mock_solarman: AsyncMock) -> None:
|
|
"""Test zeroconf discovery flow."""
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={"source": SOURCE_ZEROCONF},
|
|
data=ZeroconfServiceInfo(
|
|
ip_address=ip_address(TEST_HOST),
|
|
ip_addresses=[ip_address(TEST_HOST)],
|
|
name="mock_name",
|
|
port=8080,
|
|
hostname="mock_hostname",
|
|
type="_solarman._tcp.local.",
|
|
properties={
|
|
"product_type": "SP-2W-EU",
|
|
"serial": TEST_DEVICE_SN,
|
|
},
|
|
),
|
|
)
|
|
|
|
assert result["type"] is FlowResultType.FORM
|
|
assert result["step_id"] == "discovery_confirm"
|
|
|
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
|
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
|
assert result["title"] == f"{MODEL_NAME_MAP[TEST_MODEL]} ({TEST_HOST})"
|
|
|
|
data = result["data"]
|
|
assert data[CONF_HOST] == TEST_HOST
|
|
assert data[CONF_SN] == TEST_DEVICE_SN
|
|
assert data[CONF_MODEL] == TEST_MODEL
|
|
assert result["context"]["unique_id"] == TEST_DEVICE_SN
|
|
|
|
|
|
@pytest.mark.parametrize("device_fixture", ["SP-2W-EU"], indirect=True)
|
|
async def test_zeroconf_already_configured(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_solarman: AsyncMock,
|
|
) -> None:
|
|
"""Test zeroconf discovery when already configured."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={"source": SOURCE_ZEROCONF},
|
|
data=ZeroconfServiceInfo(
|
|
ip_address=ip_address(TEST_HOST),
|
|
ip_addresses=[ip_address(TEST_HOST)],
|
|
name="mock_name",
|
|
port=8080,
|
|
hostname="mock_hostname",
|
|
type="_solarman._tcp.local.",
|
|
properties={
|
|
"product_type": "SP-2W-EU",
|
|
"serial": TEST_DEVICE_SN,
|
|
},
|
|
),
|
|
)
|
|
|
|
assert result["type"] is FlowResultType.ABORT
|
|
assert result["reason"] == "already_configured"
|
|
|
|
|
|
@pytest.mark.parametrize("device_fixture", ["SP-2W-EU"], indirect=True)
|
|
async def test_zeroconf_ip_change(
|
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_solarman: AsyncMock
|
|
) -> None:
|
|
"""Test discovery setup updates new config data."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
|
|
# preflight check, see if the ip address is already in use
|
|
assert mock_config_entry.data[CONF_HOST] == TEST_HOST
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={"source": SOURCE_ZEROCONF},
|
|
data=ZeroconfServiceInfo(
|
|
ip_address=ip_address("192.168.1.101"),
|
|
ip_addresses=[ip_address("192.168.1.101")],
|
|
name="mock_name",
|
|
port=8080,
|
|
hostname="mock_hostname",
|
|
type="_solarman._tcp.local.",
|
|
properties={
|
|
"product_type": "SP-2W-EU",
|
|
"serial": TEST_DEVICE_SN,
|
|
},
|
|
),
|
|
)
|
|
|
|
assert result["type"] is FlowResultType.ABORT
|
|
assert result["reason"] == "already_configured"
|
|
|
|
assert mock_config_entry.data[CONF_HOST] == "192.168.1.101"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("exception", "expected_error"),
|
|
[
|
|
(TimeoutError, "timeout"),
|
|
(ConnectionError, "cannot_connect"),
|
|
(Exception("Some unknown error"), "unknown"),
|
|
],
|
|
)
|
|
@pytest.mark.parametrize("device_fixture", ["SP-2W-EU"], indirect=True)
|
|
async def test_zeroconf_error(
|
|
hass: HomeAssistant,
|
|
mock_solarman: AsyncMock,
|
|
exception: Exception,
|
|
expected_error: str,
|
|
) -> None:
|
|
"""Test discovery setup."""
|
|
mock_solarman.get_config.side_effect = exception
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={"source": SOURCE_ZEROCONF},
|
|
data=ZeroconfServiceInfo(
|
|
ip_address=ip_address(TEST_HOST),
|
|
ip_addresses=[ip_address(TEST_HOST)],
|
|
name="mock_name",
|
|
port=8080,
|
|
hostname="mock_hostname",
|
|
type="_solarman._tcp.local.",
|
|
properties={
|
|
"product_type": "SP-2W-EU",
|
|
"serial": TEST_DEVICE_SN,
|
|
},
|
|
),
|
|
)
|
|
|
|
assert result["type"] is FlowResultType.ABORT
|
|
assert result["reason"] == expected_error
|