Admin allowlist storage seeded with the setup user (Feature 03, task 4)
- admin_allowlist.py: list_admin_ids / set_admin_ids (atomic sync of the allowlist inside one transaction under the connection lock; refuses empty targets so the last admin can never be removed and the allowlist can never be emptied — fail closed) and seed_setup_admin (seeds ONLY an empty allowlist: stored entry-data copy first — disaster recovery for a lost database — else the HA user who completed the config flow from the entry context; non-empty lists are never resurrected). - __init__.py: both setup paths (first setup and the reopened-record path) seed after migrations; a failed seed closes the database and re-raises through the existing error handling. - options_flow.py: the admin picker is a multi-select of EXISTING HA users (hass.auth.async_get_users), defaulted to the current database allowlist, membership-checked at the schema level and backed up on the raw-input path; empty selections are refused (no_admins), unknown ids rejected (invalid_admin); the selection is applied to the DB when the runtime record is reachable and always stored on the entry as the recovery copy. Submissions without the key (legacy raw payloads) keep the three-key behaviour untouched. - const.CONF_ADMIN_USER_IDS; strings + translations for the field and the two errors; conftest entries gain a context dict (HA's entry context carries the flow user). - Tests: 20 allowlist business tests (round-trips, refusal matrix, seed precedence and narrowed-list persistence), 5 picker flow tests, 3 setup-seeding lifecycle tests. Suite: 901 passed.
This commit is contained in:
+16
-3
@@ -218,12 +218,18 @@ def make_config_entry(
|
||||
entry_id: str = "test_entry",
|
||||
options: dict | None = None,
|
||||
data: dict | None = None,
|
||||
context: dict | None = None,
|
||||
):
|
||||
"""Create a stub config entry shaped like HA's ConfigEntry for tests."""
|
||||
"""Create a stub config entry shaped like HA's ConfigEntry for tests.
|
||||
|
||||
``context`` mirrors HA's entry context (the flow that created the
|
||||
entry carries the acting HA user under ``context["user_id"]``).
|
||||
"""
|
||||
entry = SimpleNamespace(
|
||||
entry_id=entry_id,
|
||||
options=dict(options or {}),
|
||||
data=dict(data or {}),
|
||||
context=dict(context or {}),
|
||||
)
|
||||
entry.add_update_listener = None
|
||||
return entry
|
||||
@@ -302,8 +308,15 @@ async def hass():
|
||||
def make_entry():
|
||||
"""Provide the config-entry factory as a fixture."""
|
||||
|
||||
def _factory(entry_id: str = "test_entry", options: dict | None = None, data: dict | None = None):
|
||||
return make_config_entry(entry_id=entry_id, options=options, data=data)
|
||||
def _factory(
|
||||
entry_id: str = "test_entry",
|
||||
options: dict | None = None,
|
||||
data: dict | None = None,
|
||||
context: dict | None = None,
|
||||
):
|
||||
return make_config_entry(
|
||||
entry_id=entry_id, options=options, data=data, context=context
|
||||
)
|
||||
|
||||
return _factory
|
||||
|
||||
|
||||
Reference in New Issue
Block a user