Skip to content

Commit

Permalink
Add state check to config entry setup to ensure it cannot be setup tw…
Browse files Browse the repository at this point in the history
…ice (#117193)
  • Loading branch information
bdraco committed May 10, 2024
1 parent c21dac8 commit c74c2f3
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 7 deletions.
9 changes: 9 additions & 0 deletions homeassistant/config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,15 @@ async def async_setup(

# Only store setup result as state if it was not forwarded.
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)

if self.supports_unload is None:
Expand Down
8 changes: 4 additions & 4 deletions tests/components/upnp/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ async def test_flow_ssdp_discovery_changed_udn_match_mac(hass: HomeAssistant) ->
CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS,
},
source=config_entries.SOURCE_SSDP,
state=config_entries.ConfigEntryState.LOADED,
state=config_entries.ConfigEntryState.NOT_LOADED,
)
entry.add_to_hass(hass)

Expand Down Expand Up @@ -228,7 +228,7 @@ async def test_flow_ssdp_discovery_changed_udn_match_host(hass: HomeAssistant) -
CONFIG_ENTRY_HOST: TEST_HOST,
},
source=config_entries.SOURCE_SSDP,
state=config_entries.ConfigEntryState.LOADED,
state=config_entries.ConfigEntryState.NOT_LOADED,
)
entry.add_to_hass(hass)

Expand Down Expand Up @@ -266,7 +266,7 @@ async def test_flow_ssdp_discovery_changed_udn_but_st_differs(
CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS,
},
source=config_entries.SOURCE_SSDP,
state=config_entries.ConfigEntryState.LOADED,
state=config_entries.ConfigEntryState.NOT_LOADED,
)
entry.add_to_hass(hass)

Expand Down Expand Up @@ -320,7 +320,7 @@ async def test_flow_ssdp_discovery_changed_location(hass: HomeAssistant) -> None
CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS,
},
source=config_entries.SOURCE_SSDP,
state=config_entries.ConfigEntryState.LOADED,
state=config_entries.ConfigEntryState.NOT_LOADED,
)
entry.add_to_hass(hass)

Expand Down
46 changes: 43 additions & 3 deletions tests/test_config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ async def mock_setup_entry_platform(
]

# Setup entry
await entry.async_setup(hass)
await manager.async_setup(entry.entry_id)
await hass.async_block_till_done()

# Check entity state got added
Expand Down Expand Up @@ -1613,7 +1613,9 @@ async def test_entry_reload_succeed(
hass: HomeAssistant, manager: config_entries.ConfigEntries
) -> None:
"""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)

async_setup = AsyncMock(return_value=True)
Expand All @@ -1637,6 +1639,42 @@ async def test_entry_reload_succeed(
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(
"state",
[
Expand Down Expand Up @@ -4005,7 +4043,9 @@ async def test_entry_reload_concurrency_not_setup_setup(
hass: HomeAssistant, manager: config_entries.ConfigEntries
) -> None:
"""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)

async_setup = AsyncMock(return_value=True)
Expand Down

0 comments on commit c74c2f3

Please sign in to comment.