Review fixes: persisted flow-user admin seed and real cv.multi_select

Review rounds 1-2 (Codex gpt-5.6-terra) found two HIGHs against real HA:
- entry.context is not a durable record — the config flow now persists
  the completing HA user into entry data (CONF_ADMIN_USER_IDS) and the
  seed precedence becomes: options copy (the current list) first, then
  the config flow's entry-data copy, then the context user as a last
  resort. Config-flow tests cover the persisted user and the
  context-free path; a seed-precedence test covers options > data.
- the hand-rolled closure could not serialize as an HA multi-select.
  The harness now mocks homeassistant.helpers.config_validation with a
  faithful multi_select, and the flow uses cv.multi_select exactly as
  real HA code does. Suite: 904 passed.
This commit is contained in:
Joshua King
2026-09-15 17:13:32 -04:00
parent 0d61ea2c88
commit d90c170460
6 changed files with 114 additions and 35 deletions
+4 -24
View File
@@ -7,6 +7,7 @@ from typing import Any
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import config_validation as cv
from .const import (
CONF_ADMIN_USER_IDS,
@@ -31,29 +32,6 @@ def _parse_time(value: str) -> bool:
return _TIME_PATTERN.fullmatch(value) is not None
def _multi_select(choices: dict[str, str]):
"""A voluptuous validator for a multi-select of ``choices``.
The mock-only test harness cannot import HA's config-validation
helpers (they pull in real HA), so this mirrors the shape of
``homeassistant.helpers.config_validation.multi_select``: accepts a
list of keys from ``choices``, returns the list unchanged, rejects
anything else (None becomes the empty selection).
"""
def _validate(value):
if value is None:
return []
if not isinstance(value, list):
raise vol.Invalid("expected a list of selections")
for item in value:
if item not in choices:
raise vol.Invalid(f"{item!r} is not a valid choice")
return list(value)
return _validate
def _async_validate(user_input: dict[str, Any]) -> dict[str, str]:
"""Validate user input and return field-level errors instead of raising."""
errors: dict[str, str] = {}
@@ -106,11 +84,13 @@ def _build_schema(
): int,
}
if admin_choices is not None:
# HA's supported multi-select validator: the frontend can
# serialize the field as a real picker of the given choices.
fields[
vol.Optional(
CONF_ADMIN_USER_IDS, default=list(admin_default or [])
)
] = _multi_select(admin_choices)
] = cv.multi_select(admin_choices)
return vol.Schema(fields)