Remove unignore flow (#126765)

This commit is contained in:
Erik Montnemery 2024-09-25 20:29:14 +02:00 committed by GitHub
parent f53411b95a
commit 7d61cb1ef5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 37 additions and 730 deletions

View File

@ -106,11 +106,6 @@ SOURCE_ZEROCONF = "zeroconf"
# source and while it exists normal discoveries with the same unique id are ignored.
SOURCE_IGNORE = "ignore"
# This is used when a user uses the "Stop Ignoring" button in the UI (the
# config_entries/ignore_flow websocket command). It's triggered after the
# "ignore" config entry has been removed and unloaded.
SOURCE_UNIGNORE = "unignore"
# This is used to signal that re-authentication is required by the user.
SOURCE_REAUTH = "reauth"
@ -179,7 +174,6 @@ DISCOVERY_SOURCES = {
SOURCE_INTEGRATION_DISCOVERY,
SOURCE_MQTT,
SOURCE_SSDP,
SOURCE_UNIGNORE,
SOURCE_USB,
SOURCE_ZEROCONF,
}
@ -1264,7 +1258,7 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager[ConfigFlowResult]):
# a single config entry, but which already has an entry
if (
context.get("source")
not in {SOURCE_IGNORE, SOURCE_REAUTH, SOURCE_UNIGNORE, SOURCE_RECONFIGURE}
not in {SOURCE_IGNORE, SOURCE_REAUTH, SOURCE_RECONFIGURE}
and self.config_entries.async_has_entries(handler, include_ignore=False)
and await _support_single_config_entry_only(self.hass, handler)
):
@ -1855,20 +1849,6 @@ class ConfigEntries:
issue_id = f"config_entry_reauth_{entry.domain}_{entry.entry_id}"
ir.async_delete_issue(self.hass, HOMEASSISTANT_DOMAIN, issue_id)
# After we have fully removed an "ignore" config entry we can try and rediscover
# it so that a user is able to immediately start configuring it. We do this by
# starting a new flow with the 'unignore' step. If the integration doesn't
# implement async_step_unignore then this will be a no-op.
if entry.source == SOURCE_IGNORE:
self.hass.async_create_task_internal(
self.hass.config_entries.flow.async_init(
entry.domain,
context={"source": SOURCE_UNIGNORE},
data={"unique_id": entry.unique_id},
),
f"config entry unignore {entry.title} {entry.domain} {entry.unique_id}",
)
self._async_dispatch(ConfigEntryChange.REMOVED, entry)
for discovery_domain in entry.discovery_keys:
async_dispatcher_send_internal(
@ -2544,10 +2524,6 @@ class ConfigFlow(ConfigEntryBaseFlow):
await self.async_set_unique_id(user_input["unique_id"], raise_on_progress=False)
return self.async_create_entry(title=user_input["title"], data={})
async def async_step_unignore(self, user_input: dict[str, Any]) -> ConfigFlowResult:
"""Rediscover a config entry by it's unique_id."""
return self.async_abort(reason="not_implemented")
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:

View File

@ -1335,7 +1335,14 @@ async def test_set_fallback_interval_big(hass: HomeAssistant) -> None:
),
],
)
@pytest.mark.parametrize("entry_source", [config_entries.SOURCE_IGNORE])
@pytest.mark.parametrize(
"entry_source",
[
config_entries.SOURCE_BLUETOOTH,
config_entries.SOURCE_IGNORE,
config_entries.SOURCE_USER,
],
)
async def test_bluetooth_rediscover(
hass: HomeAssistant,
entry_domain: str,
@ -1386,205 +1393,6 @@ async def test_bluetooth_rediscover(
BluetoothScanningMode.ACTIVE,
)
class FakeScanner(BaseHaRemoteScanner):
def inject_advertisement(
self, device: BLEDevice, advertisement_data: AdvertisementData
) -> None:
"""Inject an advertisement."""
self._async_on_advertisement(
device.address,
advertisement_data.rssi,
device.name,
advertisement_data.service_uuids,
advertisement_data.service_data,
advertisement_data.manufacturer_data,
advertisement_data.tx_power,
{"scanner_specific_data": "test"},
MONOTONIC_TIME(),
)
def clear_all_devices(self) -> None:
"""Clear all devices."""
self._discovered_device_advertisement_datas.clear()
self._discovered_device_timestamps.clear()
self._previous_service_info.clear()
connector = (
HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False),
)
non_connectable_scanner = FakeScanner(
"connectable",
"connectable",
connector,
False,
)
unsetup_connectable_scanner = non_connectable_scanner.async_setup()
cancel_connectable_scanner = _get_manager().async_register_scanner(
non_connectable_scanner
)
with patch.object(hass.config_entries.flow, "async_init") as mock_config_flow:
non_connectable_scanner.inject_advertisement(
switchbot_device_non_connectable, switchbot_device_adv
)
await hass.async_block_till_done()
expected_context = {
"discovery_key": DiscoveryKey(
domain="bluetooth", key="44:44:33:11:23:45", version=1
),
"source": "bluetooth",
}
assert len(mock_config_flow.mock_calls) == 1
assert mock_config_flow.mock_calls[0][1][0] == "switchbot"
assert mock_config_flow.mock_calls[0][2]["context"] == expected_context
hass.config.components.add(entry_domain)
mock_integration(hass, MockModule(entry_domain))
entry = MockConfigEntry(
domain=entry_domain,
discovery_keys=entry_discovery_keys,
unique_id="mock-unique-id",
state=config_entries.ConfigEntryState.LOADED,
source=entry_source,
)
entry.add_to_hass(hass)
assert (
async_ble_device_from_address(hass, "44:44:33:11:23:45", False) is not None
)
assert async_scanner_count(hass, connectable=False) == 1
assert len(callbacks) == 1
assert (
"44:44:33:11:23:45"
in non_connectable_scanner.discovered_devices_and_advertisement_data
)
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()
assert (
async_ble_device_from_address(hass, "44:44:33:11:23:45", False) is not None
)
assert async_scanner_count(hass, connectable=False) == 1
assert len(callbacks) == 1
assert len(mock_config_flow.mock_calls) == 3
assert mock_config_flow.mock_calls[1][1][0] == entry_domain
assert mock_config_flow.mock_calls[1][2]["context"] == {
"source": "unignore",
}
assert mock_config_flow.mock_calls[2][1][0] == "switchbot"
assert mock_config_flow.mock_calls[2][2]["context"] == expected_context
cancel()
unsetup_connectable_scanner()
cancel_connectable_scanner()
@pytest.mark.usefixtures("mock_bluetooth_adapters")
@pytest.mark.parametrize(
(
"entry_domain",
"entry_discovery_keys",
),
[
# Matching discovery key
(
"switchbot",
{
"bluetooth": (
DiscoveryKey(
domain="bluetooth", key="44:44:33:11:23:45", version=1
),
)
},
),
# Matching discovery key
(
"switchbot",
{
"bluetooth": (
DiscoveryKey(
domain="bluetooth", key="44:44:33:11:23:45", version=1
),
),
"other": (DiscoveryKey(domain="other", key="blah", version=1),),
},
),
# Matching discovery key, other domain
# Note: Rediscovery is not currently restricted to the domain of the removed
# entry. Such a check can be added if needed.
(
"comp",
{
"bluetooth": (
DiscoveryKey(
domain="bluetooth", key="44:44:33:11:23:45", version=1
),
)
},
),
],
)
@pytest.mark.parametrize(
"entry_source", [config_entries.SOURCE_BLUETOOTH, config_entries.SOURCE_USER]
)
async def test_bluetooth_rediscover_2(
hass: HomeAssistant,
entry_domain: str,
entry_discovery_keys: tuple,
entry_source: str,
) -> None:
"""Test we reinitiate flows when an ignored config entry is removed.
This test can be merged with test_zeroconf_rediscover when
async_step_unignore has been removed from the ConfigFlow base class.
"""
mock_bt = [
{
"domain": "switchbot",
"service_data_uuid": "050a021a-0000-1000-8000-00805f9b34fb",
"connectable": False,
},
]
with patch(
"homeassistant.components.bluetooth.async_get_bluetooth", return_value=mock_bt
):
assert await async_setup_component(hass, bluetooth.DOMAIN, {})
await hass.async_block_till_done()
assert async_scanner_count(hass, connectable=False) == 0
switchbot_device_non_connectable = generate_ble_device(
"44:44:33:11:23:45",
"wohand",
{},
rssi=-100,
)
switchbot_device_adv = generate_advertisement_data(
local_name="wohand",
service_uuids=["050a021a-0000-1000-8000-00805f9b34fb"],
service_data={"050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff"},
manufacturer_data={1: b"\x01"},
rssi=-100,
)
callbacks = []
def _fake_subscriber(
service_info: BluetoothServiceInfo,
change: BluetoothChange,
) -> None:
"""Fake subscriber for the BleakScanner."""
callbacks.append((service_info, change))
cancel = bluetooth.async_register_callback(
hass,
_fake_subscriber,
{"address": "44:44:33:11:23:45", "connectable": False},
BluetoothScanningMode.ACTIVE,
)
class FakeScanner(BaseHaRemoteScanner):
def inject_advertisement(
self, device: BLEDevice, advertisement_data: AdvertisementData
@ -1847,12 +1655,7 @@ async def test_bluetooth_rediscover_no_match(
)
assert async_scanner_count(hass, connectable=False) == 1
assert len(callbacks) == 1
assert len(mock_config_flow.mock_calls) == 2
assert mock_config_flow.mock_calls[1][1][0] == entry_domain
assert mock_config_flow.mock_calls[1][2]["context"] == {
"source": "unignore",
}
assert len(mock_config_flow.mock_calls) == 1
cancel()
unsetup_connectable_scanner()

View File

@ -1201,7 +1201,14 @@ async def test_aiodiscover_finds_new_hosts_after_interval(hass: HomeAssistant) -
),
],
)
@pytest.mark.parametrize("entry_source", [config_entries.SOURCE_IGNORE])
@pytest.mark.parametrize(
"entry_source",
[
config_entries.SOURCE_DHCP,
config_entries.SOURCE_IGNORE,
config_entries.SOURCE_USER,
],
)
async def test_dhcp_rediscover(
hass: HomeAssistant,
entry_domain: str,
@ -1255,105 +1262,6 @@ async def test_dhcp_rediscover(
macaddress="b8b7f16db533",
)
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()
assert len(mock_init.mock_calls) == 2
assert mock_init.mock_calls[0][1][0] == entry_domain
assert mock_init.mock_calls[0][2]["context"] == {"source": "unignore"}
assert mock_init.mock_calls[1][1][0] == "mock-domain"
assert mock_init.mock_calls[1][2]["context"] == expected_context
@pytest.mark.parametrize(
(
"entry_domain",
"entry_discovery_keys",
),
[
# Matching discovery key
(
"mock-domain",
{"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),)},
),
# Matching discovery key
(
"mock-domain",
{
"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),),
"other": (DiscoveryKey(domain="other", key="blah", version=1),),
},
),
# Matching discovery key, other domain
# Note: Rediscovery is not currently restricted to the domain of the removed
# entry. Such a check can be added if needed.
(
"comp",
{"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),)},
),
],
)
@pytest.mark.parametrize(
"entry_source", [config_entries.SOURCE_USER, config_entries.SOURCE_ZEROCONF]
)
async def test_dhcp_rediscover_2(
hass: HomeAssistant,
entry_domain: str,
entry_discovery_keys: tuple,
entry_source: str,
) -> None:
"""Test we reinitiate flows when an ignored config entry is removed.
This test can be merged with test_zeroconf_rediscover when
async_step_unignore has been removed from the ConfigFlow base class.
"""
entry = MockConfigEntry(
domain=entry_domain,
discovery_keys=entry_discovery_keys,
unique_id="mock-unique-id",
state=config_entries.ConfigEntryState.LOADED,
source=entry_source,
)
entry.add_to_hass(hass)
address_data = {}
integration_matchers = dhcp.async_index_integration_matchers(
[{"domain": "mock-domain", "hostname": "connect", "macaddress": "B8B7F1*"}]
)
packet = Ether(RAW_DHCP_REQUEST)
async_handle_dhcp_packet = await _async_get_handle_dhcp_packet(
hass, integration_matchers, address_data
)
rediscovery_watcher = dhcp.RediscoveryWatcher(
hass, address_data, integration_matchers
)
rediscovery_watcher.async_start()
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
await async_handle_dhcp_packet(packet)
# Ensure no change is ignored
await async_handle_dhcp_packet(packet)
# Assert the cached MAC address is hexstring without :
assert address_data == {
"b8b7f16db533": {"hostname": "connect", "ip": "192.168.210.56"}
}
expected_context = {
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
"source": config_entries.SOURCE_DHCP,
}
assert len(mock_init.mock_calls) == 1
assert mock_init.mock_calls[0][1][0] == "mock-domain"
assert mock_init.mock_calls[0][2]["context"] == expected_context
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
ip="192.168.210.56",
hostname="connect",
macaddress="b8b7f16db533",
)
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()
@ -1447,6 +1355,4 @@ async def test_dhcp_rediscover_no_match(
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()
assert len(mock_init.mock_calls) == 1
assert mock_init.mock_calls[0][1][0] == entry_domain
assert mock_init.mock_calls[0][2]["context"] == {"source": "unignore"}
assert len(mock_init.mock_calls) == 0

View File

@ -938,7 +938,14 @@ async def test_flow_dismiss_on_byebye(
),
],
)
@pytest.mark.parametrize("entry_source", [config_entries.SOURCE_IGNORE])
@pytest.mark.parametrize(
"entry_source",
[
config_entries.SOURCE_IGNORE,
config_entries.SOURCE_SSDP,
config_entries.SOURCE_USER,
],
)
async def test_ssdp_rediscover(
mock_get_ssdp,
hass: HomeAssistant,
@ -999,116 +1006,6 @@ async def test_ssdp_rediscover(
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()
assert len(mock_flow_init.mock_calls) == 3
assert mock_flow_init.mock_calls[1][1][0] == entry_domain
assert mock_flow_init.mock_calls[1][2]["context"] == {"source": "unignore"}
assert mock_flow_init.mock_calls[2][1][0] == "mock-domain"
assert mock_flow_init.mock_calls[2][2]["context"] == expected_context
assert (
mock_flow_init.mock_calls[2][2]["data"]
== mock_flow_init.mock_calls[0][2]["data"]
)
@patch(
"homeassistant.components.ssdp.async_get_ssdp",
return_value={"mock-domain": [{"st": "mock-st"}]},
)
@pytest.mark.parametrize(
(
"entry_domain",
"entry_discovery_keys",
),
[
# Matching discovery key
(
"mock-domain",
{"ssdp": (DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),)},
),
# Matching discovery key
(
"mock-domain",
{
"ssdp": (DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),),
"other": (DiscoveryKey(domain="other", key="blah", version=1),),
},
),
# Matching discovery key, other domain
# Note: Rediscovery is not currently restricted to the domain of the removed
# entry. Such a check can be added if needed.
(
"comp",
{"ssdp": (DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),)},
),
],
)
@pytest.mark.parametrize(
"entry_source", [config_entries.SOURCE_USER, config_entries.SOURCE_ZEROCONF]
)
async def test_ssdp_rediscover_2(
mock_get_ssdp,
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_flow_init,
entry_domain: str,
entry_discovery_keys: tuple,
entry_source: str,
) -> None:
"""Test we reinitiate flows when an ignored config entry is removed.
This test can be merged with test_zeroconf_rediscover when
async_step_unignore has been removed from the ConfigFlow base class.
"""
entry = MockConfigEntry(
domain=entry_domain,
discovery_keys=entry_discovery_keys,
unique_id="mock-unique-id",
state=config_entries.ConfigEntryState.LOADED,
source=entry_source,
)
entry.add_to_hass(hass)
mock_ssdp_search_response = _ssdp_headers(
{
"st": "mock-st",
"location": "http://1.1.1.1",
"usn": "uuid:mock-udn::mock-st",
"server": "mock-server",
"ext": "",
"_source": "search",
}
)
aioclient_mock.get(
"http://1.1.1.1",
text="""
<root>
<device>
<deviceType>Paulus</deviceType>
<manufacturer>Paulus</manufacturer>
</device>
</root>
""",
)
ssdp_listener = await init_ssdp_component(hass)
ssdp_listener._on_search(mock_ssdp_search_response)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
expected_context = {
"discovery_key": DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),
"source": config_entries.SOURCE_SSDP,
}
assert len(mock_flow_init.mock_calls) == 1
assert mock_flow_init.mock_calls[0][1][0] == "mock-domain"
assert mock_flow_init.mock_calls[0][2]["context"] == expected_context
mock_call_data: ssdp.SsdpServiceInfo = mock_flow_init.mock_calls[0][2]["data"]
assert mock_call_data.ssdp_st == "mock-st"
assert mock_call_data.ssdp_location == "http://1.1.1.1"
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()
assert len(mock_flow_init.mock_calls) == 2
assert mock_flow_init.mock_calls[1][1][0] == "mock-domain"
assert mock_flow_init.mock_calls[1][2]["context"] == expected_context
@ -1196,6 +1093,4 @@ async def test_ssdp_rediscover_no_match(
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()
assert len(mock_flow_init.mock_calls) == 2
assert mock_flow_init.mock_calls[1][1][0] == entry_domain
assert mock_flow_init.mock_calls[1][2]["context"] == {"source": "unignore"}
assert len(mock_flow_init.mock_calls) == 1

View File

@ -1456,7 +1456,14 @@ async def test_zeroconf_removed(hass: HomeAssistant) -> None:
),
],
)
@pytest.mark.parametrize("entry_source", [config_entries.SOURCE_IGNORE])
@pytest.mark.parametrize(
"entry_source",
[
config_entries.SOURCE_IGNORE,
config_entries.SOURCE_USER,
config_entries.SOURCE_ZEROCONF,
],
)
async def test_zeroconf_rediscover(
hass: HomeAssistant,
entry_domain: str,
@ -1483,149 +1490,6 @@ async def test_zeroconf_rediscover(
)
entry.add_to_hass(hass)
with (
patch.dict(
zc_gen.ZEROCONF,
{
"_http._tcp.local.": [
{
"domain": "shelly",
"name": "shelly*",
"properties": {"macaddress": "ffaadd*"},
}
]
},
clear=True,
),
patch.object(hass.config_entries.flow, "async_init") as mock_config_flow,
patch.object(
zeroconf, "AsyncServiceBrowser", side_effect=http_only_service_update_mock
) as mock_service_browser,
patch(
"homeassistant.components.zeroconf.AsyncServiceInfo",
side_effect=get_zeroconf_info_mock("FFAADDCC11DD"),
),
):
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
expected_context = {
"discovery_key": DiscoveryKey(
domain="zeroconf",
key=("_http._tcp.local.", "Shelly108._http._tcp.local."),
version=1,
),
"source": "zeroconf",
}
assert len(mock_service_browser.mock_calls) == 1
assert len(mock_config_flow.mock_calls) == 1
assert mock_config_flow.mock_calls[0][1][0] == "shelly"
assert mock_config_flow.mock_calls[0][2]["context"] == expected_context
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()
assert len(mock_service_browser.mock_calls) == 1
assert len(mock_config_flow.mock_calls) == 3
assert mock_config_flow.mock_calls[1][1][0] == entry_domain
assert mock_config_flow.mock_calls[1][2]["context"] == {
"source": "unignore",
}
assert mock_config_flow.mock_calls[2][1][0] == "shelly"
assert mock_config_flow.mock_calls[2][2]["context"] == expected_context
@pytest.mark.usefixtures("mock_async_zeroconf")
@pytest.mark.parametrize(
(
"entry_domain",
"entry_discovery_keys",
),
[
# Matching discovery key
(
"shelly",
{
"zeroconf": (
DiscoveryKey(
domain="zeroconf",
key=("_http._tcp.local.", "Shelly108._http._tcp.local."),
version=1,
),
)
},
),
# Matching discovery key
(
"shelly",
{
"zeroconf": (
DiscoveryKey(
domain="zeroconf",
key=("_http._tcp.local.", "Shelly108._http._tcp.local."),
version=1,
),
),
"other": (
DiscoveryKey(
domain="other",
key="blah",
version=1,
),
),
},
),
# Matching discovery key, other domain
# Note: Rediscovery is not currently restricted to the domain of the removed
# entry. Such a check can be added if needed.
(
"comp",
{
"zeroconf": (
DiscoveryKey(
domain="zeroconf",
key=("_http._tcp.local.", "Shelly108._http._tcp.local."),
version=1,
),
)
},
),
],
)
@pytest.mark.parametrize(
"entry_source", [config_entries.SOURCE_USER, config_entries.SOURCE_ZEROCONF]
)
async def test_zeroconf_rediscover_2(
hass: HomeAssistant,
entry_domain: str,
entry_discovery_keys: tuple,
entry_source: str,
) -> None:
"""Test we reinitiate flows when an ignored config entry is removed.
This test can be merged with test_zeroconf_rediscover when
async_step_unignore has been removed from the ConfigFlow base class.
"""
def http_only_service_update_mock(zeroconf, services, handlers):
"""Call service update handler."""
handlers[0](
zeroconf,
"_http._tcp.local.",
"Shelly108._http._tcp.local.",
ServiceStateChange.Added,
)
entry = MockConfigEntry(
domain=entry_domain,
discovery_keys=entry_discovery_keys,
unique_id="mock-unique-id",
state=config_entries.ConfigEntryState.LOADED,
source=entry_source,
)
entry.add_to_hass(hass)
with (
patch.dict(
zc_gen.ZEROCONF,
@ -1790,8 +1654,4 @@ async def test_zeroconf_rediscover_no_match(
await hass.async_block_till_done()
assert len(mock_service_browser.mock_calls) == 1
assert len(mock_config_flow.mock_calls) == 2
assert mock_config_flow.mock_calls[1][1][0] == entry_domain
assert mock_config_flow.mock_calls[1][2]["context"] == {
"source": "unignore",
}
assert len(mock_config_flow.mock_calls) == 1

View File

@ -3271,129 +3271,6 @@ async def test_async_current_entries_explicit_include_ignore(
assert len(mock_setup_entry.mock_calls) == 0
async def test_unignore_step_form(
hass: HomeAssistant, manager: config_entries.ConfigEntries
) -> None:
"""Test that we can ignore flows that are in progress and have a unique ID, then rediscover them."""
async_setup_entry = AsyncMock(return_value=True)
mock_integration(hass, MockModule("comp", async_setup_entry=async_setup_entry))
mock_platform(hass, "comp.config_flow", None)
class TestFlow(config_entries.ConfigFlow):
"""Test flow."""
VERSION = 1
async def async_step_unignore(self, user_input):
"""Test unignore step."""
unique_id = user_input["unique_id"]
await self.async_set_unique_id(unique_id)
return self.async_show_form(step_id="discovery")
with mock_config_flow("comp", TestFlow):
result = await manager.flow.async_init(
"comp",
context={"source": config_entries.SOURCE_IGNORE},
data={"unique_id": "mock-unique-id", "title": "Ignored Title"},
)
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
entry = hass.config_entries.async_entries("comp")[0]
assert entry.source == "ignore"
assert entry.unique_id == "mock-unique-id"
assert entry.domain == "comp"
assert entry.title == "Ignored Title"
await manager.async_remove(entry.entry_id)
# But after a 'tick' the unignore step has run and we can see an active flow again.
await hass.async_block_till_done()
assert len(hass.config_entries.flow.async_progress_by_handler("comp")) == 1
# and still not config entries
assert len(hass.config_entries.async_entries("comp")) == 0
async def test_unignore_create_entry(
hass: HomeAssistant, manager: config_entries.ConfigEntries
) -> None:
"""Test that we can ignore flows that are in progress and have a unique ID, then rediscover them."""
async_setup_entry = AsyncMock(return_value=True)
mock_integration(hass, MockModule("comp", async_setup_entry=async_setup_entry))
mock_platform(hass, "comp.config_flow", None)
class TestFlow(config_entries.ConfigFlow):
"""Test flow."""
VERSION = 1
async def async_step_unignore(self, user_input):
"""Test unignore step."""
unique_id = user_input["unique_id"]
await self.async_set_unique_id(unique_id)
return self.async_create_entry(title="yo", data={})
with mock_config_flow("comp", TestFlow):
result = await manager.flow.async_init(
"comp",
context={"source": config_entries.SOURCE_IGNORE},
data={"unique_id": "mock-unique-id", "title": "Ignored Title"},
)
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
entry = hass.config_entries.async_entries("comp")[0]
assert entry.source == "ignore"
assert entry.unique_id == "mock-unique-id"
assert entry.domain == "comp"
assert entry.title == "Ignored Title"
await manager.async_remove(entry.entry_id)
# But after a 'tick' the unignore step has run and we can see a config entry.
await hass.async_block_till_done()
entry = hass.config_entries.async_entries("comp")[0]
assert entry.source == config_entries.SOURCE_UNIGNORE
assert entry.unique_id == "mock-unique-id"
assert entry.title == "yo"
# And still no active flow
assert len(hass.config_entries.flow.async_progress_by_handler("comp")) == 0
async def test_unignore_default_impl(
hass: HomeAssistant, manager: config_entries.ConfigEntries
) -> None:
"""Test that resdicovery is a no-op by default."""
async_setup_entry = AsyncMock(return_value=True)
mock_integration(hass, MockModule("comp", async_setup_entry=async_setup_entry))
mock_platform(hass, "comp.config_flow", None)
class TestFlow(config_entries.ConfigFlow):
"""Test flow."""
VERSION = 1
with mock_config_flow("comp", TestFlow):
result = await manager.flow.async_init(
"comp",
context={"source": config_entries.SOURCE_IGNORE},
data={"unique_id": "mock-unique-id", "title": "Ignored Title"},
)
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
entry = hass.config_entries.async_entries("comp")[0]
assert entry.source == "ignore"
assert entry.unique_id == "mock-unique-id"
assert entry.domain == "comp"
assert entry.title == "Ignored Title"
await manager.async_remove(entry.entry_id)
await hass.async_block_till_done()
assert len(hass.config_entries.async_entries("comp")) == 0
assert len(hass.config_entries.flow.async_progress()) == 0
async def test_partial_flows_hidden(
hass: HomeAssistant, manager: config_entries.ConfigEntries
) -> None:
@ -5396,11 +5273,6 @@ async def test_hashable_non_string_unique_id(
None,
{"type": data_entry_flow.FlowResultType.FORM, "step_id": "reauth_confirm"},
),
(
config_entries.SOURCE_UNIGNORE,
None,
{"type": data_entry_flow.FlowResultType.ABORT, "reason": "not_implemented"},
),
(
config_entries.SOURCE_USER,
None,
@ -5485,11 +5357,6 @@ async def test_starting_config_flow_on_single_config_entry(
None,
{"type": data_entry_flow.FlowResultType.FORM, "step_id": "reauth_confirm"},
),
(
config_entries.SOURCE_UNIGNORE,
None,
{"type": data_entry_flow.FlowResultType.ABORT, "reason": "not_implemented"},
),
(
config_entries.SOURCE_USER,
None,