11"""Tests for the Plugwise Climate integration."""
22from datetime import timedelta
3- import logging
43from unittest .mock import MagicMock , patch
54
65from plugwise .exceptions import (
76 ConnectionFailedError ,
87 InvalidAuthentication ,
8+ InvalidSetupError ,
99 InvalidXMLError ,
1010 PlugwiseError ,
1111 ResponseError ,
1515
1616from freezegun .api import FrozenDateTimeFactory
1717from homeassistant .components .plugwise .const import DOMAIN
18+ from homeassistant .components .plugwise .coordinator import PlugwiseDataUpdateCoordinator
1819from homeassistant .config_entries import ConfigEntryState
1920from homeassistant .const import (
2021 CONF_HOST ,
2627 Platform ,
2728)
2829from homeassistant .core import HomeAssistant
30+ from homeassistant .exceptions import ConfigEntryError
2931from homeassistant .helpers import device_registry as dr , entity_registry as er
32+ from homeassistant .helpers .update_coordinator import UpdateFailed
3033from homeassistant .setup import async_setup_component
3134
3235from tests .common import MockConfigEntry , async_fire_time_changed
3336
34- LOGGER = logging .getLogger (__package__ )
35-
3637HA_PLUGWISE_SMILE_ASYNC_UPDATE = (
3738 "homeassistant.components.plugwise.coordinator.Smile.async_update"
3839)
@@ -96,23 +97,12 @@ async def test_load_unload_config_entry(
9697
9798@pytest .mark .parametrize ("chosen_env" , ["anna_heatpump_heating" ], indirect = True )
9899@pytest .mark .parametrize ("cooling_present" , [True ], indirect = True )
99- @pytest .mark .parametrize (
100- ("side_effect" , "entry_state" ),
101- [
102- (ConnectionFailedError , ConfigEntryState .SETUP_RETRY ),
103- (InvalidAuthentication , ConfigEntryState .SETUP_ERROR ),
104- (InvalidXMLError , ConfigEntryState .SETUP_RETRY ),
105- (ResponseError , ConfigEntryState .SETUP_RETRY ),
106- (PlugwiseError , ConfigEntryState .SETUP_RETRY ),
107- (UnsupportedDeviceError , ConfigEntryState .SETUP_ERROR ),
108- ],
109- )
100+ @pytest .mark .parametrize ("side_effect" , [PlugwiseError ])
110101async def test_gateway_config_entry_not_ready (
111102 hass : HomeAssistant ,
112103 mock_config_entry : MockConfigEntry ,
113104 mock_smile_anna : MagicMock ,
114105 side_effect : Exception ,
115- entry_state : ConfigEntryState ,
116106) -> None :
117107 """Test the Plugwise configuration entry not ready."""
118108 mock_smile_anna .async_update .side_effect = side_effect
@@ -122,7 +112,38 @@ async def test_gateway_config_entry_not_ready(
122112 await hass .async_block_till_done ()
123113
124114 assert len (mock_smile_anna .connect .mock_calls ) == 1
125- assert mock_config_entry .state is entry_state
115+ assert mock_config_entry .state is ConfigEntryState .SETUP_RETRY
116+
117+
118+ @pytest .mark .parametrize ("chosen_env" , ["anna_heatpump_heating" ], indirect = True )
119+ @pytest .mark .parametrize ("cooling_present" , [True ], indirect = True )
120+ @pytest .mark .parametrize (
121+ ("side_effect" , "expected_raise" ),
122+ [
123+ (ConnectionFailedError , UpdateFailed ),
124+ (InvalidAuthentication , ConfigEntryError ),
125+ (InvalidSetupError , ConfigEntryError ),
126+ (InvalidXMLError , UpdateFailed ),
127+ (ResponseError , UpdateFailed ),
128+ (UnsupportedDeviceError , ConfigEntryError ),
129+ ],
130+ )
131+ async def test_coordinator_connect_exceptions (
132+ hass : HomeAssistant ,
133+ mock_config_entry : MockConfigEntry ,
134+ mock_smile_anna : MagicMock ,
135+ side_effect : Exception ,
136+ expected_raise : Exception ,
137+ ) -> None :
138+ """Ensure _connect raises translated errors."""
139+ mock_smile_anna .connect .side_effect = side_effect
140+ coordinator = PlugwiseDataUpdateCoordinator (
141+ hass ,
142+ cooldown = 0 ,
143+ config_entry = mock_config_entry ,
144+ )
145+ with pytest .raises (expected_raise ):
146+ await coordinator ._connect ()
126147
127148
128149@pytest .mark .parametrize ("chosen_env" , ["p1v4_442_single" ], indirect = True )
@@ -160,7 +181,7 @@ async def check_migration(
160181 mock_config_entry .add_to_hass (hass )
161182
162183 entity_registry = er .async_get (hass )
163- entity : entity_registry .RegistryEntry = entity_registry .async_get_or_create (
184+ entity : er .RegistryEntry = entity_registry .async_get_or_create (
164185 ** entitydata ,
165186 config_entry = mock_config_entry ,
166187 )
@@ -245,38 +266,6 @@ async def test_migrate_unique_id_relay(
245266 hass , mock_config_entry , entitydata , old_unique_id , new_unique_id
246267 )
247268
248- #### pw-beta only ####
249- @pytest .mark .parametrize ("chosen_env" , ["m_anna_heatpump_cooling" ], indirect = True )
250- @pytest .mark .parametrize ("cooling_present" , [True ], indirect = True )
251- async def test_entry_migration (
252- hass : HomeAssistant , mock_smile_anna : MagicMock
253- ) -> None :
254- """Test config entry 1.2 -> 1.1 migration."""
255- entry = MockConfigEntry (
256- domain = DOMAIN ,
257- data = {
258- CONF_HOST : "127.0.0.1" ,
259- CONF_MAC : "AA:BB:CC:DD:EE:FF" ,
260- CONF_PASSWORD : "test-password" ,
261- CONF_PORT : 80 ,
262- CONF_TIMEOUT : 30 ,
263- CONF_USERNAME : "smile" ,
264- },
265- minor_version = 2 ,
266- version = 1 ,
267- unique_id = "smile98765" ,
268- )
269-
270- entry .runtime_data = MagicMock (api = mock_smile_anna )
271- entry .add_to_hass (hass )
272- await hass .config_entries .async_setup (entry .entry_id )
273- await hass .async_block_till_done ()
274-
275- assert entry .version == 1
276- assert entry .minor_version == 1
277- assert entry .data .get (CONF_TIMEOUT ) is None
278- assert entry .state is ConfigEntryState .LOADED
279-
280269
281270@pytest .mark .parametrize ("chosen_env" , ["m_adam_heating" ], indirect = True )
282271@pytest .mark .parametrize ("cooling_present" , [False ], indirect = True )
@@ -358,3 +347,67 @@ async def test_update_device(
358347 for device_entry in device_registry .devices .values ():
359348 item_list .extend (x [1 ] for x in device_entry .identifiers )
360349 assert "1772a4ea304041adb83f357b751341ff" not in item_list
350+
351+
352+ @pytest .mark .parametrize ("chosen_env" , ["m_adam_heating" ], indirect = True )
353+ @pytest .mark .parametrize ("cooling_present" , [False ], indirect = True )
354+ async def test_delete_removed_device (
355+ hass : HomeAssistant ,
356+ mock_config_entry : MockConfigEntry ,
357+ mock_smile_adam_heat_cool : MagicMock ,
358+ device_registry : dr .DeviceRegistry ,
359+ init_integration : MockConfigEntry ,
360+ ) -> None :
361+ """Test device removal at integration init."""
362+ data = mock_smile_adam_heat_cool .async_update .return_value
363+ mock_config_entry .add_to_hass (hass )
364+ assert await async_setup_component (hass , DOMAIN , {})
365+ await hass .async_block_till_done ()
366+
367+ item_list : list [str ] = []
368+ for device_entry in device_registry .devices .values ():
369+ item_list .extend (x [1 ] for x in device_entry .identifiers )
370+ assert "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6" in item_list
371+
372+ data .pop ("14df5c4dc8cb4ba69f9d1ac0eaf7c5c6" )
373+ with patch (HA_PLUGWISE_SMILE_ASYNC_UPDATE , return_value = data ):
374+ await hass .config_entries .async_reload (init_integration .entry_id )
375+ await hass .async_block_till_done ()
376+
377+ item_list = []
378+ for device_entry in device_registry .devices .values ():
379+ item_list .extend (x [1 ] for x in device_entry .identifiers )
380+ assert "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6" not in item_list
381+
382+
383+ #### pw-beta only ####
384+ @pytest .mark .parametrize ("chosen_env" , ["m_anna_heatpump_cooling" ], indirect = True )
385+ @pytest .mark .parametrize ("cooling_present" , [True ], indirect = True )
386+ async def test_entry_migration (
387+ hass : HomeAssistant , mock_smile_anna : MagicMock
388+ ) -> None :
389+ """Test config entry 1.2 -> 1.1 migration."""
390+ entry = MockConfigEntry (
391+ domain = DOMAIN ,
392+ data = {
393+ CONF_HOST : "127.0.0.1" ,
394+ CONF_MAC : "AA:BB:CC:DD:EE:FF" ,
395+ CONF_PASSWORD : "test-password" ,
396+ CONF_PORT : 80 ,
397+ CONF_TIMEOUT : 30 ,
398+ CONF_USERNAME : "smile" ,
399+ },
400+ minor_version = 2 ,
401+ version = 1 ,
402+ unique_id = "smile98765" ,
403+ )
404+
405+ entry .runtime_data = MagicMock (api = mock_smile_anna )
406+ entry .add_to_hass (hass )
407+ await hass .config_entries .async_setup (entry .entry_id )
408+ await hass .async_block_till_done ()
409+
410+ assert entry .version == 1
411+ assert entry .minor_version == 1
412+ assert entry .data .get (CONF_TIMEOUT ) is None
413+ assert entry .state is ConfigEntryState .LOADED
0 commit comments