Add created_at/modified_at to config entries (#122456)

pull/122808/head
Robert Resch 2024-07-29 22:08:46 +02:00 committed by GitHub
parent 20c4f84a4e
commit ad50136dbd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
67 changed files with 440 additions and 392 deletions

View File

@ -15,6 +15,7 @@ from collections.abc import (
)
from contextvars import ContextVar
from copy import deepcopy
from datetime import datetime
from enum import Enum, StrEnum
import functools
from functools import cached_property
@ -69,6 +70,7 @@ from .setup import (
from .util import ulid as ulid_util
from .util.async_ import create_eager_task
from .util.decorator import Registry
from .util.dt import utc_from_timestamp, utcnow
from .util.enum import try_parse_enum
if TYPE_CHECKING:
@ -118,7 +120,7 @@ HANDLERS: Registry[str, type[ConfigFlow]] = Registry()
STORAGE_KEY = "core.config_entries"
STORAGE_VERSION = 1
STORAGE_VERSION_MINOR = 2
STORAGE_VERSION_MINOR = 3
SAVE_DELAY = 1
@ -303,15 +305,19 @@ class ConfigEntry(Generic[_DataT]):
_background_tasks: set[asyncio.Future[Any]]
_integration_for_domain: loader.Integration | None
_tries: int
created_at: datetime
modified_at: datetime
def __init__(
self,
*,
created_at: datetime | None = None,
data: Mapping[str, Any],
disabled_by: ConfigEntryDisabler | None = None,
domain: str,
entry_id: str | None = None,
minor_version: int,
modified_at: datetime | None = None,
options: Mapping[str, Any] | None,
pref_disable_new_entities: bool | None = None,
pref_disable_polling: bool | None = None,
@ -415,6 +421,8 @@ class ConfigEntry(Generic[_DataT]):
_setter(self, "_integration_for_domain", None)
_setter(self, "_tries", 0)
_setter(self, "created_at", created_at or utcnow())
_setter(self, "modified_at", modified_at or utcnow())
def __repr__(self) -> str:
"""Representation of ConfigEntry."""
@ -483,8 +491,10 @@ class ConfigEntry(Generic[_DataT]):
def as_json_fragment(self) -> json_fragment:
"""Return JSON fragment of a config entry."""
json_repr = {
"created_at": self.created_at.timestamp(),
"entry_id": self.entry_id,
"domain": self.domain,
"modified_at": self.modified_at.timestamp(),
"title": self.title,
"source": self.source,
"state": self.state.value,
@ -831,6 +841,10 @@ class ConfigEntry(Generic[_DataT]):
async def async_remove(self, hass: HomeAssistant) -> None:
"""Invoke remove callback on component."""
old_modified_at = self.modified_at
object.__setattr__(self, "modified_at", utcnow())
self.clear_cache()
if self.source == SOURCE_IGNORE:
return
@ -862,6 +876,8 @@ class ConfigEntry(Generic[_DataT]):
self.title,
integration.domain,
)
# Restore modified_at
object.__setattr__(self, "modified_at", old_modified_at)
@callback
def _async_set_state(
@ -950,11 +966,13 @@ class ConfigEntry(Generic[_DataT]):
def as_dict(self) -> dict[str, Any]:
"""Return dictionary version of this entry."""
return {
"created_at": self.created_at.isoformat(),
"data": dict(self.data),
"disabled_by": self.disabled_by,
"domain": self.domain,
"entry_id": self.entry_id,
"minor_version": self.minor_version,
"modified_at": self.modified_at.isoformat(),
"options": dict(self.options),
"pref_disable_new_entities": self.pref_disable_new_entities,
"pref_disable_polling": self.pref_disable_polling,
@ -1599,25 +1617,34 @@ class ConfigEntryStore(storage.Store[dict[str, list[dict[str, Any]]]]):
) -> dict[str, Any]:
"""Migrate to the new version."""
data = old_data
if old_major_version == 1 and old_minor_version < 2:
# Version 1.2 implements migration and freezes the available keys
for entry in data["entries"]:
# Populate keys which were introduced before version 1.2
if old_major_version == 1:
if old_minor_version < 2:
# Version 1.2 implements migration and freezes the available keys
for entry in data["entries"]:
# Populate keys which were introduced before version 1.2
pref_disable_new_entities = entry.get("pref_disable_new_entities")
if pref_disable_new_entities is None and "system_options" in entry:
pref_disable_new_entities = entry.get("system_options", {}).get(
"disable_new_entities"
pref_disable_new_entities = entry.get("pref_disable_new_entities")
if pref_disable_new_entities is None and "system_options" in entry:
pref_disable_new_entities = entry.get("system_options", {}).get(
"disable_new_entities"
)
entry.setdefault("disabled_by", entry.get("disabled_by"))
entry.setdefault("minor_version", entry.get("minor_version", 1))
entry.setdefault("options", entry.get("options", {}))
entry.setdefault(
"pref_disable_new_entities", pref_disable_new_entities
)
entry.setdefault(
"pref_disable_polling", entry.get("pref_disable_polling")
)
entry.setdefault("unique_id", entry.get("unique_id"))
entry.setdefault("disabled_by", entry.get("disabled_by"))
entry.setdefault("minor_version", entry.get("minor_version", 1))
entry.setdefault("options", entry.get("options", {}))
entry.setdefault("pref_disable_new_entities", pref_disable_new_entities)
entry.setdefault(
"pref_disable_polling", entry.get("pref_disable_polling")
)
entry.setdefault("unique_id", entry.get("unique_id"))
if old_minor_version < 3:
# Version 1.3 adds the created_at and modified_at fields
created_at = utc_from_timestamp(0).isoformat()
for entry in data["entries"]:
entry["created_at"] = entry["modified_at"] = created_at
if old_major_version > 1:
raise NotImplementedError
@ -1793,11 +1820,13 @@ class ConfigEntries:
entry_id = entry["entry_id"]
config_entry = ConfigEntry(
created_at=datetime.fromisoformat(entry["created_at"]),
data=entry["data"],
disabled_by=try_parse_enum(ConfigEntryDisabler, entry["disabled_by"]),
domain=entry["domain"],
entry_id=entry_id,
minor_version=entry["minor_version"],
modified_at=datetime.fromisoformat(entry["modified_at"]),
options=entry["options"],
pref_disable_new_entities=entry["pref_disable_new_entities"],
pref_disable_polling=entry["pref_disable_polling"],
@ -2014,6 +2043,8 @@ class ConfigEntries:
if not changed:
return False
_setter(entry, "modified_at", utcnow())
for listener in entry.update_listeners:
self.hass.async_create_task(
listener(self.hass, entry),

View File

@ -4,6 +4,7 @@ from unittest.mock import patch
import pytest
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.aemet.const import DOMAIN
from homeassistant.core import HomeAssistant
@ -30,4 +31,4 @@ async def test_config_entry_diagnostics(
return_value={},
):
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -1,6 +1,7 @@
"""Test Airly diagnostics."""
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -22,4 +23,4 @@ async def test_entry_diagnostics(
result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -4,6 +4,7 @@ from unittest.mock import patch
import pytest
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -27,7 +28,6 @@ async def test_entry_diagnostics(
return_value="PST",
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -1,6 +1,7 @@
"""Test AirVisual diagnostics."""
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -16,7 +17,6 @@ async def test_entry_diagnostics(
snapshot: SnapshotAssertion,
) -> None:
"""Test config entry diagnostics."""
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -1,6 +1,7 @@
"""Test AirVisual Pro diagnostics."""
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -16,7 +17,6 @@ async def test_entry_diagnostics(
snapshot: SnapshotAssertion,
) -> None:
"""Test config entry diagnostics."""
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -4,6 +4,7 @@ from unittest.mock import patch
from aioairzone.const import RAW_HVAC, RAW_VERSION, RAW_WEBSERVER
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.airzone.const import DOMAIN
from homeassistant.core import HomeAssistant
@ -37,4 +38,4 @@ async def test_config_entry_diagnostics(
},
):
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -15,6 +15,7 @@ from aioairzone_cloud.const import (
RAW_WEBSERVERS,
)
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.airzone_cloud.const import DOMAIN
from homeassistant.const import CONF_ID
@ -111,4 +112,4 @@ async def test_config_entry_diagnostics(
return_value=RAW_DATA_MOCK,
):
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -1,6 +1,7 @@
"""Test Ambient PWS diagnostics."""
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.ambient_station import AmbientStationConfigEntry
from homeassistant.core import HomeAssistant
@ -20,7 +21,6 @@ async def test_entry_diagnostics(
"""Test config entry diagnostics."""
ambient = config_entry.runtime_data
ambient.stations = data_station
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -2,6 +2,7 @@
import pytest
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -20,7 +21,6 @@ async def test_entry_diagnostics(
snapshot: SnapshotAssertion,
) -> None:
"""Test config entry diagnostics."""
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry_setup)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry_setup
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -31,4 +31,4 @@ async def test_entry_diagnostics(
hass, hass_client, mock_config_entry
)
assert result == snapshot(exclude=props("entry_id"))
assert result == snapshot(exclude=props("entry_id", "created_at", "modified_at"))

View File

@ -3,6 +3,7 @@
from unittest.mock import patch
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.braviatv.const import CONF_USE_PSK, DOMAIN
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PIN
@ -71,4 +72,4 @@ async def test_entry_diagnostics(
assert await async_setup_component(hass, DOMAIN, {})
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -2,6 +2,7 @@
import pytest
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -20,4 +21,4 @@ async def test_entry_diagnostics(
"""Test config entry diagnostics."""
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -3,6 +3,7 @@
from unittest.mock import patch
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -40,4 +41,4 @@ async def test_entry_diagnostics(
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -6,6 +6,7 @@ from http import HTTPStatus
from unittest.mock import ANY, AsyncMock, patch
from aiohttp.test_utils import TestClient
from freezegun.api import FrozenDateTimeFactory
import pytest
import voluptuous as vol
@ -18,6 +19,7 @@ from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import config_entry_flow, config_validation as cv
from homeassistant.loader import IntegrationNotFound
from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow
from tests.common import (
MockConfigEntry,
@ -69,6 +71,7 @@ def mock_flow() -> Generator[None]:
yield
@pytest.mark.usefixtures("freezer")
@pytest.mark.usefixtures("clear_handlers", "mock_flow")
async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
"""Test get entries."""
@ -124,12 +127,15 @@ async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
data = await resp.json()
for entry in data:
entry.pop("entry_id")
timestamp = utcnow().timestamp()
assert data == [
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp1",
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -142,10 +148,12 @@ async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
"title": "Test 1",
},
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp2",
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": "Unsupported API",
@ -158,10 +166,12 @@ async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
"title": "Test 2",
},
{
"created_at": timestamp,
"disabled_by": core_ce.ConfigEntryDisabler.USER,
"domain": "comp3",
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -174,10 +184,12 @@ async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
"title": "Test 3",
},
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp4",
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -190,10 +202,12 @@ async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
"title": "Test 4",
},
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp5",
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -509,7 +523,7 @@ async def test_abort(hass: HomeAssistant, client: TestClient) -> None:
}
@pytest.mark.usefixtures("enable_custom_integrations")
@pytest.mark.usefixtures("enable_custom_integrations", "freezer")
async def test_create_account(hass: HomeAssistant, client: TestClient) -> None:
"""Test a flow that creates an account."""
mock_platform(hass, "test.config_flow", None)
@ -536,6 +550,7 @@ async def test_create_account(hass: HomeAssistant, client: TestClient) -> None:
entries = hass.config_entries.async_entries("test")
assert len(entries) == 1
timestamp = utcnow().timestamp()
data = await resp.json()
data.pop("flow_id")
assert data == {
@ -544,11 +559,13 @@ async def test_create_account(hass: HomeAssistant, client: TestClient) -> None:
"type": "create_entry",
"version": 1,
"result": {
"created_at": timestamp,
"disabled_by": None,
"domain": "test",
"entry_id": entries[0].entry_id,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -567,7 +584,7 @@ async def test_create_account(hass: HomeAssistant, client: TestClient) -> None:
}
@pytest.mark.usefixtures("enable_custom_integrations")
@pytest.mark.usefixtures("enable_custom_integrations", "freezer")
async def test_two_step_flow(hass: HomeAssistant, client: TestClient) -> None:
"""Test we can finish a two step flow."""
mock_integration(
@ -616,6 +633,7 @@ async def test_two_step_flow(hass: HomeAssistant, client: TestClient) -> None:
entries = hass.config_entries.async_entries("test")
assert len(entries) == 1
timestamp = utcnow().timestamp()
data = await resp.json()
data.pop("flow_id")
assert data == {
@ -624,11 +642,13 @@ async def test_two_step_flow(hass: HomeAssistant, client: TestClient) -> None:
"title": "user-title",
"version": 1,
"result": {
"created_at": timestamp,
"disabled_by": None,
"domain": "test",
"entry_id": entries[0].entry_id,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1059,6 +1079,7 @@ async def test_options_flow_with_invalid_data(
assert data == {"errors": {"choices": "invalid is not a valid option"}}
@pytest.mark.usefixtures("freezer")
async def test_get_single(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
@ -1080,13 +1101,16 @@ async def test_get_single(
)
response = await ws_client.receive_json()
timestamp = utcnow().timestamp()
assert response["success"]
assert response["result"]["config_entry"] == {
"created_at": timestamp,
"disabled_by": None,
"domain": "test",
"entry_id": entry.entry_id,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1366,7 +1390,7 @@ async def test_ignore_flow_nonexisting(
assert response["error"]["code"] == "not_found"
@pytest.mark.usefixtures("clear_handlers")
@pytest.mark.usefixtures("clear_handlers", "freezer")
async def test_get_matching_entries_ws(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
@ -1420,13 +1444,16 @@ async def test_get_matching_entries_ws(
await ws_client.send_json_auto_id({"type": "config_entries/get"})
response = await ws_client.receive_json()
timestamp = utcnow().timestamp()
assert response["result"] == [
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1439,11 +1466,13 @@ async def test_get_matching_entries_ws(
"title": "Test 1",
},
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp2",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": "Unsupported API",
@ -1456,11 +1485,13 @@ async def test_get_matching_entries_ws(
"title": "Test 2",
},
{
"created_at": timestamp,
"disabled_by": "user",
"domain": "comp3",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1473,11 +1504,13 @@ async def test_get_matching_entries_ws(
"title": "Test 3",
},
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp4",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1490,11 +1523,13 @@ async def test_get_matching_entries_ws(
"title": "Test 4",
},
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp5",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1518,11 +1553,13 @@ async def test_get_matching_entries_ws(
response = await ws_client.receive_json()
assert response["result"] == [
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1545,11 +1582,13 @@ async def test_get_matching_entries_ws(
response = await ws_client.receive_json()
assert response["result"] == [
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp4",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1562,11 +1601,13 @@ async def test_get_matching_entries_ws(
"title": "Test 4",
},
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp5",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1589,11 +1630,13 @@ async def test_get_matching_entries_ws(
response = await ws_client.receive_json()
assert response["result"] == [
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1606,11 +1649,13 @@ async def test_get_matching_entries_ws(
"title": "Test 1",
},
{
"created_at": timestamp,
"disabled_by": "user",
"domain": "comp3",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1639,11 +1684,13 @@ async def test_get_matching_entries_ws(
assert response["result"] == [
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1656,11 +1703,13 @@ async def test_get_matching_entries_ws(
"title": "Test 1",
},
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp2",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": "Unsupported API",
@ -1673,11 +1722,13 @@ async def test_get_matching_entries_ws(
"title": "Test 2",
},
{
"created_at": timestamp,
"disabled_by": "user",
"domain": "comp3",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1690,11 +1741,13 @@ async def test_get_matching_entries_ws(
"title": "Test 3",
},
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp4",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1707,11 +1760,13 @@ async def test_get_matching_entries_ws(
"title": "Test 4",
},
{
"created_at": timestamp,
"disabled_by": None,
"domain": "comp5",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1759,7 +1814,9 @@ async def test_get_matching_entries_ws(
@pytest.mark.usefixtures("clear_handlers")
async def test_subscribe_entries_ws(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test subscribe entries with the websocket api."""
assert await async_setup_component(hass, "config", {})
@ -1805,15 +1862,18 @@ async def test_subscribe_entries_ws(
assert response["type"] == "result"
response = await ws_client.receive_json()
assert response["id"] == 5
created = utcnow().timestamp()
assert response["event"] == [
{
"type": None,
"entry": {
"created_at": created,
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": created,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1829,11 +1889,13 @@ async def test_subscribe_entries_ws(
{
"type": None,
"entry": {
"created_at": created,
"disabled_by": None,
"domain": "comp2",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": created,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": "Unsupported API",
@ -1849,11 +1911,13 @@ async def test_subscribe_entries_ws(
{
"type": None,
"entry": {
"created_at": created,
"disabled_by": "user",
"domain": "comp3",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": created,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1867,17 +1931,21 @@ async def test_subscribe_entries_ws(
},
},
]
freezer.tick()
modified = utcnow().timestamp()
assert hass.config_entries.async_update_entry(entry, title="changed")
response = await ws_client.receive_json()
assert response["id"] == 5
assert response["event"] == [
{
"entry": {
"created_at": created,
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": modified,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1892,17 +1960,21 @@ async def test_subscribe_entries_ws(
"type": "updated",
}
]
freezer.tick()
modified = utcnow().timestamp()
await hass.config_entries.async_remove(entry.entry_id)
response = await ws_client.receive_json()
assert response["id"] == 5
assert response["event"] == [
{
"entry": {
"created_at": created,
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": modified,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1917,17 +1989,20 @@ async def test_subscribe_entries_ws(
"type": "removed",
}
]
freezer.tick()
await hass.config_entries.async_add(entry)
response = await ws_client.receive_json()
assert response["id"] == 5
assert response["event"] == [
{
"entry": {
"created_at": entry.created_at.timestamp(),
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": entry.modified_at.timestamp(),
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -1946,9 +2021,12 @@ async def test_subscribe_entries_ws(
@pytest.mark.usefixtures("clear_handlers")
async def test_subscribe_entries_ws_filtered(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test subscribe entries with the websocket api with a type filter."""
created = utcnow().timestamp()
assert await async_setup_component(hass, "config", {})
mock_integration(hass, MockModule("comp1"))
mock_integration(
@ -2008,11 +2086,13 @@ async def test_subscribe_entries_ws_filtered(
{
"type": None,
"entry": {
"created_at": created,
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": created,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -2028,11 +2108,13 @@ async def test_subscribe_entries_ws_filtered(
{
"type": None,
"entry": {
"created_at": created,
"disabled_by": "user",
"domain": "comp3",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": created,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -2046,6 +2128,8 @@ async def test_subscribe_entries_ws_filtered(
},
},
]
freezer.tick()
modified = utcnow().timestamp()
assert hass.config_entries.async_update_entry(entry, title="changed")
assert hass.config_entries.async_update_entry(entry3, title="changed too")
assert hass.config_entries.async_update_entry(entry4, title="changed but ignored")
@ -2054,11 +2138,13 @@ async def test_subscribe_entries_ws_filtered(
assert response["event"] == [
{
"entry": {
"created_at": created,
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": modified,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -2078,11 +2164,13 @@ async def test_subscribe_entries_ws_filtered(
assert response["event"] == [
{
"entry": {
"created_at": created,
"disabled_by": "user",
"domain": "comp3",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": modified,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -2097,6 +2185,8 @@ async def test_subscribe_entries_ws_filtered(
"type": "updated",
}
]
freezer.tick()
modified = utcnow().timestamp()
await hass.config_entries.async_remove(entry.entry_id)
await hass.config_entries.async_remove(entry2.entry_id)
response = await ws_client.receive_json()
@ -2104,11 +2194,13 @@ async def test_subscribe_entries_ws_filtered(
assert response["event"] == [
{
"entry": {
"created_at": created,
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": modified,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -2123,17 +2215,20 @@ async def test_subscribe_entries_ws_filtered(
"type": "removed",
}
]
freezer.tick()
await hass.config_entries.async_add(entry)
response = await ws_client.receive_json()
assert response["id"] == 5
assert response["event"] == [
{
"entry": {
"created_at": entry.created_at.timestamp(),
"disabled_by": None,
"domain": "comp1",
"entry_id": ANY,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": entry.modified_at.timestamp(),
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
@ -2238,8 +2333,11 @@ async def test_flow_with_multiple_schema_errors_base(
}
@pytest.mark.usefixtures("enable_custom_integrations")
async def test_supports_reconfigure(hass: HomeAssistant, client: TestClient) -> None:
@pytest.mark.usefixtures("enable_custom_integrations", "freezer")
async def test_supports_reconfigure(
hass: HomeAssistant,
client: TestClient,
) -> None:
"""Test a flow that support reconfigure step."""
mock_platform(hass, "test.config_flow", None)
@ -2297,6 +2395,7 @@ async def test_supports_reconfigure(hass: HomeAssistant, client: TestClient) ->
assert len(entries) == 1
data = await resp.json()
timestamp = utcnow().timestamp()
data.pop("flow_id")
assert data == {
"handler": "test",
@ -2304,11 +2403,13 @@ async def test_supports_reconfigure(hass: HomeAssistant, client: TestClient) ->
"type": "create_entry",
"version": 1,
"result": {
"created_at": timestamp,
"disabled_by": None,
"domain": "test",
"entry_id": entries[0].entry_id,
"error_reason_translation_key": None,
"error_reason_translation_placeholders": None,
"modified_at": timestamp,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,

View File

@ -2,6 +2,7 @@
from pydeconz.websocket import State
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -23,7 +24,6 @@ async def test_entry_diagnostics(
await mock_websocket_state(State.RUNNING)
await hass.async_block_till_done()
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry_setup)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry_setup
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -5,6 +5,7 @@ from __future__ import annotations
from unittest.mock import patch
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
@ -35,4 +36,4 @@ async def test_entry_diagnostics(
assert entry.state is ConfigEntryState.LOADED
result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import pytest
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
@ -28,4 +29,4 @@ async def test_entry_diagnostics(
assert entry.state is ConfigEntryState.LOADED
result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -3,6 +3,7 @@
from unittest.mock import patch
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.dsmr_reader.const import DOMAIN
from homeassistant.core import HomeAssistant
@ -36,4 +37,4 @@ async def test_get_config_entry_diagnostics(
diagnostics = await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
)
assert diagnostics == snapshot
assert diagnostics == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -28,4 +28,4 @@ async def test_diagnostics(
"""Test diagnostics."""
assert await get_diagnostics_for_config_entry(
hass, hass_client, init_integration
) == snapshot(exclude=props("entry_id"))
) == snapshot(exclude=props("entry_id", "created_at", "modified_at"))

View File

@ -5,6 +5,7 @@ from unittest.mock import ANY
import pytest
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components import bluetooth
from homeassistant.core import HomeAssistant
@ -27,7 +28,7 @@ async def test_diagnostics(
"""Test diagnostics for config entry."""
result = await get_diagnostics_for_config_entry(hass, hass_client, init_integration)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))
async def test_diagnostics_with_bluetooth(
@ -61,6 +62,7 @@ async def test_diagnostics_with_bluetooth(
},
},
"config": {
"created_at": ANY,
"data": {
"device_name": "test",
"host": "test.local",
@ -71,6 +73,7 @@ async def test_diagnostics_with_bluetooth(
"domain": "esphome",
"entry_id": ANY,
"minor_version": 1,
"modified_at": ANY,
"options": {"allow_service_calls": False},
"pref_disable_new_entities": False,
"pref_disable_polling": False,

View File

@ -1,6 +1,7 @@
"""Tests for the diagnostics data provided by the Fronius integration."""
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -21,11 +22,8 @@ async def test_diagnostics(
mock_responses(aioclient_mock)
entry = await setup_fronius_integration(hass)
assert (
await get_diagnostics_for_config_entry(
hass,
hass_client,
entry,
)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass,
hass_client,
entry,
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
@ -28,4 +29,4 @@ async def test_entry_diagnostics(
hass, hass_client, mock_config_entry
)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -1,6 +1,7 @@
"""Test GIOS diagnostics."""
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -18,4 +19,6 @@ async def test_entry_diagnostics(
"""Test config entry diagnostics."""
entry = await init_integration(hass)
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot(
exclude=props("created_at", "modified_at")
)

View File

@ -3,6 +3,7 @@
from unittest.mock import MagicMock, patch
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.goodwe import CONF_MODEL_FAMILY, DOMAIN
from homeassistant.const import CONF_HOST
@ -32,4 +33,4 @@ async def test_entry_diagnostics(
assert await async_setup_component(hass, DOMAIN, {})
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -50,4 +50,4 @@ async def test_diagnostics(
config_entry = hass.config_entries.async_entries("google_assistant")[0]
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("entry_id"))
) == snapshot(exclude=props("entry_id", "created_at", "modified_at"))

View File

@ -4,7 +4,7 @@ from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.guardian import DOMAIN, GuardianData
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
from tests.common import ANY, MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@ -39,6 +39,8 @@ async def test_entry_diagnostics(
"source": "user",
"unique_id": REDACTED,
"disabled_by": None,
"created_at": ANY,
"modified_at": ANY,
},
"data": {
"valve_controller": {

View File

@ -5,6 +5,7 @@ from unittest.mock import AsyncMock
import pytest
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.husqvarna_automower.const import DOMAIN
from homeassistant.core import HomeAssistant
@ -36,7 +37,7 @@ async def test_entry_diagnostics(
result = await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))
@pytest.mark.freeze_time(datetime.datetime(2024, 2, 29, 11, tzinfo=datetime.UTC))

View File

@ -28,4 +28,4 @@ async def test_entry_diagnostics(
hass, hass_client, mock_config_entry
)
assert result == snapshot(exclude=props("entry_id"))
assert result == snapshot(exclude=props("entry_id", "created_at", "modified_at"))

View File

@ -1,6 +1,7 @@
"""Test IQVIA diagnostics."""
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -18,7 +19,6 @@ async def test_entry_diagnostics(
) -> None:
"""Test config entry diagnostics."""
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -6,7 +6,7 @@ from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.kostal_plenticore.coordinator import Plenticore
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
from tests.common import ANY, MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@ -54,6 +54,8 @@ async def test_entry_diagnostics(
"source": "user",
"unique_id": None,
"disabled_by": None,
"created_at": ANY,
"modified_at": ANY,
},
"client": {
"version": "api_version='0.2.0' hostname='scb' name='PUCK RESTful API' sw_version='01.16.05025'",

View File

@ -3,6 +3,7 @@
from unittest.mock import patch
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.lacrosse_view import DOMAIN
from homeassistant.core import HomeAssistant
@ -32,7 +33,6 @@ async def test_entry_diagnostics(
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -25,4 +26,4 @@ async def test_entry_diagnostics(
result = await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -3,6 +3,7 @@
from unittest.mock import patch
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.melcloud.const import DOMAIN
from homeassistant.core import HomeAssistant
@ -36,4 +37,4 @@ async def test_get_config_entry_diagnostics(
diagnostics = await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
)
assert diagnostics == snapshot
assert diagnostics == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -42,4 +42,11 @@ async def test_entry_diagnostics(
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=paths("info.data.token.expires_at", "info.entry_id"))
) == snapshot(
exclude=paths(
"info.data.token.expires_at",
"info.entry_id",
"info.created_at",
"info.modified_at",
)
)

View File

@ -1,6 +1,7 @@
"""Test NextDNS diagnostics."""
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -18,4 +19,6 @@ async def test_entry_diagnostics(
"""Test config entry diagnostics."""
entry = await init_integration(hass)
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot(
exclude=props("created_at", "modified_at")
)

View File

@ -4,6 +4,7 @@ from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.notion import DOMAIN
from homeassistant.core import HomeAssistant
from tests.common import ANY
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@ -33,6 +34,8 @@ async def test_entry_diagnostics(
"source": "user",
"unique_id": REDACTED,
"disabled_by": None,
"created_at": ANY,
"modified_at": ANY,
},
"data": {
"bridges": [

View File

@ -1,6 +1,7 @@
"""Test ONVIF diagnostics."""
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -19,4 +20,6 @@ async def test_diagnostics(
entry, _, _ = await setup_onvif_integration(hass)
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot(
exclude=props("created_at", "modified_at")
)

View File

@ -4,6 +4,7 @@ from homeassistant.components.diagnostics import REDACTED
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.common import ANY
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@ -35,6 +36,8 @@ async def test_entry_diagnostics(
"source": "user",
"unique_id": REDACTED,
"disabled_by": None,
"created_at": ANY,
"modified_at": ANY,
},
"data": {
"protection_window": {

View File

@ -63,4 +63,4 @@ async def test_entry_diagnostics(
hass, hass_client, mock_config_entry
)
assert result == snapshot(exclude=props("entry_id"))
assert result == snapshot(exclude=props("entry_id", "created_at", "modified_at"))

View File

@ -1,6 +1,7 @@
"""Test pi_hole component."""
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components import pi_hole
from homeassistant.core import HomeAssistant
@ -28,4 +29,6 @@ async def test_diagnostics(
await hass.async_block_till_done()
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot(
exclude=props("created_at", "modified_at")
)

View File

@ -72,5 +72,12 @@ async def test_entry_diagnostics(
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_entry
) == snapshot(
exclude=props("entry_id", "last_changed", "last_reported", "last_updated")
exclude=props(
"entry_id",
"last_changed",
"last_reported",
"last_updated",
"created_at",
"modified_at",
)
)

View File

@ -3,6 +3,7 @@
from homeassistant.components.diagnostics import REDACTED
from homeassistant.core import HomeAssistant
from tests.common import ANY
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@ -34,6 +35,8 @@ async def test_entry_diagnostics(
"source": "user",
"unique_id": REDACTED,
"disabled_by": None,
"created_at": ANY,
"modified_at": ANY,
},
"data": {
"fields": [

View File

@ -2,6 +2,7 @@
from regenmaschine.errors import RainMachineError
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -17,10 +18,9 @@ async def test_entry_diagnostics(
snapshot: SnapshotAssertion,
) -> None:
"""Test config entry diagnostics."""
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("created_at", "modified_at"))
async def test_entry_diagnostics_failed_controller_diagnostics(
@ -33,7 +33,6 @@ async def test_entry_diagnostics_failed_controller_diagnostics(
) -> None:
"""Test config entry diagnostics when the controller diagnostics API call fails."""
controller.diagnostics.current.side_effect = RainMachineError
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -5,6 +5,7 @@ from homeassistant.core import HomeAssistant
from .conftest import TEST_SERVICE_ID
from tests.common import ANY
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@ -30,6 +31,8 @@ async def test_entry_diagnostics(
"source": "user",
"unique_id": REDACTED,
"disabled_by": None,
"created_at": ANY,
"modified_at": ANY,
},
"data": [
{

View File

@ -1,6 +1,7 @@
"""Test Ridwell diagnostics."""
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -16,7 +17,6 @@ async def test_entry_diagnostics(
snapshot: SnapshotAssertion,
) -> None:
"""Test config entry diagnostics."""
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -16,6 +16,7 @@ from .const import (
SAMPLE_DEVICE_INFO_WIFI,
)
from tests.common import ANY
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@ -29,6 +30,7 @@ async def test_entry_diagnostics(
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"entry": {
"created_at": ANY,
"data": {
"host": "fake_host",
"ip_address": "test",
@ -43,6 +45,7 @@ async def test_entry_diagnostics(
"domain": "samsungtv",
"entry_id": "123456",
"minor_version": 2,
"modified_at": ANY,
"options": {},
"pref_disable_new_entities": False,
"pref_disable_polling": False,
@ -65,6 +68,7 @@ async def test_entry_diagnostics_encrypted(
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"entry": {
"created_at": ANY,
"data": {
"host": "fake_host",
"ip_address": "test",
@ -80,6 +84,7 @@ async def test_entry_diagnostics_encrypted(
"domain": "samsungtv",
"entry_id": "123456",
"minor_version": 2,
"modified_at": ANY,
"options": {},
"pref_disable_new_entities": False,
"pref_disable_polling": False,
@ -102,6 +107,7 @@ async def test_entry_diagnostics_encrypte_offline(
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"entry": {
"created_at": ANY,
"data": {
"host": "fake_host",
"ip_address": "test",
@ -116,6 +122,7 @@ async def test_entry_diagnostics_encrypte_offline(
"domain": "samsungtv",
"entry_id": "123456",
"minor_version": 2,
"modified_at": ANY,
"options": {},
"pref_disable_new_entities": False,
"pref_disable_polling": False,

View File

@ -4,6 +4,7 @@ from unittest.mock import DEFAULT, patch
from screenlogicpy import ScreenLogicGateway
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
@ -56,4 +57,4 @@ async def test_diagnostics(
hass, hass_client, mock_config_entry
)
assert diag == snapshot
assert diag == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -1,246 +1,5 @@
# serializer version: 1
# name: test_diagnostics
dict({
'modes': dict({
'auto': dict({
'fanLevels': list([
'quiet',
'low',
'medium',
]),
'horizontalSwing': list([
'stopped',
'fixedLeft',
'fixedCenterLeft',
]),
'light': list([
'on',
'off',
]),
'swing': list([
'stopped',
'fixedTop',
'fixedMiddleTop',
]),
'temperatures': dict({
'C': dict({
'isNative': True,
'values': list([
10,
16,
17,
18,
19,
20,
]),
}),
'F': dict({
'isNative': False,
'values': list([
64,
66,
68,
]),
}),
}),
}),
'cool': dict({
'fanLevels': list([
'quiet',
'low',
'medium',
]),
'horizontalSwing': list([
'stopped',
'fixedLeft',
'fixedCenterLeft',
]),
'light': list([
'on',
'off',
]),
'swing': list([
'stopped',
'fixedTop',
'fixedMiddleTop',
]),
'temperatures': dict({
'C': dict({
'isNative': True,
'values': list([
10,
16,
17,
18,
19,
20,
]),
}),
'F': dict({
'isNative': False,
'values': list([
64,
66,
68,
]),
}),
}),
}),
'dry': dict({
'horizontalSwing': list([
'stopped',
'fixedLeft',
'fixedCenterLeft',
]),
'light': list([
'on',
'off',
]),
'swing': list([
'stopped',
'fixedTop',
'fixedMiddleTop',
]),
'temperatures': dict({
'C': dict({
'isNative': True,
'values': list([
10,
16,
17,
18,
19,
20,
]),
}),
'F': dict({
'isNative': False,
'values': list([
64,
66,
68,
]),
}),
}),
}),
'fan': dict({
'fanLevels': list([
'quiet',
'low',
'medium',
]),
'horizontalSwing': list([
'stopped',
'fixedLeft',
'fixedCenterLeft',
]),
'light': list([
'on',
'off',
]),
'swing': list([
'stopped',
'fixedTop',
'fixedMiddleTop',
]),
'temperatures': dict({
}),
}),
'heat': dict({
'fanLevels': list([
'quiet',
'low',
'medium',
]),
'horizontalSwing': list([
'stopped',
'fixedLeft',
'fixedCenterLeft',
]),
'light': list([
'on',
'off',
]),
'swing': list([
'stopped',
'fixedTop',
'fixedMiddleTop',
]),
'temperatures': dict({
'C': dict({
'isNative': True,
'values': list([
10,
16,
17,
18,
19,
20,
]),
}),
'F': dict({
'isNative': False,
'values': list([
63,
64,
66,
]),
}),
}),
}),
}),
})
# ---
# name: test_diagnostics.1
dict({
'low': 'low',
'medium': 'medium',
'quiet': 'quiet',
})
# ---
# name: test_diagnostics.2
dict({
'fixedmiddletop': 'fixedMiddleTop',
'fixedtop': 'fixedTop',
'stopped': 'stopped',
})
# ---
# name: test_diagnostics.3
dict({
'fixedcenterleft': 'fixedCenterLeft',
'fixedleft': 'fixedLeft',
'stopped': 'stopped',
})
# ---
# name: test_diagnostics.4
dict({
'fanlevel': 'low',
'horizontalswing': 'stopped',
'light': 'on',
'mode': 'heat',
'on': True,
'swing': 'stopped',
'targettemperature': 21,
'temperatureunit': 'c',
})
# ---
# name: test_diagnostics.5
dict({
'fanlevel': 'high',
'horizontalswing': 'stopped',
'light': 'on',
'mode': 'cool',
'on': True,
'swing': 'stopped',
'targettemperature': 21,
'temperatureunit': 'c',
})
# ---
# name: test_diagnostics.6
dict({
})
# ---
# name: test_diagnostics[full_snapshot]
dict({
'AAZZAAZZ': dict({
'ac_states': dict({

View File

@ -3,6 +3,7 @@
from __future__ import annotations
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -10,8 +11,6 @@ from homeassistant.core import HomeAssistant
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
EXCLUDE_ATTRIBUTES = {"full_features"}
async def test_diagnostics(
hass: HomeAssistant,
@ -24,16 +23,6 @@ async def test_diagnostics(
diag = await get_diagnostics_for_config_entry(hass, hass_client, entry)
assert diag["ABC999111"]["full_capabilities"] == snapshot
assert diag["ABC999111"]["fan_modes_translated"] == snapshot
assert diag["ABC999111"]["swing_modes_translated"] == snapshot
assert diag["ABC999111"]["horizontal_swing_modes_translated"] == snapshot
assert diag["ABC999111"]["smart_low_state"] == snapshot
assert diag["ABC999111"]["smart_high_state"] == snapshot
assert diag["ABC999111"]["pure_conf"] == snapshot
def limit_attrs(prop, path):
exclude_attrs = EXCLUDE_ATTRIBUTES
return prop in exclude_attrs
assert diag == snapshot(name="full_snapshot", exclude=limit_attrs)
assert diag == snapshot(
exclude=props("full_features", "created_at", "modified_at"),
)

View File

@ -3,6 +3,7 @@
from homeassistant.components.diagnostics import REDACTED
from homeassistant.core import HomeAssistant
from tests.common import ANY
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@ -28,6 +29,8 @@ async def test_entry_diagnostics(
"source": "user",
"unique_id": REDACTED,
"disabled_by": None,
"created_at": ANY,
"modified_at": ANY,
},
"subscription_data": {
"12345": {

View File

@ -8,6 +8,7 @@ from homeassistant.core import HomeAssistant
from . import init_integration
from .consts import DUMMY_WATER_HEATER_DEVICE
from tests.common import ANY
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@ -64,5 +65,7 @@ async def test_diagnostics(
"source": "user",
"unique_id": "switcher_kis",
"disabled_by": None,
"created_at": ANY,
"modified_at": ANY,
},
}

View File

@ -23,4 +23,4 @@ async def test_diagnostics(
"""Test diagnostics."""
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_added_config_entry
) == snapshot(exclude=props("last_update", "entry_id"))
) == snapshot(exclude=props("last_update", "entry_id", "created_at", "modified_at"))

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import pytest
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -21,4 +22,4 @@ async def test_entry_diagnostics(
) -> None:
"""Test config entry diagnostics."""
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -27,4 +28,4 @@ async def test_entry_diagnostics(
hass, hass_client, mock_config_entry
)
assert result == snapshot
assert result == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -3,6 +3,7 @@
from collections.abc import Awaitable, Callable
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -26,4 +27,6 @@ async def test_diagnostics(
await setup_integration()
entry = hass.config_entries.async_entries(DOMAIN)[0]
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot(
exclude=props("created_at", "modified_at")
)

View File

@ -2,6 +2,7 @@
import pytest
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.unifi.const import (
CONF_ALLOW_BANDWIDTH_SENSORS,
@ -125,7 +126,6 @@ async def test_entry_diagnostics(
snapshot: SnapshotAssertion,
) -> None:
"""Test config entry diagnostics."""
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry_setup)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry_setup
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -4,6 +4,7 @@ from aiohttp.test_utils import TestClient
from freezegun import freeze_time
import pytest
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.auth.models import Credentials
from homeassistant.components.utility_meter.const import DOMAIN
@ -45,11 +46,6 @@ def _get_test_client_generator(
return auth_client
def limit_diagnostic_attrs(prop, path) -> bool:
"""Mark attributes to exclude from diagnostic snapshot."""
return prop in {"entry_id"}
@freeze_time("2024-04-06 00:00:00+00:00")
@pytest.mark.usefixtures("socket_enabled")
async def test_diagnostics(
@ -125,4 +121,4 @@ async def test_diagnostics(
hass, _get_test_client_generator(hass, aiohttp_client, new_token), config_entry
)
assert diag == snapshot(exclude=limit_diagnostic_attrs)
assert diag == snapshot(exclude=props("entry_id", "created_at", "modified_at"))

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from syrupy import SnapshotAssertion
from syrupy.filters import props
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -24,7 +25,6 @@ async def test_entry_diagnostics(
await init_integration(hass, mock_config_entry)
assert (
await get_diagnostics_for_config_entry(hass, hass_client, mock_config_entry)
== snapshot()
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -3,6 +3,7 @@
from unittest.mock import MagicMock
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -21,4 +22,4 @@ async def test_diagnostics(
hass, hass_client, mock_vicare_gas_boiler
)
assert diag == snapshot
assert diag == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -19,4 +19,4 @@ async def test_entry_diagnostics(
"""Test config entry diagnostics."""
assert await get_diagnostics_for_config_entry(
hass, hass_client, config_entry
) == snapshot(exclude=props("entry_id"))
) == snapshot(exclude=props("entry_id", "created_at", "modified_at"))

View File

@ -1,6 +1,7 @@
"""Tests for the diagnostics data provided by the Webmin integration."""
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
@ -16,9 +17,6 @@ async def test_diagnostics(
snapshot: SnapshotAssertion,
) -> None:
"""Test diagnostics."""
assert (
await get_diagnostics_for_config_entry(
hass, hass_client, await async_init_integration(hass)
)
== snapshot
)
assert await get_diagnostics_for_config_entry(
hass, hass_client, await async_init_integration(hass)
) == snapshot(exclude=props("created_at", "modified_at"))

View File

@ -58,5 +58,7 @@ async def test_diagnostics(
"source": "user",
"unique_id": REDACTED,
"disabled_by": None,
"created_at": entry.created_at.isoformat(),
"modified_at": entry.modified_at.isoformat(),
},
}

View File

@ -29,4 +29,4 @@ async def test_entry_diagnostics(
result = await get_diagnostics_for_config_entry(hass, hass_client, mock_entry)
assert result == snapshot(exclude=props("entry_id"))
assert result == snapshot(exclude=props("entry_id", "created_at", "modified_at"))

View File

@ -1,12 +1,14 @@
# serializer version: 1
# name: test_as_dict
dict({
'created_at': '2024-02-14T12:00:00+00:00',
'data': dict({
}),
'disabled_by': None,
'domain': 'test',
'entry_id': 'mock-entry',
'minor_version': 1,
'modified_at': '2024-02-14T12:00:00+00:00',
'options': dict({
}),
'pref_disable_new_entities': False,

View File

@ -137,7 +137,8 @@ class HomeAssistantSnapshotSerializer(AmberDataSerializer):
@classmethod
def _serializable_config_entry(cls, data: ConfigEntry) -> SerializableData:
"""Prepare a Home Assistant config entry for serialization."""
return ConfigEntrySnapshot(data.as_dict() | {"entry_id": ANY})
entry = ConfigEntrySnapshot(data.as_dict() | {"entry_id": ANY})
return cls._remove_created_and_modified_at(entry)
@classmethod
def _serializable_device_registry_entry(

View File

@ -10,6 +10,7 @@ import logging
from typing import Any
from unittest.mock import ANY, AsyncMock, Mock, patch
from freezegun import freeze_time
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
@ -51,6 +52,7 @@ from .common import (
async_capture_events,
async_fire_time_changed,
async_get_persistent_notifications,
flush_store,
mock_config_flow,
mock_integration,
mock_platform,
@ -912,6 +914,7 @@ async def test_saving_and_loading(
assert orig.as_dict() == loaded.as_dict()
@freeze_time("2024-02-14 12:00:00")
async def test_as_dict(snapshot: SnapshotAssertion) -> None:
"""Test ConfigEntry.as_dict."""
@ -1251,8 +1254,11 @@ async def test_loading_default_config(hass: HomeAssistant) -> None:
assert len(manager.async_entries()) == 0
async def test_updating_entry_data(manager: config_entries.ConfigEntries) -> None:
async def test_updating_entry_data(
manager: config_entries.ConfigEntries, freezer: FrozenDateTimeFactory
) -> None:
"""Test that we can update an entry data."""
created = dt_util.utcnow()
entry = MockConfigEntry(
domain="test",
data={"first": True},
@ -1260,17 +1266,32 @@ async def test_updating_entry_data(manager: config_entries.ConfigEntries) -> Non
)
entry.add_to_manager(manager)
assert len(manager.async_entries()) == 1
assert manager.async_entries()[0] == entry
assert entry.created_at == created
assert entry.modified_at == created
freezer.tick()
assert manager.async_update_entry(entry) is False
assert entry.data == {"first": True}
assert entry.modified_at == created
assert manager.async_entries()[0].modified_at == created
freezer.tick()
modified = dt_util.utcnow()
assert manager.async_update_entry(entry, data={"second": True}) is True
assert entry.data == {"second": True}
assert entry.modified_at == modified
assert manager.async_entries()[0].modified_at == modified
async def test_updating_entry_system_options(
manager: config_entries.ConfigEntries,
manager: config_entries.ConfigEntries, freezer: FrozenDateTimeFactory
) -> None:
"""Test that we can update an entry data."""
created = dt_util.utcnow()
entry = MockConfigEntry(
domain="test",
data={"first": True},
@ -1281,6 +1302,11 @@ async def test_updating_entry_system_options(
assert entry.pref_disable_new_entities is True
assert entry.pref_disable_polling is False
assert entry.created_at == created
assert entry.modified_at == created
freezer.tick()
modified = dt_util.utcnow()
manager.async_update_entry(
entry, pref_disable_new_entities=False, pref_disable_polling=True
@ -1288,6 +1314,8 @@ async def test_updating_entry_system_options(
assert entry.pref_disable_new_entities is False
assert entry.pref_disable_polling is True
assert entry.created_at == created
assert entry.modified_at == modified
async def test_update_entry_options_and_trigger_listener(
@ -5908,3 +5936,67 @@ async def test_config_entry_late_platform_setup(
"entry_id test2 cannot forward setup for light because it is "
"not loaded in the ConfigEntryState.NOT_LOADED state"
) not in caplog.text
@pytest.mark.parametrize("load_registries", [False])
async def test_migration_from_1_2(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
"""Test migration from version 1.2."""
hass_storage[config_entries.STORAGE_KEY] = {
"version": 1,
"minor_version": 2,
"data": {
"entries": [
{
"data": {},
"disabled_by": None,
"domain": "sun",
"entry_id": "0a8bd02d0d58c7debf5daf7941c9afe2",
"minor_version": 1,
"options": {},
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"source": "import",
"title": "Sun",
"unique_id": None,
"version": 1,
},
]
},
}
manager = config_entries.ConfigEntries(hass, {})
await manager.async_initialize()
# Test data was loaded
entries = manager.async_entries()
assert len(entries) == 1
# Check we store migrated data
await flush_store(manager._store)
assert hass_storage[config_entries.STORAGE_KEY] == {
"version": config_entries.STORAGE_VERSION,
"minor_version": config_entries.STORAGE_VERSION_MINOR,
"key": config_entries.STORAGE_KEY,
"data": {
"entries": [
{
"created_at": "1970-01-01T00:00:00+00:00",
"data": {},
"disabled_by": None,
"domain": "sun",
"entry_id": "0a8bd02d0d58c7debf5daf7941c9afe2",
"minor_version": 1,
"modified_at": "1970-01-01T00:00:00+00:00",
"options": {},
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"source": "import",
"title": "Sun",
"unique_id": None,
"version": 1,
},
]
},
}