"""Tests for the luci config flow.""" from typing import Any from unittest.mock import MagicMock import pytest from requests.exceptions import ConnectionError as RequestsConnectionError from homeassistant.components.luci.const import DOMAIN from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER from homeassistant.const import ( CONF_HOST, CONF_PASSWORD, CONF_SSL, CONF_USERNAME, CONF_VERIFY_SSL, ) from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry REAUTH_INPUT = { CONF_USERNAME: "root", CONF_PASSWORD: "new-password", } USER_INPUT = { CONF_HOST: "192.168.1.1", CONF_USERNAME: "root", CONF_PASSWORD: "password", CONF_SSL: False, CONF_VERIFY_SSL: True, } # Client configurations that make ``_try_connect`` fail, mapped to the error # the flow should report. ``cannot_connect`` is a connection error raised by # the client, ``invalid_auth`` is the client reporting it is not logged in. CONNECT_ERRORS = [ ({"side_effect": RequestsConnectionError}, "cannot_connect"), ({"return_value": False}, "invalid_auth"), ] @pytest.mark.usefixtures("mock_setup_entry") async def test_user_flow(hass: HomeAssistant, mock_luci_client: MagicMock) -> None: """Test the happy path of the 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"], user_input=USER_INPUT ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["title"] == "192.168.1.1" assert result["data"] == USER_INPUT @pytest.mark.usefixtures("mock_setup_entry", "mock_luci_client") async def test_user_flow_already_configured( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: """Test the user flow aborts when the host is already configured.""" mock_config_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) assert result["type"] is FlowResultType.FORM result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=USER_INPUT ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == "already_configured" @pytest.mark.parametrize(("client_config", "base_error"), CONNECT_ERRORS) @pytest.mark.usefixtures("mock_setup_entry") async def test_user_flow_errors( hass: HomeAssistant, mock_luci_client: MagicMock, client_config: dict[str, Any], base_error: str, ) -> None: """Test the user flow shows errors and recovers on retry.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) assert result["type"] is FlowResultType.FORM mock_luci_client.is_logged_in.configure_mock(**client_config) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=USER_INPUT ) assert result["type"] is FlowResultType.FORM assert result["errors"] == {"base": base_error} mock_luci_client.is_logged_in.configure_mock(side_effect=None, return_value=True) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=USER_INPUT ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"] == USER_INPUT @pytest.mark.usefixtures("mock_setup_entry") async def test_import_flow(hass: HomeAssistant, mock_luci_client: MagicMock) -> None: """Test the happy path of the import flow.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, data=USER_INPUT ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["title"] == "192.168.1.1" assert result["data"] == USER_INPUT @pytest.mark.usefixtures("mock_setup_entry", "mock_luci_client") async def test_import_flow_already_configured( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: """Test the import flow aborts when the host is already configured.""" mock_config_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, data=USER_INPUT ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == "already_configured" @pytest.mark.parametrize(("client_config", "reason"), CONNECT_ERRORS) @pytest.mark.usefixtures("mock_setup_entry") async def test_import_flow_errors( hass: HomeAssistant, mock_luci_client: MagicMock, client_config: dict[str, Any], reason: str, ) -> None: """Test the import flow aborts when connecting fails.""" mock_luci_client.is_logged_in.configure_mock(**client_config) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, data=USER_INPUT ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == reason @pytest.mark.usefixtures("mock_setup_entry") async def test_reauth_flow( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_luci_client: MagicMock, ) -> None: """Test the happy path of the reauth flow.""" mock_config_entry.add_to_hass(hass) result = await mock_config_entry.start_reauth_flow(hass) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "reauth_confirm" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=REAUTH_INPUT ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == "reauth_successful" assert mock_config_entry.data[CONF_PASSWORD] == "new-password" @pytest.mark.parametrize(("client_config", "base_error"), CONNECT_ERRORS) @pytest.mark.usefixtures("mock_setup_entry") async def test_reauth_flow_errors( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_luci_client: MagicMock, client_config: dict[str, Any], base_error: str, ) -> None: """Test the reauth flow shows errors and recovers on retry.""" mock_config_entry.add_to_hass(hass) result = await mock_config_entry.start_reauth_flow(hass) assert result["type"] is FlowResultType.FORM mock_luci_client.is_logged_in.configure_mock(**client_config) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=REAUTH_INPUT ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "reauth_confirm" assert result["errors"] == {"base": base_error} mock_luci_client.is_logged_in.configure_mock(side_effect=None, return_value=True) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=REAUTH_INPUT ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == "reauth_successful" assert mock_config_entry.data[CONF_PASSWORD] == "new-password"