"""Tests for the Velbus config flow.""" from collections.abc import Generator from unittest.mock import AsyncMock, MagicMock, patch import pytest import serial.tools.list_ports from velbusaio.exceptions import VelbusConnectionFailed from homeassistant import data_entry_flow from homeassistant.components import usb from homeassistant.components.velbus.const import DOMAIN from homeassistant.config_entries import SOURCE_USB, SOURCE_USER from homeassistant.const import CONF_NAME, CONF_PORT, CONF_SOURCE from homeassistant.core import HomeAssistant from .const import PORT_SERIAL, PORT_TCP from tests.common import MockConfigEntry DISCOVERY_INFO = usb.UsbServiceInfo( device=PORT_SERIAL, pid="10CF", vid="0B1B", serial_number="1234", description="Velbus VMB1USB", manufacturer="Velleman", ) def com_port(): """Mock of a serial port.""" port = serial.tools.list_ports_common.ListPortInfo(PORT_SERIAL) port.serial_number = "1234" port.manufacturer = "Virtual serial port" port.device = PORT_SERIAL port.description = "Some serial port" return port @pytest.fixture(name="controller") def mock_controller() -> Generator[MagicMock, None, None]: """Mock a successful velbus controller.""" with patch( "homeassistant.components.velbus.config_flow.velbusaio.controller.Velbus", autospec=True, ) as controller: yield controller @pytest.fixture(autouse=True) def override_async_setup_entry() -> Generator[AsyncMock, None, None]: """Override async_setup_entry.""" with patch( "homeassistant.components.velbus.async_setup_entry", return_value=True ) as mock_setup_entry: yield mock_setup_entry @pytest.fixture(name="controller_connection_failed") def mock_controller_connection_failed(): """Mock the velbus controller with an assert.""" with patch("velbusaio.controller.Velbus", side_effect=VelbusConnectionFailed()): yield @pytest.mark.usefixtures("controller") async def test_user(hass: HomeAssistant): """Test user config.""" # simple user form result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) assert result assert result.get("flow_id") assert result.get("type") == data_entry_flow.FlowResultType.FORM assert result.get("step_id") == "user" # try with a serial port result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data={CONF_NAME: "Velbus Test Serial", CONF_PORT: PORT_SERIAL}, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.CREATE_ENTRY assert result.get("title") == "velbus_test_serial" data = result.get("data") assert data assert data[CONF_PORT] == PORT_SERIAL # try with a ip:port combination result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data={CONF_NAME: "Velbus Test TCP", CONF_PORT: PORT_TCP}, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.CREATE_ENTRY assert result.get("title") == "velbus_test_tcp" data = result.get("data") assert data assert data[CONF_PORT] == PORT_TCP @pytest.mark.usefixtures("controller_connection_failed") async def test_user_fail(hass: HomeAssistant): """Test user config.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data={CONF_NAME: "Velbus Test Serial", CONF_PORT: PORT_SERIAL}, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.FORM assert result.get("errors") == {CONF_PORT: "cannot_connect"} result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data={CONF_NAME: "Velbus Test TCP", CONF_PORT: PORT_TCP}, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.FORM assert result.get("errors") == {CONF_PORT: "cannot_connect"} @pytest.mark.usefixtures("config_entry") async def test_abort_if_already_setup(hass: HomeAssistant): """Test we abort if Velbus is already setup.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data={CONF_PORT: PORT_TCP, CONF_NAME: "velbus test"}, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.ABORT assert result.get("reason") == "already_configured" @pytest.mark.usefixtures("controller") @patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()])) async def test_flow_usb(hass: HomeAssistant): """Test usb discovery flow.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={CONF_SOURCE: SOURCE_USB}, data=DISCOVERY_INFO, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.FORM assert result.get("step_id") == "discovery_confirm" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={}, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.CREATE_ENTRY # test an already configured discovery entry = MockConfigEntry( domain=DOMAIN, data={CONF_PORT: PORT_SERIAL}, ) entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={CONF_SOURCE: SOURCE_USB}, data=DISCOVERY_INFO, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.ABORT assert result.get("reason") == "already_configured" @pytest.mark.usefixtures("controller_connection_failed") @patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()])) async def test_flow_usb_failed(hass: HomeAssistant): """Test usb discovery flow with a failed velbus test.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={CONF_SOURCE: SOURCE_USB}, data=DISCOVERY_INFO, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.ABORT assert result.get("reason") == "cannot_connect"