diff --git a/homeassistant/components/recorder/auto_repairs/schema.py b/homeassistant/components/recorder/auto_repairs/schema.py index 3952f76bddd03..2a09324dfe182 100644 --- a/homeassistant/components/recorder/auto_repairs/schema.py +++ b/homeassistant/components/recorder/auto_repairs/schema.py @@ -12,7 +12,7 @@ from sqlalchemy.orm.attributes import InstrumentedAttribute from ..const import SupportedDialect -from ..db_schema import DOUBLE_PRECISION_TYPE_SQL, DOUBLE_TYPE +from ..db_schema import DOUBLE_PRECISION_TYPE_SQL, DOUBLE_TYPE, MYSQL_COLLATE from ..util import session_scope if TYPE_CHECKING: @@ -105,12 +105,13 @@ def _validate_table_schema_has_correct_collation( or dialect_kwargs.get("mariadb_collate") or connection.dialect._fetch_setting(connection, "collation_server") # type: ignore[attr-defined] # noqa: SLF001 ) - if collate and collate != "utf8mb4_unicode_ci": + if collate and collate != MYSQL_COLLATE: _LOGGER.debug( - "Database %s collation is not utf8mb4_unicode_ci", + "Database %s collation is not %s", table, + MYSQL_COLLATE, ) - schema_errors.add(f"{table}.utf8mb4_unicode_ci") + schema_errors.add(f"{table}.{MYSQL_COLLATE}") return schema_errors @@ -240,7 +241,7 @@ def correct_db_schema_utf8( table_name = table_object.__tablename__ if ( f"{table_name}.4-byte UTF-8" in schema_errors - or f"{table_name}.utf8mb4_unicode_ci" in schema_errors + or f"{table_name}.{MYSQL_COLLATE}" in schema_errors ): from ..migration import ( # noqa: PLC0415 _correct_table_character_set_and_collation, diff --git a/homeassistant/components/recorder/db_schema.py b/homeassistant/components/recorder/db_schema.py index 836924fc86049..65de7e853a370 100644 --- a/homeassistant/components/recorder/db_schema.py +++ b/homeassistant/components/recorder/db_schema.py @@ -71,7 +71,7 @@ class LegacyBase(DeclarativeBase): """Base class for tables, used for schema migration.""" -SCHEMA_VERSION = 52 +SCHEMA_VERSION = 53 _LOGGER = logging.getLogger(__name__) @@ -128,7 +128,7 @@ class LegacyBase(DeclarativeBase): LEGACY_MAX_LENGTH_EVENT_CONTEXT_ID: Final = 36 CONTEXT_ID_BIN_MAX_LENGTH = 16 -MYSQL_COLLATE = "utf8mb4_unicode_ci" +MYSQL_COLLATE = "utf8mb4_bin" MYSQL_DEFAULT_CHARSET = "utf8mb4" MYSQL_ENGINE = "InnoDB" diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index 47d7ae58c3b50..c8548dc2a97d9 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -1361,7 +1361,7 @@ def _apply_update(self) -> None: class _SchemaVersion21Migrator(_SchemaVersionMigrator, target_version=21): def _apply_update(self) -> None: """Version specific update method.""" - # Try to change the character set of the statistic_meta table + # Try to change the character set of events, states and statistics_meta tables if self.engine.dialect.name == SupportedDialect.MYSQL: for table in ("events", "states", "statistics_meta"): _correct_table_character_set_and_collation(table, self.session_maker) @@ -2125,6 +2125,23 @@ def _apply_update_postgresql_sqlite(self) -> None: ) +class _SchemaVersion53Migrator(_SchemaVersionMigrator, target_version=53): + def _apply_update(self) -> None: + """Version specific update method.""" + # Try to change the character set of events, states and statistics_meta tables + if self.engine.dialect.name == SupportedDialect.MYSQL: + for table in ( + "events", + "event_data", + "states", + "state_attributes", + "statistics", + "statistics_meta", + "statistics_short_term", + ): + _correct_table_character_set_and_collation(table, self.session_maker) + + def _migrate_statistics_columns_to_timestamp_removing_duplicates( hass: HomeAssistant, instance: Recorder, @@ -2167,8 +2184,10 @@ def _correct_table_character_set_and_collation( """Correct issues detected by validate_db_schema.""" # Attempt to convert the table to utf8mb4 _LOGGER.warning( - "Updating character set and collation of table %s to utf8mb4. %s", + "Updating table %s to character set %s and collation %s. %s", table, + MYSQL_DEFAULT_CHARSET, + MYSQL_COLLATE, MIGRATION_NOTE_MINUTES, ) with ( diff --git a/homeassistant/components/tesla_wall_connector/manifest.json b/homeassistant/components/tesla_wall_connector/manifest.json index 69ef08fa0fcbf..e01e6e5a5d823 100644 --- a/homeassistant/components/tesla_wall_connector/manifest.json +++ b/homeassistant/components/tesla_wall_connector/manifest.json @@ -20,5 +20,5 @@ "documentation": "https://www.home-assistant.io/integrations/tesla_wall_connector", "iot_class": "local_polling", "loggers": ["tesla_wall_connector"], - "requirements": ["tesla-wall-connector==1.0.2"] + "requirements": ["tesla-wall-connector==1.1.0"] } diff --git a/homeassistant/components/tuya/diagnostics.py b/homeassistant/components/tuya/diagnostics.py index b71a17f68a6c5..85fce1f9ba82b 100644 --- a/homeassistant/components/tuya/diagnostics.py +++ b/homeassistant/components/tuya/diagnostics.py @@ -2,9 +2,7 @@ from __future__ import annotations -from contextlib import suppress -import json -from typing import Any, cast +from typing import Any from tuya_sharing import CustomerDevice @@ -101,30 +99,20 @@ def _async_device_as_dict( data["status"][dpcode] = REDACTED continue - with suppress(ValueError, TypeError): - value = json.loads(value) data["status"][dpcode] = value # Gather Tuya functions for function in device.function.values(): - value = function.values - with suppress(ValueError, TypeError, AttributeError): - value = json.loads(cast(str, function.values)) - data["function"][function.code] = { "type": function.type, - "value": value, + "value": function.values, } # Gather Tuya status ranges for status_range in device.status_range.values(): - value = status_range.values - with suppress(ValueError, TypeError, AttributeError): - value = json.loads(status_range.values) - data["status_range"][status_range.code] = { "type": status_range.type, - "value": value, + "value": status_range.values, } # Gather information how this Tuya device is represented in Home Assistant diff --git a/homeassistant/components/tuya/light.py b/homeassistant/components/tuya/light.py index 0f508e01302cd..485468bda6a52 100644 --- a/homeassistant/components/tuya/light.py +++ b/homeassistant/components/tuya/light.py @@ -24,6 +24,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.util import color as color_util +from homeassistant.util.json import json_loads_object from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode, DPType, WorkMode @@ -499,11 +500,11 @@ def __init__( values = self.device.status_range[dpcode].values # Fetch color data type information - if function_data := json.loads(values): + if function_data := json_loads_object(values): self._color_data_type = ColorTypeData( - h_type=IntegerTypeData(dpcode, **function_data["h"]), - s_type=IntegerTypeData(dpcode, **function_data["s"]), - v_type=IntegerTypeData(dpcode, **function_data["v"]), + h_type=IntegerTypeData(dpcode, **cast(dict, function_data["h"])), + s_type=IntegerTypeData(dpcode, **cast(dict, function_data["s"])), + v_type=IntegerTypeData(dpcode, **cast(dict, function_data["v"])), ) else: # If no type is found, use a default one @@ -770,12 +771,12 @@ def _get_color_data(self) -> ColorData | None: if not (status_data := self.device.status[self._color_data_dpcode]): return None - if not (status := json.loads(status_data)): + if not (status := json_loads_object(status_data)): return None return ColorData( type_data=self._color_data_type, - h_value=status["h"], - s_value=status["s"], - v_value=status["v"], + h_value=cast(int, status["h"]), + s_value=cast(int, status["s"]), + v_value=cast(int, status["v"]), ) diff --git a/homeassistant/components/tuya/models.py b/homeassistant/components/tuya/models.py index 0331812d06674..6327f3b46d1b2 100644 --- a/homeassistant/components/tuya/models.py +++ b/homeassistant/components/tuya/models.py @@ -5,12 +5,11 @@ from abc import ABC, abstractmethod import base64 from dataclasses import dataclass -import json -from typing import Any, Literal, Self, overload +from typing import Any, Literal, Self, cast, overload from tuya_sharing import CustomerDevice -from homeassistant.util.json import json_loads +from homeassistant.util.json import json_loads, json_loads_object from .const import DPCode, DPType from .util import parse_dptype, remap_value @@ -88,7 +87,7 @@ def remap_value_from( @classmethod def from_json(cls, dpcode: DPCode, data: str) -> Self | None: """Load JSON string and return a IntegerTypeData object.""" - if not (parsed := json.loads(data)): + if not (parsed := cast(dict[str, Any] | None, json_loads_object(data))): return None return cls( @@ -111,9 +110,9 @@ class BitmapTypeInformation(TypeInformation): @classmethod def from_json(cls, dpcode: DPCode, data: str) -> Self | None: """Load JSON string and return a BitmapTypeInformation object.""" - if not (parsed := json.loads(data)): + if not (parsed := json_loads_object(data)): return None - return cls(dpcode, **parsed) + return cls(dpcode, **cast(dict[str, list[str]], parsed)) @dataclass @@ -125,9 +124,9 @@ class EnumTypeData(TypeInformation): @classmethod def from_json(cls, dpcode: DPCode, data: str) -> Self | None: """Load JSON string and return a EnumTypeData object.""" - if not (parsed := json.loads(data)): + if not (parsed := json_loads_object(data)): return None - return cls(dpcode, **parsed) + return cls(dpcode, **cast(dict[str, list[str]], parsed)) _TYPE_INFORMATION_MAPPINGS: dict[DPType, type[TypeInformation]] = { diff --git a/requirements_all.txt b/requirements_all.txt index 51afa65165d7a..68247ce71fd24 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2948,7 +2948,7 @@ tesla-fleet-api==1.2.5 tesla-powerwall==0.5.2 # homeassistant.components.tesla_wall_connector -tesla-wall-connector==1.0.2 +tesla-wall-connector==1.1.0 # homeassistant.components.teslemetry teslemetry-stream==0.7.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 16735a4eb3aec..679a930ff46dd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2433,7 +2433,7 @@ tesla-fleet-api==1.2.5 tesla-powerwall==0.5.2 # homeassistant.components.tesla_wall_connector -tesla-wall-connector==1.0.2 +tesla-wall-connector==1.1.0 # homeassistant.components.teslemetry teslemetry-stream==0.7.10 diff --git a/tests/components/lifx/test_config_flow.py b/tests/components/lifx/test_config_flow.py index aecc71b07e81b..dbea7c437f9d0 100644 --- a/tests/components/lifx/test_config_flow.py +++ b/tests/components/lifx/test_config_flow.py @@ -1,9 +1,10 @@ """Tests for the lifx integration config flow.""" +from collections.abc import Generator from ipaddress import ip_address import socket from typing import Any -from unittest.mock import patch +from unittest.mock import AsyncMock, patch import pytest @@ -44,6 +45,15 @@ from tests.common import MockConfigEntry +@pytest.fixture(autouse=True) +def mock_setup_entry() -> Generator[AsyncMock]: + """Override async_setup_entry.""" + with patch( + "homeassistant.components.lifx.async_setup_entry", return_value=True + ) as mock_setup_entry: + yield mock_setup_entry + + async def test_discovery(hass: HomeAssistant) -> None: """Test setting up discovery.""" with _patch_discovery(), _patch_config_flow_try_connect(): @@ -591,6 +601,7 @@ async def test_refuse_relays(hass: HomeAssistant) -> None: assert result2["errors"] == {"base": "cannot_connect"} +@pytest.mark.parametrize("mock_setup_entry", [None]) # Disable the autouse fixture async def test_suggested_area( hass: HomeAssistant, area_registry: ar.AreaRegistry, diff --git a/tests/components/lifx/test_light.py b/tests/components/lifx/test_light.py index dff43bc21b6bf..636ec87dee4d4 100644 --- a/tests/components/lifx/test_light.py +++ b/tests/components/lifx/test_light.py @@ -1619,9 +1619,10 @@ async def test_transitions_brightness_only(hass: HomeAssistant) -> None: bulb.get_color.reset_mock() # Ensure we force an update after the transition - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=5)) - await hass.async_block_till_done() - assert len(bulb.get_color.calls) == 2 + with _patch_discovery(device=bulb): + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=5)) + await hass.async_block_till_done() + assert len(bulb.get_color.calls) == 2 async def test_transitions_color_bulb(hass: HomeAssistant) -> None: @@ -1714,9 +1715,10 @@ async def test_transitions_color_bulb(hass: HomeAssistant) -> None: bulb.get_color.reset_mock() # Ensure we force an update after the transition - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=5)) - await hass.async_block_till_done() - assert len(bulb.get_color.calls) == 2 + with _patch_discovery(device=bulb): + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=5)) + await hass.async_block_till_done() + assert len(bulb.get_color.calls) == 2 bulb.set_power.reset_mock() bulb.set_color.reset_mock() diff --git a/tests/components/onkyo/test_config_flow.py b/tests/components/onkyo/test_config_flow.py index 8ea8febf7c377..ed647ec6882d7 100644 --- a/tests/components/onkyo/test_config_flow.py +++ b/tests/components/onkyo/test_config_flow.py @@ -32,7 +32,11 @@ def _receiver_display_name(receiver_info: ReceiverInfo) -> str: return f"{receiver_info.model_name} ({receiver_info.host})" -@pytest.mark.usefixtures("mock_setup_entry") +@pytest.fixture(autouse=True) +def mock_setup_entry(mock_setup_entry) -> None: + """Use async_setup_entry fixture in all tests.""" + + async def test_manual(hass: HomeAssistant) -> None: """Test successful manual.""" result = await hass.config_entries.flow.async_init( @@ -79,7 +83,6 @@ async def test_manual(hass: HomeAssistant) -> None: (mock_discovery([RECEIVER_INFO]), "cannot_connect"), ], ) -@pytest.mark.usefixtures("mock_setup_entry") async def test_manual_recoverable_error( hass: HomeAssistant, mock_discovery: AbstractContextManager, error_reason: str ) -> None: @@ -129,7 +132,6 @@ async def test_manual_recoverable_error( assert result["title"] == RECEIVER_INFO_2.model_name -@pytest.mark.usefixtures("mock_setup_entry") async def test_manual_error( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: @@ -158,7 +160,6 @@ async def test_manual_error( assert result["reason"] == "already_configured" -@pytest.mark.usefixtures("mock_setup_entry") async def test_eiscp_discovery( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: @@ -214,7 +215,6 @@ async def test_eiscp_discovery( (mock_discovery([RECEIVER_INFO]), "no_devices_found"), ], ) -@pytest.mark.usefixtures("mock_setup_entry") async def test_eiscp_discovery_error( hass: HomeAssistant, mock_config_entry: MockConfigEntry, @@ -291,7 +291,6 @@ async def test_eiscp_discovery_replace_ignored_entry( assert len(hass.config_entries.async_entries(DOMAIN)) == 1 -@pytest.mark.usefixtures("mock_setup_entry") async def test_ssdp_discovery( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: @@ -343,7 +342,6 @@ async def test_ssdp_discovery( (f"http://{RECEIVER_INFO.host}:8080", nullcontext(), "already_configured"), ], ) -@pytest.mark.usefixtures("mock_setup_entry") async def test_ssdp_discovery_error( hass: HomeAssistant, mock_config_entry: MockConfigEntry, @@ -371,7 +369,6 @@ async def test_ssdp_discovery_error( assert result["reason"] == error_reason -@pytest.mark.usefixtures("mock_setup_entry") async def test_configure(hass: HomeAssistant) -> None: """Test receiver configure.""" result = await hass.config_entries.flow.async_init( @@ -442,7 +439,6 @@ async def test_configure(hass: HomeAssistant) -> None: } -@pytest.mark.usefixtures("mock_setup_entry") async def test_reconfigure( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: @@ -479,7 +475,6 @@ async def test_reconfigure( assert mock_config_entry.options[option] == option_value -@pytest.mark.usefixtures("mock_setup_entry") async def test_reconfigure_error( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: @@ -504,7 +499,6 @@ async def test_reconfigure_error( assert mock_config_entry.unique_id == old_unique_id -@pytest.mark.usefixtures("mock_setup_entry") @pytest.mark.parametrize( "ignore_missing_translations", [ diff --git a/tests/components/recorder/auto_repairs/events/test_schema.py b/tests/components/recorder/auto_repairs/events/test_schema.py index 91f5bd50298eb..277749ce18a28 100644 --- a/tests/components/recorder/auto_repairs/events/test_schema.py +++ b/tests/components/recorder/auto_repairs/events/test_schema.py @@ -82,7 +82,7 @@ async def test_validate_db_schema_fix_utf8_issue_event_data( in caplog.text ) assert ( - "Updating character set and collation of table event_data to utf8mb4" + "Updating table event_data to character set utf8mb4 and collation utf8mb4_bin" in caplog.text ) @@ -103,7 +103,7 @@ async def test_validate_db_schema_fix_collation_issue( with ( patch( "homeassistant.components.recorder.auto_repairs.schema._validate_table_schema_has_correct_collation", - return_value={"events.utf8mb4_unicode_ci"}, + return_value={"events.utf8mb4_bin"}, ), ): async with async_test_recorder(hass): @@ -111,9 +111,10 @@ async def test_validate_db_schema_fix_collation_issue( assert "Schema validation failed" not in caplog.text assert ( - "Database is about to correct DB schema errors: events.utf8mb4_unicode_ci" + "Database is about to correct DB schema errors: events.utf8mb4_bin" in caplog.text ) assert ( - "Updating character set and collation of table events to utf8mb4" in caplog.text + "Updating table events to character set utf8mb4 and collation utf8mb4_bin" + in caplog.text ) diff --git a/tests/components/recorder/auto_repairs/states/test_schema.py b/tests/components/recorder/auto_repairs/states/test_schema.py index 982a6a732b65f..8a831409a71b1 100644 --- a/tests/components/recorder/auto_repairs/states/test_schema.py +++ b/tests/components/recorder/auto_repairs/states/test_schema.py @@ -84,7 +84,8 @@ async def test_validate_db_schema_fix_utf8_issue_states( in caplog.text ) assert ( - "Updating character set and collation of table states to utf8mb4" in caplog.text + "Updating table states to character set utf8mb4 and collation utf8mb4_bin" + in caplog.text ) @@ -116,7 +117,7 @@ async def test_validate_db_schema_fix_utf8_issue_state_attributes( in caplog.text ) assert ( - "Updating character set and collation of table state_attributes to utf8mb4" + "Updating table state_attributes to character set utf8mb4 and collation utf8mb4_bin" in caplog.text ) @@ -137,7 +138,7 @@ async def test_validate_db_schema_fix_collation_issue( with ( patch( "homeassistant.components.recorder.auto_repairs.schema._validate_table_schema_has_correct_collation", - return_value={"states.utf8mb4_unicode_ci"}, + return_value={"states.utf8mb4_bin"}, ), ): async with async_test_recorder(hass): @@ -145,9 +146,10 @@ async def test_validate_db_schema_fix_collation_issue( assert "Schema validation failed" not in caplog.text assert ( - "Database is about to correct DB schema errors: states.utf8mb4_unicode_ci" + "Database is about to correct DB schema errors: states.utf8mb4_bin" in caplog.text ) assert ( - "Updating character set and collation of table states to utf8mb4" in caplog.text + "Updating table states to character set utf8mb4 and collation utf8mb4_bin" + in caplog.text ) diff --git a/tests/components/recorder/auto_repairs/statistics/test_schema.py b/tests/components/recorder/auto_repairs/statistics/test_schema.py index 99d6705e4a4bd..0cbb0648f34d2 100644 --- a/tests/components/recorder/auto_repairs/statistics/test_schema.py +++ b/tests/components/recorder/auto_repairs/statistics/test_schema.py @@ -46,7 +46,7 @@ async def test_validate_db_schema_fix_utf8_issue( in caplog.text ) assert ( - "Updating character set and collation of table statistics_meta to utf8mb4" + "Updating table statistics_meta to character set utf8mb4 and collation utf8mb4_bin" in caplog.text ) @@ -113,7 +113,7 @@ async def test_validate_db_schema_fix_collation_issue( with ( patch( "homeassistant.components.recorder.auto_repairs.schema._validate_table_schema_has_correct_collation", - return_value={"statistics.utf8mb4_unicode_ci"}, + return_value={"statistics.utf8mb4_bin"}, ), ): async with async_test_recorder(hass): @@ -121,10 +121,10 @@ async def test_validate_db_schema_fix_collation_issue( assert "Schema validation failed" not in caplog.text assert ( - "Database is about to correct DB schema errors: statistics.utf8mb4_unicode_ci" + "Database is about to correct DB schema errors: statistics.utf8mb4_bin" in caplog.text ) assert ( - "Updating character set and collation of table statistics to utf8mb4" + "Updating table statistics to character set utf8mb4 and collation utf8mb4_bin" in caplog.text ) diff --git a/tests/components/recorder/auto_repairs/test_schema.py b/tests/components/recorder/auto_repairs/test_schema.py index 55b034197675a..24330db1b7e58 100644 --- a/tests/components/recorder/auto_repairs/test_schema.py +++ b/tests/components/recorder/auto_repairs/test_schema.py @@ -103,10 +103,16 @@ def _break_states_schema(): @pytest.mark.skip_on_db_engine(["postgresql", "sqlite"]) @pytest.mark.usefixtures("skip_by_db_engine") +@pytest.mark.parametrize( + ("charset", "collation"), + [("utf8mb3", "utf8_general_ci"), ("utf8mb4", "utf8mb4_unicode_ci")], +) async def test_validate_db_schema_fix_incorrect_collation( hass: HomeAssistant, recorder_mock: Recorder, caplog: pytest.LogCaptureFixture, + charset: str, + collation: str, ) -> None: """Test validating DB schema with MySQL when the collation is incorrect.""" await async_wait_recording_done(hass) @@ -116,7 +122,7 @@ def _break_states_schema(): with session_scope(session=session_maker()) as session: session.execute( text( - "ALTER TABLE states CHARACTER SET utf8mb3 COLLATE utf8_general_ci, " + f"ALTER TABLE states CHARACTER SET {charset} COLLATE {collation}, " "LOCK=EXCLUSIVE;" ) ) @@ -125,7 +131,7 @@ def _break_states_schema(): schema_errors = await recorder_mock.async_add_executor_job( validate_table_schema_has_correct_collation, recorder_mock, States ) - assert schema_errors == {"states.utf8mb4_unicode_ci"} + assert schema_errors == {"states.utf8mb4_bin"} # Now repair the schema await recorder_mock.async_add_executor_job( diff --git a/tests/components/romy/test_config_flow.py b/tests/components/romy/test_config_flow.py index 55d54f3a80b97..5286801ac0ee7 100644 --- a/tests/components/romy/test_config_flow.py +++ b/tests/components/romy/test_config_flow.py @@ -1,8 +1,10 @@ """Test the ROMY config flow.""" +from collections.abc import Generator from ipaddress import ip_address -from unittest.mock import Mock, PropertyMock, patch +from unittest.mock import AsyncMock, Mock, PropertyMock, patch +import pytest from romy import RomyRobot from homeassistant import config_entries @@ -44,6 +46,15 @@ def _create_mocked_romy( } +@pytest.fixture(autouse=True) +def mock_setup_entry() -> Generator[AsyncMock]: + """Override async_setup_entry.""" + with patch( + "homeassistant.components.romy.async_setup_entry", return_value=True + ) as mock_setup_entry: + yield mock_setup_entry + + async def test_show_user_form_robot_is_offline_and_locked(hass: HomeAssistant) -> None: """Test that the user set up form with config.""" diff --git a/tests/components/steamist/conftest.py b/tests/components/steamist/conftest.py new file mode 100644 index 0000000000000..a99b977bf9346 --- /dev/null +++ b/tests/components/steamist/conftest.py @@ -0,0 +1,16 @@ +"""Test fixtures for the Steamist integration.""" + +from collections.abc import Generator +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + + +@pytest.fixture +def mock_aio_discovery() -> Generator[MagicMock]: + """Mock AIODiscovery30303.""" + with patch( + "homeassistant.components.steamist.discovery.AIODiscovery30303" + ) as mock_aio_discovery: + mock_aio_discovery.return_value.async_scan = AsyncMock() + yield mock_aio_discovery diff --git a/tests/components/steamist/test_init.py b/tests/components/steamist/test_init.py index 0ef8edca9a837..aa0fc891052f1 100644 --- a/tests/components/steamist/test_init.py +++ b/tests/components/steamist/test_init.py @@ -42,6 +42,7 @@ def mock_single_broadcast_address(): yield +@pytest.mark.usefixtures("mock_aio_discovery") async def test_config_entry_reload(hass: HomeAssistant) -> None: """Test that a config entry can be reloaded.""" _, config_entry = await _async_setup_entry_with_status( @@ -52,6 +53,7 @@ async def test_config_entry_reload(hass: HomeAssistant) -> None: assert config_entry.state is ConfigEntryState.NOT_LOADED +@pytest.mark.usefixtures("mock_aio_discovery") async def test_config_entry_retry_later(hass: HomeAssistant) -> None: """Test that a config entry retry on connection error.""" config_entry = MockConfigEntry( diff --git a/tests/components/steamist/test_sensor.py b/tests/components/steamist/test_sensor.py index 8731e803e0b01..9242587c179d0 100644 --- a/tests/components/steamist/test_sensor.py +++ b/tests/components/steamist/test_sensor.py @@ -2,6 +2,8 @@ from __future__ import annotations +import pytest + from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, UnitOfTemperature, UnitOfTime from homeassistant.core import HomeAssistant @@ -12,6 +14,7 @@ ) +@pytest.mark.usefixtures("mock_aio_discovery") async def test_steam_active(hass: HomeAssistant) -> None: """Test that the sensors are setup with the expected values when steam is active.""" await _async_setup_entry_with_status(hass, MOCK_ASYNC_GET_STATUS_ACTIVE) @@ -23,6 +26,7 @@ async def test_steam_active(hass: HomeAssistant) -> None: assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfTime.MINUTES +@pytest.mark.usefixtures("mock_aio_discovery") async def test_steam_inactive(hass: HomeAssistant) -> None: """Test that the sensors are setup with the expected values when steam is not active.""" await _async_setup_entry_with_status(hass, MOCK_ASYNC_GET_STATUS_INACTIVE) diff --git a/tests/components/steamist/test_switch.py b/tests/components/steamist/test_switch.py index cd62c18590a04..ca70c00b20dab 100644 --- a/tests/components/steamist/test_switch.py +++ b/tests/components/steamist/test_switch.py @@ -5,6 +5,8 @@ from datetime import timedelta from unittest.mock import AsyncMock +import pytest + from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant @@ -19,6 +21,7 @@ from tests.common import async_fire_time_changed +@pytest.mark.usefixtures("mock_aio_discovery") async def test_steam_active(hass: HomeAssistant) -> None: """Test that the switches are setup with the expected values when steam is active.""" client, _ = await _async_setup_entry_with_status(hass, MOCK_ASYNC_GET_STATUS_ACTIVE) @@ -38,6 +41,7 @@ async def test_steam_active(hass: HomeAssistant) -> None: assert hass.states.get("switch.steam_active").state == STATE_OFF +@pytest.mark.usefixtures("mock_aio_discovery") async def test_steam_inactive(hass: HomeAssistant) -> None: """Test that the switches are setup with the expected values when steam is not active.""" client, _ = await _async_setup_entry_with_status( diff --git a/tests/components/tuya/snapshots/test_diagnostics.ambr b/tests/components/tuya/snapshots/test_diagnostics.ambr index 33248655d31df..18e80a2eff1e5 100644 --- a/tests/components/tuya/snapshots/test_diagnostics.ambr +++ b/tests/components/tuya/snapshots/test_diagnostics.ambr @@ -10,8 +10,7 @@ 'function': dict({ 'null': dict({ 'type': 'Boolean', - 'value': dict({ - }), + 'value': '{}', }), }), 'home_assistant': dict({ @@ -77,8 +76,7 @@ 'status_range': dict({ 'null': dict({ 'type': 'Boolean', - 'value': dict({ - }), + 'value': '{}', }), }), 'sub': False, @@ -98,8 +96,7 @@ 'function': dict({ 'null': dict({ 'type': 'Boolean', - 'value': dict({ - }), + 'value': '{}', }), }), 'home_assistant': dict({ @@ -164,8 +161,7 @@ 'status_range': dict({ 'null': dict({ 'type': 'Boolean', - 'value': dict({ - }), + 'value': '{}', }), }), 'sub': False, diff --git a/tests/components/wiz/test_init.py b/tests/components/wiz/test_init.py index 78a60c34fdcdd..34dd75a0de79e 100644 --- a/tests/components/wiz/test_init.py +++ b/tests/components/wiz/test_init.py @@ -74,7 +74,9 @@ async def test_wrong_device_now_has_our_ip(hass: HomeAssistant) -> None: bulb.mac = "dddddddddddd" _, entry = await async_setup_integration(hass, wizlight=bulb) assert entry.state is ConfigEntryState.SETUP_RETRY - await hass.async_block_till_done(wait_background_tasks=True) + + with _patch_wizlight(): + await hass.async_block_till_done(wait_background_tasks=True) async def test_reload_on_title_change(hass: HomeAssistant) -> None: