fix(config_flow): D3 — await the discovery-dedup coroutine (t37)
`ConfigFlow._async_handle_discovery_without_unique_id` is an `async def` in core (verified: `inspect.iscoroutinefunction` is True) and core awaits it. t32 called it without `await`, so the coroutine body never ran: no `already_configured` abort when the handler already had entries, no `DEFAULT_DISCOVERY_UNIQUE_ID` claim, no `already_in_progress` abort — HA's discovery dedup was dead, Python warned "coroutine ... was never awaited", and the host-equality loop that used to sit here was the only accidental dedup. The call is awaited and the now-unreachable host loop is gone (the real helper is stricter: any entry for the domain, or any in-flight flow). The harness mirrors the real contract instead of a sync no-op: * `entity_stublib`'s hub stub makes the helper a coroutine (a sync stub would turn the production `await` into a TypeError); * `test_standalone_setup`'s stub models the real semantics — a coroutine raising `DiscoveryDedupAbortError` (core raises AbortFlow), driven by the shared holder's entry list and in-progress list — and its tests now assert all three outcomes: awaited-and-proceeds (counter + claimed discovery unique id), `already_configured` when entries exist, `already_in_progress` when another flow runs. A fourth test collects the un-awaited-coroutine warning, and the prefill test in the UX suite asserts the awaited counter. Red proof: with the pre-fix config_flow.py the repo-owned sweep reports `config_flow.py:425: missing-await: self._async_handle_discovery_without_unique_id()`. Verified: 289 passed, 2 skipped (D3 stage); ruff clean.
This commit is contained in:
1 parent
bd72ae3170
commit
60fb4f7557
4 files changed
+115
-34
No files matched your search
@@ -114,7 +114,14 @@ def _install_stubs() -> None:
|
||||
def _async_current_entries(self):
|
||||
return []
|
||||
|
||||
def _async_handle_discovery_without_unique_id(self):
|
||||
async def _async_handle_discovery_without_unique_id(self):
|
||||
"""Real HA helper is a coroutine (t37 D3) — awaiting is mandatory.
|
||||
|
||||
This file never configures entries for the handler, so the real
|
||||
aborts (already_configured / already_in_progress) cannot trigger;
|
||||
tests/test_standalone_setup.py exercises those semantics.
|
||||
"""
|
||||
self._discovery_dedup_calls = getattr(self, "_discovery_dedup_calls", 0) + 1
|
||||
return None
|
||||
|
||||
def _get_reauth_entry(self, entry=None):
|
||||
@@ -496,6 +503,9 @@ class TestDiscoveryPrefill:
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
# t37 D3: MAC-less discovery needs the AWAITED coroutine helper — the
|
||||
# counter only moves when the call is awaited
|
||||
assert flow._discovery_dedup_calls == 1
|
||||
defaults = vol_mirror.defaults_by_key(result["data_schema"])
|
||||
# schema-level, not "the attribute was stored": this is precisely what
|
||||
# the old assertion missed while the form rendered blank
|
||||
|
||||
Reference in new issue
Block a user