diff --git a/tests/components/duco/__init__.py b/tests/components/duco/__init__.py index 71b8a2a1175c..a7e3ccc5787b 100644 --- a/tests/components/duco/__init__.py +++ b/tests/components/duco/__init__.py @@ -3,10 +3,22 @@ from collections.abc import Sequence from unittest.mock import patch +from freezegun.api import FrozenDateTimeFactory + +from homeassistant.components.duco.const import SCAN_INTERVAL from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_fire_time_changed + + +async def async_fire_coordinator_update( + hass: HomeAssistant, freezer: FrozenDateTimeFactory +) -> None: + """Trigger a scheduled coordinator update.""" + freezer.tick(SCAN_INTERVAL) + async_fire_time_changed(hass) + await hass.async_block_till_done(wait_background_tasks=True) async def setup_integration( diff --git a/tests/components/duco/test_binary_sensor.py b/tests/components/duco/test_binary_sensor.py index bc8bdfd18da8..bc3aa92b39eb 100644 --- a/tests/components/duco/test_binary_sensor.py +++ b/tests/components/duco/test_binary_sensor.py @@ -14,7 +14,7 @@ from freezegun.api import FrozenDateTimeFactory import pytest from homeassistant.components.binary_sensor import BinarySensorDeviceClass -from homeassistant.components.duco.const import BOX_NODE_ID, SCAN_INTERVAL +from homeassistant.components.duco.const import BOX_NODE_ID from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ( STATE_OFF, @@ -26,9 +26,9 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er -from . import setup_platform_integration +from . import async_fire_coordinator_update, setup_platform_integration -from tests.common import MockConfigEntry, async_fire_time_changed +from tests.common import MockConfigEntry VENTILATION_PROBLEM_ENTITY_ID = "binary_sensor.living_ventilation" @@ -38,13 +38,6 @@ DIAGNOSTIC_ERROR_TYPES = [ ] -async def _async_refresh(hass: HomeAssistant, freezer: FrozenDateTimeFactory) -> None: - """Trigger a coordinator refresh.""" - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) - - async def test_diagnostic_binary_sensor_entity_registry_defaults( hass: HomeAssistant, mock_config_entry: MockConfigEntry, @@ -144,7 +137,7 @@ async def test_diagnostic_binary_sensors_added_after_initial_empty_response( diagnostic_subsystems=(DiagComponent(component="Ventilation", status="Error"),) ) - await _async_refresh(hass, freezer) + await async_fire_coordinator_update(hass, freezer) assert hass.states.is_state(VENTILATION_PROBLEM_ENTITY_ID, STATE_ON) @@ -155,7 +148,7 @@ async def test_diagnostic_binary_sensors_added_after_initial_empty_response( ) ) - await _async_refresh(hass, freezer) + await async_fire_coordinator_update(hass, freezer) assert hass.states.is_state("binary_sensor.living_filter", STATE_OFF) @@ -178,7 +171,7 @@ async def test_diagnostic_binary_sensors_wait_for_box_node( assert hass.states.get(VENTILATION_PROBLEM_ENTITY_ID) is None mock_duco_client.async_get_nodes.return_value = mock_sensor_nodes - await _async_refresh(hass, freezer) + await async_fire_coordinator_update(hass, freezer) assert hass.states.is_state(VENTILATION_PROBLEM_ENTITY_ID, STATE_OFF) @@ -209,7 +202,7 @@ async def test_diagnostic_binary_sensor_becomes_unknown_without_known_status( diagnostic_subsystems=diagnostic_subsystems ) - await _async_refresh(hass, freezer) + await async_fire_coordinator_update(hass, freezer) assert hass.states.is_state(VENTILATION_PROBLEM_ENTITY_ID, STATE_UNKNOWN) @@ -233,13 +226,13 @@ async def test_diagnostics_refresh_failure_is_isolated_and_recovers( mock_duco_client.async_get_diagnostics_info.side_effect = exception_type("error") - await _async_refresh(hass, freezer) + await async_fire_coordinator_update(hass, freezer) assert hass.states.is_state(VENTILATION_PROBLEM_ENTITY_ID, STATE_UNAVAILABLE) assert hass.states.is_state("sensor.office_co2_carbon_dioxide", "405") mock_duco_client.async_get_diagnostics_info.side_effect = None - await _async_refresh(hass, freezer) + await async_fire_coordinator_update(hass, freezer) assert hass.states.is_state(VENTILATION_PROBLEM_ENTITY_ID, STATE_OFF) @@ -266,7 +259,7 @@ async def test_initial_diagnostics_failure_is_isolated_and_recovers( assert hass.states.is_state("sensor.office_co2_carbon_dioxide", "405") mock_duco_client.async_get_diagnostics_info.side_effect = None - await _async_refresh(hass, freezer) + await async_fire_coordinator_update(hass, freezer) assert hass.states.is_state(VENTILATION_PROBLEM_ENTITY_ID, STATE_OFF) @@ -283,11 +276,11 @@ async def test_diagnostics_availability_transitions_logged( mock_duco_client.async_get_diagnostics_info.side_effect = DucoError("error") await setup_platform_integration(hass, mock_config_entry, [Platform.BINARY_SENSOR]) - await _async_refresh(hass, freezer) + await async_fire_coordinator_update(hass, freezer) mock_duco_client.async_get_diagnostics_info.side_effect = None - await _async_refresh(hass, freezer) - await _async_refresh(hass, freezer) + await async_fire_coordinator_update(hass, freezer) + await async_fire_coordinator_update(hass, freezer) assert [ record.message diff --git a/tests/components/duco/test_fan.py b/tests/components/duco/test_fan.py index 2db0a6b49f6a..8a6f3561eee9 100644 --- a/tests/components/duco/test_fan.py +++ b/tests/components/duco/test_fan.py @@ -8,7 +8,6 @@ from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion -from homeassistant.components.duco.const import SCAN_INTERVAL from homeassistant.components.fan import ( ATTR_PERCENTAGE, ATTR_PRESET_MODE, @@ -21,9 +20,9 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er -from . import setup_platform_integration +from . import async_fire_coordinator_update, setup_platform_integration -from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform +from tests.common import MockConfigEntry, snapshot_platform _FAN_ENTITY = "fan.living" @@ -145,9 +144,7 @@ async def test_coordinator_update_marks_unavailable( side_effect=DucoConnectionError("offline") ) - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get(_FAN_ENTITY) assert state is not None diff --git a/tests/components/duco/test_init.py b/tests/components/duco/test_init.py index aca738a00a83..4ff7b1f65913 100644 --- a/tests/components/duco/test_init.py +++ b/tests/components/duco/test_init.py @@ -21,13 +21,13 @@ from duco_connectivity import ( from freezegun.api import FrozenDateTimeFactory import pytest -from homeassistant.components.duco.const import BOX_NODE_ID, DOMAIN, SCAN_INTERVAL +from homeassistant.components.duco.const import BOX_NODE_ID, DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er -from . import setup_platform_integration +from . import async_fire_coordinator_update, setup_platform_integration from .conftest import ( TEST_HOST, TEST_MAC, @@ -223,9 +223,7 @@ async def test_setup_entry_recovers_from_optional_temperature_capability_failure assert mock_config_entry.state is ConfigEntryState.LOADED assert hass.states.get("sensor.living_outdoor_air_temperature") is None - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get("sensor.living_outdoor_air_temperature") assert state is not None @@ -289,9 +287,7 @@ async def test_empty_bypass_temperature_targets_are_retried( assert hass.states.get("number.living_bypass_target_2") is None mock_duco_client.async_get_bypass_supply_temperature_targets.assert_awaited_once_with() - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) assert mock_duco_client.async_get_bypass_supply_temperature_targets.await_count == 2 assert hass.states.get("number.living_bypass_target_1") is not None @@ -321,9 +317,7 @@ async def test_missing_bypass_temperature_targets_are_retried( assert mock_config_entry.state is ConfigEntryState.LOADED assert hass.states.get("number.living_bypass_target_1") is None - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get("number.living_bypass_target_1") assert state is not None diff --git a/tests/components/duco/test_number.py b/tests/components/duco/test_number.py index 8f59182f4659..91591c106274 100644 --- a/tests/components/duco/test_number.py +++ b/tests/components/duco/test_number.py @@ -13,7 +13,6 @@ from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion -from homeassistant.components.duco.const import SCAN_INTERVAL from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN, SERVICE_SET_VALUE from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform from homeassistant.core import HomeAssistant @@ -21,9 +20,9 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM -from . import setup_platform_integration +from . import async_fire_coordinator_update, setup_platform_integration -from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform +from tests.common import MockConfigEntry, snapshot_platform _ZONE_1_ENTITY_ID = "number.living_bypass_target_1" _ZONE_2_ENTITY_ID = "number.living_bypass_target_2" @@ -298,18 +297,14 @@ async def test_bypass_supply_temperature_target_becomes_unavailable_when_missing assert state.state == "20.0" updated_target = replace(mock_bypass_supply_temperature_targets.pop(1), value=20.5) - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get(_ZONE_1_ENTITY_ID) assert state is not None assert state.state == STATE_UNAVAILABLE mock_bypass_supply_temperature_targets[1] = updated_target - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get(_ZONE_1_ENTITY_ID) assert state is not None @@ -334,17 +329,13 @@ async def test_bypass_supply_temperature_target_recovers_from_refresh_error( DucoError("Temporary bypass target failure"), mock_bypass_supply_temperature_targets.copy(), ] - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get(_ZONE_1_ENTITY_ID) assert state is not None assert state.state == STATE_UNAVAILABLE - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get(_ZONE_1_ENTITY_ID) assert state is not None diff --git a/tests/components/duco/test_sensor.py b/tests/components/duco/test_sensor.py index 2973478af573..e6e9f7683067 100644 --- a/tests/components/duco/test_sensor.py +++ b/tests/components/duco/test_sensor.py @@ -19,14 +19,14 @@ from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion -from homeassistant.components.duco.const import BOX_NODE_ID, DOMAIN, SCAN_INTERVAL +from homeassistant.components.duco.const import BOX_NODE_ID, DOMAIN from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er -from . import setup_platform_integration +from . import async_fire_coordinator_update, setup_platform_integration -from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform +from tests.common import MockConfigEntry, snapshot_platform FILTER_REMAINING_ENTITY_ID = "sensor.living_filter_remaining" VENTILATION_TEMPERATURE_ENTITY_IDS = ( @@ -195,14 +195,15 @@ async def test_iaq_sensor_entities_disabled_by_default( @pytest.mark.usefixtures("init_integration") -async def test_rssi_sensor_disabled_by_default( +async def test_diagnostic_sensor_entities_disabled_by_default( hass: HomeAssistant, entity_registry: er.EntityRegistry, ) -> None: - """Test that the RSSI sensor is disabled by default.""" - entry = entity_registry.async_get("sensor.living_signal_strength") - assert entry is not None - assert entry.disabled_by == er.RegistryEntryDisabler.INTEGRATION + """Test that diagnostic sensor entities are disabled by default.""" + for entity_id in ("sensor.living_signal_strength",): + entry = entity_registry.async_get(entity_id) + assert entry is not None + assert entry.disabled_by == er.RegistryEntryDisabler.INTEGRATION @pytest.mark.usefixtures("init_integration") @@ -223,9 +224,7 @@ async def test_coordinator_update_failure_marks_unavailable( """Test sensor entities become unavailable when the coordinator update fails.""" mock_duco_client.async_get_nodes.side_effect = exception_type(exception_message) - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get("sensor.office_co2_carbon_dioxide") assert state is not None @@ -249,9 +248,7 @@ async def test_lan_info_failures_keep_node_entities_available( """Test node entities stay available when LAN info retrieval fails.""" mock_duco_client.async_get_lan_info = AsyncMock(side_effect=exception) - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get("sensor.office_co2_carbon_dioxide") assert state is not None @@ -280,9 +277,7 @@ async def test_time_filter_remaining_missing_is_retried( assert hass.states.get(FILTER_REMAINING_ENTITY_ID) is None - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) assert mock_duco_client.async_get_time_filter_remaining.await_count == 2 state = hass.states.get(FILTER_REMAINING_ENTITY_ID) @@ -307,9 +302,7 @@ async def test_empty_ventilation_temperatures_are_retried( for entity_id in VENTILATION_TEMPERATURE_ENTITY_IDS: assert hass.states.get(entity_id) is None - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) assert mock_duco_client.async_get_ventilation_temperature_info.await_count == 2 state = hass.states.get("sensor.living_outdoor_air_temperature") @@ -358,9 +351,7 @@ async def test_time_filter_remaining_transient_failure_recovers_sensor_creation( assert hass.states.get(FILTER_REMAINING_ENTITY_ID) is None - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get(FILTER_REMAINING_ENTITY_ID) assert state is not None @@ -414,9 +405,7 @@ async def test_new_node_added_dynamically( new_node = dynamic_sensor_nodes[node_id] mock_duco_client.async_get_nodes.return_value = [*mock_sensor_nodes, new_node] - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get(expected_entity_id) assert state is not None @@ -448,9 +437,7 @@ async def test_deregistered_node_removes_device( node for node in mock_sensor_nodes if node.node_id != 2 ] - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) # The device should be removed from the device registry. device = device_registry.async_get_device_by_identifier( @@ -485,9 +472,7 @@ async def test_box_node_not_removed_on_transient_incomplete_node_list( node for node in mock_sensor_nodes if node.node_id != BOX_NODE_ID ] - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) assert ( device_registry.async_get_device_by_identifier( @@ -502,9 +487,7 @@ async def test_box_node_not_removed_on_transient_incomplete_node_list( mock_duco_client.async_get_nodes.return_value = mock_sensor_nodes - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get("fan.living") assert state is not None @@ -538,9 +521,7 @@ async def test_unknown_node_type_logs_warning_and_creates_no_entities( ) mock_duco_client.async_get_nodes.return_value = [*mock_sensor_nodes, unknown_node] - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) assert "99" in caplog.text assert "unsupported" in caplog.text.lower() @@ -589,9 +570,7 @@ async def test_previously_unknown_node_gets_entities_after_type_becomes_known( *mock_sensor_nodes, _make_node(NodeType.UNKNOWN), ] - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) assert hass.states.get("sensor.future_sensor_humidity") is None @@ -600,9 +579,7 @@ async def test_previously_unknown_node_gets_entities_after_type_becomes_known( *mock_sensor_nodes, _make_node("BSRH"), ] - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get("sensor.future_sensor_humidity") assert state is not None @@ -642,16 +619,12 @@ async def test_unknown_node_logged_at_debug( mock_duco_client.async_get_nodes.return_value = [*mock_sensor_nodes, unknown_node] with caplog.at_level(logging.WARNING, logger="homeassistant.components.duco"): - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) assert "has an unsupported device type" not in caplog.text with caplog.at_level(logging.DEBUG, logger="homeassistant.components.duco"): - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) assert "has an unsupported device type" in caplog.text @@ -684,9 +657,7 @@ async def test_ventilation_state_unknown_returns_state_unknown( ] mock_duco_client.async_get_nodes.return_value = updated_nodes - freezer.tick(SCAN_INTERVAL) - async_fire_time_changed(hass) - await hass.async_block_till_done(wait_background_tasks=True) + await async_fire_coordinator_update(hass, freezer) state = hass.states.get("sensor.living_ventilation_state") assert state is not None