diff --git a/homeassistant/components/homematicip_cloud/hap.py b/homeassistant/components/homematicip_cloud/hap.py index ad0699f5d89a..de71dfe03334 100644 --- a/homeassistant/components/homematicip_cloud/hap.py +++ b/homeassistant/components/homematicip_cloud/hap.py @@ -105,6 +105,9 @@ class HomematicipAuth: _LOGGER.debug("Connection request result: %s", result) except HmipConnectionError: return None + # a rejected SGTIN or PIN comes back as a failed result, not an exception + if not result.success: + return None return auth diff --git a/tests/components/homematicip_cloud/test_config_flow.py b/tests/components/homematicip_cloud/test_config_flow.py index fedf7902a392..4fd0e67ba1c0 100644 --- a/tests/components/homematicip_cloud/test_config_flow.py +++ b/tests/components/homematicip_cloud/test_config_flow.py @@ -1,6 +1,9 @@ """Tests for HomematicIP Cloud config flow.""" -from unittest.mock import patch +from unittest.mock import AsyncMock, patch + +from homematicip.connection.connection_context import ConnectionContext +from homematicip.connection.rest_connection import RestResult from homeassistant import config_entries from homeassistant.components.homematicip_cloud.const import ( @@ -104,6 +107,37 @@ async def test_flow_init_connection_error(hass: HomeAssistant) -> None: assert result["errors"] == {"base": "invalid_sgtin_or_pin"} +async def test_flow_init_rejected_pin( + hass: HomeAssistant, simple_mock_auth: AsyncMock +) -> None: + """Test the flow stays on the first step when the cloud rejects the PIN.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + # the library hands a 400 back as a failed result instead of raising + simple_mock_auth.connection_request.return_value = RestResult( + status=400, text='{"errorCode":"INVALID_PIN"}' + ) + with ( + patch( + "homeassistant.components.homematicip_cloud.hap.Auth", + return_value=simple_mock_auth, + ), + patch( + "homeassistant.components.homematicip_cloud.hap.ConnectionContextBuilder.build_context_async", + return_value=ConnectionContext(), + ), + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=DEFAULT_CONFIG + ) + + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "init" + assert result["errors"] == {"base": "invalid_sgtin_or_pin"} + + async def test_flow_link_connection_error(hass: HomeAssistant) -> None: """Test config flow client registration connection error.""" result = await hass.config_entries.flow.async_init( diff --git a/tests/components/homematicip_cloud/test_hap.py b/tests/components/homematicip_cloud/test_hap.py index 1fa4b859097e..48ed9e9ffb79 100644 --- a/tests/components/homematicip_cloud/test_hap.py +++ b/tests/components/homematicip_cloud/test_hap.py @@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, Mock, patch from homematicip.auth import Auth from homematicip.connection.connection_context import ConnectionContext +from homematicip.connection.rest_connection import RestResult from homematicip.exceptions.connection_exceptions import ( HmipAuthenticationError, HmipConnectionError, @@ -197,6 +198,7 @@ async def test_auth_create(hass: HomeAssistant, simple_mock_auth) -> None: """Mock AsyncAuth to execute get_auth.""" config = {HMIPC_HAPID: HAPID, HMIPC_PIN: HAPPIN, HMIPC_NAME: "hmip"} hmip_auth = HomematicipAuth(hass, config) + simple_mock_auth.connection_request.return_value = RestResult(status=200) assert hmip_auth with ( @@ -245,6 +247,29 @@ async def test_auth_create_exception(hass: HomeAssistant, simple_mock_auth) -> N assert not await hmip_auth.get_auth(hass, HAPID, HAPPIN) +async def test_auth_create_rejected( + hass: HomeAssistant, simple_mock_auth: AsyncMock +) -> None: + """Test a connection request the cloud rejects does not yield an auth.""" + config = {HMIPC_HAPID: HAPID, HMIPC_PIN: HAPPIN, HMIPC_NAME: "hmip"} + hmip_auth = HomematicipAuth(hass, config) + # a wrong or missing PIN is answered with a 400, which the library returns + simple_mock_auth.connection_request.return_value = RestResult( + status=400, text='{"errorCode":"INVALID_PIN"}' + ) + with ( + patch( + "homeassistant.components.homematicip_cloud.hap.Auth", + return_value=simple_mock_auth, + ), + patch( + "homeassistant.components.homematicip_cloud.hap.ConnectionContextBuilder.build_context_async", + return_value=ConnectionContext(), + ), + ): + assert not await hmip_auth.async_setup() + + async def test_get_state_after_disconnect( hass: HomeAssistant, hmip_config_entry: MockConfigEntry, simple_mock_home ) -> None: