"""Tests for the Elmax config flow.""" from unittest.mock import patch from elmax_api.exceptions import ElmaxBadLoginError, ElmaxBadPinError, ElmaxNetworkError from homeassistant import config_entries, data_entry_flow from homeassistant.components.elmax.const import ( CONF_ELMAX_PANEL_ID, CONF_ELMAX_PANEL_NAME, CONF_ELMAX_PANEL_PIN, CONF_ELMAX_PASSWORD, CONF_ELMAX_USERNAME, DOMAIN, ) from homeassistant.config_entries import SOURCE_REAUTH from tests.common import MockConfigEntry from tests.components.elmax import ( MOCK_PANEL_ID, MOCK_PANEL_NAME, MOCK_PANEL_PIN, MOCK_PASSWORD, MOCK_USERNAME, ) CONF_POLLING = "polling" async def test_show_form(hass): """Test that the form is served with no input.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "user" async def test_standard_setup(hass): """Test the standard setup case.""" # Setup once. show_form_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) with patch( "homeassistant.components.elmax.async_setup_entry", return_value=True, ): login_result = await hass.config_entries.flow.async_configure( show_form_result["flow_id"], { CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) result = await hass.config_entries.flow.async_configure( login_result["flow_id"], { CONF_ELMAX_PANEL_NAME: MOCK_PANEL_NAME, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, }, ) await hass.async_block_till_done() assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY async def test_one_config_allowed(hass): """Test that only one Elmax configuration is allowed for each panel.""" MockConfigEntry( domain=DOMAIN, data={ CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, }, unique_id=MOCK_PANEL_ID, ).add_to_hass(hass) # Attempt to add another instance of the integration for the very same panel, it must fail. show_form_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) login_result = await hass.config_entries.flow.async_configure( show_form_result["flow_id"], { CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) result = await hass.config_entries.flow.async_configure( login_result["flow_id"], { CONF_ELMAX_PANEL_NAME: MOCK_PANEL_NAME, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, }, ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "already_configured" async def test_invalid_credentials(hass): """Test that invalid credentials throws an error.""" with patch( "elmax_api.http.Elmax.login", side_effect=ElmaxBadLoginError(), ): show_form_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) login_result = await hass.config_entries.flow.async_configure( show_form_result["flow_id"], { CONF_ELMAX_USERNAME: "wrong_user_name@email.com", CONF_ELMAX_PASSWORD: "incorrect_password", }, ) assert login_result["step_id"] == "user" assert login_result["type"] == data_entry_flow.RESULT_TYPE_FORM assert login_result["errors"] == {"base": "invalid_auth"} async def test_connection_error(hass): """Test other than invalid credentials throws an error.""" with patch( "elmax_api.http.Elmax.login", side_effect=ElmaxNetworkError(), ): show_form_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) login_result = await hass.config_entries.flow.async_configure( show_form_result["flow_id"], { CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) assert login_result["step_id"] == "user" assert login_result["type"] == data_entry_flow.RESULT_TYPE_FORM assert login_result["errors"] == {"base": "network_error"} async def test_unhandled_error(hass): """Test unhandled exceptions.""" with patch( "elmax_api.http.Elmax.get_panel_status", side_effect=Exception(), ): show_form_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) login_result = await hass.config_entries.flow.async_configure( show_form_result["flow_id"], { CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) result = await hass.config_entries.flow.async_configure( login_result["flow_id"], { CONF_ELMAX_PANEL_NAME: MOCK_PANEL_NAME, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, }, ) assert result["step_id"] == "panels" assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["errors"] == {"base": "unknown"} async def test_invalid_pin(hass): """Test error is thrown when a wrong pin is used to pair a panel.""" # Simulate bad pin response. with patch( "elmax_api.http.Elmax.get_panel_status", side_effect=ElmaxBadPinError(), ): show_form_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) login_result = await hass.config_entries.flow.async_configure( show_form_result["flow_id"], { CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) result = await hass.config_entries.flow.async_configure( login_result["flow_id"], { CONF_ELMAX_PANEL_NAME: MOCK_PANEL_NAME, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, }, ) assert result["step_id"] == "panels" assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["errors"] == {"base": "invalid_pin"} async def test_no_online_panel(hass): """Test no-online panel is available.""" # Simulate low-level api returns no panels. with patch( "elmax_api.http.Elmax.list_control_panels", return_value=[], ): show_form_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) login_result = await hass.config_entries.flow.async_configure( show_form_result["flow_id"], { CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) assert login_result["step_id"] == "user" assert login_result["type"] == data_entry_flow.RESULT_TYPE_FORM assert login_result["errors"] == {"base": "no_panel_online"} async def test_show_reauth(hass): """Test that the reauth form shows.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_REAUTH}, data={ CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "reauth_confirm" async def test_reauth_flow(hass): """Test that the reauth flow works.""" MockConfigEntry( domain=DOMAIN, data={ CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, }, unique_id=MOCK_PANEL_ID, ).add_to_hass(hass) # Trigger reauth with patch( "homeassistant.components.elmax.async_setup_entry", return_value=True, ): reauth_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_REAUTH}, data={ CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) result = await hass.config_entries.flow.async_configure( reauth_result["flow_id"], { CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT await hass.async_block_till_done() assert result["reason"] == "reauth_successful" async def test_reauth_panel_disappeared(hass): """Test that the case where panel is no longer associated with the user.""" # Simulate a first setup MockConfigEntry( domain=DOMAIN, data={ CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, }, unique_id=MOCK_PANEL_ID, ).add_to_hass(hass) # Trigger reauth with patch( "elmax_api.http.Elmax.list_control_panels", return_value=[], ): reauth_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_REAUTH}, data={ CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) result = await hass.config_entries.flow.async_configure( reauth_result["flow_id"], { CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) assert result["step_id"] == "reauth_confirm" assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["errors"] == {"base": "reauth_panel_disappeared"} async def test_reauth_invalid_pin(hass): """Test that the case where panel is no longer associated with the user.""" MockConfigEntry( domain=DOMAIN, data={ CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, }, unique_id=MOCK_PANEL_ID, ).add_to_hass(hass) # Trigger reauth with patch( "elmax_api.http.Elmax.get_panel_status", side_effect=ElmaxBadPinError(), ): reauth_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_REAUTH}, data={ CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) result = await hass.config_entries.flow.async_configure( reauth_result["flow_id"], { CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) assert result["step_id"] == "reauth_confirm" assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["errors"] == {"base": "invalid_pin"} async def test_reauth_bad_login(hass): """Test bad login attempt at reauth time.""" MockConfigEntry( domain=DOMAIN, data={ CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, }, unique_id=MOCK_PANEL_ID, ).add_to_hass(hass) # Trigger reauth with patch( "elmax_api.http.Elmax.login", side_effect=ElmaxBadLoginError(), ): reauth_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_REAUTH}, data={ CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) result = await hass.config_entries.flow.async_configure( reauth_result["flow_id"], { CONF_ELMAX_PANEL_ID: MOCK_PANEL_ID, CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, CONF_ELMAX_USERNAME: MOCK_USERNAME, CONF_ELMAX_PASSWORD: MOCK_PASSWORD, }, ) assert result["step_id"] == "reauth_confirm" assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["errors"] == {"base": "invalid_auth"}