Ensure config entry operations are always holding the lock (#117214)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com> Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
f55fcca0bb
commit
481de8cdc9
25 changed files with 256 additions and 84 deletions
|
@ -523,8 +523,14 @@ class ConfigEntry(Generic[_DataT]):
|
||||||
):
|
):
|
||||||
raise OperationNotAllowed(
|
raise OperationNotAllowed(
|
||||||
f"The config entry {self.title} ({self.domain}) with entry_id"
|
f"The config entry {self.title} ({self.domain}) with entry_id"
|
||||||
f" {self.entry_id} cannot be setup because is already loaded in the"
|
f" {self.entry_id} cannot be set up because it is already loaded "
|
||||||
f" {self.state} state"
|
f"in the {self.state} state"
|
||||||
|
)
|
||||||
|
if not self.setup_lock.locked():
|
||||||
|
raise OperationNotAllowed(
|
||||||
|
f"The config entry {self.title} ({self.domain}) with entry_id"
|
||||||
|
f" {self.entry_id} cannot be set up because it does not hold "
|
||||||
|
"the setup lock"
|
||||||
)
|
)
|
||||||
self._async_set_state(hass, ConfigEntryState.SETUP_IN_PROGRESS, None)
|
self._async_set_state(hass, ConfigEntryState.SETUP_IN_PROGRESS, None)
|
||||||
|
|
||||||
|
@ -763,6 +769,13 @@ class ConfigEntry(Generic[_DataT]):
|
||||||
component = await integration.async_get_component()
|
component = await integration.async_get_component()
|
||||||
|
|
||||||
if domain_is_integration := self.domain == integration.domain:
|
if domain_is_integration := self.domain == integration.domain:
|
||||||
|
if not self.setup_lock.locked():
|
||||||
|
raise OperationNotAllowed(
|
||||||
|
f"The config entry {self.title} ({self.domain}) with entry_id"
|
||||||
|
f" {self.entry_id} cannot be unloaded because it does not hold "
|
||||||
|
"the setup lock"
|
||||||
|
)
|
||||||
|
|
||||||
if not self.state.recoverable:
|
if not self.state.recoverable:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -807,6 +820,13 @@ class ConfigEntry(Generic[_DataT]):
|
||||||
if self.source == SOURCE_IGNORE:
|
if self.source == SOURCE_IGNORE:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not self.setup_lock.locked():
|
||||||
|
raise OperationNotAllowed(
|
||||||
|
f"The config entry {self.title} ({self.domain}) with entry_id"
|
||||||
|
f" {self.entry_id} cannot be removed because it does not hold "
|
||||||
|
"the setup lock"
|
||||||
|
)
|
||||||
|
|
||||||
if not (integration := self._integration_for_domain):
|
if not (integration := self._integration_for_domain):
|
||||||
try:
|
try:
|
||||||
integration = await loader.async_get_integration(hass, self.domain)
|
integration = await loader.async_get_integration(hass, self.domain)
|
||||||
|
@ -1639,7 +1659,7 @@ class ConfigEntries:
|
||||||
if not entry.state.recoverable:
|
if not entry.state.recoverable:
|
||||||
unload_success = entry.state is not ConfigEntryState.FAILED_UNLOAD
|
unload_success = entry.state is not ConfigEntryState.FAILED_UNLOAD
|
||||||
else:
|
else:
|
||||||
unload_success = await self.async_unload(entry_id)
|
unload_success = await self.async_unload(entry_id, _lock=False)
|
||||||
|
|
||||||
await entry.async_remove(self.hass)
|
await entry.async_remove(self.hass)
|
||||||
|
|
||||||
|
@ -1741,7 +1761,7 @@ class ConfigEntries:
|
||||||
|
|
||||||
self._entries = entries
|
self._entries = entries
|
||||||
|
|
||||||
async def async_setup(self, entry_id: str) -> bool:
|
async def async_setup(self, entry_id: str, _lock: bool = True) -> bool:
|
||||||
"""Set up a config entry.
|
"""Set up a config entry.
|
||||||
|
|
||||||
Return True if entry has been successfully loaded.
|
Return True if entry has been successfully loaded.
|
||||||
|
@ -1752,13 +1772,17 @@ class ConfigEntries:
|
||||||
if entry.state is not ConfigEntryState.NOT_LOADED:
|
if entry.state is not ConfigEntryState.NOT_LOADED:
|
||||||
raise OperationNotAllowed(
|
raise OperationNotAllowed(
|
||||||
f"The config entry {entry.title} ({entry.domain}) with entry_id"
|
f"The config entry {entry.title} ({entry.domain}) with entry_id"
|
||||||
f" {entry.entry_id} cannot be setup because is already loaded in the"
|
f" {entry.entry_id} cannot be set up because it is already loaded"
|
||||||
f" {entry.state} state"
|
f" in the {entry.state} state"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Setup Component if not set up yet
|
# Setup Component if not set up yet
|
||||||
if entry.domain in self.hass.config.components:
|
if entry.domain in self.hass.config.components:
|
||||||
await entry.async_setup(self.hass)
|
if _lock:
|
||||||
|
async with entry.setup_lock:
|
||||||
|
await entry.async_setup(self.hass)
|
||||||
|
else:
|
||||||
|
await entry.async_setup(self.hass)
|
||||||
else:
|
else:
|
||||||
# Setting up the component will set up all its config entries
|
# Setting up the component will set up all its config entries
|
||||||
result = await async_setup_component(
|
result = await async_setup_component(
|
||||||
|
@ -1772,7 +1796,7 @@ class ConfigEntries:
|
||||||
entry.state is ConfigEntryState.LOADED # type: ignore[comparison-overlap]
|
entry.state is ConfigEntryState.LOADED # type: ignore[comparison-overlap]
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_unload(self, entry_id: str) -> bool:
|
async def async_unload(self, entry_id: str, _lock: bool = True) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
if (entry := self.async_get_entry(entry_id)) is None:
|
if (entry := self.async_get_entry(entry_id)) is None:
|
||||||
raise UnknownEntry
|
raise UnknownEntry
|
||||||
|
@ -1784,6 +1808,10 @@ class ConfigEntries:
|
||||||
f" recoverable state ({entry.state})"
|
f" recoverable state ({entry.state})"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if _lock:
|
||||||
|
async with entry.setup_lock:
|
||||||
|
return await entry.async_unload(self.hass)
|
||||||
|
|
||||||
return await entry.async_unload(self.hass)
|
return await entry.async_unload(self.hass)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -1825,12 +1853,12 @@ class ConfigEntries:
|
||||||
return entry.state is ConfigEntryState.LOADED
|
return entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
async with entry.setup_lock:
|
async with entry.setup_lock:
|
||||||
unload_result = await self.async_unload(entry_id)
|
unload_result = await self.async_unload(entry_id, _lock=False)
|
||||||
|
|
||||||
if not unload_result or entry.disabled_by:
|
if not unload_result or entry.disabled_by:
|
||||||
return unload_result
|
return unload_result
|
||||||
|
|
||||||
return await self.async_setup(entry_id)
|
return await self.async_setup(entry_id, _lock=False)
|
||||||
|
|
||||||
async def async_set_disabled_by(
|
async def async_set_disabled_by(
|
||||||
self, entry_id: str, disabled_by: ConfigEntryDisabler | None
|
self, entry_id: str, disabled_by: ConfigEntryDisabler | None
|
||||||
|
|
|
@ -138,7 +138,7 @@ async def test_load_and_unload(
|
||||||
assert config_entry.state is ConfigEntryState.LOADED
|
assert config_entry.state is ConfigEntryState.LOADED
|
||||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||||
|
|
||||||
assert await config_entry.async_unload(hass)
|
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ async def test_stale_device_removal(
|
||||||
for device in device_entries_other
|
for device in device_entries_other
|
||||||
)
|
)
|
||||||
|
|
||||||
assert await config_entry.async_unload(hass)
|
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ async def mock_entry_fixture(hass, filter_schema, mock_create_batch, mock_send_b
|
||||||
|
|
||||||
yield entry
|
yield entry
|
||||||
|
|
||||||
await entry.async_unload(hass)
|
await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
# fixtures for init tests
|
# fixtures for init tests
|
||||||
|
|
|
@ -150,7 +150,8 @@ async def test_unload_entry_multiple_gateways_parallel(
|
||||||
assert len(hass.data[DECONZ_DOMAIN]) == 2
|
assert len(hass.data[DECONZ_DOMAIN]) == 2
|
||||||
|
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
config_entry.async_unload(hass), config_entry2.async_unload(hass)
|
hass.config_entries.async_unload(config_entry.entry_id),
|
||||||
|
hass.config_entries.async_unload(config_entry2.entry_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert len(hass.data[DECONZ_DOMAIN]) == 0
|
assert len(hass.data[DECONZ_DOMAIN]) == 0
|
||||||
|
|
|
@ -447,7 +447,7 @@ async def test_unload_entry(hass: HomeAssistant, mock_get_station) -> None:
|
||||||
state = hass.states.get("sensor.my_station_water_level_stage")
|
state = hass.states.get("sensor.my_station_water_level_stage")
|
||||||
assert state.state == "5"
|
assert state.state == "5"
|
||||||
|
|
||||||
assert await entry.async_unload(hass)
|
await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
|
||||||
# And the entity should be unavailable
|
# And the entity should be unavailable
|
||||||
assert (
|
assert (
|
||||||
|
|
|
@ -146,8 +146,7 @@ async def test_service_called_with_unloaded_entry(
|
||||||
service: str,
|
service: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test service calls with unloaded config entry."""
|
"""Test service calls with unloaded config entry."""
|
||||||
|
await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
||||||
await mock_config_entry.async_unload(hass)
|
|
||||||
|
|
||||||
data = {"config_entry": mock_config_entry.entry_id, "incl_vat": True}
|
data = {"config_entry": mock_config_entry.entry_id, "incl_vat": True}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ async def test_service_unloaded_entry(hass: HomeAssistant) -> None:
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert config_entry
|
assert config_entry
|
||||||
await config_entry.async_unload(hass)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
|
|
||||||
with pytest.raises(HomeAssistantError) as exc:
|
with pytest.raises(HomeAssistantError) as exc:
|
||||||
await hass.services.async_call(DOMAIN, SERVICE_NAME, blocking=True)
|
await hass.services.async_call(DOMAIN, SERVICE_NAME, blocking=True)
|
||||||
|
|
|
@ -347,7 +347,7 @@ async def test_unload_config_entry(
|
||||||
"""Test the player is set unavailable when the config entry is unloaded."""
|
"""Test the player is set unavailable when the config entry is unloaded."""
|
||||||
assert hass.states.get(TEST_MASTER_ENTITY_NAME)
|
assert hass.states.get(TEST_MASTER_ENTITY_NAME)
|
||||||
assert hass.states.get(TEST_ZONE_ENTITY_NAMES[0])
|
assert hass.states.get(TEST_ZONE_ENTITY_NAMES[0])
|
||||||
await config_entry.async_unload(hass)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
assert hass.states.get(TEST_MASTER_ENTITY_NAME).state == STATE_UNAVAILABLE
|
assert hass.states.get(TEST_MASTER_ENTITY_NAME).state == STATE_UNAVAILABLE
|
||||||
assert hass.states.get(TEST_ZONE_ENTITY_NAMES[0]).state == STATE_UNAVAILABLE
|
assert hass.states.get(TEST_ZONE_ENTITY_NAMES[0]).state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ async def test_service_unloaded_entry(
|
||||||
init_integration: MockConfigEntry,
|
init_integration: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test service not called when config entry unloaded."""
|
"""Test service not called when config entry unloaded."""
|
||||||
await init_integration.async_unload(hass)
|
await hass.config_entries.async_unload(init_integration.entry_id)
|
||||||
|
|
||||||
device_entry = device_registry.async_get_device(
|
device_entry = device_registry.async_get_device(
|
||||||
identifiers={(DOMAIN, "abcdef-123456")}
|
identifiers={(DOMAIN, "abcdef-123456")}
|
||||||
|
|
|
@ -688,7 +688,7 @@ async def test_unload_config_entry(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the player is set unavailable when the config entry is unloaded."""
|
"""Test the player is set unavailable when the config entry is unloaded."""
|
||||||
await setup_platform(hass, config_entry, config)
|
await setup_platform(hass, config_entry, config)
|
||||||
await config_entry.async_unload(hass)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
assert hass.states.get("media_player.test_player").state == STATE_UNAVAILABLE
|
assert hass.states.get("media_player.test_player").state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ async def test_async_remove_entry(hass: HomeAssistant) -> None:
|
||||||
assert hkid in hass.data[ENTITY_MAP].storage_data
|
assert hkid in hass.data[ENTITY_MAP].storage_data
|
||||||
|
|
||||||
# Remove it via config entry and number of pairings should go down
|
# Remove it via config entry and number of pairings should go down
|
||||||
await helper.config_entry.async_remove(hass)
|
await hass.config_entries.async_remove(helper.config_entry.entry_id)
|
||||||
assert len(controller.pairings) == 0
|
assert len(controller.pairings) == 0
|
||||||
|
|
||||||
assert hkid not in hass.data[ENTITY_MAP].storage_data
|
assert hkid not in hass.data[ENTITY_MAP].storage_data
|
||||||
|
|
|
@ -364,7 +364,7 @@ async def test_light_unloaded_removed(hass: HomeAssistant) -> None:
|
||||||
state = await helper.poll_and_get_state()
|
state = await helper.poll_and_get_state()
|
||||||
assert state.state == "off"
|
assert state.state == "off"
|
||||||
|
|
||||||
unload_result = await helper.config_entry.async_unload(hass)
|
unload_result = await hass.config_entries.async_unload(helper.config_entry.entry_id)
|
||||||
assert unload_result is True
|
assert unload_result is True
|
||||||
|
|
||||||
# Make sure entity is set to unavailable state
|
# Make sure entity is set to unavailable state
|
||||||
|
@ -374,11 +374,11 @@ async def test_light_unloaded_removed(hass: HomeAssistant) -> None:
|
||||||
conn = hass.data[KNOWN_DEVICES]["00:00:00:00:00:00"]
|
conn = hass.data[KNOWN_DEVICES]["00:00:00:00:00:00"]
|
||||||
assert not conn.pollable_characteristics
|
assert not conn.pollable_characteristics
|
||||||
|
|
||||||
await helper.config_entry.async_remove(hass)
|
await hass.config_entries.async_remove(helper.config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# Make sure entity is removed
|
# Make sure entity is removed
|
||||||
assert hass.states.get(helper.entity_id).state == STATE_UNAVAILABLE
|
assert hass.states.get(helper.entity_id) is None
|
||||||
|
|
||||||
|
|
||||||
async def test_migrate_unique_id(
|
async def test_migrate_unique_id(
|
||||||
|
|
|
@ -76,7 +76,7 @@ async def test_entry_startup_and_unload(
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert await config_entry.async_unload(hass)
|
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -449,7 +449,7 @@ async def test_handle_cleanup_exception(
|
||||||
# Fail cleaning up
|
# Fail cleaning up
|
||||||
mock_imap_protocol.close.side_effect = imap_close
|
mock_imap_protocol.close.side_effect = imap_close
|
||||||
|
|
||||||
assert await config_entry.async_unload(hass)
|
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "Error while cleaning up imap connection" in caplog.text
|
assert "Error while cleaning up imap connection" in caplog.text
|
||||||
|
|
||||||
|
|
|
@ -290,7 +290,7 @@ async def test_reload_service(
|
||||||
async def test_service_setup_failed(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
async def test_service_setup_failed(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||||
"""Test service setup failed."""
|
"""Test service setup failed."""
|
||||||
await knx.setup_integration({})
|
await knx.setup_integration({})
|
||||||
await knx.mock_config_entry.async_unload(hass)
|
await hass.config_entries.async_unload(knx.mock_config_entry.entry_id)
|
||||||
|
|
||||||
with pytest.raises(HomeAssistantError) as exc_info:
|
with pytest.raises(HomeAssistantError) as exc_info:
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
|
|
|
@ -1825,7 +1825,7 @@ async def help_test_reloadable(
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
mqtt_client_mock.connect.return_value = 0
|
mqtt_client_mock.connect.return_value = 0
|
||||||
with patch("homeassistant.config.load_yaml_config_file", return_value=old_config):
|
with patch("homeassistant.config.load_yaml_config_file", return_value=old_config):
|
||||||
await entry.async_setup(hass)
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
|
||||||
assert hass.states.get(f"{domain}.test_old_1")
|
assert hass.states.get(f"{domain}.test_old_1")
|
||||||
assert hass.states.get(f"{domain}.test_old_2")
|
assert hass.states.get(f"{domain}.test_old_2")
|
||||||
|
|
|
@ -1927,7 +1927,7 @@ async def test_reload_entry_with_restored_subscriptions(
|
||||||
hass.config.components.add(mqtt.DOMAIN)
|
hass.config.components.add(mqtt.DOMAIN)
|
||||||
mqtt_client_mock.connect.return_value = 0
|
mqtt_client_mock.connect.return_value = 0
|
||||||
with patch("homeassistant.config.load_yaml_config_file", return_value={}):
|
with patch("homeassistant.config.load_yaml_config_file", return_value={}):
|
||||||
await entry.async_setup(hass)
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
|
||||||
await mqtt.async_subscribe(hass, "test-topic", record_calls)
|
await mqtt.async_subscribe(hass, "test-topic", record_calls)
|
||||||
await mqtt.async_subscribe(hass, "wild/+/card", record_calls)
|
await mqtt.async_subscribe(hass, "wild/+/card", record_calls)
|
||||||
|
|
|
@ -1369,7 +1369,7 @@ async def test_upnp_shutdown(
|
||||||
state = hass.states.get(ENTITY_ID)
|
state = hass.states.get(ENTITY_ID)
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
assert await entry.async_unload(hass)
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
|
||||||
state = hass.states.get(ENTITY_ID)
|
state = hass.states.get(ENTITY_ID)
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
|
|
|
@ -473,7 +473,7 @@ async def test_service_config_entry_not_loaded(
|
||||||
assert hass.services.has_service(DOMAIN, SERVICE_SET_COLOR_MODE)
|
assert hass.services.has_service(DOMAIN, SERVICE_SET_COLOR_MODE)
|
||||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||||
|
|
||||||
await mock_config_entry.async_unload(hass)
|
await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
|
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ async def test_tv_load_and_unload(
|
||||||
assert len(hass.states.async_entity_ids(Platform.MEDIA_PLAYER)) == 1
|
assert len(hass.states.async_entity_ids(Platform.MEDIA_PLAYER)) == 1
|
||||||
assert DOMAIN in hass.data
|
assert DOMAIN in hass.data
|
||||||
|
|
||||||
assert await config_entry.async_unload(hass)
|
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
entities = hass.states.async_entity_ids(Platform.MEDIA_PLAYER)
|
entities = hass.states.async_entity_ids(Platform.MEDIA_PLAYER)
|
||||||
assert len(entities) == 1
|
assert len(entities) == 1
|
||||||
|
@ -67,7 +67,7 @@ async def test_speaker_load_and_unload(
|
||||||
assert len(hass.states.async_entity_ids(Platform.MEDIA_PLAYER)) == 1
|
assert len(hass.states.async_entity_ids(Platform.MEDIA_PLAYER)) == 1
|
||||||
assert DOMAIN in hass.data
|
assert DOMAIN in hass.data
|
||||||
|
|
||||||
assert await config_entry.async_unload(hass)
|
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
entities = hass.states.async_entity_ids(Platform.MEDIA_PLAYER)
|
entities = hass.states.async_entity_ids(Platform.MEDIA_PLAYER)
|
||||||
assert len(entities) == 1
|
assert len(entities) == 1
|
||||||
|
|
|
@ -74,7 +74,7 @@ async def test_unload_config_entry(hass: HomeAssistant) -> None:
|
||||||
assert hass.data[DOMAIN][config_entry.entry_id]
|
assert hass.data[DOMAIN][config_entry.entry_id]
|
||||||
|
|
||||||
with patch.object(MockWs66i, "close") as method_call:
|
with patch.object(MockWs66i, "close") as method_call:
|
||||||
await config_entry.async_unload(hass)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert method_call.called
|
assert method_call.called
|
||||||
|
|
|
@ -824,7 +824,7 @@ async def test_device_types(
|
||||||
target_properties["music_mode"] = False
|
target_properties["music_mode"] = False
|
||||||
assert dict(state.attributes) == target_properties
|
assert dict(state.attributes) == target_properties
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await config_entry.async_remove(hass)
|
await hass.config_entries.async_remove(config_entry.entry_id)
|
||||||
registry = er.async_get(hass)
|
registry = er.async_get(hass)
|
||||||
registry.async_clear_config_entry(config_entry.entry_id)
|
registry.async_clear_config_entry(config_entry.entry_id)
|
||||||
mocked_bulb.last_properties["nl_br"] = original_nightlight_brightness
|
mocked_bulb.last_properties["nl_br"] = original_nightlight_brightness
|
||||||
|
@ -846,7 +846,7 @@ async def test_device_types(
|
||||||
assert dict(state.attributes) == nightlight_mode_properties
|
assert dict(state.attributes) == nightlight_mode_properties
|
||||||
|
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await config_entry.async_remove(hass)
|
await hass.config_entries.async_remove(config_entry.entry_id)
|
||||||
registry.async_clear_config_entry(config_entry.entry_id)
|
registry.async_clear_config_entry(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
mocked_bulb.last_properties.pop("active_mode")
|
mocked_bulb.last_properties.pop("active_mode")
|
||||||
|
@ -869,7 +869,7 @@ async def test_device_types(
|
||||||
assert dict(state.attributes) == nightlight_entity_properties
|
assert dict(state.attributes) == nightlight_entity_properties
|
||||||
|
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await config_entry.async_remove(hass)
|
await hass.config_entries.async_remove(config_entry.entry_id)
|
||||||
registry.async_clear_config_entry(config_entry.entry_id)
|
registry.async_clear_config_entry(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import pytest
|
||||||
import zigpy.backups
|
import zigpy.backups
|
||||||
import zigpy.state
|
import zigpy.state
|
||||||
|
|
||||||
from homeassistant.components import zha
|
|
||||||
from homeassistant.components.zha import api
|
from homeassistant.components.zha import api
|
||||||
from homeassistant.components.zha.core.const import RadioType
|
from homeassistant.components.zha.core.const import RadioType
|
||||||
from homeassistant.components.zha.core.helpers import get_zha_gateway
|
from homeassistant.components.zha.core.helpers import get_zha_gateway
|
||||||
|
@ -43,7 +42,7 @@ async def test_async_get_network_settings_inactive(
|
||||||
await setup_zha()
|
await setup_zha()
|
||||||
|
|
||||||
gateway = get_zha_gateway(hass)
|
gateway = get_zha_gateway(hass)
|
||||||
await zha.async_unload_entry(hass, gateway.config_entry)
|
await hass.config_entries.async_unload(gateway.config_entry.entry_id)
|
||||||
|
|
||||||
backup = zigpy.backups.NetworkBackup()
|
backup = zigpy.backups.NetworkBackup()
|
||||||
backup.network_info.channel = 20
|
backup.network_info.channel = 20
|
||||||
|
@ -70,7 +69,7 @@ async def test_async_get_network_settings_missing(
|
||||||
await setup_zha()
|
await setup_zha()
|
||||||
|
|
||||||
gateway = get_zha_gateway(hass)
|
gateway = get_zha_gateway(hass)
|
||||||
await gateway.config_entry.async_unload(hass)
|
await hass.config_entries.async_unload(gateway.config_entry.entry_id)
|
||||||
|
|
||||||
# Network settings were never loaded for whatever reason
|
# Network settings were never loaded for whatever reason
|
||||||
zigpy_app_controller.state.network_info = zigpy.state.NetworkInfo()
|
zigpy_app_controller.state.network_info = zigpy.state.NetworkInfo()
|
||||||
|
|
|
@ -487,7 +487,7 @@ async def test_group_probe_cleanup_called(
|
||||||
"""Test cleanup happens when ZHA is unloaded."""
|
"""Test cleanup happens when ZHA is unloaded."""
|
||||||
await setup_zha()
|
await setup_zha()
|
||||||
disc.GROUP_PROBE.cleanup = mock.Mock(wraps=disc.GROUP_PROBE.cleanup)
|
disc.GROUP_PROBE.cleanup = mock.Mock(wraps=disc.GROUP_PROBE.cleanup)
|
||||||
await config_entry.async_unload(hass_disable_services)
|
await hass_disable_services.config_entries.async_unload(config_entry.entry_id)
|
||||||
await hass_disable_services.async_block_till_done()
|
await hass_disable_services.async_block_till_done()
|
||||||
disc.GROUP_PROBE.cleanup.assert_called()
|
disc.GROUP_PROBE.cleanup.assert_called()
|
||||||
|
|
||||||
|
|
|
@ -566,7 +566,9 @@ async def hass(
|
||||||
if loaded_entries:
|
if loaded_entries:
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
*(
|
*(
|
||||||
create_eager_task(config_entry.async_unload(hass))
|
create_eager_task(
|
||||||
|
hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
|
)
|
||||||
for config_entry in loaded_entries
|
for config_entry in loaded_entries
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -431,7 +431,7 @@ async def test_remove_entry_cancels_reauth(
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
flows = hass.config_entries.flow.async_progress_by_handler("test")
|
flows = hass.config_entries.flow.async_progress_by_handler("test")
|
||||||
|
@ -472,7 +472,7 @@ async def test_remove_entry_handles_callback_error(
|
||||||
# Check all config entries exist
|
# Check all config entries exist
|
||||||
assert manager.async_entry_ids() == ["test1"]
|
assert manager.async_entry_ids() == ["test1"]
|
||||||
# Setup entry
|
# Setup entry
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# Remove entry
|
# Remove entry
|
||||||
|
@ -1036,7 +1036,9 @@ async def test_reauth_notification(hass: HomeAssistant) -> None:
|
||||||
assert "config_entry_reconfigure" not in notifications
|
assert "config_entry_reconfigure" not in notifications
|
||||||
|
|
||||||
|
|
||||||
async def test_reauth_issue(hass: HomeAssistant) -> None:
|
async def test_reauth_issue(
|
||||||
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
|
) -> None:
|
||||||
"""Test that we create/delete an issue when source is reauth."""
|
"""Test that we create/delete an issue when source is reauth."""
|
||||||
issue_registry = ir.async_get(hass)
|
issue_registry = ir.async_get(hass)
|
||||||
assert len(issue_registry.issues) == 0
|
assert len(issue_registry.issues) == 0
|
||||||
|
@ -1048,7 +1050,7 @@ async def test_reauth_issue(hass: HomeAssistant) -> None:
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
flows = hass.config_entries.flow.async_progress_by_handler("test")
|
flows = hass.config_entries.flow.async_progress_by_handler("test")
|
||||||
|
@ -1175,10 +1177,13 @@ async def test_update_entry_options_and_trigger_listener(
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_raise_not_ready(
|
async def test_setup_raise_not_ready(
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
hass: HomeAssistant,
|
||||||
|
manager: config_entries.ConfigEntries,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test a setup raising not ready."""
|
"""Test a setup raising not ready."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
mock_setup_entry = AsyncMock(
|
mock_setup_entry = AsyncMock(
|
||||||
side_effect=ConfigEntryNotReady("The internet connection is offline")
|
side_effect=ConfigEntryNotReady("The internet connection is offline")
|
||||||
|
@ -1187,7 +1192,7 @@ async def test_setup_raise_not_ready(
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
with patch("homeassistant.config_entries.async_call_later") as mock_call:
|
with patch("homeassistant.config_entries.async_call_later") as mock_call:
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
|
|
||||||
assert len(mock_call.mock_calls) == 1
|
assert len(mock_call.mock_calls) == 1
|
||||||
assert (
|
assert (
|
||||||
|
@ -1212,10 +1217,13 @@ async def test_setup_raise_not_ready(
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_raise_not_ready_from_exception(
|
async def test_setup_raise_not_ready_from_exception(
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
hass: HomeAssistant,
|
||||||
|
manager: config_entries.ConfigEntries,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test a setup raising not ready from another exception."""
|
"""Test a setup raising not ready from another exception."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
original_exception = HomeAssistantError("The device dropped the connection")
|
original_exception = HomeAssistantError("The device dropped the connection")
|
||||||
config_entry_exception = ConfigEntryNotReady()
|
config_entry_exception = ConfigEntryNotReady()
|
||||||
|
@ -1226,7 +1234,7 @@ async def test_setup_raise_not_ready_from_exception(
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
with patch("homeassistant.config_entries.async_call_later") as mock_call:
|
with patch("homeassistant.config_entries.async_call_later") as mock_call:
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
|
|
||||||
assert len(mock_call.mock_calls) == 1
|
assert len(mock_call.mock_calls) == 1
|
||||||
assert (
|
assert (
|
||||||
|
@ -1235,29 +1243,35 @@ async def test_setup_raise_not_ready_from_exception(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_retrying_during_unload(hass: HomeAssistant) -> None:
|
async def test_setup_retrying_during_unload(
|
||||||
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
|
) -> None:
|
||||||
"""Test if we unload an entry that is in retry mode."""
|
"""Test if we unload an entry that is in retry mode."""
|
||||||
entry = MockConfigEntry(domain="test")
|
entry = MockConfigEntry(domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
mock_setup_entry = AsyncMock(side_effect=ConfigEntryNotReady)
|
mock_setup_entry = AsyncMock(side_effect=ConfigEntryNotReady)
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
with patch("homeassistant.config_entries.async_call_later") as mock_call:
|
with patch("homeassistant.config_entries.async_call_later") as mock_call:
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
|
|
||||||
assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
||||||
assert len(mock_call.return_value.mock_calls) == 0
|
assert len(mock_call.return_value.mock_calls) == 0
|
||||||
|
|
||||||
await entry.async_unload(hass)
|
await manager.async_unload(entry.entry_id)
|
||||||
|
|
||||||
assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
|
assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
|
||||||
assert len(mock_call.return_value.mock_calls) == 1
|
assert len(mock_call.return_value.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_retrying_during_unload_before_started(hass: HomeAssistant) -> None:
|
async def test_setup_retrying_during_unload_before_started(
|
||||||
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
|
) -> None:
|
||||||
"""Test if we unload an entry that is in retry mode before started."""
|
"""Test if we unload an entry that is in retry mode before started."""
|
||||||
entry = MockConfigEntry(domain="test")
|
entry = MockConfigEntry(domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
hass.set_state(CoreState.starting)
|
hass.set_state(CoreState.starting)
|
||||||
initial_listeners = hass.bus.async_listeners()[EVENT_HOMEASSISTANT_STARTED]
|
initial_listeners = hass.bus.async_listeners()[EVENT_HOMEASSISTANT_STARTED]
|
||||||
|
|
||||||
|
@ -1265,7 +1279,7 @@ async def test_setup_retrying_during_unload_before_started(hass: HomeAssistant)
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
||||||
|
@ -1273,7 +1287,7 @@ async def test_setup_retrying_during_unload_before_started(hass: HomeAssistant)
|
||||||
hass.bus.async_listeners()[EVENT_HOMEASSISTANT_STARTED] == initial_listeners + 1
|
hass.bus.async_listeners()[EVENT_HOMEASSISTANT_STARTED] == initial_listeners + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
await entry.async_unload(hass)
|
await manager.async_unload(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
|
assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
|
||||||
|
@ -1282,15 +1296,18 @@ async def test_setup_retrying_during_unload_before_started(hass: HomeAssistant)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_does_not_retry_during_shutdown(hass: HomeAssistant) -> None:
|
async def test_setup_does_not_retry_during_shutdown(
|
||||||
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
|
) -> None:
|
||||||
"""Test we do not retry when HASS is shutting down."""
|
"""Test we do not retry when HASS is shutting down."""
|
||||||
entry = MockConfigEntry(domain="test")
|
entry = MockConfigEntry(domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
mock_setup_entry = AsyncMock(side_effect=ConfigEntryNotReady)
|
mock_setup_entry = AsyncMock(side_effect=ConfigEntryNotReady)
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
|
|
||||||
assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
@ -1693,6 +1710,98 @@ async def test_entry_cannot_be_loaded_twice(
|
||||||
assert entry.state is state
|
assert entry.state is state
|
||||||
|
|
||||||
|
|
||||||
|
async def test_entry_setup_without_lock_raises(hass: HomeAssistant) -> None:
|
||||||
|
"""Test trying to setup a config entry without the lock."""
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain="comp", state=config_entries.ConfigEntryState.NOT_LOADED
|
||||||
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
async_setup = AsyncMock(return_value=True)
|
||||||
|
async_setup_entry = AsyncMock(return_value=True)
|
||||||
|
async_unload_entry = AsyncMock(return_value=True)
|
||||||
|
|
||||||
|
mock_integration(
|
||||||
|
hass,
|
||||||
|
MockModule(
|
||||||
|
"comp",
|
||||||
|
async_setup=async_setup,
|
||||||
|
async_setup_entry=async_setup_entry,
|
||||||
|
async_unload_entry=async_unload_entry,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_platform(hass, "comp.config_flow", None)
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
config_entries.OperationNotAllowed,
|
||||||
|
match="cannot be set up because it does not hold the setup lock",
|
||||||
|
):
|
||||||
|
await entry.async_setup(hass)
|
||||||
|
assert len(async_setup.mock_calls) == 0
|
||||||
|
assert len(async_setup_entry.mock_calls) == 0
|
||||||
|
assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
|
|
||||||
|
async def test_entry_unload_without_lock_raises(hass: HomeAssistant) -> None:
|
||||||
|
"""Test trying to unload a config entry without the lock."""
|
||||||
|
entry = MockConfigEntry(domain="comp", state=config_entries.ConfigEntryState.LOADED)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
async_setup = AsyncMock(return_value=True)
|
||||||
|
async_setup_entry = AsyncMock(return_value=True)
|
||||||
|
async_unload_entry = AsyncMock(return_value=True)
|
||||||
|
|
||||||
|
mock_integration(
|
||||||
|
hass,
|
||||||
|
MockModule(
|
||||||
|
"comp",
|
||||||
|
async_setup=async_setup,
|
||||||
|
async_setup_entry=async_setup_entry,
|
||||||
|
async_unload_entry=async_unload_entry,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_platform(hass, "comp.config_flow", None)
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
config_entries.OperationNotAllowed,
|
||||||
|
match="cannot be unloaded because it does not hold the setup lock",
|
||||||
|
):
|
||||||
|
await entry.async_unload(hass)
|
||||||
|
assert len(async_setup.mock_calls) == 0
|
||||||
|
assert len(async_setup_entry.mock_calls) == 0
|
||||||
|
assert entry.state is config_entries.ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
|
||||||
|
async def test_entry_remove_without_lock_raises(hass: HomeAssistant) -> None:
|
||||||
|
"""Test trying to remove a config entry without the lock."""
|
||||||
|
entry = MockConfigEntry(domain="comp", state=config_entries.ConfigEntryState.LOADED)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
async_setup = AsyncMock(return_value=True)
|
||||||
|
async_setup_entry = AsyncMock(return_value=True)
|
||||||
|
async_unload_entry = AsyncMock(return_value=True)
|
||||||
|
|
||||||
|
mock_integration(
|
||||||
|
hass,
|
||||||
|
MockModule(
|
||||||
|
"comp",
|
||||||
|
async_setup=async_setup,
|
||||||
|
async_setup_entry=async_setup_entry,
|
||||||
|
async_unload_entry=async_unload_entry,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_platform(hass, "comp.config_flow", None)
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
config_entries.OperationNotAllowed,
|
||||||
|
match="cannot be removed because it does not hold the setup lock",
|
||||||
|
):
|
||||||
|
await entry.async_remove(hass)
|
||||||
|
assert len(async_setup.mock_calls) == 0
|
||||||
|
assert len(async_setup_entry.mock_calls) == 0
|
||||||
|
assert entry.state is config_entries.ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"state",
|
"state",
|
||||||
[
|
[
|
||||||
|
@ -3475,10 +3584,13 @@ async def test_entry_reload_calls_on_unload_listeners(
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_raise_entry_error(
|
async def test_setup_raise_entry_error(
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
hass: HomeAssistant,
|
||||||
|
manager: config_entries.ConfigEntries,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test a setup raising ConfigEntryError."""
|
"""Test a setup raising ConfigEntryError."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
mock_setup_entry = AsyncMock(
|
mock_setup_entry = AsyncMock(
|
||||||
side_effect=ConfigEntryError("Incompatible firmware version")
|
side_effect=ConfigEntryError("Incompatible firmware version")
|
||||||
|
@ -3486,7 +3598,7 @@ async def test_setup_raise_entry_error(
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert (
|
assert (
|
||||||
"Error setting up entry test_title for test: Incompatible firmware version"
|
"Error setting up entry test_title for test: Incompatible firmware version"
|
||||||
|
@ -3498,10 +3610,13 @@ async def test_setup_raise_entry_error(
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_raise_entry_error_from_first_coordinator_update(
|
async def test_setup_raise_entry_error_from_first_coordinator_update(
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
hass: HomeAssistant,
|
||||||
|
manager: config_entries.ConfigEntries,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test async_config_entry_first_refresh raises ConfigEntryError."""
|
"""Test async_config_entry_first_refresh raises ConfigEntryError."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
async def async_setup_entry(hass, entry):
|
async def async_setup_entry(hass, entry):
|
||||||
"""Mock setup entry with a simple coordinator."""
|
"""Mock setup entry with a simple coordinator."""
|
||||||
|
@ -3523,7 +3638,7 @@ async def test_setup_raise_entry_error_from_first_coordinator_update(
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=async_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=async_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert (
|
assert (
|
||||||
"Error setting up entry test_title for test: Incompatible firmware version"
|
"Error setting up entry test_title for test: Incompatible firmware version"
|
||||||
|
@ -3535,10 +3650,13 @@ async def test_setup_raise_entry_error_from_first_coordinator_update(
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_not_raise_entry_error_from_future_coordinator_update(
|
async def test_setup_not_raise_entry_error_from_future_coordinator_update(
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
hass: HomeAssistant,
|
||||||
|
manager: config_entries.ConfigEntries,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test a coordinator not raises ConfigEntryError in the future."""
|
"""Test a coordinator not raises ConfigEntryError in the future."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
async def async_setup_entry(hass, entry):
|
async def async_setup_entry(hass, entry):
|
||||||
"""Mock setup entry with a simple coordinator."""
|
"""Mock setup entry with a simple coordinator."""
|
||||||
|
@ -3560,7 +3678,7 @@ async def test_setup_not_raise_entry_error_from_future_coordinator_update(
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=async_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=async_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert (
|
assert (
|
||||||
"Config entry setup failed while fetching any data: Incompatible firmware"
|
"Config entry setup failed while fetching any data: Incompatible firmware"
|
||||||
|
@ -3571,10 +3689,13 @@ async def test_setup_not_raise_entry_error_from_future_coordinator_update(
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_raise_auth_failed(
|
async def test_setup_raise_auth_failed(
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
hass: HomeAssistant,
|
||||||
|
manager: config_entries.ConfigEntries,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test a setup raising ConfigEntryAuthFailed."""
|
"""Test a setup raising ConfigEntryAuthFailed."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
mock_setup_entry = AsyncMock(
|
mock_setup_entry = AsyncMock(
|
||||||
side_effect=ConfigEntryAuthFailed("The password is no longer valid")
|
side_effect=ConfigEntryAuthFailed("The password is no longer valid")
|
||||||
|
@ -3582,7 +3703,7 @@ async def test_setup_raise_auth_failed(
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "could not authenticate: The password is no longer valid" in caplog.text
|
assert "could not authenticate: The password is no longer valid" in caplog.text
|
||||||
|
|
||||||
|
@ -3597,7 +3718,7 @@ async def test_setup_raise_auth_failed(
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
entry._async_set_state(hass, config_entries.ConfigEntryState.NOT_LOADED, None)
|
entry._async_set_state(hass, config_entries.ConfigEntryState.NOT_LOADED, None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "could not authenticate: The password is no longer valid" in caplog.text
|
assert "could not authenticate: The password is no longer valid" in caplog.text
|
||||||
|
|
||||||
|
@ -3608,10 +3729,13 @@ async def test_setup_raise_auth_failed(
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_raise_auth_failed_from_first_coordinator_update(
|
async def test_setup_raise_auth_failed_from_first_coordinator_update(
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
hass: HomeAssistant,
|
||||||
|
manager: config_entries.ConfigEntries,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test async_config_entry_first_refresh raises ConfigEntryAuthFailed."""
|
"""Test async_config_entry_first_refresh raises ConfigEntryAuthFailed."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
async def async_setup_entry(hass, entry):
|
async def async_setup_entry(hass, entry):
|
||||||
"""Mock setup entry with a simple coordinator."""
|
"""Mock setup entry with a simple coordinator."""
|
||||||
|
@ -3633,7 +3757,7 @@ async def test_setup_raise_auth_failed_from_first_coordinator_update(
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=async_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=async_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "could not authenticate: The password is no longer valid" in caplog.text
|
assert "could not authenticate: The password is no longer valid" in caplog.text
|
||||||
|
|
||||||
|
@ -3646,7 +3770,7 @@ async def test_setup_raise_auth_failed_from_first_coordinator_update(
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
entry._async_set_state(hass, config_entries.ConfigEntryState.NOT_LOADED, None)
|
entry._async_set_state(hass, config_entries.ConfigEntryState.NOT_LOADED, None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "could not authenticate: The password is no longer valid" in caplog.text
|
assert "could not authenticate: The password is no longer valid" in caplog.text
|
||||||
|
|
||||||
|
@ -3657,10 +3781,13 @@ async def test_setup_raise_auth_failed_from_first_coordinator_update(
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_raise_auth_failed_from_future_coordinator_update(
|
async def test_setup_raise_auth_failed_from_future_coordinator_update(
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
hass: HomeAssistant,
|
||||||
|
manager: config_entries.ConfigEntries,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test a coordinator raises ConfigEntryAuthFailed in the future."""
|
"""Test a coordinator raises ConfigEntryAuthFailed in the future."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
async def async_setup_entry(hass, entry):
|
async def async_setup_entry(hass, entry):
|
||||||
"""Mock setup entry with a simple coordinator."""
|
"""Mock setup entry with a simple coordinator."""
|
||||||
|
@ -3682,7 +3809,7 @@ async def test_setup_raise_auth_failed_from_future_coordinator_update(
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=async_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=async_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "Authentication failed while fetching" in caplog.text
|
assert "Authentication failed while fetching" in caplog.text
|
||||||
assert "The password is no longer valid" in caplog.text
|
assert "The password is no longer valid" in caplog.text
|
||||||
|
@ -3696,7 +3823,7 @@ async def test_setup_raise_auth_failed_from_future_coordinator_update(
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
entry._async_set_state(hass, config_entries.ConfigEntryState.NOT_LOADED, None)
|
entry._async_set_state(hass, config_entries.ConfigEntryState.NOT_LOADED, None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "Authentication failed while fetching" in caplog.text
|
assert "Authentication failed while fetching" in caplog.text
|
||||||
assert "The password is no longer valid" in caplog.text
|
assert "The password is no longer valid" in caplog.text
|
||||||
|
@ -3719,16 +3846,19 @@ async def test_initialize_and_shutdown(hass: HomeAssistant) -> None:
|
||||||
assert mock_async_shutdown.called
|
assert mock_async_shutdown.called
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_retrying_during_shutdown(hass: HomeAssistant) -> None:
|
async def test_setup_retrying_during_shutdown(
|
||||||
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
|
) -> None:
|
||||||
"""Test if we shutdown an entry that is in retry mode."""
|
"""Test if we shutdown an entry that is in retry mode."""
|
||||||
entry = MockConfigEntry(domain="test")
|
entry = MockConfigEntry(domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
mock_setup_entry = AsyncMock(side_effect=ConfigEntryNotReady)
|
mock_setup_entry = AsyncMock(side_effect=ConfigEntryNotReady)
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
with patch("homeassistant.helpers.event.async_call_later") as mock_call:
|
with patch("homeassistant.helpers.event.async_call_later") as mock_call:
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
|
|
||||||
assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
||||||
assert len(mock_call.return_value.mock_calls) == 0
|
assert len(mock_call.return_value.mock_calls) == 0
|
||||||
|
@ -3747,7 +3877,9 @@ async def test_setup_retrying_during_shutdown(hass: HomeAssistant) -> None:
|
||||||
entry.async_cancel_retry_setup()
|
entry.async_cancel_retry_setup()
|
||||||
|
|
||||||
|
|
||||||
async def test_scheduling_reload_cancels_setup_retry(hass: HomeAssistant) -> None:
|
async def test_scheduling_reload_cancels_setup_retry(
|
||||||
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
|
) -> None:
|
||||||
"""Test scheduling a reload cancels setup retry."""
|
"""Test scheduling a reload cancels setup retry."""
|
||||||
entry = MockConfigEntry(domain="test")
|
entry = MockConfigEntry(domain="test")
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
@ -3760,7 +3892,7 @@ async def test_scheduling_reload_cancels_setup_retry(hass: HomeAssistant) -> Non
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.config_entries.async_call_later", return_value=cancel_mock
|
"homeassistant.config_entries.async_call_later", return_value=cancel_mock
|
||||||
):
|
):
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
|
|
||||||
assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
||||||
assert len(cancel_mock.mock_calls) == 0
|
assert len(cancel_mock.mock_calls) == 0
|
||||||
|
@ -4190,16 +4322,20 @@ async def test_disallow_entry_reload_with_setup_in_progress(
|
||||||
assert entry.state is config_entries.ConfigEntryState.SETUP_IN_PROGRESS
|
assert entry.state is config_entries.ConfigEntryState.SETUP_IN_PROGRESS
|
||||||
|
|
||||||
|
|
||||||
async def test_reauth(hass: HomeAssistant) -> None:
|
async def test_reauth(
|
||||||
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
|
) -> None:
|
||||||
"""Test the async_reauth_helper."""
|
"""Test the async_reauth_helper."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
entry2 = MockConfigEntry(title="test_title", domain="test")
|
entry2 = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry2.add_to_hass(hass)
|
||||||
|
|
||||||
mock_setup_entry = AsyncMock(return_value=True)
|
mock_setup_entry = AsyncMock(return_value=True)
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
flow = hass.config_entries.flow
|
flow = hass.config_entries.flow
|
||||||
|
@ -4252,16 +4388,20 @@ async def test_reauth(hass: HomeAssistant) -> None:
|
||||||
assert len(hass.config_entries.flow.async_progress()) == 1
|
assert len(hass.config_entries.flow.async_progress()) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_reconfigure(hass: HomeAssistant) -> None:
|
async def test_reconfigure(
|
||||||
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
|
) -> None:
|
||||||
"""Test the async_reconfigure_helper."""
|
"""Test the async_reconfigure_helper."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
entry2 = MockConfigEntry(title="test_title", domain="test")
|
entry2 = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry2.add_to_hass(hass)
|
||||||
|
|
||||||
mock_setup_entry = AsyncMock(return_value=True)
|
mock_setup_entry = AsyncMock(return_value=True)
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
flow = hass.config_entries.flow
|
flow = hass.config_entries.flow
|
||||||
|
@ -4340,14 +4480,17 @@ async def test_reconfigure(hass: HomeAssistant) -> None:
|
||||||
assert len(hass.config_entries.flow.async_progress()) == 1
|
assert len(hass.config_entries.flow.async_progress()) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_get_active_flows(hass: HomeAssistant) -> None:
|
async def test_get_active_flows(
|
||||||
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
|
) -> None:
|
||||||
"""Test the async_get_active_flows helper."""
|
"""Test the async_get_active_flows helper."""
|
||||||
entry = MockConfigEntry(title="test_title", domain="test")
|
entry = MockConfigEntry(title="test_title", domain="test")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
mock_setup_entry = AsyncMock(return_value=True)
|
mock_setup_entry = AsyncMock(return_value=True)
|
||||||
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
await entry.async_setup(hass)
|
await manager.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
flow = hass.config_entries.flow
|
flow = hass.config_entries.flow
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue