399 lines
13 KiB
Python
399 lines
13 KiB
Python
|
"""Test the Matter integration init."""
|
||
|
from __future__ import annotations
|
||
|
|
||
|
import asyncio
|
||
|
from unittest.mock import AsyncMock, MagicMock, call
|
||
|
|
||
|
from matter_server.client.exceptions import InvalidServerVersion
|
||
|
import pytest
|
||
|
|
||
|
from homeassistant.components.hassio import HassioAPIError
|
||
|
from homeassistant.components.matter.const import DOMAIN
|
||
|
from homeassistant.config_entries import ConfigEntryDisabler, ConfigEntryState
|
||
|
from homeassistant.core import HomeAssistant
|
||
|
from homeassistant.helpers import issue_registry as ir
|
||
|
|
||
|
from tests.common import MockConfigEntry
|
||
|
|
||
|
|
||
|
async def test_raise_addon_task_in_progress(
|
||
|
hass: HomeAssistant,
|
||
|
addon_not_installed: AsyncMock,
|
||
|
install_addon: AsyncMock,
|
||
|
start_addon: AsyncMock,
|
||
|
) -> None:
|
||
|
"""Test raise ConfigEntryNotReady if an add-on task is in progress."""
|
||
|
install_event = asyncio.Event()
|
||
|
|
||
|
install_addon_original_side_effect = install_addon.side_effect
|
||
|
|
||
|
async def install_addon_side_effect(hass: HomeAssistant, slug: str) -> None:
|
||
|
"""Mock install add-on."""
|
||
|
await install_event.wait()
|
||
|
await install_addon_original_side_effect(hass, slug)
|
||
|
|
||
|
install_addon.side_effect = install_addon_side_effect
|
||
|
|
||
|
entry = MockConfigEntry(
|
||
|
domain=DOMAIN,
|
||
|
title="Matter",
|
||
|
data={
|
||
|
"url": "ws://host1:5581/ws",
|
||
|
"use_addon": True,
|
||
|
},
|
||
|
)
|
||
|
entry.add_to_hass(hass)
|
||
|
|
||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||
|
await asyncio.sleep(0.05)
|
||
|
|
||
|
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||
|
assert install_addon.call_count == 1
|
||
|
assert start_addon.call_count == 0
|
||
|
|
||
|
# Check that we only call install add-on once if a task is in progress.
|
||
|
await hass.config_entries.async_reload(entry.entry_id)
|
||
|
await asyncio.sleep(0.05)
|
||
|
|
||
|
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||
|
assert install_addon.call_count == 1
|
||
|
assert start_addon.call_count == 0
|
||
|
|
||
|
install_event.set()
|
||
|
await hass.async_block_till_done()
|
||
|
|
||
|
assert install_addon.call_count == 1
|
||
|
assert start_addon.call_count == 1
|
||
|
|
||
|
|
||
|
async def test_start_addon(
|
||
|
hass: HomeAssistant,
|
||
|
addon_installed: AsyncMock,
|
||
|
addon_info: AsyncMock,
|
||
|
install_addon: AsyncMock,
|
||
|
start_addon: AsyncMock,
|
||
|
) -> None:
|
||
|
"""Test start the Matter Server add-on during entry setup."""
|
||
|
entry = MockConfigEntry(
|
||
|
domain=DOMAIN,
|
||
|
title="Matter",
|
||
|
data={
|
||
|
"url": "ws://host1:5581/ws",
|
||
|
"use_addon": True,
|
||
|
},
|
||
|
)
|
||
|
entry.add_to_hass(hass)
|
||
|
|
||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||
|
await hass.async_block_till_done()
|
||
|
|
||
|
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||
|
assert addon_info.call_count == 1
|
||
|
assert install_addon.call_count == 0
|
||
|
assert start_addon.call_count == 1
|
||
|
assert start_addon.call_args == call(hass, "core_matter_server")
|
||
|
|
||
|
|
||
|
async def test_install_addon(
|
||
|
hass: HomeAssistant,
|
||
|
addon_not_installed: AsyncMock,
|
||
|
addon_store_info: AsyncMock,
|
||
|
install_addon: AsyncMock,
|
||
|
start_addon: AsyncMock,
|
||
|
) -> None:
|
||
|
"""Test install and start the Matter add-on during entry setup."""
|
||
|
entry = MockConfigEntry(
|
||
|
domain=DOMAIN,
|
||
|
title="Matter",
|
||
|
data={
|
||
|
"url": "ws://host1:5581/ws",
|
||
|
"use_addon": True,
|
||
|
},
|
||
|
)
|
||
|
entry.add_to_hass(hass)
|
||
|
|
||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||
|
await hass.async_block_till_done()
|
||
|
|
||
|
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||
|
assert addon_store_info.call_count == 2
|
||
|
assert install_addon.call_count == 1
|
||
|
assert install_addon.call_args == call(hass, "core_matter_server")
|
||
|
assert start_addon.call_count == 1
|
||
|
assert start_addon.call_args == call(hass, "core_matter_server")
|
||
|
|
||
|
|
||
|
async def test_addon_info_failure(
|
||
|
hass: HomeAssistant,
|
||
|
addon_installed: AsyncMock,
|
||
|
addon_info: AsyncMock,
|
||
|
install_addon: AsyncMock,
|
||
|
start_addon: AsyncMock,
|
||
|
) -> None:
|
||
|
"""Test failure to get add-on info for Matter add-on during entry setup."""
|
||
|
addon_info.side_effect = HassioAPIError("Boom")
|
||
|
entry = MockConfigEntry(
|
||
|
domain=DOMAIN,
|
||
|
title="Matter",
|
||
|
data={
|
||
|
"url": "ws://host1:5581/ws",
|
||
|
"use_addon": True,
|
||
|
},
|
||
|
)
|
||
|
entry.add_to_hass(hass)
|
||
|
|
||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||
|
await hass.async_block_till_done()
|
||
|
|
||
|
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||
|
assert addon_info.call_count == 1
|
||
|
assert install_addon.call_count == 0
|
||
|
assert start_addon.call_count == 0
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"addon_version, update_available, update_calls, backup_calls, "
|
||
|
"update_addon_side_effect, create_backup_side_effect",
|
||
|
[
|
||
|
("1.0.0", True, 1, 1, None, None),
|
||
|
("1.0.0", False, 0, 0, None, None),
|
||
|
("1.0.0", True, 1, 1, HassioAPIError("Boom"), None),
|
||
|
("1.0.0", True, 0, 1, None, HassioAPIError("Boom")),
|
||
|
],
|
||
|
)
|
||
|
async def test_update_addon(
|
||
|
hass: HomeAssistant,
|
||
|
addon_installed: AsyncMock,
|
||
|
addon_running: AsyncMock,
|
||
|
addon_info: AsyncMock,
|
||
|
install_addon: AsyncMock,
|
||
|
start_addon: AsyncMock,
|
||
|
create_backup: AsyncMock,
|
||
|
update_addon: AsyncMock,
|
||
|
matter_client: MagicMock,
|
||
|
addon_version: str,
|
||
|
update_available: bool,
|
||
|
update_calls: int,
|
||
|
backup_calls: int,
|
||
|
update_addon_side_effect: Exception | None,
|
||
|
create_backup_side_effect: Exception | None,
|
||
|
):
|
||
|
"""Test update the Matter add-on during entry setup."""
|
||
|
addon_info.return_value["version"] = addon_version
|
||
|
addon_info.return_value["update_available"] = update_available
|
||
|
create_backup.side_effect = create_backup_side_effect
|
||
|
update_addon.side_effect = update_addon_side_effect
|
||
|
matter_client.connect.side_effect = InvalidServerVersion("Invalid version")
|
||
|
entry = MockConfigEntry(
|
||
|
domain=DOMAIN,
|
||
|
title="Matter",
|
||
|
data={
|
||
|
"url": "ws://host1:5581/ws",
|
||
|
"use_addon": True,
|
||
|
},
|
||
|
)
|
||
|
entry.add_to_hass(hass)
|
||
|
|
||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||
|
await hass.async_block_till_done()
|
||
|
|
||
|
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||
|
assert create_backup.call_count == backup_calls
|
||
|
assert update_addon.call_count == update_calls
|
||
|
|
||
|
|
||
|
async def test_issue_registry_invalid_version(
|
||
|
hass: HomeAssistant,
|
||
|
matter_client: MagicMock,
|
||
|
) -> None:
|
||
|
"""Test issue registry for invalid version."""
|
||
|
original_connect_side_effect = matter_client.connect.side_effect
|
||
|
matter_client.connect.side_effect = InvalidServerVersion("Invalid version")
|
||
|
entry = MockConfigEntry(
|
||
|
domain=DOMAIN,
|
||
|
title="Matter",
|
||
|
data={
|
||
|
"url": "ws://host1:5581/ws",
|
||
|
"use_addon": False,
|
||
|
},
|
||
|
)
|
||
|
entry.add_to_hass(hass)
|
||
|
|
||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||
|
await hass.async_block_till_done()
|
||
|
|
||
|
issue_reg = ir.async_get(hass)
|
||
|
entry_state = entry.state
|
||
|
assert entry_state is ConfigEntryState.SETUP_RETRY
|
||
|
assert issue_reg.async_get_issue(DOMAIN, "invalid_server_version")
|
||
|
|
||
|
matter_client.connect.side_effect = original_connect_side_effect
|
||
|
|
||
|
await hass.config_entries.async_reload(entry.entry_id)
|
||
|
await hass.async_block_till_done()
|
||
|
|
||
|
assert entry.state is ConfigEntryState.LOADED
|
||
|
assert not issue_reg.async_get_issue(DOMAIN, "invalid_server_version")
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"stop_addon_side_effect, entry_state",
|
||
|
[
|
||
|
(None, ConfigEntryState.NOT_LOADED),
|
||
|
(HassioAPIError("Boom"), ConfigEntryState.LOADED),
|
||
|
],
|
||
|
)
|
||
|
async def test_stop_addon(
|
||
|
hass,
|
||
|
matter_client: MagicMock,
|
||
|
addon_installed: AsyncMock,
|
||
|
addon_running: AsyncMock,
|
||
|
addon_info: AsyncMock,
|
||
|
stop_addon: AsyncMock,
|
||
|
stop_addon_side_effect: Exception | None,
|
||
|
entry_state: ConfigEntryState,
|
||
|
):
|
||
|
"""Test stop the Matter add-on on entry unload if entry is disabled."""
|
||
|
stop_addon.side_effect = stop_addon_side_effect
|
||
|
entry = MockConfigEntry(
|
||
|
domain=DOMAIN,
|
||
|
title="Matter",
|
||
|
data={
|
||
|
"url": "ws://host1:5581/ws",
|
||
|
"use_addon": True,
|
||
|
},
|
||
|
)
|
||
|
entry.add_to_hass(hass)
|
||
|
|
||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||
|
await hass.async_block_till_done()
|
||
|
|
||
|
assert entry.state is ConfigEntryState.LOADED
|
||
|
assert addon_info.call_count == 1
|
||
|
addon_info.reset_mock()
|
||
|
|
||
|
await hass.config_entries.async_set_disabled_by(
|
||
|
entry.entry_id, ConfigEntryDisabler.USER
|
||
|
)
|
||
|
await hass.async_block_till_done()
|
||
|
|
||
|
assert entry.state == entry_state
|
||
|
assert stop_addon.call_count == 1
|
||
|
assert stop_addon.call_args == call(hass, "core_matter_server")
|
||
|
|
||
|
|
||
|
async def test_remove_entry(
|
||
|
hass: HomeAssistant,
|
||
|
addon_installed: AsyncMock,
|
||
|
stop_addon: AsyncMock,
|
||
|
create_backup: AsyncMock,
|
||
|
uninstall_addon: AsyncMock,
|
||
|
caplog: pytest.LogCaptureFixture,
|
||
|
) -> None:
|
||
|
"""Test remove the config entry."""
|
||
|
# test successful remove without created add-on
|
||
|
entry = MockConfigEntry(
|
||
|
domain=DOMAIN,
|
||
|
title="Matter",
|
||
|
data={"integration_created_addon": False},
|
||
|
)
|
||
|
entry.add_to_hass(hass)
|
||
|
assert entry.state is ConfigEntryState.NOT_LOADED
|
||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||
|
|
||
|
await hass.config_entries.async_remove(entry.entry_id)
|
||
|
|
||
|
assert entry.state is ConfigEntryState.NOT_LOADED
|
||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||
|
|
||
|
# test successful remove with created add-on
|
||
|
entry = MockConfigEntry(
|
||
|
domain=DOMAIN,
|
||
|
title="Matter",
|
||
|
data={"integration_created_addon": True},
|
||
|
)
|
||
|
entry.add_to_hass(hass)
|
||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||
|
|
||
|
await hass.config_entries.async_remove(entry.entry_id)
|
||
|
|
||
|
assert stop_addon.call_count == 1
|
||
|
assert stop_addon.call_args == call(hass, "core_matter_server")
|
||
|
assert create_backup.call_count == 1
|
||
|
assert create_backup.call_args == call(
|
||
|
hass,
|
||
|
{"name": "addon_core_matter_server_1.0.0", "addons": ["core_matter_server"]},
|
||
|
partial=True,
|
||
|
)
|
||
|
assert uninstall_addon.call_count == 1
|
||
|
assert uninstall_addon.call_args == call(hass, "core_matter_server")
|
||
|
assert entry.state is ConfigEntryState.NOT_LOADED
|
||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||
|
stop_addon.reset_mock()
|
||
|
create_backup.reset_mock()
|
||
|
uninstall_addon.reset_mock()
|
||
|
|
||
|
# test add-on stop failure
|
||
|
entry.add_to_hass(hass)
|
||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||
|
stop_addon.side_effect = HassioAPIError()
|
||
|
|
||
|
await hass.config_entries.async_remove(entry.entry_id)
|
||
|
|
||
|
assert stop_addon.call_count == 1
|
||
|
assert stop_addon.call_args == call(hass, "core_matter_server")
|
||
|
assert create_backup.call_count == 0
|
||
|
assert uninstall_addon.call_count == 0
|
||
|
assert entry.state is ConfigEntryState.NOT_LOADED
|
||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||
|
assert "Failed to stop the Matter Server add-on" in caplog.text
|
||
|
stop_addon.side_effect = None
|
||
|
stop_addon.reset_mock()
|
||
|
create_backup.reset_mock()
|
||
|
uninstall_addon.reset_mock()
|
||
|
|
||
|
# test create backup failure
|
||
|
entry.add_to_hass(hass)
|
||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||
|
create_backup.side_effect = HassioAPIError()
|
||
|
|
||
|
await hass.config_entries.async_remove(entry.entry_id)
|
||
|
|
||
|
assert stop_addon.call_count == 1
|
||
|
assert stop_addon.call_args == call(hass, "core_matter_server")
|
||
|
assert create_backup.call_count == 1
|
||
|
assert create_backup.call_args == call(
|
||
|
hass,
|
||
|
{"name": "addon_core_matter_server_1.0.0", "addons": ["core_matter_server"]},
|
||
|
partial=True,
|
||
|
)
|
||
|
assert uninstall_addon.call_count == 0
|
||
|
assert entry.state is ConfigEntryState.NOT_LOADED
|
||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||
|
assert "Failed to create a backup of the Matter Server add-on" in caplog.text
|
||
|
create_backup.side_effect = None
|
||
|
stop_addon.reset_mock()
|
||
|
create_backup.reset_mock()
|
||
|
uninstall_addon.reset_mock()
|
||
|
|
||
|
# test add-on uninstall failure
|
||
|
entry.add_to_hass(hass)
|
||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||
|
uninstall_addon.side_effect = HassioAPIError()
|
||
|
|
||
|
await hass.config_entries.async_remove(entry.entry_id)
|
||
|
|
||
|
assert stop_addon.call_count == 1
|
||
|
assert stop_addon.call_args == call(hass, "core_matter_server")
|
||
|
assert create_backup.call_count == 1
|
||
|
assert create_backup.call_args == call(
|
||
|
hass,
|
||
|
{"name": "addon_core_matter_server_1.0.0", "addons": ["core_matter_server"]},
|
||
|
partial=True,
|
||
|
)
|
||
|
assert uninstall_addon.call_count == 1
|
||
|
assert uninstall_addon.call_args == call(hass, "core_matter_server")
|
||
|
assert entry.state is ConfigEntryState.NOT_LOADED
|
||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||
|
assert "Failed to uninstall the Matter Server add-on" in caplog.text
|