Files
core/tests/components/solax/test_config_flow.py
T

201 lines
6.6 KiB
Python

"""Tests for the solax config flow."""
import asyncio
from unittest.mock import patch
import pytest
from solax.inverter import Inverter, InverterError, InverterResponse
from solax.inverters import X1Boost, X1MiniV34
from homeassistant import config_entries
from homeassistant.components.solax.const import DOMAIN
from homeassistant.const import CONF_IP_ADDRESS, CONF_MODEL, CONF_PASSWORD, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
CONNECTION_INPUT: dict[str, str | int] = {
CONF_IP_ADDRESS: "192.168.1.87",
CONF_PORT: 80,
CONF_PASSWORD: "password",
}
def _build_inverter(inverter_cls: type[Inverter]) -> Inverter:
"""Build a real inverter instance to use as a discover() return value."""
return next(
iter(
inverter_cls.build_all_variants(
CONNECTION_INPUT[CONF_IP_ADDRESS],
CONNECTION_INPUT[CONF_PORT],
CONNECTION_INPUT[CONF_PASSWORD],
)
)
)
def __mock_get_data() -> InverterResponse:
return InverterResponse(
data=None,
dongle_serial_number="ABCDEFGHIJ",
version="2.034.06",
type=4,
inverter_serial_number="XXXXXXX",
)
@pytest.mark.parametrize(
("discovered_inverter_classes", "steps", "expected_model"),
[
pytest.param({X1MiniV34}, [CONNECTION_INPUT], "x1_mini_v34", id="single_match"),
pytest.param(
{X1MiniV34, X1Boost},
[CONNECTION_INPUT, {CONF_MODEL: "x1_mini_v34"}],
"x1_mini_v34",
id="multiple_matches",
),
],
)
async def test_form_success(
hass: HomeAssistant,
discovered_inverter_classes: set[type[Inverter]],
steps: list[dict[str, str | int]],
expected_model: str,
) -> None:
"""Test successful discovery and entry creation, with/without a model choice."""
flow = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert flow["type"] is FlowResultType.FORM
assert flow["errors"] is None
discovered = {_build_inverter(cls) for cls in discovered_inverter_classes}
with (
patch(
"homeassistant.components.solax.config_flow.discover",
return_value=discovered,
) as mock_discover,
patch("solax.RealTimeAPI.get_data", return_value=__mock_get_data()),
patch(
"homeassistant.components.solax.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result = flow
for step_input in steps:
result = await hass.config_entries.flow.async_configure(
result["flow_id"], step_input
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "ABCDEFGHIJ"
assert result["data"] == {**CONNECTION_INPUT, CONF_MODEL: expected_model}
assert len(mock_setup_entry.mock_calls) == 1
assert mock_discover.call_count == 1
assert mock_discover.call_args.kwargs == {"return_when": asyncio.ALL_COMPLETED}
@pytest.mark.parametrize(
("side_effect", "expected_error"),
[
pytest.param(ConnectionError, "cannot_connect", id="cannot_connect"),
pytest.param(Exception, "unknown", id="unknown"),
],
)
async def test_form_discover_error(
hass: HomeAssistant, side_effect: type[Exception], expected_error: str
) -> None:
"""Test discovery failures redisplay the user step with an error."""
flow = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert flow["type"] is FlowResultType.FORM
assert flow["errors"] is None
with patch(
"homeassistant.components.solax.config_flow.discover", side_effect=side_effect
):
result = await hass.config_entries.flow.async_configure(
flow["flow_id"], CONNECTION_INPUT
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {"base": expected_error}
@pytest.mark.parametrize(
("side_effect", "expected_error"),
[
pytest.param(ConnectionError, "cannot_connect", id="cannot_connect"),
pytest.param(InverterError, "cannot_connect", id="inverter_error"),
pytest.param(Exception, "unknown", id="unknown"),
],
)
@pytest.mark.parametrize(
("discovered_inverter_classes", "steps", "expected_step_id"),
[
pytest.param({X1MiniV34}, [CONNECTION_INPUT], "user", id="single_match"),
pytest.param(
{X1MiniV34, X1Boost},
[CONNECTION_INPUT, {CONF_MODEL: "x1_mini_v34"}],
"select_model",
id="multiple_matches",
),
],
)
async def test_form_finalize_error(
hass: HomeAssistant,
discovered_inverter_classes: set[type[Inverter]],
steps: list[dict[str, str | int]],
expected_step_id: str,
side_effect: type[Exception],
expected_error: str,
) -> None:
"""Test serial-number fetch failures redisplay the current step with an error."""
flow = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
discovered = {_build_inverter(cls) for cls in discovered_inverter_classes}
with (
patch(
"homeassistant.components.solax.config_flow.discover",
return_value=discovered,
),
patch("solax.RealTimeAPI.get_data", side_effect=side_effect),
):
result = flow
for step_input in steps:
result = await hass.config_entries.flow.async_configure(
result["flow_id"], step_input
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == expected_step_id
assert result["errors"] == {"base": expected_error}
async def test_form_select_model_step_options(hass: HomeAssistant) -> None:
"""Test the select_model step only lists the discovered inverter models."""
flow = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
discovered = {_build_inverter(X1MiniV34), _build_inverter(X1Boost)}
with patch(
"homeassistant.components.solax.config_flow.discover",
return_value=discovered,
):
result = await hass.config_entries.flow.async_configure(
flow["flow_id"], CONNECTION_INPUT
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "select_model"
assert result["data_schema"].schema[CONF_MODEL].config["options"] == [
"x1_boost",
"x1_mini_v34",
]