mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 08:17:08 +00:00
Add re-authentication to Uptime Robot (#54226)
* Add reauthentication to Uptime Robot * Fix en strings * format * Fix docstring * Remove unused patch * Handle no existing entry * Handle account mismatch during reauthentication * Add test to validate reauth is triggered properly * Test reauth after setup * Adjust tests * Add full context for reauth init
This commit is contained in:
parent
aaddeb0bcd
commit
89bb95b0be
@ -1,11 +1,17 @@
|
|||||||
"""The Uptime Robot integration."""
|
"""The Uptime Robot integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pyuptimerobot import UptimeRobot, UptimeRobotException, UptimeRobotMonitor
|
from pyuptimerobot import (
|
||||||
|
UptimeRobot,
|
||||||
|
UptimeRobotAuthenticationException,
|
||||||
|
UptimeRobotException,
|
||||||
|
UptimeRobotMonitor,
|
||||||
|
)
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_API_KEY
|
from homeassistant.const import CONF_API_KEY
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.device_registry import (
|
from homeassistant.helpers.device_registry import (
|
||||||
DeviceRegistry,
|
DeviceRegistry,
|
||||||
@ -74,6 +80,8 @@ class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
"""Update data."""
|
"""Update data."""
|
||||||
try:
|
try:
|
||||||
response = await self._api.async_get_monitors()
|
response = await self._api.async_get_monitors()
|
||||||
|
except UptimeRobotAuthenticationException as exception:
|
||||||
|
raise ConfigEntryAuthFailed(exception) from exception
|
||||||
except UptimeRobotException as exception:
|
except UptimeRobotException as exception:
|
||||||
raise UpdateFailed(exception) from exception
|
raise UpdateFailed(exception) from exception
|
||||||
else:
|
else:
|
||||||
|
@ -58,15 +58,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
if response and response.data and response.data.email
|
if response and response.data and response.data.email
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
if account:
|
|
||||||
await self.async_set_unique_id(str(account.user_id))
|
|
||||||
self._abort_if_unique_id_configured()
|
|
||||||
|
|
||||||
return errors, account
|
return errors, account
|
||||||
|
|
||||||
async def async_step_user(self, user_input: ConfigType | None = None) -> FlowResult:
|
async def async_step_user(self, user_input: ConfigType | None = None) -> FlowResult:
|
||||||
"""Handle the initial step."""
|
"""Handle the initial step."""
|
||||||
errors: dict[str, str] = {}
|
|
||||||
if user_input is None:
|
if user_input is None:
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
|
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
|
||||||
@ -74,12 +70,48 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
errors, account = await self._validate_input(user_input)
|
errors, account = await self._validate_input(user_input)
|
||||||
if account:
|
if account:
|
||||||
|
await self.async_set_unique_id(str(account.user_id))
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
return self.async_create_entry(title=account.email, data=user_input)
|
return self.async_create_entry(title=account.email, data=user_input)
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_step_reauth(
|
||||||
|
self, user_input: ConfigType | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Return the reauth confirm step."""
|
||||||
|
return await self.async_step_reauth_confirm()
|
||||||
|
|
||||||
|
async def async_step_reauth_confirm(
|
||||||
|
self, user_input: ConfigType | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Dialog that informs the user that reauth is required."""
|
||||||
|
if user_input is None:
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="reauth_confirm", data_schema=STEP_USER_DATA_SCHEMA
|
||||||
|
)
|
||||||
|
errors, account = await self._validate_input(user_input)
|
||||||
|
if account:
|
||||||
|
if self.context.get("unique_id") and self.context["unique_id"] != str(
|
||||||
|
account.user_id
|
||||||
|
):
|
||||||
|
errors["base"] = "reauth_failed_matching_account"
|
||||||
|
else:
|
||||||
|
existing_entry = await self.async_set_unique_id(str(account.user_id))
|
||||||
|
if existing_entry:
|
||||||
|
self.hass.config_entries.async_update_entry(
|
||||||
|
existing_entry, data=user_input
|
||||||
|
)
|
||||||
|
await self.hass.config_entries.async_reload(existing_entry.entry_id)
|
||||||
|
return self.async_abort(reason="reauth_successful")
|
||||||
|
return self.async_abort(reason="reauth_failed_existing")
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="reauth_confirm", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||||
|
)
|
||||||
|
|
||||||
async def async_step_import(self, import_config: ConfigType) -> FlowResult:
|
async def async_step_import(self, import_config: ConfigType) -> FlowResult:
|
||||||
"""Import a config entry from configuration.yaml."""
|
"""Import a config entry from configuration.yaml."""
|
||||||
for entry in self._async_current_entries():
|
for entry in self._async_current_entries():
|
||||||
@ -93,5 +125,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
_, account = await self._validate_input(imported_config)
|
_, account = await self._validate_input(imported_config)
|
||||||
if account:
|
if account:
|
||||||
|
await self.async_set_unique_id(str(account.user_id))
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
return self.async_create_entry(title=account.email, data=imported_config)
|
return self.async_create_entry(title=account.email, data=imported_config)
|
||||||
return self.async_abort(reason="unknown")
|
return self.async_abort(reason="unknown")
|
||||||
|
@ -1,20 +1,31 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
"data": {
|
"description": "You need to supply a read-only API key from Uptime Robot",
|
||||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
"data": {
|
||||||
|
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reauth_confirm": {
|
||||||
|
"title": "[%key:common::config_flow::title::reauth%]",
|
||||||
|
"description": "You need to supply a new read-only API key from Uptime Robot",
|
||||||
|
"data": {
|
||||||
|
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]",
|
||||||
|
"reauth_failed_matching_account": "The API key you provided does not match the account ID for existing configuration."
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||||
|
"reauth_failed_existing": "Could not update the config entry, please remove the integration and set it up again.",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
|
||||||
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
|
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
|
||||||
},
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -2,7 +2,8 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Account is already configured",
|
"already_configured": "Account is already configured",
|
||||||
"unknown": "Unexpected error"
|
"unknown": "Unexpected error",
|
||||||
|
"reauth_successful": "Re-authentication was successful"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "Failed to connect",
|
"cannot_connect": "Failed to connect",
|
||||||
@ -11,6 +12,14 @@
|
|||||||
},
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
|
"description": "You need to supply a read-only API key from Uptime Robot",
|
||||||
|
"data": {
|
||||||
|
"api_key": "API Key"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reauth_confirm": {
|
||||||
|
"title": "Reauthenticate Integration",
|
||||||
|
"description": "You need to supply a read-only API key from Uptime Robot",
|
||||||
"data": {
|
"data": {
|
||||||
"api_key": "API Key"
|
"api_key": "API Key"
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert result2["type"] == RESULT_TYPE_FORM
|
assert result2["type"] == RESULT_TYPE_FORM
|
||||||
assert result2["errors"] == {"base": "cannot_connect"}
|
assert result2["errors"]["base"] == "cannot_connect"
|
||||||
|
|
||||||
|
|
||||||
async def test_form_unexpected_error(hass: HomeAssistant) -> None:
|
async def test_form_unexpected_error(hass: HomeAssistant) -> None:
|
||||||
@ -88,7 +88,7 @@ async def test_form_unexpected_error(hass: HomeAssistant) -> None:
|
|||||||
{"api_key": "1234"},
|
{"api_key": "1234"},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result2["errors"] == {"base": "unknown"}
|
assert result2["errors"]["base"] == "unknown"
|
||||||
|
|
||||||
|
|
||||||
async def test_form_api_key_error(hass: HomeAssistant) -> None:
|
async def test_form_api_key_error(hass: HomeAssistant) -> None:
|
||||||
@ -106,7 +106,7 @@ async def test_form_api_key_error(hass: HomeAssistant) -> None:
|
|||||||
{"api_key": "1234"},
|
{"api_key": "1234"},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result2["errors"] == {"base": "invalid_api_key"}
|
assert result2["errors"]["base"] == "invalid_api_key"
|
||||||
|
|
||||||
|
|
||||||
async def test_form_api_error(hass: HomeAssistant, caplog: LogCaptureFixture) -> None:
|
async def test_form_api_error(hass: HomeAssistant, caplog: LogCaptureFixture) -> None:
|
||||||
@ -129,7 +129,7 @@ async def test_form_api_error(hass: HomeAssistant, caplog: LogCaptureFixture) ->
|
|||||||
{"api_key": "1234"},
|
{"api_key": "1234"},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result2["errors"] == {"base": "unknown"}
|
assert result2["errors"]["base"] == "unknown"
|
||||||
assert "test error from API." in caplog.text
|
assert "test error from API." in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ async def test_user_unique_id_already_exists(hass):
|
|||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
assert result["type"] == "form"
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
assert result["errors"] is None
|
assert result["errors"] is None
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
@ -233,5 +233,190 @@ async def test_user_unique_id_already_exists(hass):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(mock_setup_entry.mock_calls) == 0
|
assert len(mock_setup_entry.mock_calls) == 0
|
||||||
assert result2["type"] == "abort"
|
assert result2["type"] == RESULT_TYPE_ABORT
|
||||||
assert result2["reason"] == "already_configured"
|
assert result2["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reauthentication(hass):
|
||||||
|
"""Test Uptime Robot reauthentication."""
|
||||||
|
old_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={"platform": DOMAIN, "api_key": "1234"},
|
||||||
|
unique_id="1234567890",
|
||||||
|
)
|
||||||
|
old_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={
|
||||||
|
"source": config_entries.SOURCE_REAUTH,
|
||||||
|
"unique_id": old_entry.unique_id,
|
||||||
|
"entry_id": old_entry.entry_id,
|
||||||
|
},
|
||||||
|
data=old_entry.data,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
assert result["step_id"] == "reauth_confirm"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_get_account_details",
|
||||||
|
return_value=UptimeRobotApiResponse.from_dict(
|
||||||
|
{
|
||||||
|
"stat": "ok",
|
||||||
|
"account": {"email": "test@test.test", "user_id": 1234567890},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{"api_key": "1234"},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result2["type"] == RESULT_TYPE_ABORT
|
||||||
|
assert result2["reason"] == "reauth_successful"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reauthentication_failure(hass):
|
||||||
|
"""Test Uptime Robot reauthentication failure."""
|
||||||
|
old_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={"platform": DOMAIN, "api_key": "1234"},
|
||||||
|
unique_id="1234567890",
|
||||||
|
)
|
||||||
|
old_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={
|
||||||
|
"source": config_entries.SOURCE_REAUTH,
|
||||||
|
"unique_id": old_entry.unique_id,
|
||||||
|
"entry_id": old_entry.entry_id,
|
||||||
|
},
|
||||||
|
data=old_entry.data,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
assert result["step_id"] == "reauth_confirm"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_get_account_details",
|
||||||
|
return_value=UptimeRobotApiResponse.from_dict(
|
||||||
|
{
|
||||||
|
"stat": "fail",
|
||||||
|
"error": {"message": "test error from API."},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{"api_key": "1234"},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result2["step_id"] == "reauth_confirm"
|
||||||
|
assert result2["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result2["errors"]["base"] == "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reauthentication_failure_no_existing_entry(hass):
|
||||||
|
"""Test Uptime Robot reauthentication with no existing entry."""
|
||||||
|
old_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={"platform": DOMAIN, "api_key": "1234"},
|
||||||
|
)
|
||||||
|
old_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={
|
||||||
|
"source": config_entries.SOURCE_REAUTH,
|
||||||
|
"unique_id": old_entry.unique_id,
|
||||||
|
"entry_id": old_entry.entry_id,
|
||||||
|
},
|
||||||
|
data=old_entry.data,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
assert result["step_id"] == "reauth_confirm"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_get_account_details",
|
||||||
|
return_value=UptimeRobotApiResponse.from_dict(
|
||||||
|
{
|
||||||
|
"stat": "ok",
|
||||||
|
"account": {"email": "test@test.test", "user_id": 1234567890},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{"api_key": "1234"},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result2["type"] == RESULT_TYPE_ABORT
|
||||||
|
assert result2["reason"] == "reauth_failed_existing"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reauthentication_failure_account_not_matching(hass):
|
||||||
|
"""Test Uptime Robot reauthentication failure when using another account."""
|
||||||
|
old_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={"platform": DOMAIN, "api_key": "1234"},
|
||||||
|
unique_id="1234567890",
|
||||||
|
)
|
||||||
|
old_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={
|
||||||
|
"source": config_entries.SOURCE_REAUTH,
|
||||||
|
"unique_id": old_entry.unique_id,
|
||||||
|
"entry_id": old_entry.entry_id,
|
||||||
|
},
|
||||||
|
data=old_entry.data,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
assert result["step_id"] == "reauth_confirm"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_get_account_details",
|
||||||
|
return_value=UptimeRobotApiResponse.from_dict(
|
||||||
|
{
|
||||||
|
"stat": "ok",
|
||||||
|
"account": {"email": "test@test.test", "user_id": 1234567891},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.uptimerobot.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{"api_key": "1234"},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result2["step_id"] == "reauth_confirm"
|
||||||
|
assert result2["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result2["errors"]["base"] == "reauth_failed_matching_account"
|
||||||
|
107
tests/components/uptimerobot/test_init.py
Normal file
107
tests/components/uptimerobot/test_init.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
"""Test the Uptime Robot init."""
|
||||||
|
import datetime
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from pytest import LogCaptureFixture
|
||||||
|
from pyuptimerobot import UptimeRobotApiResponse
|
||||||
|
from pyuptimerobot.exceptions import UptimeRobotAuthenticationException
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components.uptimerobot.const import DOMAIN
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.util import dt
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reauthentication_trigger_in_setup(
|
||||||
|
hass: HomeAssistant, caplog: LogCaptureFixture
|
||||||
|
):
|
||||||
|
"""Test reauthentication trigger."""
|
||||||
|
mock_config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="test@test.test",
|
||||||
|
data={"platform": DOMAIN, "api_key": "1234"},
|
||||||
|
unique_id="1234567890",
|
||||||
|
source=config_entries.SOURCE_USER,
|
||||||
|
)
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||||
|
side_effect=UptimeRobotAuthenticationException,
|
||||||
|
):
|
||||||
|
|
||||||
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
flows = hass.config_entries.flow.async_progress()
|
||||||
|
|
||||||
|
assert mock_config_entry.state == config_entries.ConfigEntryState.SETUP_ERROR
|
||||||
|
assert mock_config_entry.reason == "could not authenticate"
|
||||||
|
|
||||||
|
assert len(flows) == 1
|
||||||
|
flow = flows[0]
|
||||||
|
assert flow["step_id"] == "reauth_confirm"
|
||||||
|
assert flow["handler"] == DOMAIN
|
||||||
|
assert flow["context"]["source"] == config_entries.SOURCE_REAUTH
|
||||||
|
assert flow["context"]["entry_id"] == mock_config_entry.entry_id
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Config entry 'test@test.test' for uptimerobot integration could not authenticate"
|
||||||
|
in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reauthentication_trigger_after_setup(
|
||||||
|
hass: HomeAssistant, caplog: LogCaptureFixture
|
||||||
|
):
|
||||||
|
"""Test reauthentication trigger."""
|
||||||
|
mock_config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="test@test.test",
|
||||||
|
data={"platform": DOMAIN, "api_key": "1234"},
|
||||||
|
unique_id="1234567890",
|
||||||
|
source=config_entries.SOURCE_USER,
|
||||||
|
)
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||||
|
return_value=UptimeRobotApiResponse.from_dict(
|
||||||
|
{
|
||||||
|
"stat": "ok",
|
||||||
|
"monitors": [
|
||||||
|
{"id": 1234, "friendly_name": "Test monitor", "status": 2}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
):
|
||||||
|
|
||||||
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
binary_sensor = hass.states.get("binary_sensor.test_monitor")
|
||||||
|
assert mock_config_entry.state == config_entries.ConfigEntryState.LOADED
|
||||||
|
assert binary_sensor.state == "on"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"pyuptimerobot.UptimeRobot.async_get_monitors",
|
||||||
|
side_effect=UptimeRobotAuthenticationException,
|
||||||
|
):
|
||||||
|
|
||||||
|
async_fire_time_changed(hass, dt.utcnow() + datetime.timedelta(seconds=10))
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
flows = hass.config_entries.flow.async_progress()
|
||||||
|
binary_sensor = hass.states.get("binary_sensor.test_monitor")
|
||||||
|
|
||||||
|
assert binary_sensor.state == "unavailable"
|
||||||
|
assert "Authentication failed while fetching uptimerobot data" in caplog.text
|
||||||
|
|
||||||
|
assert len(flows) == 1
|
||||||
|
flow = flows[0]
|
||||||
|
assert flow["step_id"] == "reauth_confirm"
|
||||||
|
assert flow["handler"] == DOMAIN
|
||||||
|
assert flow["context"]["source"] == config_entries.SOURCE_REAUTH
|
||||||
|
assert flow["context"]["entry_id"] == mock_config_entry.entry_id
|
Loading…
x
Reference in New Issue
Block a user