Compare commits

...

3 Commits

Author SHA1 Message Date
G Johansson
e70f1895d1 Mods 2026-04-17 14:55:04 +00:00
G Johansson
1c8d326043 Add _abort_if_unique_id_configured 2026-04-07 19:20:26 +00:00
G Johansson
e0deff9ced WIP: Don't use reloading in async_update_reload_and_abort with update listeners 2026-04-07 19:20:24 +00:00
2 changed files with 70 additions and 22 deletions

View File

@@ -3059,6 +3059,9 @@ class ConfigFlow(ConfigEntryBaseFlow):
# Existing config entry present, and the
# entry data just changed
should_reload = True
if entry.update_listeners:
# If listeners are present, those should be used to reload instead
raise ValueError("Cannot update and reload entry with update listeners")
elif (
self.source in DISCOVERY_SOURCES
and entry.state is ConfigEntryState.SETUP_RETRY
@@ -3446,6 +3449,8 @@ class ConfigFlow(ConfigEntryBaseFlow):
options=options,
)
if reload_even_if_entry_is_unchanged or result:
if entry.update_listeners:
raise ValueError("Cannot update and reload entry with update listeners")
self.hass.config_entries.async_schedule_reload(entry.entry_id)
if reason is UNDEFINED:
reason = "reauth_successful"

View File

@@ -6461,7 +6461,7 @@ def test_raise_trying_to_add_same_config_entry_twice(
{"vendor": "data2"},
{"vendor": "options2"},
(2, 1),
None,
does_not_raise(),
),
(
{
@@ -6475,7 +6475,7 @@ def test_raise_trying_to_add_same_config_entry_twice(
{"vendor": "data"},
{"vendor": "options"},
(2, 1),
None,
does_not_raise(),
),
(
{
@@ -6490,7 +6490,7 @@ def test_raise_trying_to_add_same_config_entry_twice(
{"vendor": "data2"},
{"vendor": "options2"},
(2, 1),
None,
does_not_raise(),
),
(
{
@@ -6505,7 +6505,7 @@ def test_raise_trying_to_add_same_config_entry_twice(
{"vendor": "data"},
{"vendor": "options"},
(1, 0),
None,
does_not_raise(),
),
(
{},
@@ -6514,7 +6514,7 @@ def test_raise_trying_to_add_same_config_entry_twice(
{"vendor": "data"},
{"vendor": "options"},
(2, 1),
None,
does_not_raise(),
),
(
{"data": {"buyer": "me"}, "options": {}},
@@ -6523,7 +6523,7 @@ def test_raise_trying_to_add_same_config_entry_twice(
{"buyer": "me"},
{},
(2, 1),
None,
does_not_raise(),
),
(
{"data_updates": {"buyer": "me"}},
@@ -6532,7 +6532,7 @@ def test_raise_trying_to_add_same_config_entry_twice(
{"vendor": "data", "buyer": "me"},
{"vendor": "options"},
(2, 1),
None,
does_not_raise(),
),
(
{
@@ -6547,7 +6547,7 @@ def test_raise_trying_to_add_same_config_entry_twice(
{"vendor": "data"},
{"vendor": "options"},
(1, 0),
ValueError,
pytest.raises(ValueError),
),
],
ids=[
@@ -6613,15 +6613,11 @@ async def test_update_entry_and_reload(
"""Mock Reconfigure."""
return self.async_update_reload_and_abort(entry, **kwargs)
err: Exception
with mock_config_flow("comp", MockFlowHandler):
try:
if source == config_entries.SOURCE_REAUTH:
result = await entry.start_reauth_flow(hass)
elif source == config_entries.SOURCE_RECONFIGURE:
result = await entry.start_reconfigure_flow(hass)
except Exception as ex: # noqa: BLE001
err = ex
with mock_config_flow("comp", MockFlowHandler), raises:
if source == config_entries.SOURCE_REAUTH:
await entry.start_reauth_flow(hass)
elif source == config_entries.SOURCE_RECONFIGURE:
await entry.start_reconfigure_flow(hass)
await hass.async_block_till_done()
@@ -6630,16 +6626,63 @@ async def test_update_entry_and_reload(
assert entry.data == expected_data
assert entry.options == expected_options
assert entry.state == config_entries.ConfigEntryState.LOADED
if raises:
assert isinstance(err, raises)
else:
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == reason
# Assert entry was reloaded
assert len(comp.async_setup_entry.mock_calls) == calls_entry_load_unload[0]
assert len(comp.async_unload_entry.mock_calls) == calls_entry_load_unload[1]
async def test_update_entry_and_reload_with_listener(hass: HomeAssistant) -> None:
"""Test updating an entry and reloading."""
entry = MockConfigEntry(
domain="comp",
unique_id="1234",
title="Test",
data={"vendor": "data"},
options={"vendor": "options"},
)
entry.add_to_hass(hass)
entry.add_update_listener(AsyncMock())
comp = MockModule(
"comp",
async_setup_entry=AsyncMock(return_value=True),
async_unload_entry=AsyncMock(return_value=True),
)
mock_integration(hass, comp)
mock_platform(hass, "comp.config_flow", None)
await hass.config_entries.async_setup(entry.entry_id)
class MockFlowHandler(config_entries.ConfigFlow):
"""Define a mock flow handler."""
VERSION = 1
async def async_step_reauth(self, data):
"""Mock Reauth."""
self._abort_if_unique_id_configured(updates={"vendor": "data2"})
async def async_step_reconfigure(self, data):
"""Mock Reconfigure."""
return self.async_update_reload_and_abort(entry, data={"vendor": "data2"})
with (
mock_config_flow("comp", MockFlowHandler),
pytest.raises(
ValueError, match="Cannot update and reload entry with update listeners"
),
):
await entry.start_reauth_flow(hass)
with (
mock_config_flow("comp", MockFlowHandler),
pytest.raises(
ValueError, match="Cannot update and reload entry with update listeners"
),
):
await entry.start_reconfigure_flow(hass)
@pytest.mark.parametrize(
("source", "reason"),
[