diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index b5491d83800..6bdb850e643 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -1021,14 +1021,20 @@ class ConfigFlow(data_entry_flow.FlowHandler): self.context["confirm_only"] = True @callback - def _async_current_entries(self, include_ignore: bool = False) -> list[ConfigEntry]: + def _async_current_entries( + self, include_ignore: bool | None = None + ) -> list[ConfigEntry]: """Return current entries. If the flow is user initiated, filter out ignored entries unless include_ignore is True. """ config_entries = self.hass.config_entries.async_entries(self.handler) - if include_ignore or self.source != SOURCE_USER: + if ( + include_ignore is True + or include_ignore is None + and self.source != SOURCE_USER + ): return config_entries return [entry for entry in config_entries if entry.source != SOURCE_IGNORE] diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 09ab60e4300..5d774f7877d 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -1760,6 +1760,115 @@ async def test_manual_add_overrides_ignored_entry_singleton(hass, manager): assert p_entry.data == {"token": "supersecret"} +async def test__async_current_entries_does_not_skip_ignore_non_user(hass, manager): + """Test that _async_current_entries does not skip ignore by default for non user step.""" + hass.config.components.add("comp") + entry = MockConfigEntry( + domain="comp", + state=config_entries.ENTRY_STATE_LOADED, + source=config_entries.SOURCE_IGNORE, + ) + entry.add_to_hass(hass) + + mock_setup_entry = AsyncMock(return_value=True) + + mock_integration(hass, MockModule("comp", async_setup_entry=mock_setup_entry)) + mock_entity_platform(hass, "config_flow.comp", None) + + class TestFlow(config_entries.ConfigFlow): + """Test flow.""" + + VERSION = 1 + + async def async_step_import(self, user_input=None): + """Test not the user step.""" + if self._async_current_entries(): + return self.async_abort(reason="single_instance_allowed") + return self.async_create_entry(title="title", data={"token": "supersecret"}) + + with patch.dict(config_entries.HANDLERS, {"comp": TestFlow, "beer": 5}): + await manager.flow.async_init( + "comp", context={"source": config_entries.SOURCE_IMPORT} + ) + await hass.async_block_till_done() + + assert len(mock_setup_entry.mock_calls) == 0 + + +async def test__async_current_entries_explict_skip_ignore(hass, manager): + """Test that _async_current_entries can explicitly include ignore.""" + hass.config.components.add("comp") + entry = MockConfigEntry( + domain="comp", + state=config_entries.ENTRY_STATE_LOADED, + source=config_entries.SOURCE_IGNORE, + ) + entry.add_to_hass(hass) + + mock_setup_entry = AsyncMock(return_value=True) + + mock_integration(hass, MockModule("comp", async_setup_entry=mock_setup_entry)) + mock_entity_platform(hass, "config_flow.comp", None) + + class TestFlow(config_entries.ConfigFlow): + """Test flow.""" + + VERSION = 1 + + async def async_step_import(self, user_input=None): + """Test not the user step.""" + if self._async_current_entries(include_ignore=False): + return self.async_abort(reason="single_instance_allowed") + return self.async_create_entry(title="title", data={"token": "supersecret"}) + + with patch.dict(config_entries.HANDLERS, {"comp": TestFlow, "beer": 5}): + await manager.flow.async_init( + "comp", context={"source": config_entries.SOURCE_IMPORT} + ) + await hass.async_block_till_done() + + assert len(mock_setup_entry.mock_calls) == 1 + p_hass, p_entry = mock_setup_entry.mock_calls[0][1] + + assert p_hass is hass + assert p_entry.data == {"token": "supersecret"} + + +async def test__async_current_entries_explict_include_ignore(hass, manager): + """Test that _async_current_entries can explicitly include ignore.""" + hass.config.components.add("comp") + entry = MockConfigEntry( + domain="comp", + state=config_entries.ENTRY_STATE_LOADED, + source=config_entries.SOURCE_IGNORE, + ) + entry.add_to_hass(hass) + + mock_setup_entry = AsyncMock(return_value=True) + + mock_integration(hass, MockModule("comp", async_setup_entry=mock_setup_entry)) + mock_entity_platform(hass, "config_flow.comp", None) + + class TestFlow(config_entries.ConfigFlow): + """Test flow.""" + + VERSION = 1 + + async def async_step_import(self, user_input=None): + """Test not the user step.""" + if self._async_current_entries(include_ignore=True): + return self.async_abort(reason="single_instance_allowed") + return self.async_create_entry(title="title", data={"token": "supersecret"}) + + with patch.dict(config_entries.HANDLERS, {"comp": TestFlow, "beer": 5}): + await manager.flow.async_init( + "comp", context={"source": config_entries.SOURCE_IMPORT} + ) + await hass.async_block_till_done() + + assert len(mock_setup_entry.mock_calls) == 0 + + async def test_unignore_step_form(hass, manager): """Test that we can ignore flows that are in progress and have a unique ID, then rediscover them.""" async_setup_entry = AsyncMock(return_value=True)