1
0
mirror of https://github.com/home-assistant/core.git synced 2026-06-01 13:14:35 +01:00
Files
core/tests/components/esphome/test_repairs.py
T

225 lines
7.8 KiB
Python

"""Test ESPHome repairs."""
import asyncio
from unittest.mock import AsyncMock
from aioesphomeapi import APIClient, BinarySensorInfo, BinarySensorState, DeviceInfo
import pytest
from homeassistant.components.esphome import repairs
from homeassistant.components.esphome.const import DOMAIN
from homeassistant.components.esphome.manager import DEVICE_CONFLICT_ISSUE_FORMAT
from homeassistant.const import STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import (
device_registry as dr,
entity_registry as er,
issue_registry as ir,
)
from .conftest import MockESPHomeDeviceType
from tests.common import MockConfigEntry
from tests.components.repairs import (
async_process_repairs_platforms,
get_repairs,
process_repair_fix_flow,
start_repair_fix_flow,
)
from tests.typing import ClientSessionGenerator, WebSocketGenerator
async def test_create_fix_flow_raises_on_unknown_issue_id(hass: HomeAssistant) -> None:
"""Test create_fix_flow raises on unknown issue_id."""
with pytest.raises(ValueError):
await repairs.async_create_fix_flow(hass, "no_such_issue", None)
async def test_device_conflict_manual(
hass: HomeAssistant,
mock_client: APIClient,
hass_client: ClientSessionGenerator,
hass_ws_client: WebSocketGenerator,
mock_config_entry: MockConfigEntry,
issue_registry: ir.IssueRegistry,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test guided manual conflict resolution."""
disconnect_done = hass.loop.create_future()
async def async_disconnect(*args, **kwargs) -> None:
disconnect_done.set_result(None)
mock_client.disconnect = async_disconnect
device_info = DeviceInfo(
mac_address="1122334455ab", name="test", model="esp32-iso-poe"
)
mock_client.device_info = AsyncMock(return_value=device_info)
mock_client.list_entities_services = AsyncMock(return_value=([], []))
mock_client.device_info_and_list_entities = AsyncMock(
return_value=(device_info, [], [])
)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
async with asyncio.timeout(1):
await disconnect_done
assert "Unexpected device found" in caplog.text
issue_id = DEVICE_CONFLICT_ISSUE_FORMAT.format(mock_config_entry.entry_id)
issues = await get_repairs(hass, hass_ws_client)
assert issues
assert issue_registry.async_get_issue(DOMAIN, issue_id) is not None
await async_process_repairs_platforms(hass)
client = await hass_client()
data = await start_repair_fix_flow(client, DOMAIN, issue_id)
flow_id = data["flow_id"]
assert data["type"] == FlowResultType.MENU
assert data["step_id"] == "init"
data = await process_repair_fix_flow(
client, flow_id, json={"next_step_id": "manual"}
)
flow_id = data["flow_id"]
assert data["type"] == FlowResultType.FORM
assert data["step_id"] == "manual"
device_info = DeviceInfo(
mac_address="11:22:33:44:55:aa", name="test", model="esp32-iso-poe"
)
mock_client.device_info = AsyncMock(return_value=device_info)
mock_client.list_entities_services = AsyncMock(return_value=([], []))
mock_client.device_info_and_list_entities = AsyncMock(
return_value=(device_info, [], [])
)
caplog.clear()
data = await process_repair_fix_flow(client, flow_id)
assert data["type"] == FlowResultType.CREATE_ENTRY
await hass.async_block_till_done()
assert "Unexpected device found" not in caplog.text
assert issue_registry.async_get_issue(DOMAIN, issue_id) is None
async def test_device_conflict_migration(
hass: HomeAssistant,
mock_client: APIClient,
hass_client: ClientSessionGenerator,
hass_ws_client: WebSocketGenerator,
issue_registry: ir.IssueRegistry,
entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry,
caplog: pytest.LogCaptureFixture,
mock_esphome_device: MockESPHomeDeviceType,
) -> None:
"""Test migrating existing configuration to new hardware."""
entity_info = [
BinarySensorInfo(
object_id="mybinary_sensor",
key=1,
name="my binary_sensor",
is_status_binary_sensor=True,
)
]
states = [BinarySensorState(key=1, state=None)]
user_service = []
device = await mock_esphome_device(
mock_client=mock_client,
entity_info=entity_info,
user_service=user_service,
states=states,
)
state = hass.states.get("binary_sensor.test_my_binary_sensor")
assert state is not None
assert state.state == STATE_ON
mock_config_entry = device.entry
ent_reg_entry = entity_registry.async_get("binary_sensor.test_my_binary_sensor")
assert ent_reg_entry
assert ent_reg_entry.unique_id == "11:22:33:44:55:AA-binary_sensor-mybinary_sensor"
entries = er.async_entries_for_config_entry(
entity_registry, mock_config_entry.entry_id
)
assert entries is not None
for entry in entries:
assert entry.unique_id.startswith("11:22:33:44:55:AA-")
disconnect_done = hass.loop.create_future()
async def async_disconnect(*args, **kwargs) -> None:
if not disconnect_done.done():
disconnect_done.set_result(None)
mock_client.disconnect = async_disconnect
new_device_info = DeviceInfo(
mac_address="11:22:33:44:55:AB", name="test", model="esp32-iso-poe"
)
mock_client.device_info = AsyncMock(return_value=new_device_info)
# Keep the same entity_info when reloading
mock_client.list_entities_services = AsyncMock(return_value=(entity_info, []))
mock_client.device_info_and_list_entities = AsyncMock(
return_value=(new_device_info, entity_info, [])
)
device.device_info = new_device_info
await hass.config_entries.async_reload(mock_config_entry.entry_id)
await hass.async_block_till_done()
async with asyncio.timeout(1):
await disconnect_done
assert "Unexpected device found" in caplog.text
issue_id = DEVICE_CONFLICT_ISSUE_FORMAT.format(mock_config_entry.entry_id)
issues = await get_repairs(hass, hass_ws_client)
assert issues
assert issue_registry.async_get_issue(DOMAIN, issue_id) is not None
await async_process_repairs_platforms(hass)
client = await hass_client()
data = await start_repair_fix_flow(client, DOMAIN, issue_id)
flow_id = data["flow_id"]
assert data["type"] == FlowResultType.MENU
assert data["step_id"] == "init"
data = await process_repair_fix_flow(
client, flow_id, json={"next_step_id": "migrate"}
)
flow_id = data["flow_id"]
assert data["type"] == FlowResultType.FORM
assert data["step_id"] == "migrate"
caplog.clear()
data = await process_repair_fix_flow(client, flow_id)
assert data["type"] == FlowResultType.CREATE_ENTRY
await hass.async_block_till_done()
assert "Unexpected device found" not in caplog.text
assert issue_registry.async_get_issue(DOMAIN, issue_id) is None
assert mock_config_entry.unique_id == "11:22:33:44:55:ab"
ent_reg_entry = entity_registry.async_get("binary_sensor.test_my_binary_sensor")
assert ent_reg_entry
assert ent_reg_entry.unique_id == "11:22:33:44:55:AB-binary_sensor-mybinary_sensor"
entries = er.async_entries_for_config_entry(
entity_registry, mock_config_entry.entry_id
)
assert entries is not None
for entry in entries:
assert entry.unique_id.startswith("11:22:33:44:55:AB-")
dev_entry = device_registry.async_get_device(
identifiers={}, connections={(dr.CONNECTION_NETWORK_MAC, "11:22:33:44:55:ab")}
)
assert dev_entry is not None
old_dev_entry = device_registry.async_get_device(
identifiers={}, connections={(dr.CONNECTION_NETWORK_MAC, "11:22:33:44:55:aa")}
)
assert old_dev_entry is None