mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 01:37:08 +00:00
Handle late abort when creating subentry (#145765)
* Handle late abort when creating subentry * Move error handling to the base class * Narrow down expected error in test
This commit is contained in:
parent
e4cc842584
commit
a857461059
@ -543,8 +543,17 @@ class FlowManager(abc.ABC, Generic[_FlowContextT, _FlowResultT, _HandlerT]):
|
|||||||
flow.cur_step = result
|
flow.cur_step = result
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# We pass a copy of the result because we're mutating our version
|
try:
|
||||||
result = await self.async_finish_flow(flow, result.copy())
|
# We pass a copy of the result because we're mutating our version
|
||||||
|
result = await self.async_finish_flow(flow, result.copy())
|
||||||
|
except AbortFlow as err:
|
||||||
|
result = self._flow_result(
|
||||||
|
type=FlowResultType.ABORT,
|
||||||
|
flow_id=flow.flow_id,
|
||||||
|
handler=flow.handler,
|
||||||
|
reason=err.reason,
|
||||||
|
description_placeholders=err.description_placeholders,
|
||||||
|
)
|
||||||
|
|
||||||
# _async_finish_flow may change result type, check it again
|
# _async_finish_flow may change result type, check it again
|
||||||
if result["type"] == FlowResultType.FORM:
|
if result["type"] == FlowResultType.FORM:
|
||||||
|
@ -1526,6 +1526,88 @@ async def test_subentry_reconfigure_flow(hass: HomeAssistant, client) -> None:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_subentry_flow_abort_duplicate(hass: HomeAssistant, client) -> None:
|
||||||
|
"""Test we can handle a subentry flow raising due to unique_id collision."""
|
||||||
|
|
||||||
|
class TestFlow(core_ce.ConfigFlow):
|
||||||
|
class SubentryFlowHandler(core_ce.ConfigSubentryFlow):
|
||||||
|
async def async_step_user(self, user_input=None):
|
||||||
|
return await self.async_step_finish()
|
||||||
|
|
||||||
|
async def async_step_finish(self, user_input=None):
|
||||||
|
if user_input:
|
||||||
|
return self.async_create_entry(
|
||||||
|
title="Mock title", data=user_input, unique_id="test"
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="finish", data_schema=vol.Schema({"enabled": bool})
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@callback
|
||||||
|
def async_get_supported_subentry_types(
|
||||||
|
cls, config_entry: core_ce.ConfigEntry
|
||||||
|
) -> dict[str, type[core_ce.ConfigSubentryFlow]]:
|
||||||
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
|
mock_integration(hass, MockModule("test"))
|
||||||
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
MockConfigEntry(
|
||||||
|
domain="test",
|
||||||
|
entry_id="test1",
|
||||||
|
source="bla",
|
||||||
|
subentries_data=[
|
||||||
|
core_ce.ConfigSubentryData(
|
||||||
|
data={},
|
||||||
|
subentry_id="mock_id",
|
||||||
|
subentry_type="test",
|
||||||
|
title="Title",
|
||||||
|
unique_id="test",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
).add_to_hass(hass)
|
||||||
|
entry = hass.config_entries.async_entries()[0]
|
||||||
|
|
||||||
|
with mock_config_flow("test", TestFlow):
|
||||||
|
url = "/api/config/config_entries/subentries/flow"
|
||||||
|
resp = await client.post(url, json={"handler": [entry.entry_id, "test"]})
|
||||||
|
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
data = await resp.json()
|
||||||
|
|
||||||
|
flow_id = data.pop("flow_id")
|
||||||
|
assert data == {
|
||||||
|
"type": "form",
|
||||||
|
"handler": ["test1", "test"],
|
||||||
|
"step_id": "finish",
|
||||||
|
"data_schema": [{"name": "enabled", "type": "boolean"}],
|
||||||
|
"description_placeholders": None,
|
||||||
|
"errors": None,
|
||||||
|
"last_step": None,
|
||||||
|
"preview": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
with mock_config_flow("test", TestFlow):
|
||||||
|
resp = await client.post(
|
||||||
|
f"/api/config/config_entries/subentries/flow/{flow_id}",
|
||||||
|
json={"enabled": True},
|
||||||
|
)
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
|
||||||
|
entries = hass.config_entries.async_entries("test")
|
||||||
|
assert len(entries) == 1
|
||||||
|
|
||||||
|
data = await resp.json()
|
||||||
|
data.pop("flow_id")
|
||||||
|
assert data == {
|
||||||
|
"handler": ["test1", "test"],
|
||||||
|
"reason": "already_configured",
|
||||||
|
"type": "abort",
|
||||||
|
"description_placeholders": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_subentry_does_not_support_reconfigure(
|
async def test_subentry_does_not_support_reconfigure(
|
||||||
hass: HomeAssistant, client: TestClient
|
hass: HomeAssistant, client: TestClient
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -2226,7 +2226,7 @@ async def test_entry_subentry_no_context(
|
|||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("unique_id", "expected_result"),
|
("unique_id", "expected_result"),
|
||||||
[(None, does_not_raise()), ("test", pytest.raises(HomeAssistantError))],
|
[(None, does_not_raise()), ("test", pytest.raises(data_entry_flow.AbortFlow))],
|
||||||
)
|
)
|
||||||
async def test_entry_subentry_duplicate(
|
async def test_entry_subentry_duplicate(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -886,8 +886,8 @@ async def test_show_progress_fires_only_when_changed(
|
|||||||
) # change (description placeholder)
|
) # change (description placeholder)
|
||||||
|
|
||||||
|
|
||||||
async def test_abort_flow_exception(manager: MockFlowManager) -> None:
|
async def test_abort_flow_exception_step(manager: MockFlowManager) -> None:
|
||||||
"""Test that the AbortFlow exception works."""
|
"""Test that the AbortFlow exception works in a step."""
|
||||||
|
|
||||||
@manager.mock_reg_handler("test")
|
@manager.mock_reg_handler("test")
|
||||||
class TestFlow(data_entry_flow.FlowHandler):
|
class TestFlow(data_entry_flow.FlowHandler):
|
||||||
@ -900,6 +900,33 @@ async def test_abort_flow_exception(manager: MockFlowManager) -> None:
|
|||||||
assert form["description_placeholders"] == {"placeholder": "yo"}
|
assert form["description_placeholders"] == {"placeholder": "yo"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_abort_flow_exception_finish_flow(hass: HomeAssistant) -> None:
|
||||||
|
"""Test that the AbortFlow exception works when finishing a flow."""
|
||||||
|
|
||||||
|
class TestFlow(data_entry_flow.FlowHandler):
|
||||||
|
VERSION = 1
|
||||||
|
|
||||||
|
async def async_step_init(self, input):
|
||||||
|
"""Return init form with one input field 'count'."""
|
||||||
|
return self.async_create_entry(title="init", data=input)
|
||||||
|
|
||||||
|
class FlowManager(data_entry_flow.FlowManager):
|
||||||
|
async def async_create_flow(self, handler_key, *, context, data):
|
||||||
|
"""Create a test flow."""
|
||||||
|
return TestFlow()
|
||||||
|
|
||||||
|
async def async_finish_flow(self, flow, result):
|
||||||
|
"""Raise AbortFlow."""
|
||||||
|
raise data_entry_flow.AbortFlow("mock-reason", {"placeholder": "yo"})
|
||||||
|
|
||||||
|
manager = FlowManager(hass)
|
||||||
|
|
||||||
|
form = await manager.async_init("test")
|
||||||
|
assert form["type"] == data_entry_flow.FlowResultType.ABORT
|
||||||
|
assert form["reason"] == "mock-reason"
|
||||||
|
assert form["description_placeholders"] == {"placeholder": "yo"}
|
||||||
|
|
||||||
|
|
||||||
async def test_init_unknown_flow(manager: MockFlowManager) -> None:
|
async def test_init_unknown_flow(manager: MockFlowManager) -> None:
|
||||||
"""Test that UnknownFlow is raised when async_create_flow returns None."""
|
"""Test that UnknownFlow is raised when async_create_flow returns None."""
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user