From 0f4a989db3259a08caf0c69ecc2e7ddc1cd0c451 Mon Sep 17 00:00:00 2001 From: Joshua King Date: Sun, 13 Sep 2026 19:07:42 -0400 Subject: [PATCH] test: cover multi-entry unload retention and wire flow tests to the hass fixture --- tests/conftest.py | 27 ++++++++++++++++++++------- tests/test_config_flow.py | 12 ++++++++++-- tests/test_lifecycle.py | 26 ++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 3bf89cb..ad8a4ea 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -96,7 +96,13 @@ class _ConfigFlowBase: return {"type": "abort", "reason": reason} def async_create_entry(self, *, title, data, **kwargs): - return {"type": "create_entry", "title": title, "data": data} + result = {"type": "create_entry", "title": title, "data": data} + hass = getattr(self, "hass", None) + manager = getattr(hass, "config_entries", None) if hass is not None else None + add = getattr(manager, "async_add", None) + if add is not None: + add(SimpleNamespace(domain=self._domain, title=title, data=data)) + return result async def async_set_unique_id(self, unique_id, raise_on_progress=True): self.context["unique_id"] = unique_id @@ -222,6 +228,15 @@ def make_hass() -> tuple: registry.reloaded.append(entry_id) hass.config_entries.async_reload = MagicMock(side_effect=_async_reload) + hass.config_entries._entries = [] + hass.config_entries.async_add = ( + lambda entry: hass.config_entries._entries.append(entry) + ) + + def _async_entries(domain: str): + return [e for e in hass.config_entries._entries if e.domain == domain] + + hass.config_entries.async_entries = _async_entries return hass, registry @@ -270,12 +285,10 @@ def make_flow(): from custom_components.nestquest import config_flow flow = config_flow.NestQuestConfigFlow() - entries = [ - e for e in (existing_entries or []) if getattr(e, "domain", None) == flow._domain - ] - flow.hass = SimpleNamespace( - config_entries=SimpleNamespace(async_entries=lambda domain: entries) - ) + flow.hass = hass if hass is not None else make_hass()[0] + for entry in existing_entries or []: + if getattr(entry, "domain", None) == flow._domain: + flow.hass.config_entries.async_add(entry) if in_progress: flow._async_in_progress = lambda: in_progress return flow diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 40927ee..4087336 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -24,19 +24,27 @@ async def test_flow_shows_form_initially(hass, make_flow) -> None: async def test_flow_creates_entry_on_submit(hass, make_flow) -> None: - """Submitting the user step creates a config entry.""" + """Submitting the user step creates a config entry on the fixture's manager.""" flow = make_flow(hass) + assert hass.config_entries.async_entries(DOMAIN) == [] result = await flow.async_step_user({}) assert result["type"] == "create_entry" assert result["title"] == "NestQuest" assert result["data"] == {} + created = hass.config_entries.async_entries(DOMAIN) + assert len(created) == 1 + assert created[0].domain == DOMAIN + assert created[0].title == "NestQuest" + assert created[0].data == {} async def test_second_flow_aborts_single_instance( hass, make_flow, make_config_flow_entry ) -> None: """A second flow aborts with single_instance_allowed.""" - flow = make_flow(hass, existing_entries=[make_config_flow_entry(DOMAIN)]) + existing = make_config_flow_entry(DOMAIN) + flow = make_flow(hass, existing_entries=[existing]) + assert hass.config_entries.async_entries(DOMAIN) == [existing] result = await flow.async_step_user(None) assert result["type"] == "abort" assert result["reason"] == "single_instance_allowed" diff --git a/tests/test_lifecycle.py b/tests/test_lifecycle.py index a6d8837..b12ed36 100644 --- a/tests/test_lifecycle.py +++ b/tests/test_lifecycle.py @@ -41,6 +41,32 @@ async def test_unload_returns_true_and_removes_hass_data(hass, make_entry) -> No assert registry.size == 0 +async def test_unload_of_one_entry_keeps_domain_while_other_remains( + hass, make_entry +) -> None: + """Unloading one of two entries keeps DOMAIN alive with the other entry.""" + registry = hass.registry + entry_a = _wire(make_entry(entry_id="entry_a"), registry) + entry_b = _wire(make_entry(entry_id="entry_b"), registry) + assert await async_setup_entry(hass, entry_a) is True + assert await async_setup_entry(hass, entry_b) is True + assert set(hass.data[DOMAIN]) == {"entry_a", "entry_b"} + + assert await async_unload_entry(hass, entry_a) is True + assert DOMAIN in hass.data + assert set(hass.data[DOMAIN]) == {"entry_b"} + assert hass.data[DOMAIN]["entry_b"] is entry_b.runtime_data + assert entry_a.runtime_data is None + assert entry_b.runtime_data is not None + assert registry.size == 1 + + assert await async_unload_entry(hass, entry_b) is True + assert DOMAIN not in hass.data + assert hass.data == {} + assert entry_b.runtime_data is None + assert registry.size == 0 + + async def test_unload_is_idempotent(hass, make_entry) -> None: entry = _wire(make_entry(), hass.registry) assert await async_setup_entry(hass, entry) is True