1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-25 03:09:52 +01:00

Add WS command config_entries/flow/subscribe (#142459)

This commit is contained in:
Erik Montnemery
2025-04-10 16:58:46 +02:00
committed by GitHub
parent a26cdef427
commit eee6e8a2c3
5 changed files with 458 additions and 4 deletions

View File

@@ -8,6 +8,7 @@ from unittest.mock import ANY, AsyncMock, patch
from aiohttp.test_utils import TestClient
from freezegun.api import FrozenDateTimeFactory
import pytest
from pytest_unordered import unordered
import voluptuous as vol
from homeassistant import config_entries as core_ce, data_entry_flow, loader
@@ -882,6 +883,256 @@ async def test_get_progress_flow_unauth(
assert resp2.status == HTTPStatus.UNAUTHORIZED
async def test_get_progress_subscribe(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
"""Test querying for the flows that are in progress."""
assert await async_setup_component(hass, "config", {})
mock_platform(hass, "test.config_flow", None)
ws_client = await hass_ws_client(hass)
mock_integration(
hass, MockModule("test", async_setup_entry=AsyncMock(return_value=True))
)
entry = MockConfigEntry(domain="test", title="Test", entry_id="1234")
entry.add_to_hass(hass)
class TestFlow(core_ce.ConfigFlow):
VERSION = 5
async def async_step_bluetooth(
self, discovery_info: HassioServiceInfo
) -> ConfigFlowResult:
"""Handle a bluetooth discovery."""
return self.async_abort(reason="already_configured")
async def async_step_hassio(
self, discovery_info: HassioServiceInfo
) -> ConfigFlowResult:
"""Handle a Hass.io discovery."""
return await self.async_step_account()
async def async_step_account(self, user_input: dict[str, Any] | None = None):
"""Show a form to the user."""
return self.async_show_form(step_id="account")
async def async_step_user(self, user_input: dict[str, Any] | None = None):
"""Handle a config flow initialized by the user."""
return await self.async_step_account()
async def async_step_reauth(self, user_input: dict[str, Any] | None = None):
"""Handle a reauthentication flow."""
nonlocal entry
assert self._get_reauth_entry() is entry
return await self.async_step_account()
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
):
"""Handle a reconfiguration flow initialized by the user."""
nonlocal entry
assert self._get_reconfigure_entry() is entry
return await self.async_step_account()
await ws_client.send_json({"id": 1, "type": "config_entries/flow/subscribe"})
response = await ws_client.receive_json()
assert response == {"id": 1, "event": [], "type": "event"}
response = await ws_client.receive_json()
assert response == {"id": 1, "result": None, "success": True, "type": "result"}
flow_context = {
"bluetooth": {"source": core_ce.SOURCE_BLUETOOTH},
"hassio": {"source": core_ce.SOURCE_HASSIO},
"user": {"source": core_ce.SOURCE_USER},
"reauth": {"source": core_ce.SOURCE_REAUTH, "entry_id": "1234"},
"reconfigure": {"source": core_ce.SOURCE_RECONFIGURE, "entry_id": "1234"},
}
forms = {}
with mock_config_flow("test", TestFlow):
for key, context in flow_context.items():
forms[key] = await hass.config_entries.flow.async_init(
"test", context=context
)
assert forms["bluetooth"]["type"] == data_entry_flow.FlowResultType.ABORT
for key in ("hassio", "user", "reauth", "reconfigure"):
assert forms[key]["type"] == data_entry_flow.FlowResultType.FORM
assert forms[key]["step_id"] == "account"
for key in ("hassio", "user", "reauth", "reconfigure"):
hass.config_entries.flow.async_abort(forms[key]["flow_id"])
# Uninitialized flows and flows with SOURCE_USER and SOURCE_RECONFIGURE
# should be filtered out
for key in ("hassio", "reauth"):
response = await ws_client.receive_json()
assert response == {
"event": [
{
"flow": {
"flow_id": forms[key]["flow_id"],
"handler": "test",
"step_id": "account",
"context": flow_context[key],
},
"flow_id": forms[key]["flow_id"],
"type": "added",
}
],
"id": 1,
"type": "event",
}
for key in ("hassio", "reauth"):
response = await ws_client.receive_json()
assert response == {
"event": [
{
"flow_id": forms[key]["flow_id"],
"type": "removed",
}
],
"id": 1,
"type": "event",
}
async def test_get_progress_subscribe_in_progress(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
"""Test querying for the flows that are in progress."""
assert await async_setup_component(hass, "config", {})
mock_platform(hass, "test.config_flow", None)
ws_client = await hass_ws_client(hass)
mock_integration(
hass, MockModule("test", async_setup_entry=AsyncMock(return_value=True))
)
entry = MockConfigEntry(domain="test", title="Test", entry_id="1234")
entry.add_to_hass(hass)
class TestFlow(core_ce.ConfigFlow):
VERSION = 5
async def async_step_bluetooth(
self, discovery_info: HassioServiceInfo
) -> ConfigFlowResult:
"""Handle a bluetooth discovery."""
return self.async_abort(reason="already_configured")
async def async_step_hassio(
self, discovery_info: HassioServiceInfo
) -> ConfigFlowResult:
"""Handle a Hass.io discovery."""
return await self.async_step_account()
async def async_step_account(self, user_input: dict[str, Any] | None = None):
"""Show a form to the user."""
return self.async_show_form(step_id="account")
async def async_step_user(self, user_input: dict[str, Any] | None = None):
"""Handle a config flow initialized by the user."""
return await self.async_step_account()
async def async_step_reauth(self, user_input: dict[str, Any] | None = None):
"""Handle a reauthentication flow."""
nonlocal entry
assert self._get_reauth_entry() is entry
return await self.async_step_account()
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
):
"""Handle a reconfiguration flow initialized by the user."""
nonlocal entry
assert self._get_reconfigure_entry() is entry
return await self.async_step_account()
flow_context = {
"bluetooth": {"source": core_ce.SOURCE_BLUETOOTH},
"hassio": {"source": core_ce.SOURCE_HASSIO},
"user": {"source": core_ce.SOURCE_USER},
"reauth": {"source": core_ce.SOURCE_REAUTH, "entry_id": "1234"},
"reconfigure": {"source": core_ce.SOURCE_RECONFIGURE, "entry_id": "1234"},
}
forms = {}
with mock_config_flow("test", TestFlow):
for key, context in flow_context.items():
forms[key] = await hass.config_entries.flow.async_init(
"test", context=context
)
assert forms["bluetooth"]["type"] == data_entry_flow.FlowResultType.ABORT
for key in ("hassio", "user", "reauth", "reconfigure"):
assert forms[key]["type"] == data_entry_flow.FlowResultType.FORM
assert forms[key]["step_id"] == "account"
await ws_client.send_json({"id": 1, "type": "config_entries/flow/subscribe"})
# Uninitialized flows and flows with SOURCE_USER and SOURCE_RECONFIGURE
# should be filtered out
responses = []
responses.append(await ws_client.receive_json())
assert responses == [
{
"event": unordered(
[
{
"flow": {
"flow_id": forms[key]["flow_id"],
"handler": "test",
"step_id": "account",
"context": flow_context[key],
},
"flow_id": forms[key]["flow_id"],
"type": None,
}
for key in ("hassio", "reauth")
]
),
"id": 1,
"type": "event",
}
]
response = await ws_client.receive_json()
assert response == {"id": ANY, "result": None, "success": True, "type": "result"}
for key in ("hassio", "user", "reauth", "reconfigure"):
hass.config_entries.flow.async_abort(forms[key]["flow_id"])
for key in ("hassio", "reauth"):
response = await ws_client.receive_json()
assert response == {
"event": [
{
"flow_id": forms[key]["flow_id"],
"type": "removed",
}
],
"id": 1,
"type": "event",
}
async def test_get_progress_subscribe_unauth(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, hass_admin_user: MockUser
) -> None:
"""Test we can't subscribe to flows."""
assert await async_setup_component(hass, "config", {})
hass_admin_user.groups = []
ws_client = await hass_ws_client(hass)
await ws_client.send_json({"id": 5, "type": "config_entries/flow/subscribe"})
response = await ws_client.receive_json()
assert not response["success"]
assert response["error"]["code"] == "unauthorized"
async def test_options_flow(hass: HomeAssistant, client: TestClient) -> None:
"""Test we can change options."""