diff --git a/homeassistant/components/blink/config_flow.py b/homeassistant/components/blink/config_flow.py index d969e6a75e21..5c0f8c395c7f 100644 --- a/homeassistant/components/blink/config_flow.py +++ b/homeassistant/components/blink/config_flow.py @@ -27,15 +27,17 @@ _LOGGER = logging.getLogger(__name__) async def validate_input(blink: Blink) -> None: """Validate the user input allows us to connect.""" try: - await blink.start() + result = await blink.start() except (LoginError, TokenRefreshFailed) as err: raise InvalidAuth from err + if result is False: + raise InvalidAuth -async def _send_blink_2fa_pin(blink: Blink, pin: str | None) -> bool: +async def _send_blink_2fa_pin(blink: Blink, pin: str | None) -> None: """Send 2FA pin to blink servers.""" - await blink.send_2fa_code(pin) - return True + if not await blink.send_2fa_code(pin): + raise InvalidAuth class BlinkConfigFlow(ConfigFlow, domain=DOMAIN): @@ -104,6 +106,8 @@ class BlinkConfigFlow(ConfigFlow, domain=DOMAIN): errors["base"] = "cannot_connect" except TokenRefreshFailed: errors["base"] = "invalid_access_token" + except InvalidAuth: + errors["base"] = "invalid_auth" except Exception: _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" diff --git a/tests/components/blink/test_config_flow.py b/tests/components/blink/test_config_flow.py index 848d9a15f24a..c758e0520e02 100644 --- a/tests/components/blink/test_config_flow.py +++ b/tests/components/blink/test_config_flow.py @@ -173,6 +173,44 @@ async def test_form_2fa_unknown_error(hass: HomeAssistant) -> None: assert result3["errors"] == {"base": "unknown"} +async def test_form_2fa_wrong_pin(hass: HomeAssistant) -> None: + """Test we report invalid auth when send_2fa_code returns False.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.blink.config_flow.Blink.start", + side_effect=BlinkTwoFARequiredError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"username": "blink@example.com", "password": "example"}, + ) + + assert result2["type"] is FlowResultType.FORM + assert result2["step_id"] == "2fa" + + with ( + patch("homeassistant.components.blink.config_flow.Blink.start"), + patch( + "homeassistant.components.blink.config_flow.Blink.send_2fa_code", + return_value=False, + ), + patch( + "homeassistant.components.blink.async_setup_entry", + return_value=True, + ), + ): + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], {"pin": "1234"} + ) + + assert result3["type"] is FlowResultType.FORM + assert result3["errors"] == {"base": "invalid_auth"} + + async def test_form_invalid_auth(hass: HomeAssistant) -> None: """Test we handle invalid auth.""" result = await hass.config_entries.flow.async_init( @@ -191,6 +229,28 @@ async def test_form_invalid_auth(hass: HomeAssistant) -> None: assert result2["errors"] == {"base": "invalid_auth"} +async def test_form_start_returns_false(hass: HomeAssistant) -> None: + """Test we handle auth failure when blink.start() returns False without raising. + + blink.start() catches LoginError/TokenRefreshFailed internally and returns + False instead of re-raising, so validate_input must check the return value. + """ + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.blink.config_flow.Blink.start", + return_value=False, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], {"username": "blink@example.com", "password": "example"} + ) + + assert result2["type"] is FlowResultType.FORM + assert result2["errors"] == {"base": "invalid_auth"} + + async def test_form_unknown_error(hass: HomeAssistant) -> None: """Test we handle unknown error at startup.""" result = await hass.config_entries.flow.async_init(