From 53b8aab9b9f0611a36de5b95c2d0689bbe53bcd2 Mon Sep 17 00:00:00 2001 From: Ariel Ebersberger Date: Fri, 27 Mar 2026 14:08:16 +0100 Subject: [PATCH 1/3] Fix shelly tests --- tests/components/shelly/conftest.py | 11 +- tests/components/shelly/test_binary_sensor.py | 5 + tests/components/shelly/test_button.py | 3 + tests/components/shelly/test_climate.py | 4 + tests/components/shelly/test_config_flow.py | 142 ++++++++---------- tests/components/shelly/test_coordinator.py | 6 +- .../components/shelly/test_device_trigger.py | 6 +- tests/components/shelly/test_update.py | 2 + 8 files changed, 92 insertions(+), 87 deletions(-) diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index f3a583035e791c..137d5dc9f3e06e 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -793,10 +793,13 @@ def _initialize(): @pytest.fixture def mock_setup_entry() -> Generator[AsyncMock]: - """Override async_setup_entry.""" - with patch( - "homeassistant.components.shelly.async_setup_entry", return_value=True - ) as mock_setup_entry: + """Override async_setup_entry and async_unload_entry.""" + with ( + patch( + "homeassistant.components.shelly.async_setup_entry", return_value=True + ) as mock_setup_entry, + patch("homeassistant.components.shelly.async_unload_entry", return_value=True), + ): yield mock_setup_entry diff --git a/tests/components/shelly/test_binary_sensor.py b/tests/components/shelly/test_binary_sensor.py index 9ce7ecd77df80f..2a16799e85afe4 100644 --- a/tests/components/shelly/test_binary_sensor.py +++ b/tests/components/shelly/test_binary_sensor.py @@ -9,6 +9,7 @@ MODEL_MOTION, MODEL_PLUS_SMOKE, ) +from aioshelly.exceptions import DeviceConnectionError from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion @@ -332,6 +333,7 @@ async def test_rpc_sleeping_binary_sensor( ) -> None: """Test RPC online sleeping binary sensor.""" entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_cloud" + mock_rpc_device.initialize.side_effect = DeviceConnectionError monkeypatch.setattr(mock_rpc_device, "connected", False) monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000) config_entry = await init_integration(hass, 2, sleep_period=1000) @@ -344,6 +346,7 @@ async def test_rpc_sleeping_binary_sensor( ) # Make device online + mock_rpc_device.initialize.side_effect = None mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) @@ -373,6 +376,7 @@ async def test_rpc_sleeping_binary_sensor_with_channel_name( ) -> None: """Test RPC online sleeping binary sensor with channel name.""" entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_test_channel_name_smoke" + mock_rpc_device.initialize.side_effect = DeviceConnectionError monkeypatch.setattr(mock_rpc_device, "connected", False) monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000) await init_integration(hass, 2, sleep_period=1000, model=MODEL_PLUS_SMOKE) @@ -381,6 +385,7 @@ async def test_rpc_sleeping_binary_sensor_with_channel_name( assert hass.states.get(entity_id) is None # Make device online + mock_rpc_device.initialize.side_effect = None mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) diff --git a/tests/components/shelly/test_button.py b/tests/components/shelly/test_button.py index a32ab642df0840..93f2daf298eb94 100644 --- a/tests/components/shelly/test_button.py +++ b/tests/components/shelly/test_button.py @@ -493,6 +493,7 @@ async def test_rpc_smoke_mute_alarm_button( entity_id = f"{BUTTON_DOMAIN}.test_name_mute_alarm" monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000) monkeypatch.setattr(mock_rpc_device, "config", {"smoke:0": {"id": 0, "name": None}}) + mock_rpc_device.initialize.side_effect = DeviceConnectionError monkeypatch.setattr(mock_rpc_device, "connected", False) await init_integration(hass, 2, sleep_period=1000, model=MODEL_PLUS_SMOKE) @@ -500,8 +501,10 @@ async def test_rpc_smoke_mute_alarm_button( assert hass.states.get(entity_id) is None # Make device online + mock_rpc_device.initialize.side_effect = None mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) + await hass.async_block_till_done(wait_background_tasks=True) assert (state := hass.states.get(entity_id)) assert state.state == STATE_UNAVAILABLE diff --git a/tests/components/shelly/test_climate.py b/tests/components/shelly/test_climate.py index d50f06906124dd..0f72f33e48bbb1 100644 --- a/tests/components/shelly/test_climate.py +++ b/tests/components/shelly/test_climate.py @@ -479,6 +479,8 @@ async def test_block_set_mode_connection_error( hass: HomeAssistant, mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch ) -> None: """Test block device set mode connection error.""" + monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp") + monkeypatch.delattr(mock_block_device.blocks[GAS_VALVE_BLOCK_ID], "targetTemp") monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) monkeypatch.setattr( mock_block_device, @@ -507,6 +509,8 @@ async def test_block_set_mode_auth_error( hass: HomeAssistant, mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch ) -> None: """Test block device set mode authentication error.""" + monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp") + monkeypatch.delattr(mock_block_device.blocks[GAS_VALVE_BLOCK_ID], "targetTemp") monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) monkeypatch.setattr( mock_block_device, diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index 2fd0eb71d5d823..9f6ae53d4efe3b 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -58,6 +58,16 @@ ) from tests.typing import WebSocketGenerator + +async def _async_inject_ble_discovery( + hass: HomeAssistant, info: BluetoothServiceInfoBleak +) -> None: + """Inject BLE discovery info and wait for processing without triggering config flows.""" + with patch.object(hass.config_entries.flow, "async_init"): + inject_bluetooth_service_info_bleak(hass, info) + await hass.async_block_till_done() + + DISCOVERY_INFO = ZeroconfServiceInfo( ip_address=ip_address("1.1.1.1"), ip_addresses=[ip_address("1.1.1.1")], @@ -1078,7 +1088,7 @@ async def test_user_flow_both_ble_and_zeroconf_prefers_zeroconf( # Inject BLE device with same MAC (from manufacturer data) # The manufacturer data contains WiFi MAC CCBA97C2D670 - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_GEN3) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_GEN3) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -1140,7 +1150,7 @@ async def test_user_flow_with_ble_devices( # Inject BLE device with RPC-over-BLE enabled # The manufacturer data contains WiFi MAC CCBA97C2D670 - inject_bluetooth_service_info_bleak( + await _async_inject_ble_discovery( hass, BluetoothServiceInfoBleak( name="ShellyPlusGen3", # Name without MAC so it uses manufacturer data @@ -1163,15 +1173,6 @@ async def test_user_flow_with_ble_devices( ), ) - # Wait for bluetooth discovery to process - await hass.async_block_till_done() - - # Abort any auto-discovered bluetooth flows - flows = hass.config_entries.flow.async_progress_by_handler(DOMAIN) - for flow in flows: - if flow["context"]["source"] == config_entries.SOURCE_BLUETOOTH: - hass.config_entries.flow.async_abort(flow["flow_id"]) - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -1844,10 +1845,7 @@ async def test_user_flow_select_ble_device( mock_discovery.return_value = [] # Inject BLE device with RPC-over-BLE enabled (no discovery flow created) - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_GEN3) - - # Wait for bluetooth discovery to process - await hass.async_block_till_done() + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_GEN3) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -2747,6 +2745,7 @@ async def test_zeroconf_sleeping_device_not_triggers_refresh( caplog: pytest.LogCaptureFixture, ) -> None: """Test zeroconf discovery does not triggers refresh for sleeping device.""" + mock_rpc_device.initialize.side_effect = DeviceConnectionError monkeypatch.setattr(mock_rpc_device, "connected", False) monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000) entry = MockConfigEntry( @@ -2763,6 +2762,8 @@ async def test_zeroconf_sleeping_device_not_triggers_refresh( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() + mock_rpc_device.initialize.side_effect = None + mock_rpc_device.initialize.reset_mock() mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) @@ -2799,6 +2800,7 @@ async def test_zeroconf_sleeping_device_attempts_configure( caplog: pytest.LogCaptureFixture, ) -> None: """Test zeroconf discovery configures a sleeping device outbound websocket.""" + mock_rpc_device.initialize.side_effect = DeviceConnectionError monkeypatch.setattr(mock_rpc_device, "connected", False) monkeypatch.setattr(mock_rpc_device, "initialized", False) monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000) @@ -2815,9 +2817,9 @@ async def test_zeroconf_sleeping_device_attempts_configure( entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - mock_rpc_device.mock_disconnected() - await hass.async_block_till_done() + mock_rpc_device.initialize.side_effect = None + mock_rpc_device.initialize.reset_mock() mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) @@ -2862,6 +2864,7 @@ async def test_zeroconf_sleeping_device_attempts_configure_ws_disabled( caplog: pytest.LogCaptureFixture, ) -> None: """Test zeroconf discovery configures a sleeping device outbound websocket when its disabled.""" + mock_rpc_device.initialize.side_effect = DeviceConnectionError monkeypatch.setattr(mock_rpc_device, "connected", False) monkeypatch.setattr(mock_rpc_device, "initialized", False) monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000) @@ -2881,9 +2884,9 @@ async def test_zeroconf_sleeping_device_attempts_configure_ws_disabled( entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - mock_rpc_device.mock_disconnected() - await hass.async_block_till_done() + mock_rpc_device.initialize.side_effect = None + mock_rpc_device.initialize.reset_mock() mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) @@ -2931,8 +2934,8 @@ async def test_zeroconf_sleeping_device_attempts_configure_no_url_available( hass.config.internal_url = None hass.config.external_url = None hass.config.api = None + mock_rpc_device.initialize.side_effect = DeviceConnectionError monkeypatch.setattr(mock_rpc_device, "connected", False) - monkeypatch.setattr(mock_rpc_device, "initialized", False) monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000) entry = MockConfigEntry( domain="shelly", @@ -2947,9 +2950,9 @@ async def test_zeroconf_sleeping_device_attempts_configure_no_url_available( entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - mock_rpc_device.mock_disconnected() - await hass.async_block_till_done() + mock_rpc_device.initialize.side_effect = None + mock_rpc_device.initialize.reset_mock() mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) @@ -3226,7 +3229,7 @@ async def test_bluetooth_discovery( ] # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3293,7 +3296,7 @@ async def test_bluetooth_provisioning_clears_match_history( ] # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_FOR_CLEAR_TEST) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_FOR_CLEAR_TEST) with patch( "homeassistant.components.shelly.config_flow.async_clear_address_from_match_history", @@ -3382,7 +3385,7 @@ async def test_bluetooth_factory_reset_rediscovery( # First discovery: device is already provisioned (no RPC-over-BLE) # Inject the device without RPC so it's in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_NO_RPC) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_NO_RPC) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3465,9 +3468,7 @@ async def test_bluetooth_discovery_mac_in_manufacturer_data( ) -> None: """Test bluetooth discovery with MAC in manufacturer data (newer devices).""" # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak( - hass, BLE_DISCOVERY_INFO_MAC_IN_MANUFACTURER_DATA - ) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_MAC_IN_MANUFACTURER_DATA) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3490,7 +3491,7 @@ async def test_bluetooth_discovery_mac_unknown_model( ) -> None: """Test bluetooth discovery with MAC but unknown model ID.""" # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_MAC_UNKNOWN_MODEL) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_MAC_UNKNOWN_MODEL) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3512,7 +3513,7 @@ async def test_bluetooth_discovery_already_configured( ) -> None: """Test bluetooth discovery when device is already configured.""" # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) entry = MockConfigEntry( domain=DOMAIN, @@ -3541,7 +3542,7 @@ async def test_bluetooth_discovery_already_configured_clears_match_history( ) -> None: """Test bluetooth discovery clears match history when device already configured.""" # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) entry = MockConfigEntry( domain=DOMAIN, @@ -3603,7 +3604,7 @@ async def test_bluetooth_wifi_scan_success( ] # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3673,7 +3674,7 @@ async def test_bluetooth_wifi_scan_failure( mock_ble_rpc_device.wifi_scan.side_effect = DeviceConnectionError # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3756,7 +3757,7 @@ async def test_bluetooth_wifi_scan_ble_not_permitted( ) # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3790,7 +3791,7 @@ async def test_bluetooth_wifi_credentials_and_provision_success( ] # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3866,7 +3867,7 @@ async def test_bluetooth_wifi_provision_failure( mock_ble_rpc_device.wifi_setconfig.side_effect = DeviceConnectionError # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3954,7 +3955,7 @@ async def test_bluetooth_wifi_scan_unexpected_exception( mock_ble_rpc_device.wifi_scan.side_effect = RuntimeError("Unexpected error") # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3985,7 +3986,7 @@ async def test_bluetooth_provision_unexpected_exception( mock_ble_rpc_device.wifi_setconfig.side_effect = RuntimeError("Unexpected error") # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4031,7 +4032,7 @@ async def test_bluetooth_provision_device_connection_error_after_wifi( ] # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4092,7 +4093,7 @@ async def test_bluetooth_provision_requires_auth( ] # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4170,7 +4171,7 @@ async def test_bluetooth_provision_validate_input_fails( ] # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4243,7 +4244,7 @@ async def test_bluetooth_provision_firmware_not_fully_provisioned( ] # Inject BLE device so it's available in the bluetooth scanner - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4299,7 +4300,7 @@ async def test_bluetooth_provision_with_zeroconf_discovery_fast_path( ] # Inject BLE device - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4387,7 +4388,7 @@ async def test_bluetooth_provision_timeout_active_lookup_fails( mock_ble_rpc_device.status = {"wifi": {"sta_ip": None}} # Inject BLE device - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4460,7 +4461,7 @@ async def test_bluetooth_provision_timeout_ble_fallback_succeeds( mock_ble_rpc_device.status = {"wifi": {"sta_ip": "192.168.1.100"}} # Inject BLE device - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4540,7 +4541,7 @@ async def test_bluetooth_provision_timeout_ble_fallback_fails( mock_ble_rpc_device.status = {"wifi": {"sta_ip": None}} # Inject BLE device - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4600,7 +4601,7 @@ async def test_bluetooth_provision_timeout_ble_exception( ] # Inject BLE device - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4674,7 +4675,7 @@ async def test_bluetooth_provision_secure_device_both_enabled( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4689,11 +4690,7 @@ async def test_bluetooth_provision_secure_device_both_enabled( ) # Provision and verify security calls - mock_device = AsyncMock() - mock_device.initialize = AsyncMock() - mock_device.wifi_setconfig = AsyncMock(return_value={}) - mock_device.ble_setconfig = AsyncMock(return_value={"restart_required": False}) - mock_device.shutdown = AsyncMock() + mock_device = create_mock_rpc_device() with ( patch( @@ -4738,7 +4735,7 @@ async def test_bluetooth_provision_secure_device_both_disabled( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4787,7 +4784,7 @@ async def test_bluetooth_provision_secure_device_only_ap_disabled( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4802,10 +4799,7 @@ async def test_bluetooth_provision_secure_device_only_ap_disabled( ) # Provision and verify only AP disabled - mock_device = AsyncMock() - mock_device.initialize = AsyncMock() - mock_device.wifi_setconfig = AsyncMock(return_value={}) - mock_device.shutdown = AsyncMock() + mock_device = create_mock_rpc_device() with ( patch( @@ -4849,7 +4843,7 @@ async def test_bluetooth_provision_secure_device_only_ble_disabled( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4864,10 +4858,7 @@ async def test_bluetooth_provision_secure_device_only_ble_disabled( ) # Provision and verify only BLE disabled - mock_device = AsyncMock() - mock_device.initialize = AsyncMock() - mock_device.ble_setconfig = AsyncMock(return_value={"restart_required": False}) - mock_device.shutdown = AsyncMock() + mock_device = create_mock_rpc_device() with ( patch( @@ -4911,7 +4902,7 @@ async def test_bluetooth_provision_secure_device_with_restart_required( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4926,12 +4917,9 @@ async def test_bluetooth_provision_secure_device_with_restart_required( ) # Provision and verify restart is triggered - mock_device = AsyncMock() - mock_device.initialize = AsyncMock() - mock_device.wifi_setconfig = AsyncMock(return_value={}) + mock_device = create_mock_rpc_device() mock_device.ble_setconfig = AsyncMock(return_value={"restart_required": True}) mock_device.trigger_reboot = AsyncMock() - mock_device.shutdown = AsyncMock() with ( patch( @@ -4975,7 +4963,7 @@ async def test_bluetooth_provision_secure_device_fails_gracefully( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4990,10 +4978,8 @@ async def test_bluetooth_provision_secure_device_fails_gracefully( ) # Provision with security calls failing - wifi_setconfig will fail - mock_device = AsyncMock() - mock_device.initialize = AsyncMock() + mock_device = create_mock_rpc_device() mock_device.wifi_setconfig = AsyncMock(side_effect=RpcCallError("RPC call failed")) - mock_device.shutdown = AsyncMock() with ( patch( @@ -5028,7 +5014,7 @@ async def test_zeroconf_aborts_idle_ble_flow( ) -> None: """Test zeroconf discovery aborts idle BLE flow (lines 316-321).""" # Start BLE discovery flow and leave it idle at bluetooth_confirm - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) ble_result = await hass.config_entries.flow.async_init( DOMAIN, @@ -5086,7 +5072,7 @@ async def test_bluetooth_flow_abort_cleans_up_ble_connection( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) # Start BLE flow result = await hass.config_entries.flow.async_init( @@ -5125,7 +5111,7 @@ async def test_bluetooth_ble_initialize_failure_cleans_up( mock_device.initialize = AsyncMock(side_effect=DeviceConnectionError) mock_device.shutdown = AsyncMock() - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) # Start BLE flow result = await hass.config_entries.flow.async_init( @@ -5172,7 +5158,7 @@ async def test_bluetooth_ble_shutdown_exception_handled( # Make shutdown raise an exception mock_ble_rpc_device.shutdown.side_effect = RuntimeError("Shutdown failed") - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) # Start BLE flow result = await hass.config_entries.flow.async_init( @@ -5209,7 +5195,7 @@ async def test_bluetooth_provision_ble_reconnect_fails_during_ip_fetch( ] # Inject BLE device - inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) + await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, diff --git a/tests/components/shelly/test_coordinator.py b/tests/components/shelly/test_coordinator.py index d0d41dda76b7e7..20efccf0f198fc 100644 --- a/tests/components/shelly/test_coordinator.py +++ b/tests/components/shelly/test_coordinator.py @@ -1097,16 +1097,14 @@ async def test_rpc_sleeping_device_late_setup( monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000) assert entry.data[CONF_SLEEP_PERIOD] == 1000 register_device(device_registry, entry) + mock_rpc_device.initialize.side_effect = DeviceConnectionError monkeypatch.setattr(mock_rpc_device, "connected", False) - monkeypatch.setattr(mock_rpc_device, "initialized", False) await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - monkeypatch.setattr(mock_rpc_device, "initialized", True) + mock_rpc_device.initialize.side_effect = None mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) - monkeypatch.setattr(mock_rpc_device, "connected", True) - mock_rpc_device.mock_initialized() await hass.async_block_till_done(wait_background_tasks=True) assert hass.states.get("sensor.test_name_temperature") diff --git a/tests/components/shelly/test_device_trigger.py b/tests/components/shelly/test_device_trigger.py index b23f56ef4a9796..b85b6d9a0ef3e0 100644 --- a/tests/components/shelly/test_device_trigger.py +++ b/tests/components/shelly/test_device_trigger.py @@ -1,6 +1,6 @@ """The tests for Shelly device triggers.""" -from unittest.mock import Mock +from unittest.mock import AsyncMock, Mock from aioshelly.const import MODEL_BUTTON1 import pytest @@ -402,6 +402,10 @@ async def test_rpc_no_runtime_data( ) -> None: """Test the device trigger for the RPC device when there is no runtime_data in the entry.""" entry = await init_integration(hass, 2) + monkeypatch.setattr( + "homeassistant.components.shelly.async_unload_entry", + AsyncMock(return_value=True), + ) monkeypatch.delattr(entry, "runtime_data") device = dr.async_entries_for_config_entry(device_registry, entry.entry_id)[0] diff --git a/tests/components/shelly/test_update.py b/tests/components/shelly/test_update.py index 8007ecc361534d..f3c559a560be67 100644 --- a/tests/components/shelly/test_update.py +++ b/tests/components/shelly/test_update.py @@ -409,6 +409,7 @@ async def test_rpc_sleeping_update( monkeypatch: pytest.MonkeyPatch, ) -> None: """Test RPC sleeping device update entity.""" + mock_rpc_device.initialize.side_effect = DeviceConnectionError monkeypatch.setattr(mock_rpc_device, "connected", False) monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000) monkeypatch.setitem(mock_rpc_device.shelly, "ver", "1") @@ -426,6 +427,7 @@ async def test_rpc_sleeping_update( assert hass.states.get(entity_id) is None # Make device online + mock_rpc_device.initialize.side_effect = None mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) From 696f552b05a4a8ad2f32c4d1a119052424d7026b Mon Sep 17 00:00:00 2001 From: Ariel Ebersberger Date: Mon, 30 Mar 2026 10:25:49 +0200 Subject: [PATCH 2/3] Remove conftest and bluetooth changes (split to separate PRs) --- tests/components/shelly/conftest.py | 11 +- tests/components/shelly/test_config_flow.py | 124 ++++++++++++-------- 2 files changed, 76 insertions(+), 59 deletions(-) diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index 137d5dc9f3e06e..f3a583035e791c 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -793,13 +793,10 @@ def _initialize(): @pytest.fixture def mock_setup_entry() -> Generator[AsyncMock]: - """Override async_setup_entry and async_unload_entry.""" - with ( - patch( - "homeassistant.components.shelly.async_setup_entry", return_value=True - ) as mock_setup_entry, - patch("homeassistant.components.shelly.async_unload_entry", return_value=True), - ): + """Override async_setup_entry.""" + with patch( + "homeassistant.components.shelly.async_setup_entry", return_value=True + ) as mock_setup_entry: yield mock_setup_entry diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index 9f6ae53d4efe3b..da85a3d72b6b44 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -58,16 +58,6 @@ ) from tests.typing import WebSocketGenerator - -async def _async_inject_ble_discovery( - hass: HomeAssistant, info: BluetoothServiceInfoBleak -) -> None: - """Inject BLE discovery info and wait for processing without triggering config flows.""" - with patch.object(hass.config_entries.flow, "async_init"): - inject_bluetooth_service_info_bleak(hass, info) - await hass.async_block_till_done() - - DISCOVERY_INFO = ZeroconfServiceInfo( ip_address=ip_address("1.1.1.1"), ip_addresses=[ip_address("1.1.1.1")], @@ -1088,7 +1078,7 @@ async def test_user_flow_both_ble_and_zeroconf_prefers_zeroconf( # Inject BLE device with same MAC (from manufacturer data) # The manufacturer data contains WiFi MAC CCBA97C2D670 - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_GEN3) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_GEN3) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -1150,7 +1140,7 @@ async def test_user_flow_with_ble_devices( # Inject BLE device with RPC-over-BLE enabled # The manufacturer data contains WiFi MAC CCBA97C2D670 - await _async_inject_ble_discovery( + inject_bluetooth_service_info_bleak( hass, BluetoothServiceInfoBleak( name="ShellyPlusGen3", # Name without MAC so it uses manufacturer data @@ -1173,6 +1163,15 @@ async def test_user_flow_with_ble_devices( ), ) + # Wait for bluetooth discovery to process + await hass.async_block_till_done() + + # Abort any auto-discovered bluetooth flows + flows = hass.config_entries.flow.async_progress_by_handler(DOMAIN) + for flow in flows: + if flow["context"]["source"] == config_entries.SOURCE_BLUETOOTH: + hass.config_entries.flow.async_abort(flow["flow_id"]) + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -1845,7 +1844,10 @@ async def test_user_flow_select_ble_device( mock_discovery.return_value = [] # Inject BLE device with RPC-over-BLE enabled (no discovery flow created) - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_GEN3) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_GEN3) + + # Wait for bluetooth discovery to process + await hass.async_block_till_done() result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -2936,6 +2938,7 @@ async def test_zeroconf_sleeping_device_attempts_configure_no_url_available( hass.config.api = None mock_rpc_device.initialize.side_effect = DeviceConnectionError monkeypatch.setattr(mock_rpc_device, "connected", False) + monkeypatch.setattr(mock_rpc_device, "initialized", False) monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000) entry = MockConfigEntry( domain="shelly", @@ -3229,7 +3232,7 @@ async def test_bluetooth_discovery( ] # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3296,7 +3299,7 @@ async def test_bluetooth_provisioning_clears_match_history( ] # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_FOR_CLEAR_TEST) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_FOR_CLEAR_TEST) with patch( "homeassistant.components.shelly.config_flow.async_clear_address_from_match_history", @@ -3385,7 +3388,7 @@ async def test_bluetooth_factory_reset_rediscovery( # First discovery: device is already provisioned (no RPC-over-BLE) # Inject the device without RPC so it's in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_NO_RPC) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_NO_RPC) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3468,7 +3471,9 @@ async def test_bluetooth_discovery_mac_in_manufacturer_data( ) -> None: """Test bluetooth discovery with MAC in manufacturer data (newer devices).""" # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_MAC_IN_MANUFACTURER_DATA) + inject_bluetooth_service_info_bleak( + hass, BLE_DISCOVERY_INFO_MAC_IN_MANUFACTURER_DATA + ) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3491,7 +3496,7 @@ async def test_bluetooth_discovery_mac_unknown_model( ) -> None: """Test bluetooth discovery with MAC but unknown model ID.""" # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_MAC_UNKNOWN_MODEL) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_MAC_UNKNOWN_MODEL) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3513,7 +3518,7 @@ async def test_bluetooth_discovery_already_configured( ) -> None: """Test bluetooth discovery when device is already configured.""" # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) entry = MockConfigEntry( domain=DOMAIN, @@ -3542,7 +3547,7 @@ async def test_bluetooth_discovery_already_configured_clears_match_history( ) -> None: """Test bluetooth discovery clears match history when device already configured.""" # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) entry = MockConfigEntry( domain=DOMAIN, @@ -3604,7 +3609,7 @@ async def test_bluetooth_wifi_scan_success( ] # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3674,7 +3679,7 @@ async def test_bluetooth_wifi_scan_failure( mock_ble_rpc_device.wifi_scan.side_effect = DeviceConnectionError # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3757,7 +3762,7 @@ async def test_bluetooth_wifi_scan_ble_not_permitted( ) # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3791,7 +3796,7 @@ async def test_bluetooth_wifi_credentials_and_provision_success( ] # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3867,7 +3872,7 @@ async def test_bluetooth_wifi_provision_failure( mock_ble_rpc_device.wifi_setconfig.side_effect = DeviceConnectionError # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3955,7 +3960,7 @@ async def test_bluetooth_wifi_scan_unexpected_exception( mock_ble_rpc_device.wifi_scan.side_effect = RuntimeError("Unexpected error") # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -3986,7 +3991,7 @@ async def test_bluetooth_provision_unexpected_exception( mock_ble_rpc_device.wifi_setconfig.side_effect = RuntimeError("Unexpected error") # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4032,7 +4037,7 @@ async def test_bluetooth_provision_device_connection_error_after_wifi( ] # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4093,7 +4098,7 @@ async def test_bluetooth_provision_requires_auth( ] # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4171,7 +4176,7 @@ async def test_bluetooth_provision_validate_input_fails( ] # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4244,7 +4249,7 @@ async def test_bluetooth_provision_firmware_not_fully_provisioned( ] # Inject BLE device so it's available in the bluetooth scanner - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4300,7 +4305,7 @@ async def test_bluetooth_provision_with_zeroconf_discovery_fast_path( ] # Inject BLE device - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4388,7 +4393,7 @@ async def test_bluetooth_provision_timeout_active_lookup_fails( mock_ble_rpc_device.status = {"wifi": {"sta_ip": None}} # Inject BLE device - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4461,7 +4466,7 @@ async def test_bluetooth_provision_timeout_ble_fallback_succeeds( mock_ble_rpc_device.status = {"wifi": {"sta_ip": "192.168.1.100"}} # Inject BLE device - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4541,7 +4546,7 @@ async def test_bluetooth_provision_timeout_ble_fallback_fails( mock_ble_rpc_device.status = {"wifi": {"sta_ip": None}} # Inject BLE device - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4601,7 +4606,7 @@ async def test_bluetooth_provision_timeout_ble_exception( ] # Inject BLE device - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4675,7 +4680,7 @@ async def test_bluetooth_provision_secure_device_both_enabled( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4690,7 +4695,11 @@ async def test_bluetooth_provision_secure_device_both_enabled( ) # Provision and verify security calls - mock_device = create_mock_rpc_device() + mock_device = AsyncMock() + mock_device.initialize = AsyncMock() + mock_device.wifi_setconfig = AsyncMock(return_value={}) + mock_device.ble_setconfig = AsyncMock(return_value={"restart_required": False}) + mock_device.shutdown = AsyncMock() with ( patch( @@ -4735,7 +4744,7 @@ async def test_bluetooth_provision_secure_device_both_disabled( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4784,7 +4793,7 @@ async def test_bluetooth_provision_secure_device_only_ap_disabled( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4799,7 +4808,10 @@ async def test_bluetooth_provision_secure_device_only_ap_disabled( ) # Provision and verify only AP disabled - mock_device = create_mock_rpc_device() + mock_device = AsyncMock() + mock_device.initialize = AsyncMock() + mock_device.wifi_setconfig = AsyncMock(return_value={}) + mock_device.shutdown = AsyncMock() with ( patch( @@ -4843,7 +4855,7 @@ async def test_bluetooth_provision_secure_device_only_ble_disabled( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4858,7 +4870,10 @@ async def test_bluetooth_provision_secure_device_only_ble_disabled( ) # Provision and verify only BLE disabled - mock_device = create_mock_rpc_device() + mock_device = AsyncMock() + mock_device.initialize = AsyncMock() + mock_device.ble_setconfig = AsyncMock(return_value={"restart_required": False}) + mock_device.shutdown = AsyncMock() with ( patch( @@ -4902,7 +4917,7 @@ async def test_bluetooth_provision_secure_device_with_restart_required( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4917,9 +4932,12 @@ async def test_bluetooth_provision_secure_device_with_restart_required( ) # Provision and verify restart is triggered - mock_device = create_mock_rpc_device() + mock_device = AsyncMock() + mock_device.initialize = AsyncMock() + mock_device.wifi_setconfig = AsyncMock(return_value={}) mock_device.ble_setconfig = AsyncMock(return_value={"restart_required": True}) mock_device.trigger_reboot = AsyncMock() + mock_device.shutdown = AsyncMock() with ( patch( @@ -4963,7 +4981,7 @@ async def test_bluetooth_provision_secure_device_fails_gracefully( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -4978,8 +4996,10 @@ async def test_bluetooth_provision_secure_device_fails_gracefully( ) # Provision with security calls failing - wifi_setconfig will fail - mock_device = create_mock_rpc_device() + mock_device = AsyncMock() + mock_device.initialize = AsyncMock() mock_device.wifi_setconfig = AsyncMock(side_effect=RpcCallError("RPC call failed")) + mock_device.shutdown = AsyncMock() with ( patch( @@ -5014,7 +5034,7 @@ async def test_zeroconf_aborts_idle_ble_flow( ) -> None: """Test zeroconf discovery aborts idle BLE flow (lines 316-321).""" # Start BLE discovery flow and leave it idle at bluetooth_confirm - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) ble_result = await hass.config_entries.flow.async_init( DOMAIN, @@ -5072,7 +5092,7 @@ async def test_bluetooth_flow_abort_cleans_up_ble_connection( {"ssid": "MyNetwork", "rssi": -50, "auth": 2} ] - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) # Start BLE flow result = await hass.config_entries.flow.async_init( @@ -5111,7 +5131,7 @@ async def test_bluetooth_ble_initialize_failure_cleans_up( mock_device.initialize = AsyncMock(side_effect=DeviceConnectionError) mock_device.shutdown = AsyncMock() - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) # Start BLE flow result = await hass.config_entries.flow.async_init( @@ -5158,7 +5178,7 @@ async def test_bluetooth_ble_shutdown_exception_handled( # Make shutdown raise an exception mock_ble_rpc_device.shutdown.side_effect = RuntimeError("Shutdown failed") - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) # Start BLE flow result = await hass.config_entries.flow.async_init( @@ -5195,7 +5215,7 @@ async def test_bluetooth_provision_ble_reconnect_fails_during_ip_fetch( ] # Inject BLE device - await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO) + inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, From 2b7f301235f987a7d46e59d4a95a370cea5cf8ad Mon Sep 17 00:00:00 2001 From: Ariel Ebersberger Date: Mon, 30 Mar 2026 10:52:12 +0200 Subject: [PATCH 3/3] Remove unneeded block_till_done_calls --- tests/components/shelly/test_button.py | 1 - tests/components/shelly/test_coordinator.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/components/shelly/test_button.py b/tests/components/shelly/test_button.py index 93f2daf298eb94..b2aa57a9e4bbd3 100644 --- a/tests/components/shelly/test_button.py +++ b/tests/components/shelly/test_button.py @@ -504,7 +504,6 @@ async def test_rpc_smoke_mute_alarm_button( mock_rpc_device.initialize.side_effect = None mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) - await hass.async_block_till_done(wait_background_tasks=True) assert (state := hass.states.get(entity_id)) assert state.state == STATE_UNAVAILABLE diff --git a/tests/components/shelly/test_coordinator.py b/tests/components/shelly/test_coordinator.py index 20efccf0f198fc..e2e69c06a3095c 100644 --- a/tests/components/shelly/test_coordinator.py +++ b/tests/components/shelly/test_coordinator.py @@ -1105,7 +1105,6 @@ async def test_rpc_sleeping_device_late_setup( mock_rpc_device.initialize.side_effect = None mock_rpc_device.mock_online() await hass.async_block_till_done(wait_background_tasks=True) - await hass.async_block_till_done(wait_background_tasks=True) assert hass.states.get("sensor.test_name_temperature")