core/tests/components/insteon/test_config_flow.py

320 lines
9.8 KiB
Python

"""Test the config flow for the Insteon integration."""
from collections.abc import Callable
from typing import Any
from unittest.mock import AsyncMock, patch
import pytest
from voluptuous_serialize import convert
from homeassistant import config_entries
from homeassistant.components import dhcp, usb
from homeassistant.components.insteon.config_flow import (
STEP_HUB_V1,
STEP_HUB_V2,
STEP_PLM,
STEP_PLM_MANUALLY,
)
from homeassistant.components.insteon.const import CONF_HUB_VERSION, DOMAIN
from homeassistant.config_entries import ConfigEntryState, ConfigFlowResult
from homeassistant.const import CONF_DEVICE, CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from .const import (
MOCK_DEVICE,
MOCK_USER_INPUT_HUB_V1,
MOCK_USER_INPUT_HUB_V2,
MOCK_USER_INPUT_PLM,
MOCK_USER_INPUT_PLM_MANUAL,
PATCH_ASYNC_SETUP_ENTRY,
PATCH_CONNECTION,
PATCH_USB_LIST,
)
from tests.common import MockConfigEntry
USB_PORTS = {"/dev/ttyUSB0": "/dev/ttyUSB0", MOCK_DEVICE: MOCK_DEVICE}
async def mock_successful_connection(*args, **kwargs):
"""Return a successful connection."""
return True
async def mock_usb_list(hass: HomeAssistant):
"""Return a mock list of USB devices."""
return USB_PORTS
@pytest.fixture(autouse=True)
def patch_usb_list():
"""Only setup the lock and required base platforms to speed up tests."""
with patch(
PATCH_USB_LIST,
mock_usb_list,
):
yield
async def mock_failed_connection(*args, **kwargs):
"""Return a failed connection."""
raise ConnectionError("Connection failed")
async def _init_form(hass: HomeAssistant, modem_type: str) -> ConfigFlowResult:
"""Run the user form."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
return await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": modem_type},
)
async def _device_form(
hass: HomeAssistant,
flow_id: str,
connection: Callable[..., Any],
user_input: dict[str, Any] | None,
) -> tuple[ConfigFlowResult, AsyncMock]:
"""Test the PLM, Hub v1 or Hub v2 form."""
with (
patch(
PATCH_CONNECTION,
new=connection,
),
patch(
PATCH_ASYNC_SETUP_ENTRY,
return_value=True,
) as mock_setup_entry,
):
result = await hass.config_entries.flow.async_configure(flow_id, user_input)
await hass.async_block_till_done()
return result, mock_setup_entry
async def test_form_select_modem(hass: HomeAssistant) -> None:
"""Test we get a modem form."""
result = await _init_form(hass, STEP_HUB_V2)
assert result["step_id"] == STEP_HUB_V2
assert result["type"] is FlowResultType.FORM
async def test_fail_on_existing(hass: HomeAssistant) -> None:
"""Test we fail if the integration is already configured."""
config_entry = MockConfigEntry(
domain=DOMAIN,
entry_id="abcde12345",
data={**MOCK_USER_INPUT_HUB_V2, CONF_HUB_VERSION: 2},
options={},
)
config_entry.add_to_hass(hass)
assert config_entry.state is ConfigEntryState.NOT_LOADED
result = await hass.config_entries.flow.async_init(
DOMAIN,
data={**MOCK_USER_INPUT_HUB_V2, CONF_HUB_VERSION: 2},
context={"source": config_entries.SOURCE_USER},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "single_instance_allowed"
async def test_form_select_plm(hass: HomeAssistant) -> None:
"""Test we set up the PLM correctly."""
result = await _init_form(hass, STEP_PLM)
result2, mock_setup_entry = await _device_form(
hass, result["flow_id"], mock_successful_connection, MOCK_USER_INPUT_PLM
)
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["data"] == MOCK_USER_INPUT_PLM
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_select_plm_no_usb(hass: HomeAssistant) -> None:
"""Test we set up the PLM when no comm ports are found."""
temp_usb_list = dict(USB_PORTS)
USB_PORTS.clear()
result = await _init_form(hass, STEP_PLM)
result2, _ = await _device_form(
hass, result["flow_id"], mock_successful_connection, None
)
USB_PORTS.update(temp_usb_list)
assert result2["type"] is FlowResultType.FORM
assert result2["step_id"] == STEP_PLM_MANUALLY
async def test_form_select_plm_manual(hass: HomeAssistant) -> None:
"""Test we set up the PLM correctly."""
result = await _init_form(hass, STEP_PLM)
result2, mock_setup_entry = await _device_form(
hass, result["flow_id"], mock_failed_connection, MOCK_USER_INPUT_PLM_MANUAL
)
result3, mock_setup_entry = await _device_form(
hass, result2["flow_id"], mock_successful_connection, MOCK_USER_INPUT_PLM
)
assert result2["type"] is FlowResultType.FORM
assert result3["type"] is FlowResultType.CREATE_ENTRY
assert result3["data"] == MOCK_USER_INPUT_PLM
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_select_hub_v1(hass: HomeAssistant) -> None:
"""Test we set up the Hub v1 correctly."""
result = await _init_form(hass, STEP_HUB_V1)
result2, mock_setup_entry = await _device_form(
hass, result["flow_id"], mock_successful_connection, MOCK_USER_INPUT_HUB_V1
)
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["data"] == {
**MOCK_USER_INPUT_HUB_V1,
CONF_HUB_VERSION: 1,
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_select_hub_v2(hass: HomeAssistant) -> None:
"""Test we set up the Hub v2 correctly."""
result = await _init_form(hass, STEP_HUB_V2)
result2, mock_setup_entry = await _device_form(
hass, result["flow_id"], mock_successful_connection, MOCK_USER_INPUT_HUB_V2
)
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["data"] == {
**MOCK_USER_INPUT_HUB_V2,
CONF_HUB_VERSION: 2,
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_discovery_dhcp(hass: HomeAssistant) -> None:
"""Test the discovery of the Hub via DHCP."""
discovery_info = dhcp.DhcpServiceInfo("1.2.3.4", "", "aabbccddeeff")
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=discovery_info
)
assert result["type"] is FlowResultType.MENU
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": STEP_HUB_V2},
)
assert result2["type"] is FlowResultType.FORM
schema = convert(result2["data_schema"])
found_host = False
for field in schema:
if field["name"] == CONF_HOST:
assert field["default"] == "1.2.3.4"
found_host = True
assert found_host
async def test_failed_connection_plm(hass: HomeAssistant) -> None:
"""Test a failed connection with the PLM."""
result = await _init_form(hass, STEP_PLM)
result2, _ = await _device_form(
hass, result["flow_id"], mock_failed_connection, MOCK_USER_INPUT_PLM
)
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "cannot_connect"}
async def test_failed_connection_plm_manually(hass: HomeAssistant) -> None:
"""Test a failed connection with the PLM."""
result = await _init_form(hass, STEP_PLM)
result2, _ = await _device_form(
hass, result["flow_id"], mock_successful_connection, MOCK_USER_INPUT_PLM_MANUAL
)
result3, _ = await _device_form(
hass, result["flow_id"], mock_failed_connection, MOCK_USER_INPUT_PLM
)
assert result3["type"] is FlowResultType.FORM
assert result3["errors"] == {"base": "cannot_connect"}
async def test_failed_connection_hub(hass: HomeAssistant) -> None:
"""Test a failed connection with a Hub."""
result = await _init_form(hass, STEP_HUB_V2)
result2, _ = await _device_form(
hass, result["flow_id"], mock_failed_connection, MOCK_USER_INPUT_HUB_V2
)
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "cannot_connect"}
async def test_discovery_via_usb(hass: HomeAssistant) -> None:
"""Test usb flow."""
discovery_info = usb.UsbServiceInfo(
device="/dev/ttyINSTEON",
pid="AAAA",
vid="AAAA",
serial_number="1234",
description="insteon radio",
manufacturer="test",
)
result = await hass.config_entries.flow.async_init(
"insteon", context={"source": config_entries.SOURCE_USB}, data=discovery_info
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm_usb"
with patch(PATCH_CONNECTION):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["data"] == {"device": "/dev/ttyINSTEON"}
async def test_discovery_via_usb_already_setup(hass: HomeAssistant) -> None:
"""Test usb flow -- already setup."""
MockConfigEntry(
domain=DOMAIN, data={CONF_DEVICE: {CONF_DEVICE: "/dev/ttyUSB1"}}
).add_to_hass(hass)
discovery_info = usb.UsbServiceInfo(
device="/dev/ttyINSTEON",
pid="AAAA",
vid="AAAA",
serial_number="1234",
description="insteon radio",
manufacturer="test",
)
result = await hass.config_entries.flow.async_init(
"insteon", context={"source": config_entries.SOURCE_USB}, data=discovery_info
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "single_instance_allowed"