"""Test the A. O. Smith config flow.""" from datetime import timedelta from unittest.mock import AsyncMock, MagicMock, patch from freezegun.api import FrozenDateTimeFactory from py_aosmith import AOSmithInvalidCredentialsException import pytest from homeassistant import config_entries from homeassistant.components.aosmith.const import ( DOMAIN, ENERGY_USAGE_INTERVAL, REGULAR_INTERVAL, ) from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_EMAIL, CONF_PASSWORD from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry, async_fire_time_changed from tests.components.aosmith.conftest import FIXTURE_USER_INPUT async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None: """Test we get the form.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == FlowResultType.FORM assert result["errors"] == {} with patch( "homeassistant.components.aosmith.config_flow.AOSmithAPIClient.get_devices", return_value=[], ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], FIXTURE_USER_INPUT, ) await hass.async_block_till_done() assert result2["type"] == FlowResultType.CREATE_ENTRY assert result2["title"] == FIXTURE_USER_INPUT[CONF_EMAIL] assert result2["data"] == FIXTURE_USER_INPUT assert len(mock_setup_entry.mock_calls) == 1 @pytest.mark.parametrize( ("exception", "expected_error_key"), [ (AOSmithInvalidCredentialsException("Invalid credentials"), "invalid_auth"), (Exception, "unknown"), ], ) async def test_form_exception( hass: HomeAssistant, mock_setup_entry: AsyncMock, exception: Exception, expected_error_key: str, ) -> None: """Test handling an exception and then recovering on the second attempt.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == FlowResultType.FORM assert result["errors"] == {} with patch( "homeassistant.components.aosmith.config_flow.AOSmithAPIClient.get_devices", side_effect=exception, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], FIXTURE_USER_INPUT, ) assert result2["type"] == FlowResultType.FORM assert result2["errors"] == {"base": expected_error_key} with patch( "homeassistant.components.aosmith.config_flow.AOSmithAPIClient.get_devices", return_value=[], ): result3 = await hass.config_entries.flow.async_configure( result2["flow_id"], FIXTURE_USER_INPUT, ) await hass.async_block_till_done() assert result3["type"] == FlowResultType.CREATE_ENTRY assert result3["title"] == FIXTURE_USER_INPUT[CONF_EMAIL] assert result3["data"] == FIXTURE_USER_INPUT assert len(mock_setup_entry.mock_calls) == 1 @pytest.mark.parametrize( ("api_method", "wait_interval"), [ ("get_devices", REGULAR_INTERVAL), ("get_energy_use_data", ENERGY_USAGE_INTERVAL), ], ) async def test_reauth_flow( freezer: FrozenDateTimeFactory, hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock, api_method: str, wait_interval: timedelta, ) -> None: """Test reauth works.""" entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 assert entries[0].state is ConfigEntryState.LOADED getattr(mock_client, api_method).side_effect = AOSmithInvalidCredentialsException( "Authentication error" ) freezer.tick(wait_interval) async_fire_time_changed(hass) await hass.async_block_till_done() flows = hass.config_entries.flow.async_progress() assert len(flows) == 1 assert flows[0]["step_id"] == "reauth_confirm" with patch( "homeassistant.components.aosmith.config_flow.AOSmithAPIClient.get_devices", return_value=[], ), patch( "homeassistant.components.aosmith.config_flow.AOSmithAPIClient.get_energy_use_data", return_value=[], ), patch("homeassistant.components.aosmith.async_setup_entry", return_value=True): result2 = await hass.config_entries.flow.async_configure( flows[0]["flow_id"], {CONF_PASSWORD: FIXTURE_USER_INPUT[CONF_PASSWORD]}, ) await hass.async_block_till_done() assert result2["type"] == FlowResultType.ABORT assert result2["reason"] == "reauth_successful" async def test_reauth_flow_retry( freezer: FrozenDateTimeFactory, hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock, ) -> None: """Test reauth works with retry.""" entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 assert entries[0].state is ConfigEntryState.LOADED mock_client.get_devices.side_effect = AOSmithInvalidCredentialsException( "Authentication error" ) freezer.tick(REGULAR_INTERVAL) async_fire_time_changed(hass) await hass.async_block_till_done() flows = hass.config_entries.flow.async_progress() assert len(flows) == 1 assert flows[0]["step_id"] == "reauth_confirm" # First attempt at reauth - authentication fails again with patch( "homeassistant.components.aosmith.config_flow.AOSmithAPIClient.get_devices", side_effect=AOSmithInvalidCredentialsException("Authentication error"), ): result2 = await hass.config_entries.flow.async_configure( flows[0]["flow_id"], {CONF_PASSWORD: FIXTURE_USER_INPUT[CONF_PASSWORD]}, ) await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["errors"] == {"base": "invalid_auth"} # Second attempt at reauth - authentication succeeds with patch( "homeassistant.components.aosmith.config_flow.AOSmithAPIClient.get_devices", return_value=[], ), patch("homeassistant.components.aosmith.async_setup_entry", return_value=True): result3 = await hass.config_entries.flow.async_configure( flows[0]["flow_id"], {CONF_PASSWORD: FIXTURE_USER_INPUT[CONF_PASSWORD]}, ) await hass.async_block_till_done() assert result3["type"] == FlowResultType.ABORT assert result3["reason"] == "reauth_successful"