Add state check to config entry setup to ensure it cannot be setup twice (#117193)

This commit is contained in:
J. Nick Koston 2024-05-10 17:09:28 -05:00 committed by GitHub
parent c21dac855a
commit c74c2f3652
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 56 additions and 7 deletions

View file

@ -517,6 +517,15 @@ class ConfigEntry(Generic[_DataT]):
# Only store setup result as state if it was not forwarded. # Only store setup result as state if it was not forwarded.
if domain_is_integration := self.domain == integration.domain: if domain_is_integration := self.domain == integration.domain:
if self.state in (
ConfigEntryState.LOADED,
ConfigEntryState.SETUP_IN_PROGRESS,
):
raise OperationNotAllowed(
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.state} state"
)
self._async_set_state(hass, ConfigEntryState.SETUP_IN_PROGRESS, None) self._async_set_state(hass, ConfigEntryState.SETUP_IN_PROGRESS, None)
if self.supports_unload is None: if self.supports_unload is None:

View file

@ -196,7 +196,7 @@ async def test_flow_ssdp_discovery_changed_udn_match_mac(hass: HomeAssistant) ->
CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS, CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS,
}, },
source=config_entries.SOURCE_SSDP, source=config_entries.SOURCE_SSDP,
state=config_entries.ConfigEntryState.LOADED, state=config_entries.ConfigEntryState.NOT_LOADED,
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
@ -228,7 +228,7 @@ async def test_flow_ssdp_discovery_changed_udn_match_host(hass: HomeAssistant) -
CONFIG_ENTRY_HOST: TEST_HOST, CONFIG_ENTRY_HOST: TEST_HOST,
}, },
source=config_entries.SOURCE_SSDP, source=config_entries.SOURCE_SSDP,
state=config_entries.ConfigEntryState.LOADED, state=config_entries.ConfigEntryState.NOT_LOADED,
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
@ -266,7 +266,7 @@ async def test_flow_ssdp_discovery_changed_udn_but_st_differs(
CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS, CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS,
}, },
source=config_entries.SOURCE_SSDP, source=config_entries.SOURCE_SSDP,
state=config_entries.ConfigEntryState.LOADED, state=config_entries.ConfigEntryState.NOT_LOADED,
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
@ -320,7 +320,7 @@ async def test_flow_ssdp_discovery_changed_location(hass: HomeAssistant) -> None
CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS, CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS,
}, },
source=config_entries.SOURCE_SSDP, source=config_entries.SOURCE_SSDP,
state=config_entries.ConfigEntryState.LOADED, state=config_entries.ConfigEntryState.NOT_LOADED,
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)

View file

@ -386,7 +386,7 @@ async def test_remove_entry(
] ]
# 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()
# Check entity state got added # Check entity state got added
@ -1613,7 +1613,9 @@ async def test_entry_reload_succeed(
hass: HomeAssistant, manager: config_entries.ConfigEntries hass: HomeAssistant, manager: config_entries.ConfigEntries
) -> None: ) -> None:
"""Test that we can reload an entry.""" """Test that we can reload an entry."""
entry = MockConfigEntry(domain="comp", state=config_entries.ConfigEntryState.LOADED) entry = MockConfigEntry(
domain="comp", state=config_entries.ConfigEntryState.NOT_LOADED
)
entry.add_to_hass(hass) entry.add_to_hass(hass)
async_setup = AsyncMock(return_value=True) async_setup = AsyncMock(return_value=True)
@ -1637,6 +1639,42 @@ async def test_entry_reload_succeed(
assert entry.state is config_entries.ConfigEntryState.LOADED assert entry.state is config_entries.ConfigEntryState.LOADED
@pytest.mark.parametrize(
"state",
[
config_entries.ConfigEntryState.LOADED,
config_entries.ConfigEntryState.SETUP_IN_PROGRESS,
],
)
async def test_entry_cannot_be_loaded_twice(
hass: HomeAssistant, state: config_entries.ConfigEntryState
) -> None:
"""Test that a config entry cannot be loaded twice."""
entry = MockConfigEntry(domain="comp", state=state)
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=str(state)):
await entry.async_setup(hass)
assert len(async_setup.mock_calls) == 0
assert len(async_setup_entry.mock_calls) == 0
assert entry.state is state
@pytest.mark.parametrize( @pytest.mark.parametrize(
"state", "state",
[ [
@ -4005,7 +4043,9 @@ async def test_entry_reload_concurrency_not_setup_setup(
hass: HomeAssistant, manager: config_entries.ConfigEntries hass: HomeAssistant, manager: config_entries.ConfigEntries
) -> None: ) -> None:
"""Test multiple reload calls do not cause a reload race.""" """Test multiple reload calls do not cause a reload race."""
entry = MockConfigEntry(domain="comp", state=config_entries.ConfigEntryState.LOADED) entry = MockConfigEntry(
domain="comp", state=config_entries.ConfigEntryState.NOT_LOADED
)
entry.add_to_hass(hass) entry.add_to_hass(hass)
async_setup = AsyncMock(return_value=True) async_setup = AsyncMock(return_value=True)