Handle Mastodon auth fail in coordinator (#163234)

This commit is contained in:
Andrew Jackson
2026-02-19 18:14:14 +01:00
committed by GitHub
parent 6f49f9a12a
commit 2055082993
2 changed files with 89 additions and 6 deletions
@@ -6,13 +6,20 @@ from dataclasses import dataclass
from datetime import timedelta
from mastodon import Mastodon
from mastodon.Mastodon import Account, Instance, InstanceV2, MastodonError
from mastodon.Mastodon import (
Account,
Instance,
InstanceV2,
MastodonError,
MastodonUnauthorizedError,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import LOGGER
from .const import DOMAIN, LOGGER
@dataclass
@@ -51,6 +58,11 @@ class MastodonCoordinator(DataUpdateCoordinator[Account]):
account: Account = await self.hass.async_add_executor_job(
self.client.account_verify_credentials
)
except MastodonUnauthorizedError as error:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="auth_failed",
) from error
except MastodonError as ex:
raise UpdateFailed(ex) from ex
+75 -4
View File
@@ -1,21 +1,33 @@
"""Tests for the Mastodon integration."""
from datetime import timedelta
from unittest.mock import AsyncMock
from mastodon.Mastodon import MastodonNotFoundError, MastodonUnauthorizedError
from freezegun.api import FrozenDateTimeFactory
from mastodon.Mastodon import (
MastodonError,
MastodonNotFoundError,
MastodonUnauthorizedError,
)
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.mastodon.config_flow import MastodonConfigFlow
from homeassistant.components.mastodon.const import CONF_BASE_URL, DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
from homeassistant.const import (
CONF_ACCESS_TOKEN,
CONF_CLIENT_ID,
CONF_CLIENT_SECRET,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from . import setup_integration
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, async_fire_time_changed
async def test_device_info(
@@ -107,3 +119,62 @@ async def test_migrate(
assert config_entry.version == MastodonConfigFlow.VERSION
assert config_entry.minor_version == MastodonConfigFlow.MINOR_VERSION
assert config_entry.unique_id == "trwnh_mastodon_social"
async def test_coordinator_general_error(
hass: HomeAssistant,
mock_mastodon_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test general error during coordinator update makes entities unavailable."""
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.LOADED
state = hass.states.get("binary_sensor.mastodon_trwnh_mastodon_social_bot")
assert state is not None
assert state.state == STATE_ON
mock_mastodon_client.account_verify_credentials.side_effect = MastodonError
freezer.tick(timedelta(hours=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.LOADED
state = hass.states.get("binary_sensor.mastodon_trwnh_mastodon_social_bot")
assert state is not None
assert state.state == STATE_UNAVAILABLE
# No reauth flow should be triggered (unlike auth errors)
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 0
async def test_coordinator_auth_failure_triggers_reauth(
hass: HomeAssistant,
mock_mastodon_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test auth failure during coordinator update triggers reauth flow."""
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.LOADED
mock_mastodon_client.account_verify_credentials.side_effect = (
MastodonUnauthorizedError
)
freezer.tick(timedelta(hours=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.LOADED
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
flow = flows[0]
assert flow["context"]["source"] == SOURCE_REAUTH
assert flow["context"]["entry_id"] == mock_config_entry.entry_id