"""Test the Control4 config flow.""" from unittest.mock import AsyncMock from aiohttp.client_exceptions import ClientError from pyControl4.error_handling import BadCredentials, NotFound, Unauthorized import pytest from homeassistant.components.control4.const import DEFAULT_SCAN_INTERVAL, DOMAIN from homeassistant.config_entries import SOURCE_USER from homeassistant.const import ( CONF_HOST, CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_USERNAME, ) from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType from .conftest import MOCK_HOST, MOCK_PASSWORD, MOCK_USERNAME from tests.common import MockConfigEntry async def test_full_flow( hass: HomeAssistant, mock_c4_account: AsyncMock, mock_c4_director: AsyncMock, mock_setup_entry: AsyncMock, ) -> None: """Test full config 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" assert result["errors"] == {} result = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_HOST: MOCK_HOST, CONF_USERNAME: MOCK_USERNAME, CONF_PASSWORD: MOCK_PASSWORD, }, ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["title"] == "control4_model_00AA00AA00AA" assert result["data"] == { CONF_HOST: MOCK_HOST, CONF_USERNAME: MOCK_USERNAME, CONF_PASSWORD: MOCK_PASSWORD, "controller_unique_id": "control4_model_00AA00AA00AA", } assert result["result"].unique_id == "00:aa:00:aa:00:aa" assert len(mock_setup_entry.mock_calls) == 1 @pytest.mark.parametrize( ("exception", "error"), [ (BadCredentials("Invalid username or password"), "invalid_auth"), (Unauthorized("Permission denied"), "invalid_auth"), (NotFound("something"), "controller_not_found"), (Exception("Some other exception"), "unknown"), ], ) @pytest.mark.usefixtures("mock_setup_entry") async def test_user_flow_errors( hass: HomeAssistant, mock_c4_account: AsyncMock, mock_c4_director: AsyncMock, exception: Exception, error: str, ) -> None: """Test we handle errors in the user flow.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) mock_c4_account.getAccountBearerToken.side_effect = exception result = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_HOST: MOCK_HOST, CONF_USERNAME: MOCK_USERNAME, CONF_PASSWORD: MOCK_PASSWORD, }, ) assert result["type"] is FlowResultType.FORM assert result["errors"] == {"base": error} mock_c4_account.getAccountBearerToken.side_effect = None result = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_HOST: MOCK_HOST, CONF_USERNAME: MOCK_USERNAME, CONF_PASSWORD: MOCK_PASSWORD, }, ) assert result["type"] is FlowResultType.CREATE_ENTRY @pytest.mark.parametrize( ("exception", "error"), [ (Unauthorized("Permission denied"), "director_auth_failed"), (ClientError, "cannot_connect"), (TimeoutError, "cannot_connect"), (Exception("Some other exception"), "unknown"), ], ) @pytest.mark.usefixtures("mock_setup_entry") async def test_user_flow_director_errors( hass: HomeAssistant, mock_c4_account: AsyncMock, mock_c4_director: AsyncMock, exception: Exception, error: str, ) -> None: """Test we handle director auth failure.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) mock_c4_director.getAllItemInfo.side_effect = exception result = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_HOST: MOCK_HOST, CONF_USERNAME: MOCK_USERNAME, CONF_PASSWORD: MOCK_PASSWORD, }, ) assert result["type"] is FlowResultType.FORM assert result["errors"] == {"base": error} mock_c4_director.getAllItemInfo.side_effect = None result = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_HOST: MOCK_HOST, CONF_USERNAME: MOCK_USERNAME, CONF_PASSWORD: MOCK_PASSWORD, }, ) assert result["type"] is FlowResultType.CREATE_ENTRY async def test_duplicate_entry( hass: HomeAssistant, mock_c4_account: AsyncMock, mock_c4_director: AsyncMock, mock_config_entry: MockConfigEntry, ) -> None: """Test that duplicate entries are not created.""" 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 assert result["step_id"] == "user" assert result["errors"] == {} result = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_HOST: MOCK_HOST, CONF_USERNAME: MOCK_USERNAME, CONF_PASSWORD: MOCK_PASSWORD, }, ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == "already_configured" async def test_option_flow( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: """Test config flow options.""" mock_config_entry.add_to_hass(hass) result = await hass.config_entries.options.async_init(mock_config_entry.entry_id) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "init" result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={CONF_SCAN_INTERVAL: 4}, ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"] == { CONF_SCAN_INTERVAL: 4, } async def test_option_flow_defaults( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: """Test config flow options.""" mock_config_entry.add_to_hass(hass) result = await hass.config_entries.options.async_init(mock_config_entry.entry_id) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "init" result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={} ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"] == { CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL, }