From e77bc12d81c19325ae439e9495a0330a2b8f9619 Mon Sep 17 00:00:00 2001 From: James Mackay Date: Fri, 9 Oct 2026 07:38:24 +0100 Subject: [PATCH] =?UTF-8?q?test:=20cross-file=20stub-scoping=20repairs=20?= =?UTF-8?q?=E2=80=94=20hub=20ConfigFlow=20base=20carries=20the=20full=20pl?= =?UTF-8?q?umbing,=20standalone=20rebuilds=20when=20its=20holder=20is=20go?= =?UTF-8?q?ne,=20test=5Finit=20scoped=20hygiene=20mirrors=20scoped=5Fstubs?= =?UTF-8?q?=20(completes=20t30=20G6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/entity_stublib.py | 32 +++++++++++++++++++++++++++++++- tests/test_standalone_setup.py | 11 +++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/tests/entity_stublib.py b/tests/entity_stublib.py index 4790c9c..7c795ce 100644 --- a/tests/entity_stublib.py +++ b/tests/entity_stublib.py @@ -131,8 +131,38 @@ def install() -> None: config_entries = types.ModuleType("homeassistant.config_entries") config_entries.ConfigEntry = type("ConfigEntry", (), {}) + async def _stub_set_unique_id(self, unique_id): + # hub shape: record; a full flow test file scopes its own version + self._unique_id = unique_id + + def _noop_abort(self, *, error=None, **kwargs): + return None + class _ConfigFlowStub: - """ConfigFlow shape used by flow tests (t29 G3a API included).""" + """ConfigFlow shape used by entity tests (t29 G3a API included). + + Late loads under the hub stubs (e.g. test_init designs run AFTER the + other flow files) still get the base plumbing the flow modules rely + on — show/create/abort/unique-id — so the flow classes never import + against a bare base. + """ + + async_set_unique_id = _stub_set_unique_id + _abort_if_unique_id_configured = _noop_abort + + def async_show_form(self, *, step_id, data_schema=None, errors=None): + return { + "type": "form", + "step_id": step_id, + "errors": dict(errors or {}), + "data_schema": data_schema, + } + + def async_create_entry(self, *, title, data, options=None): + return {"type": "create_entry", "title": title, "data": data} + + def async_abort(self, *, reason): + return {"type": "abort", "reason": reason} def _get_reauth_entry(self, entry=None): if entry is not None: diff --git a/tests/test_standalone_setup.py b/tests/test_standalone_setup.py index 728b953..1609e66 100644 --- a/tests/test_standalone_setup.py +++ b/tests/test_standalone_setup.py @@ -37,8 +37,15 @@ _STUB_NAMES = ( def _install_stubs() -> None: - """Import-time stand-ins (idempotent; same shape as the pairing tests).""" - if sys.modules.get("voluptuous") is not None: + """Import-time stand-ins (idempotent; same shape as the pairing tests). + + t30: the guard rebuilds whenever the module-level holder is missing — + a later file's scoped teardown may pop the shared stubs this file's + tests rely on (the flow-class surfaces live on the local base which we + MUST rebind against).""" + if sys.modules.get("voluptuous") is not None and hasattr( + sys.modules.get(__name__), "_zc_holder" + ): return vol = types.ModuleType("voluptuous")