diff --git a/homeassistant/components/caldav/api.py b/homeassistant/components/caldav/api.py index b64b7fb8e734..0f0b5a60e409 100644 --- a/homeassistant/components/caldav/api.py +++ b/homeassistant/components/caldav/api.py @@ -1,5 +1,4 @@ """Library for working with CalDAV api.""" -# pylint: disable=home-assistant-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern import logging @@ -8,7 +7,7 @@ from caldav.lib.error import DAVError from homeassistant.core import HomeAssistant -from .const import DOMAIN +from .const import WARNED_CALENDARS _LOGGER = logging.getLogger(__name__) @@ -45,9 +44,7 @@ async def async_get_calendars( calendars, needs_warning = await hass.async_add_executor_job(_get_calendars) if needs_warning: - warned_calendars: set[tuple[str, str]] = hass.data.setdefault( - DOMAIN, {} - ).setdefault("warned_calendars", set()) + warned_calendars = hass.data.setdefault(WARNED_CALENDARS, set()) for url, name, comp in needs_warning: # This workaround and warning can be removed when we upgrade to caldav 3.0 if (url, comp) not in warned_calendars: diff --git a/homeassistant/components/caldav/const.py b/homeassistant/components/caldav/const.py index e133bb1b8bc8..5c3b512be451 100644 --- a/homeassistant/components/caldav/const.py +++ b/homeassistant/components/caldav/const.py @@ -2,5 +2,12 @@ from typing import Final +from homeassistant.util.hass_dict import HassKey + DOMAIN: Final = "caldav" TIMEOUT: Final = 30 + +# Calendars we have already warned about, keyed by (url, component). This is +# deliberately not stored on a config entry: the warning is per CalDAV server +# and must survive reloads, and the same server may back more than one entry. +WARNED_CALENDARS: HassKey[set[tuple[str, str]]] = HassKey(f"{DOMAIN}_warned_calendars") diff --git a/tests/components/caldav/test_init.py b/tests/components/caldav/test_init.py index 543446b146f9..aefe6a73ecf3 100644 --- a/tests/components/caldav/test_init.py +++ b/tests/components/caldav/test_init.py @@ -1,12 +1,14 @@ """Unit tests for the CalDav integration.""" -from unittest.mock import patch +import logging +from unittest.mock import MagicMock, Mock, patch from caldav.lib.error import AuthorizationError, DAVError import pytest import requests from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry @@ -71,3 +73,40 @@ async def test_client_failure( flows = hass.config_entries.flow.async_progress() assert [flow.get("step_id") for flow in flows] == expected_flows + + +@pytest.fixture(name="calendars") +def mock_unsupported_calendar() -> list[Mock]: + """Fixture for a calendar that does not report its supported components.""" + calendar = Mock() + calendar.name = "Example" + calendar.search = MagicMock(return_value=[]) + calendar.get_supported_components = MagicMock(side_effect=KeyError()) + return [calendar] + + +@pytest.mark.parametrize("platforms", [[Platform.CALENDAR]]) +async def test_supported_components_warning_survives_reload( + hass: HomeAssistant, + config_entry: MockConfigEntry, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test the unsupported-components warning is not repeated after a reload. + + The de-duplication cache is per CalDAV server rather than per config entry, + so reloading the entry must not warn about the same calendar again. + """ + caplog.set_level(logging.WARNING, logger="homeassistant.components.caldav.api") + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state is ConfigEntryState.LOADED + assert "does not report supported components" in caplog.text + + caplog.clear() + await hass.config_entries.async_reload(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state is ConfigEntryState.LOADED + assert "does not report supported components" not in caplog.text