mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Use aiohasupervisor for addon info calls (#125926)
* Use aiohasupervisor for addon info calls * Fix issue/repair tests in supervisor * Fixes from feedback
This commit is contained in:
parent
37cdc6d500
commit
97d0d91d2c
@ -177,6 +177,7 @@ class Analytics:
|
||||
hass = self.hass
|
||||
supervisor_info = None
|
||||
operating_system_info: dict[str, Any] = {}
|
||||
supervisor_client = hassio.get_supervisor_client(hass)
|
||||
|
||||
if not self.onboarded or not self.preferences.get(ATTR_BASE, False):
|
||||
LOGGER.debug("Nothing to submit")
|
||||
@ -263,16 +264,16 @@ class Analytics:
|
||||
if supervisor_info is not None:
|
||||
installed_addons = await asyncio.gather(
|
||||
*(
|
||||
hassio.async_get_addon_info(hass, addon[ATTR_SLUG])
|
||||
supervisor_client.addons.addon_info(addon[ATTR_SLUG])
|
||||
for addon in supervisor_info[ATTR_ADDONS]
|
||||
)
|
||||
)
|
||||
addons.extend(
|
||||
{
|
||||
ATTR_SLUG: addon[ATTR_SLUG],
|
||||
ATTR_PROTECTED: addon[ATTR_PROTECTED],
|
||||
ATTR_VERSION: addon[ATTR_VERSION],
|
||||
ATTR_AUTO_UPDATE: addon[ATTR_AUTO_UPDATE],
|
||||
ATTR_SLUG: addon.slug,
|
||||
ATTR_PROTECTED: addon.protected,
|
||||
ATTR_VERSION: addon.version,
|
||||
ATTR_AUTO_UPDATE: addon.auto_update,
|
||||
}
|
||||
for addon in installed_addons
|
||||
)
|
||||
|
@ -102,7 +102,6 @@ from .handler import ( # noqa: F401
|
||||
HassioAPIError,
|
||||
async_create_backup,
|
||||
async_get_addon_discovery_info,
|
||||
async_get_addon_info,
|
||||
async_get_addon_store_info,
|
||||
async_get_green_settings,
|
||||
async_get_yellow_settings,
|
||||
@ -120,6 +119,7 @@ from .handler import ( # noqa: F401
|
||||
async_update_diagnostics,
|
||||
async_update_os,
|
||||
async_update_supervisor,
|
||||
get_supervisor_client,
|
||||
)
|
||||
from .http import HassIOView
|
||||
from .ingress import async_setup_ingress_view
|
||||
|
@ -10,6 +10,12 @@ from functools import partial, wraps
|
||||
import logging
|
||||
from typing import Any, Concatenate
|
||||
|
||||
from aiohasupervisor import SupervisorError
|
||||
from aiohasupervisor.models import (
|
||||
AddonState as SupervisorAddonState,
|
||||
InstalledAddonComplete,
|
||||
)
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
@ -17,7 +23,6 @@ from .handler import (
|
||||
HassioAPIError,
|
||||
async_create_backup,
|
||||
async_get_addon_discovery_info,
|
||||
async_get_addon_info,
|
||||
async_get_addon_store_info,
|
||||
async_install_addon,
|
||||
async_restart_addon,
|
||||
@ -26,6 +31,7 @@ from .handler import (
|
||||
async_stop_addon,
|
||||
async_uninstall_addon,
|
||||
async_update_addon,
|
||||
get_supervisor_client,
|
||||
)
|
||||
|
||||
type _FuncType[_T, **_P, _R] = Callable[Concatenate[_T, _P], Awaitable[_R]]
|
||||
@ -53,7 +59,7 @@ def api_error[_AddonManagerT: AddonManager, **_P, _R](
|
||||
"""Wrap an add-on manager method."""
|
||||
try:
|
||||
return_value = await func(self, *args, **kwargs)
|
||||
except HassioAPIError as err:
|
||||
except (HassioAPIError, SupervisorError) as err:
|
||||
raise AddonError(
|
||||
f"{error_message.format(addon_name=self.addon_name)}: {err}"
|
||||
) from err
|
||||
@ -140,6 +146,7 @@ class AddonManager:
|
||||
@api_error("Failed to get the {addon_name} add-on info")
|
||||
async def async_get_addon_info(self) -> AddonInfo:
|
||||
"""Return and cache manager add-on info."""
|
||||
supervisor_client = get_supervisor_client(self._hass)
|
||||
addon_store_info = await async_get_addon_store_info(self._hass, self.addon_slug)
|
||||
self._logger.debug("Add-on store info: %s", addon_store_info)
|
||||
if not addon_store_info["installed"]:
|
||||
@ -152,23 +159,23 @@ class AddonManager:
|
||||
version=None,
|
||||
)
|
||||
|
||||
addon_info = await async_get_addon_info(self._hass, self.addon_slug)
|
||||
addon_info = await supervisor_client.addons.addon_info(self.addon_slug)
|
||||
addon_state = self.async_get_addon_state(addon_info)
|
||||
return AddonInfo(
|
||||
available=addon_info["available"],
|
||||
hostname=addon_info["hostname"],
|
||||
options=addon_info["options"],
|
||||
available=addon_info.available,
|
||||
hostname=addon_info.hostname,
|
||||
options=addon_info.options,
|
||||
state=addon_state,
|
||||
update_available=addon_info["update_available"],
|
||||
version=addon_info["version"],
|
||||
update_available=addon_info.update_available,
|
||||
version=addon_info.version,
|
||||
)
|
||||
|
||||
@callback
|
||||
def async_get_addon_state(self, addon_info: dict[str, Any]) -> AddonState:
|
||||
def async_get_addon_state(self, addon_info: InstalledAddonComplete) -> AddonState:
|
||||
"""Return the current state of the managed add-on."""
|
||||
addon_state = AddonState.NOT_RUNNING
|
||||
|
||||
if addon_info["state"] == "started":
|
||||
if addon_info.state == SupervisorAddonState.STARTED:
|
||||
addon_state = AddonState.RUNNING
|
||||
if self._install_task and not self._install_task.done():
|
||||
addon_state = AddonState.INSTALLING
|
||||
|
@ -7,6 +7,8 @@ from collections import defaultdict
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from aiohasupervisor import SupervisorError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_MANUFACTURER, ATTR_NAME
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||
@ -514,11 +516,15 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
async def _update_addon_info(self, slug: str) -> tuple[str, dict[str, Any] | None]:
|
||||
"""Return the info for an add-on."""
|
||||
try:
|
||||
info = await self.hassio.get_addon_info(slug)
|
||||
except HassioAPIError as err:
|
||||
info = await self.hassio.client.addons.addon_info(slug)
|
||||
except SupervisorError as err:
|
||||
_LOGGER.warning("Could not fetch info for %s: %s", slug, err)
|
||||
return (slug, None)
|
||||
return (slug, info)
|
||||
# Translate to legacy hassio names for compatibility
|
||||
info_dict = info.to_dict()
|
||||
info_dict["hassio_api"] = info_dict.pop("supervisor_api")
|
||||
info_dict["hassio_role"] = info_dict.pop("supervisor_role")
|
||||
return (slug, info_dict)
|
||||
|
||||
@callback
|
||||
def async_enable_container_updates(
|
||||
|
@ -12,7 +12,7 @@ from aiohttp.web_exceptions import HTTPServiceUnavailable
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.http import HomeAssistantView
|
||||
from homeassistant.const import ATTR_NAME, ATTR_SERVICE, EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.const import ATTR_SERVICE, EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import BaseServiceInfo
|
||||
from homeassistant.helpers import discovery_flow
|
||||
@ -99,20 +99,21 @@ class HassIODiscovery(HomeAssistantView):
|
||||
|
||||
# Read additional Add-on info
|
||||
try:
|
||||
addon_info = await self.hassio.get_addon_info(slug)
|
||||
addon_info = await self.hassio.client.addons.addon_info(slug)
|
||||
except HassioAPIError as err:
|
||||
_LOGGER.error("Can't read add-on info: %s", err)
|
||||
return
|
||||
|
||||
name: str = addon_info[ATTR_NAME]
|
||||
config_data[ATTR_ADDON] = name
|
||||
config_data[ATTR_ADDON] = addon_info.name
|
||||
|
||||
# Use config flow
|
||||
discovery_flow.async_create_flow(
|
||||
self.hass,
|
||||
service,
|
||||
context={"source": config_entries.SOURCE_HASSIO},
|
||||
data=HassioServiceInfo(config=config_data, name=name, slug=slug, uuid=uuid),
|
||||
data=HassioServiceInfo(
|
||||
config=config_data, name=addon_info.name, slug=slug, uuid=uuid
|
||||
),
|
||||
)
|
||||
|
||||
async def async_process_del(self, data: dict[str, Any]) -> None:
|
||||
|
@ -9,6 +9,7 @@ import logging
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
from aiohasupervisor import SupervisorClient
|
||||
import aiohttp
|
||||
from yarl import URL
|
||||
|
||||
@ -62,17 +63,6 @@ def api_data[**_P](
|
||||
return _wrapper
|
||||
|
||||
|
||||
@bind_hass
|
||||
async def async_get_addon_info(hass: HomeAssistant, slug: str) -> dict:
|
||||
"""Return add-on info.
|
||||
|
||||
The add-on must be installed.
|
||||
The caller of the function should handle HassioAPIError.
|
||||
"""
|
||||
hassio: HassIO = hass.data[DOMAIN]
|
||||
return await hassio.get_addon_info(slug)
|
||||
|
||||
|
||||
@api_data
|
||||
async def async_get_addon_store_info(hass: HomeAssistant, slug: str) -> dict:
|
||||
"""Return add-on store info.
|
||||
@ -332,7 +322,16 @@ class HassIO:
|
||||
self.loop = loop
|
||||
self.websession = websession
|
||||
self._ip = ip
|
||||
self._base_url = URL(f"http://{ip}")
|
||||
base_url = f"http://{ip}"
|
||||
self._base_url = URL(base_url)
|
||||
self._client = SupervisorClient(
|
||||
base_url, os.environ.get("SUPERVISOR_TOKEN", ""), session=websession
|
||||
)
|
||||
|
||||
@property
|
||||
def client(self) -> SupervisorClient:
|
||||
"""Return aiohasupervisor client."""
|
||||
return self._client
|
||||
|
||||
@_api_bool
|
||||
def is_connected(self) -> Coroutine:
|
||||
@ -390,14 +389,6 @@ class HassIO:
|
||||
"""
|
||||
return self.send_command("/network/info", method="get")
|
||||
|
||||
@api_data
|
||||
def get_addon_info(self, addon: str) -> Coroutine:
|
||||
"""Return data for a Add-on.
|
||||
|
||||
This method returns a coroutine.
|
||||
"""
|
||||
return self.send_command(f"/addons/{addon}/info", method="get")
|
||||
|
||||
@api_data
|
||||
def get_core_stats(self) -> Coroutine:
|
||||
"""Return stats for the core.
|
||||
@ -617,3 +608,9 @@ class HassIO:
|
||||
_LOGGER.error("Client error on %s request %s", command, err)
|
||||
|
||||
raise HassioAPIError
|
||||
|
||||
|
||||
def get_supervisor_client(hass: HomeAssistant) -> SupervisorClient:
|
||||
"""Return supervisor client."""
|
||||
hassio: HassIO = hass.data[DOMAIN]
|
||||
return hassio.client
|
||||
|
@ -5,5 +5,6 @@
|
||||
"dependencies": ["http", "repairs"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/hassio",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "internal"
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["aiohasupervisor==0.1.0b0"]
|
||||
}
|
||||
|
@ -304,5 +304,5 @@ class SupervisorCoreUpdateEntity(HassioCoreEntity, UpdateEntity):
|
||||
await async_update_core(self.hass, version=version, backup=backup)
|
||||
except HassioAPIError as err:
|
||||
raise HomeAssistantError(
|
||||
f"Error updating Home Assistant Core {err}"
|
||||
f"Error updating Home Assistant Core: {err}"
|
||||
) from err
|
||||
|
@ -13,16 +13,12 @@ from python_otbr_api.tlv_parser import MeshcopTLVType
|
||||
import voluptuous as vol
|
||||
import yarl
|
||||
|
||||
from homeassistant.components.hassio import (
|
||||
HassioAPIError,
|
||||
HassioServiceInfo,
|
||||
async_get_addon_info,
|
||||
)
|
||||
from homeassistant.components.hassio import AddonError, AddonManager, HassioServiceInfo
|
||||
from homeassistant.components.homeassistant_yellow import hardware as yellow_hardware
|
||||
from homeassistant.components.thread import async_get_preferred_dataset
|
||||
from homeassistant.config_entries import SOURCE_HASSIO, ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_URL
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
@ -43,6 +39,12 @@ class AlreadyConfigured(HomeAssistantError):
|
||||
"""Raised when the router is already configured."""
|
||||
|
||||
|
||||
@callback
|
||||
def get_addon_manager(hass: HomeAssistant, slug: str) -> AddonManager:
|
||||
"""Get the add-on manager."""
|
||||
return AddonManager(hass, _LOGGER, "OpenThread Border Router", slug)
|
||||
|
||||
|
||||
def _is_yellow(hass: HomeAssistant) -> bool:
|
||||
"""Return True if Home Assistant is running on a Home Assistant Yellow."""
|
||||
try:
|
||||
@ -55,10 +57,11 @@ def _is_yellow(hass: HomeAssistant) -> bool:
|
||||
async def _title(hass: HomeAssistant, discovery_info: HassioServiceInfo) -> str:
|
||||
"""Return config entry title."""
|
||||
device: str | None = None
|
||||
addon_manager = get_addon_manager(hass, discovery_info.slug)
|
||||
|
||||
with suppress(HassioAPIError):
|
||||
addon_info = await async_get_addon_info(hass, discovery_info.slug)
|
||||
device = addon_info.get("options", {}).get("device")
|
||||
with suppress(AddonError):
|
||||
addon_info = await addon_manager.async_get_addon_info()
|
||||
device = addon_info.options.get("device")
|
||||
|
||||
if _is_yellow(hass) and device == "/dev/ttyAMA1":
|
||||
return f"Home Assistant Yellow ({discovery_info.name})"
|
||||
|
@ -257,6 +257,9 @@ aioguardian==2022.07.0
|
||||
# homeassistant.components.harmony
|
||||
aioharmony==0.2.10
|
||||
|
||||
# homeassistant.components.hassio
|
||||
aiohasupervisor==0.1.0b0
|
||||
|
||||
# homeassistant.components.homekit_controller
|
||||
aiohomekit==3.2.3
|
||||
|
||||
|
@ -242,6 +242,9 @@ aioguardian==2022.07.0
|
||||
# homeassistant.components.harmony
|
||||
aioharmony==0.2.10
|
||||
|
||||
# homeassistant.components.hassio
|
||||
aiohasupervisor==0.1.0b0
|
||||
|
||||
# homeassistant.components.homekit_controller
|
||||
aiohomekit==3.2.3
|
||||
|
||||
|
@ -67,6 +67,7 @@ def _last_call_payload(aioclient: AiohttpClientMocker) -> dict[str, Any]:
|
||||
return aioclient.mock_calls[-1][2]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_no_send(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -126,6 +127,7 @@ async def test_load_with_supervisor_without_diagnostics(hass: HomeAssistant) ->
|
||||
assert not analytics.preferences[ATTR_DIAGNOSTICS]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_failed_to_send(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -144,6 +146,7 @@ async def test_failed_to_send(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_failed_to_send_raises(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -159,7 +162,7 @@ async def test_failed_to_send_raises(
|
||||
assert "Error sending analytics" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("installation_type_mock")
|
||||
@pytest.mark.usefixtures("installation_type_mock", "supervisor_client")
|
||||
async def test_send_base(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -182,6 +185,7 @@ async def test_send_base(
|
||||
assert snapshot == submitted_data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_send_base_with_supervisor(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -230,7 +234,7 @@ async def test_send_base_with_supervisor(
|
||||
assert snapshot == submitted_data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("installation_type_mock")
|
||||
@pytest.mark.usefixtures("installation_type_mock", "supervisor_client")
|
||||
async def test_send_usage(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -271,6 +275,7 @@ async def test_send_usage_with_supervisor(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
snapshot: SnapshotAssertion,
|
||||
supervisor_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test send usage with supervisor preferences are defined."""
|
||||
aioclient_mock.post(ANALYTICS_ENDPOINT_URL, status=200)
|
||||
@ -281,6 +286,9 @@ async def test_send_usage_with_supervisor(
|
||||
assert analytics.preferences[ATTR_USAGE]
|
||||
hass.config.components.add("default_config")
|
||||
|
||||
supervisor_client.addons.addon_info.return_value = Mock(
|
||||
slug="test_addon", protected=True, version="1", auto_update=False
|
||||
)
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.hassio.get_supervisor_info",
|
||||
@ -305,17 +313,6 @@ async def test_send_usage_with_supervisor(
|
||||
"homeassistant.components.hassio.get_host_info",
|
||||
side_effect=Mock(return_value={}),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.hassio.async_get_addon_info",
|
||||
side_effect=AsyncMock(
|
||||
return_value={
|
||||
"slug": "test_addon",
|
||||
"protected": True,
|
||||
"version": "1",
|
||||
"auto_update": False,
|
||||
}
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.hassio.is_hassio",
|
||||
side_effect=Mock(return_value=True),
|
||||
@ -330,7 +327,7 @@ async def test_send_usage_with_supervisor(
|
||||
assert snapshot == submitted_data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("installation_type_mock")
|
||||
@pytest.mark.usefixtures("installation_type_mock", "supervisor_client")
|
||||
async def test_send_statistics(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -358,9 +355,10 @@ async def test_send_statistics(
|
||||
assert snapshot == submitted_data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_hass_config")
|
||||
@pytest.mark.usefixtures("mock_hass_config", "supervisor_client")
|
||||
async def test_send_statistics_one_integration_fails(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> None:
|
||||
"""Test send statistics preferences are defined."""
|
||||
aioclient_mock.post(ANALYTICS_ENDPOINT_URL, status=200)
|
||||
@ -381,7 +379,9 @@ async def test_send_statistics_one_integration_fails(
|
||||
assert post_call[2]["integration_count"] == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("installation_type_mock", "mock_hass_config")
|
||||
@pytest.mark.usefixtures(
|
||||
"installation_type_mock", "mock_hass_config", "supervisor_client"
|
||||
)
|
||||
async def test_send_statistics_disabled_integration(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -418,7 +418,9 @@ async def test_send_statistics_disabled_integration(
|
||||
assert snapshot == submitted_data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("installation_type_mock", "mock_hass_config")
|
||||
@pytest.mark.usefixtures(
|
||||
"installation_type_mock", "mock_hass_config", "supervisor_client"
|
||||
)
|
||||
async def test_send_statistics_ignored_integration(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -461,9 +463,10 @@ async def test_send_statistics_ignored_integration(
|
||||
assert snapshot == submitted_data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_hass_config")
|
||||
@pytest.mark.usefixtures("mock_hass_config", "supervisor_client")
|
||||
async def test_send_statistics_async_get_integration_unknown_exception(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> None:
|
||||
"""Test send statistics preferences are defined."""
|
||||
aioclient_mock.post(ANALYTICS_ENDPOINT_URL, status=200)
|
||||
@ -489,6 +492,7 @@ async def test_send_statistics_with_supervisor(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
snapshot: SnapshotAssertion,
|
||||
supervisor_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test send statistics preferences are defined."""
|
||||
aioclient_mock.post(ANALYTICS_ENDPOINT_URL, status=200)
|
||||
@ -497,6 +501,9 @@ async def test_send_statistics_with_supervisor(
|
||||
assert analytics.preferences[ATTR_BASE]
|
||||
assert analytics.preferences[ATTR_STATISTICS]
|
||||
|
||||
supervisor_client.addons.addon_info.return_value = Mock(
|
||||
slug="test_addon", protected=True, version="1", auto_update=False
|
||||
)
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.hassio.get_supervisor_info",
|
||||
@ -521,17 +528,6 @@ async def test_send_statistics_with_supervisor(
|
||||
"homeassistant.components.hassio.get_host_info",
|
||||
side_effect=Mock(return_value={}),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.hassio.async_get_addon_info",
|
||||
side_effect=AsyncMock(
|
||||
return_value={
|
||||
"slug": "test_addon",
|
||||
"protected": True,
|
||||
"version": "1",
|
||||
"auto_update": False,
|
||||
}
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.hassio.is_hassio",
|
||||
side_effect=Mock(return_value=True),
|
||||
@ -546,6 +542,7 @@ async def test_send_statistics_with_supervisor(
|
||||
assert snapshot == submitted_data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_reusing_uuid(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
@ -563,7 +560,9 @@ async def test_reusing_uuid(
|
||||
assert analytics.uuid == "NOT_MOCK_UUID"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("enable_custom_integrations", "installation_type_mock")
|
||||
@pytest.mark.usefixtures(
|
||||
"enable_custom_integrations", "installation_type_mock", "supervisor_client"
|
||||
)
|
||||
async def test_custom_integrations(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
@ -590,8 +589,10 @@ async def test_custom_integrations(
|
||||
assert snapshot == submitted_data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_dev_url(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> None:
|
||||
"""Test sending payload to dev url."""
|
||||
aioclient_mock.post(ANALYTICS_ENDPOINT_URL_DEV, status=200)
|
||||
@ -607,6 +608,7 @@ async def test_dev_url(
|
||||
assert str(payload[1]) == ANALYTICS_ENDPOINT_URL_DEV
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_dev_url_error(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
@ -630,8 +632,10 @@ async def test_dev_url_error(
|
||||
) in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_nightly_endpoint(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> None:
|
||||
"""Test sending payload to production url when running nightly."""
|
||||
aioclient_mock.post(ANALYTICS_ENDPOINT_URL, status=200)
|
||||
@ -647,7 +651,9 @@ async def test_nightly_endpoint(
|
||||
assert str(payload[1]) == ANALYTICS_ENDPOINT_URL
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("installation_type_mock", "mock_hass_config")
|
||||
@pytest.mark.usefixtures(
|
||||
"installation_type_mock", "mock_hass_config", "supervisor_client"
|
||||
)
|
||||
async def test_send_with_no_energy(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
@ -683,7 +689,9 @@ async def test_send_with_no_energy(
|
||||
assert snapshot == submitted_data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("recorder_mock", "installation_type_mock", "mock_hass_config")
|
||||
@pytest.mark.usefixtures(
|
||||
"recorder_mock", "installation_type_mock", "mock_hass_config", "supervisor_client"
|
||||
)
|
||||
async def test_send_with_no_energy_config(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
@ -714,7 +722,9 @@ async def test_send_with_no_energy_config(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("recorder_mock", "installation_type_mock", "mock_hass_config")
|
||||
@pytest.mark.usefixtures(
|
||||
"recorder_mock", "installation_type_mock", "mock_hass_config", "supervisor_client"
|
||||
)
|
||||
async def test_send_with_energy_config(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
@ -745,7 +755,9 @@ async def test_send_with_energy_config(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("installation_type_mock", "mock_hass_config")
|
||||
@pytest.mark.usefixtures(
|
||||
"installation_type_mock", "mock_hass_config", "supervisor_client"
|
||||
)
|
||||
async def test_send_usage_with_certificate(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -771,7 +783,7 @@ async def test_send_usage_with_certificate(
|
||||
assert snapshot == submitted_data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("recorder_mock", "installation_type_mock")
|
||||
@pytest.mark.usefixtures("recorder_mock", "installation_type_mock", "supervisor_client")
|
||||
async def test_send_with_recorder(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
@ -802,6 +814,7 @@ async def test_send_with_recorder(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_send_with_problems_loading_yaml(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -821,7 +834,7 @@ async def test_send_with_problems_loading_yaml(
|
||||
assert len(aioclient_mock.mock_calls) == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_hass_config")
|
||||
@pytest.mark.usefixtures("mock_hass_config", "supervisor_client")
|
||||
async def test_timeout_while_sending(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
@ -840,7 +853,7 @@ async def test_timeout_while_sending(
|
||||
assert "Timeout sending analytics" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("installation_type_mock")
|
||||
@pytest.mark.usefixtures("installation_type_mock", "supervisor_client")
|
||||
async def test_not_check_config_entries_if_yaml(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.analytics.const import ANALYTICS_ENDPOINT_URL, DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
@ -20,6 +22,7 @@ async def test_setup(hass: HomeAssistant) -> None:
|
||||
assert DOMAIN in hass.data
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_websocket(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
|
@ -6,7 +6,7 @@ from collections.abc import Callable, Generator
|
||||
from importlib.util import find_spec
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@ -243,12 +243,14 @@ def addon_info_side_effect_fixture() -> Any | None:
|
||||
|
||||
|
||||
@pytest.fixture(name="addon_info")
|
||||
def addon_info_fixture(addon_info_side_effect: Any | None) -> Generator[AsyncMock]:
|
||||
def addon_info_fixture(
|
||||
supervisor_client: AsyncMock, addon_info_side_effect: Any | None
|
||||
) -> Generator[AsyncMock]:
|
||||
"""Mock Supervisor add-on info."""
|
||||
# pylint: disable-next=import-outside-toplevel
|
||||
from .hassio.common import mock_addon_info
|
||||
|
||||
yield from mock_addon_info(addon_info_side_effect)
|
||||
yield from mock_addon_info(supervisor_client, addon_info_side_effect)
|
||||
|
||||
|
||||
@pytest.fixture(name="addon_not_installed")
|
||||
@ -409,3 +411,29 @@ def update_addon_fixture() -> Generator[AsyncMock]:
|
||||
from .hassio.common import mock_update_addon
|
||||
|
||||
yield from mock_update_addon()
|
||||
|
||||
|
||||
@pytest.fixture(name="supervisor_client")
|
||||
def supervisor_client() -> Generator[AsyncMock]:
|
||||
"""Mock the supervisor client."""
|
||||
supervisor_client = AsyncMock()
|
||||
supervisor_client.addons = AsyncMock()
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.hassio.get_supervisor_client",
|
||||
return_value=supervisor_client,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.hassio.handler.get_supervisor_client",
|
||||
return_value=supervisor_client,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.hassio.addon_manager.get_supervisor_client",
|
||||
return_value=supervisor_client,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.hassio.handler.HassIO.client",
|
||||
new=PropertyMock(return_value=supervisor_client),
|
||||
),
|
||||
):
|
||||
yield supervisor_client
|
||||
|
@ -3,14 +3,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
from dataclasses import fields
|
||||
import logging
|
||||
from types import MethodType
|
||||
from typing import Any
|
||||
from unittest.mock import DEFAULT, AsyncMock, patch
|
||||
from unittest.mock import DEFAULT, AsyncMock, Mock, patch
|
||||
|
||||
from aiohasupervisor.models import InstalledAddonComplete
|
||||
|
||||
from homeassistant.components.hassio.addon_manager import AddonManager
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
INSTALLED_ADDON_FIELDS = [field.name for field in fields(InstalledAddonComplete)]
|
||||
|
||||
|
||||
def mock_to_dict(obj: Mock, fields: list[str]) -> dict[str, Any]:
|
||||
"""Aiohasupervisor mocks to dictionary representation."""
|
||||
return {
|
||||
field: getattr(obj, field)
|
||||
for field in fields
|
||||
if not isinstance(getattr(obj, field), Mock)
|
||||
}
|
||||
|
||||
|
||||
def mock_addon_manager(hass: HomeAssistant) -> AddonManager:
|
||||
@ -52,21 +66,31 @@ def mock_addon_store_info(
|
||||
yield addon_store_info
|
||||
|
||||
|
||||
def mock_addon_info(addon_info_side_effect: Any | None) -> Generator[AsyncMock]:
|
||||
def mock_addon_info(
|
||||
supervisor_client: AsyncMock, addon_info_side_effect: Any | None
|
||||
) -> Generator[AsyncMock]:
|
||||
"""Mock Supervisor add-on info."""
|
||||
with patch(
|
||||
"homeassistant.components.hassio.addon_manager.async_get_addon_info",
|
||||
side_effect=addon_info_side_effect,
|
||||
) as addon_info:
|
||||
addon_info.return_value = {
|
||||
"available": False,
|
||||
"hostname": None,
|
||||
"options": {},
|
||||
"state": None,
|
||||
"update_available": False,
|
||||
"version": None,
|
||||
}
|
||||
yield addon_info
|
||||
supervisor_client.addons.addon_info.side_effect = addon_info_side_effect
|
||||
|
||||
supervisor_client.addons.addon_info.return_value = addon_info = Mock(
|
||||
spec=InstalledAddonComplete,
|
||||
slug="test",
|
||||
repository="core",
|
||||
available=False,
|
||||
hostname="",
|
||||
options={},
|
||||
state="unknown",
|
||||
update_available=False,
|
||||
version=None,
|
||||
supervisor_api=False,
|
||||
supervisor_role="default",
|
||||
)
|
||||
addon_info.name = "test"
|
||||
addon_info.to_dict = MethodType(
|
||||
lambda self: mock_to_dict(self, INSTALLED_ADDON_FIELDS),
|
||||
addon_info,
|
||||
)
|
||||
yield supervisor_client.addons.addon_info
|
||||
|
||||
|
||||
def mock_addon_not_installed(
|
||||
@ -87,10 +111,10 @@ def mock_addon_installed(
|
||||
"state": "stopped",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
addon_info.return_value["available"] = True
|
||||
addon_info.return_value["hostname"] = "core-test-addon"
|
||||
addon_info.return_value["state"] = "stopped"
|
||||
addon_info.return_value["version"] = "1.0.0"
|
||||
addon_info.return_value.available = True
|
||||
addon_info.return_value.hostname = "core-test-addon"
|
||||
addon_info.return_value.state = "stopped"
|
||||
addon_info.return_value.version = "1.0.0"
|
||||
return addon_info
|
||||
|
||||
|
||||
@ -102,10 +126,7 @@ def mock_addon_running(addon_store_info: AsyncMock, addon_info: AsyncMock) -> As
|
||||
"state": "started",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
addon_info.return_value["available"] = True
|
||||
addon_info.return_value["hostname"] = "core-test-addon"
|
||||
addon_info.return_value["state"] = "started"
|
||||
addon_info.return_value["version"] = "1.0.0"
|
||||
addon_info.return_value.state = "started"
|
||||
return addon_info
|
||||
|
||||
|
||||
@ -122,9 +143,10 @@ def mock_install_addon_side_effect(
|
||||
"state": "stopped",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
addon_info.return_value["available"] = True
|
||||
addon_info.return_value["state"] = "stopped"
|
||||
addon_info.return_value["version"] = "1.0.0"
|
||||
|
||||
addon_info.return_value.available = True
|
||||
addon_info.return_value.state = "stopped"
|
||||
addon_info.return_value.version = "1.0.0"
|
||||
|
||||
return install_addon
|
||||
|
||||
@ -152,8 +174,8 @@ def mock_start_addon_side_effect(
|
||||
"state": "started",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
addon_info.return_value["available"] = True
|
||||
addon_info.return_value["state"] = "started"
|
||||
addon_info.return_value.available = True
|
||||
addon_info.return_value.state = "started"
|
||||
|
||||
return start_addon
|
||||
|
||||
@ -194,7 +216,7 @@ def mock_uninstall_addon() -> Generator[AsyncMock]:
|
||||
|
||||
def mock_addon_options(addon_info: AsyncMock) -> dict[str, Any]:
|
||||
"""Mock add-on options."""
|
||||
return addon_info.return_value["options"]
|
||||
return addon_info.return_value.options
|
||||
|
||||
|
||||
def mock_set_addon_options_side_effect(addon_options: dict[str, Any]) -> Any | None:
|
||||
|
@ -43,7 +43,7 @@ async def test_not_available_raises_exception(
|
||||
) -> None:
|
||||
"""Test addon not available raises exception."""
|
||||
addon_store_info.return_value["available"] = False
|
||||
addon_info.return_value["available"] = False
|
||||
addon_info.return_value.available = False
|
||||
|
||||
with pytest.raises(AddonError) as err:
|
||||
await addon_manager.async_install_addon()
|
||||
@ -118,7 +118,7 @@ async def test_get_addon_info(
|
||||
addon_state: AddonState,
|
||||
) -> None:
|
||||
"""Test get addon info when addon is installed."""
|
||||
addon_installed.return_value["state"] = addon_info_state
|
||||
addon_installed.return_value.state = addon_info_state
|
||||
assert await addon_manager.async_get_addon_info() == AddonInfo(
|
||||
available=True,
|
||||
hostname="core-test-addon",
|
||||
@ -198,7 +198,7 @@ async def test_install_addon(
|
||||
) -> None:
|
||||
"""Test install addon."""
|
||||
addon_store_info.return_value["available"] = True
|
||||
addon_info.return_value["available"] = True
|
||||
addon_info.return_value.available = True
|
||||
|
||||
await addon_manager.async_install_addon()
|
||||
|
||||
@ -213,7 +213,7 @@ async def test_install_addon_error(
|
||||
) -> None:
|
||||
"""Test install addon raises error."""
|
||||
addon_store_info.return_value["available"] = True
|
||||
addon_info.return_value["available"] = True
|
||||
addon_info.return_value.available = True
|
||||
install_addon.side_effect = HassioAPIError("Boom")
|
||||
|
||||
with pytest.raises(AddonError) as err:
|
||||
@ -501,7 +501,7 @@ async def test_update_addon(
|
||||
update_addon: AsyncMock,
|
||||
) -> None:
|
||||
"""Test update addon."""
|
||||
addon_info.return_value["update_available"] = True
|
||||
addon_info.return_value.update_available = True
|
||||
|
||||
await addon_manager.async_update_addon()
|
||||
|
||||
@ -521,7 +521,7 @@ async def test_update_addon_no_update(
|
||||
update_addon: AsyncMock,
|
||||
) -> None:
|
||||
"""Test update addon without update available."""
|
||||
addon_info.return_value["update_available"] = False
|
||||
addon_info.return_value.update_available = False
|
||||
|
||||
await addon_manager.async_update_addon()
|
||||
|
||||
@ -539,7 +539,7 @@ async def test_update_addon_error(
|
||||
update_addon: AsyncMock,
|
||||
) -> None:
|
||||
"""Test update addon raises error."""
|
||||
addon_info.return_value["update_available"] = True
|
||||
addon_info.return_value.update_available = True
|
||||
update_addon.side_effect = HassioAPIError("Boom")
|
||||
|
||||
with pytest.raises(AddonError) as err:
|
||||
@ -564,7 +564,7 @@ async def test_schedule_update_addon(
|
||||
update_addon: AsyncMock,
|
||||
) -> None:
|
||||
"""Test schedule update addon."""
|
||||
addon_info.return_value["update_available"] = True
|
||||
addon_info.return_value.update_available = True
|
||||
|
||||
update_task = addon_manager.async_schedule_update_addon()
|
||||
|
||||
@ -637,7 +637,7 @@ async def test_schedule_update_addon_error(
|
||||
error_message: str,
|
||||
) -> None:
|
||||
"""Test schedule update addon raises error."""
|
||||
addon_installed.return_value["update_available"] = True
|
||||
addon_installed.return_value.update_available = True
|
||||
create_backup.side_effect = create_backup_error
|
||||
update_addon.side_effect = update_addon_error
|
||||
|
||||
@ -688,7 +688,7 @@ async def test_schedule_update_addon_logs_error(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test schedule update addon logs error."""
|
||||
addon_installed.return_value["update_available"] = True
|
||||
addon_installed.return_value.update_available = True
|
||||
create_backup.side_effect = create_backup_error
|
||||
update_addon.side_effect = update_addon_error
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""The tests for the hassio binary sensors."""
|
||||
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@ -17,7 +17,7 @@ MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_all(aioclient_mock: AiohttpClientMocker) -> None:
|
||||
def mock_all(aioclient_mock: AiohttpClientMocker, addon_installed) -> None:
|
||||
"""Mock all setup requests."""
|
||||
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"})
|
||||
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={"result": "ok"})
|
||||
@ -193,20 +193,23 @@ def mock_all(aioclient_mock: AiohttpClientMocker) -> None:
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity_id", "expected"),
|
||||
("entity_id", "expected", "addon_state"),
|
||||
[
|
||||
("binary_sensor.test_running", "on"),
|
||||
("binary_sensor.test2_running", "off"),
|
||||
("binary_sensor.test_running", "on", "started"),
|
||||
("binary_sensor.test2_running", "off", "stopped"),
|
||||
],
|
||||
)
|
||||
async def test_binary_sensor(
|
||||
hass: HomeAssistant,
|
||||
entity_id,
|
||||
expected,
|
||||
entity_id: str,
|
||||
expected: str,
|
||||
addon_state: str,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
entity_registry: er.EntityRegistry,
|
||||
addon_installed: AsyncMock,
|
||||
) -> None:
|
||||
"""Test hassio OS and addons binary sensor."""
|
||||
addon_installed.return_value.state = addon_state
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
|
@ -18,7 +18,7 @@ MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_all(aioclient_mock: AiohttpClientMocker) -> None:
|
||||
def mock_all(aioclient_mock: AiohttpClientMocker, addon_installed) -> None:
|
||||
"""Mock all setup requests."""
|
||||
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"})
|
||||
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={"result": "ok"})
|
||||
|
@ -43,6 +43,7 @@ async def test_hassio_discovery_startup(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_mqtt: type[config_entries.ConfigFlow],
|
||||
addon_installed: AsyncMock,
|
||||
) -> None:
|
||||
"""Test startup and discovery after event."""
|
||||
aioclient_mock.get(
|
||||
@ -67,10 +68,7 @@ async def test_hassio_discovery_startup(
|
||||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/addons/mosquitto/info",
|
||||
json={"result": "ok", "data": {"name": "Mosquitto Test"}},
|
||||
)
|
||||
addon_installed.return_value.name = "Mosquitto Test"
|
||||
|
||||
assert aioclient_mock.call_count == 0
|
||||
|
||||
@ -78,7 +76,7 @@ async def test_hassio_discovery_startup(
|
||||
await hass.async_block_till_done()
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
assert aioclient_mock.call_count == 2
|
||||
assert aioclient_mock.call_count == 1
|
||||
assert mock_mqtt.async_step_hassio.called
|
||||
mock_mqtt.async_step_hassio.assert_called_with(
|
||||
HassioServiceInfo(
|
||||
@ -102,6 +100,7 @@ async def test_hassio_discovery_startup_done(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_mqtt: type[config_entries.ConfigFlow],
|
||||
addon_installed: AsyncMock,
|
||||
) -> None:
|
||||
"""Test startup and discovery with hass discovery."""
|
||||
aioclient_mock.post(
|
||||
@ -130,10 +129,7 @@ async def test_hassio_discovery_startup_done(
|
||||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/addons/mosquitto/info",
|
||||
json={"result": "ok", "data": {"name": "Mosquitto Test"}},
|
||||
)
|
||||
addon_installed.return_value.name = "Mosquitto Test"
|
||||
|
||||
with (
|
||||
patch(
|
||||
@ -149,7 +145,7 @@ async def test_hassio_discovery_startup_done(
|
||||
await async_setup_component(hass, "hassio", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 2
|
||||
assert aioclient_mock.call_count == 1
|
||||
assert mock_mqtt.async_step_hassio.called
|
||||
mock_mqtt.async_step_hassio.assert_called_with(
|
||||
HassioServiceInfo(
|
||||
@ -173,6 +169,7 @@ async def test_hassio_discovery_webhook(
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
hassio_client: TestClient,
|
||||
mock_mqtt: type[config_entries.ConfigFlow],
|
||||
addon_installed: AsyncMock,
|
||||
) -> None:
|
||||
"""Test discovery webhook."""
|
||||
aioclient_mock.get(
|
||||
@ -193,10 +190,7 @@ async def test_hassio_discovery_webhook(
|
||||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/addons/mosquitto/info",
|
||||
json={"result": "ok", "data": {"name": "Mosquitto Test"}},
|
||||
)
|
||||
addon_installed.return_value.name = "Mosquitto Test"
|
||||
|
||||
resp = await hassio_client.post(
|
||||
"/api/hassio_push/discovery/testuuid",
|
||||
@ -207,7 +201,7 @@ async def test_hassio_discovery_webhook(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
assert aioclient_mock.call_count == 2
|
||||
assert aioclient_mock.call_count == 1
|
||||
assert mock_mqtt.async_step_hassio.called
|
||||
mock_mqtt.async_step_hassio.assert_called_with(
|
||||
HassioServiceInfo(
|
||||
|
@ -201,20 +201,6 @@ async def test_api_homeassistant_restart(
|
||||
assert aioclient_mock.call_count == 1
|
||||
|
||||
|
||||
async def test_api_addon_info(
|
||||
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
"""Test setup with API Add-on info."""
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/addons/test/info",
|
||||
json={"result": "ok", "data": {"name": "bla"}},
|
||||
)
|
||||
|
||||
data = await hassio_handler.get_addon_info("test")
|
||||
assert data["name"] == "bla"
|
||||
assert aioclient_mock.call_count == 1
|
||||
|
||||
|
||||
async def test_api_addon_stats(
|
||||
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
|
@ -509,6 +509,7 @@ async def test_service_calls(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
addon_installed,
|
||||
) -> None:
|
||||
"""Call service and check the API calls behind that."""
|
||||
with (
|
||||
@ -546,14 +547,14 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 24
|
||||
assert aioclient_mock.call_count == 22
|
||||
assert aioclient_mock.mock_calls[-1][2] == "test"
|
||||
|
||||
await hass.services.async_call("hassio", "host_shutdown", {})
|
||||
await hass.services.async_call("hassio", "host_reboot", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 26
|
||||
assert aioclient_mock.call_count == 24
|
||||
|
||||
await hass.services.async_call("hassio", "backup_full", {})
|
||||
await hass.services.async_call(
|
||||
@ -568,7 +569,7 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 28
|
||||
assert aioclient_mock.call_count == 26
|
||||
assert aioclient_mock.mock_calls[-1][2] == {
|
||||
"name": "2021-11-13 03:48:00",
|
||||
"homeassistant": True,
|
||||
@ -593,7 +594,7 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 30
|
||||
assert aioclient_mock.call_count == 28
|
||||
assert aioclient_mock.mock_calls[-1][2] == {
|
||||
"addons": ["test"],
|
||||
"folders": ["ssl"],
|
||||
@ -612,7 +613,7 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 31
|
||||
assert aioclient_mock.call_count == 29
|
||||
assert aioclient_mock.mock_calls[-1][2] == {
|
||||
"name": "backup_name",
|
||||
"location": "backup_share",
|
||||
@ -628,7 +629,7 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 32
|
||||
assert aioclient_mock.call_count == 30
|
||||
assert aioclient_mock.mock_calls[-1][2] == {
|
||||
"name": "2021-11-13 03:48:00",
|
||||
"location": None,
|
||||
@ -647,7 +648,7 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 34
|
||||
assert aioclient_mock.call_count == 32
|
||||
assert aioclient_mock.mock_calls[-1][2] == {
|
||||
"name": "2021-11-13 11:48:00",
|
||||
"location": None,
|
||||
@ -749,6 +750,7 @@ async def test_service_calls_core(
|
||||
assert aioclient_mock.call_count == 6
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("addon_installed")
|
||||
async def test_entry_load_and_unload(hass: HomeAssistant) -> None:
|
||||
"""Test loading and unloading config entry."""
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
@ -775,6 +777,7 @@ async def test_migration_off_hassio(hass: HomeAssistant) -> None:
|
||||
assert hass.config_entries.async_entries(DOMAIN) == []
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("addon_installed")
|
||||
async def test_device_registry_calls(
|
||||
hass: HomeAssistant, device_registry: dr.DeviceRegistry
|
||||
) -> None:
|
||||
@ -927,6 +930,7 @@ async def test_device_registry_calls(
|
||||
assert len(device_registry.devices) == 5
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("addon_installed")
|
||||
async def test_coordinator_updates(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
@ -1002,7 +1006,7 @@ async def test_coordinator_updates(
|
||||
assert "Error on Supervisor API: Unknown" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default", "addon_installed")
|
||||
async def test_coordinator_updates_stats_entities_enabled(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
|
@ -835,7 +835,7 @@ async def test_system_is_not_ready(
|
||||
@pytest.mark.parametrize(
|
||||
"all_setup_requests", [{"include_addons": True}], indirect=True
|
||||
)
|
||||
@pytest.mark.usefixtures("all_setup_requests")
|
||||
@pytest.mark.usefixtures("all_setup_requests", "addon_installed")
|
||||
async def test_supervisor_issues_detached_addon_missing(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
|
@ -563,7 +563,7 @@ async def test_mount_failed_repair_flow(
|
||||
@pytest.mark.parametrize(
|
||||
"all_setup_requests", [{"include_addons": True}], indirect=True
|
||||
)
|
||||
@pytest.mark.usefixtures("all_setup_requests")
|
||||
@pytest.mark.usefixtures("all_setup_requests", "addon_installed")
|
||||
async def test_supervisor_issue_docker_config_repair_flow(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
@ -786,7 +786,7 @@ async def test_supervisor_issue_repair_flow_multiple_data_disks(
|
||||
@pytest.mark.parametrize(
|
||||
"all_setup_requests", [{"include_addons": True}], indirect=True
|
||||
)
|
||||
@pytest.mark.usefixtures("all_setup_requests")
|
||||
@pytest.mark.usefixtures("all_setup_requests", "addon_installed")
|
||||
async def test_supervisor_issue_detached_addon_removed(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
|
@ -28,7 +28,7 @@ MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_all(aioclient_mock: AiohttpClientMocker) -> None:
|
||||
def mock_all(aioclient_mock: AiohttpClientMocker, addon_installed) -> None:
|
||||
"""Mock all setup requests."""
|
||||
_install_default_mocks(aioclient_mock)
|
||||
_install_test_addon_stats_mock(aioclient_mock)
|
||||
|
@ -2,8 +2,9 @@
|
||||
|
||||
from datetime import timedelta
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from aiohasupervisor import SupervisorBadRequestError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.hassio import DOMAIN, HassioAPIError
|
||||
@ -21,7 +22,7 @@ MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_all(aioclient_mock: AiohttpClientMocker) -> None:
|
||||
def mock_all(aioclient_mock: AiohttpClientMocker, addon_installed) -> None:
|
||||
"""Mock all setup requests."""
|
||||
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"})
|
||||
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={"result": "ok"})
|
||||
@ -217,8 +218,10 @@ async def test_update_entities(
|
||||
expected_state,
|
||||
auto_update,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
addon_installed: AsyncMock,
|
||||
) -> None:
|
||||
"""Test update entities."""
|
||||
addon_installed.return_value.auto_update = auto_update
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
@ -375,7 +378,7 @@ async def test_update_addon_with_error(
|
||||
exc=HassioAPIError,
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
with pytest.raises(HomeAssistantError, match=r"^Error updating test:"):
|
||||
assert not await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
@ -404,7 +407,9 @@ async def test_update_os_with_error(
|
||||
exc=HassioAPIError,
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
with pytest.raises(
|
||||
HomeAssistantError, match=r"^Error updating Home Assistant Operating System:"
|
||||
):
|
||||
assert not await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
@ -433,7 +438,9 @@ async def test_update_supervisor_with_error(
|
||||
exc=HassioAPIError,
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
with pytest.raises(
|
||||
HomeAssistantError, match=r"^Error updating Home Assistant Supervisor:"
|
||||
):
|
||||
assert not await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
@ -462,7 +469,9 @@ async def test_update_core_with_error(
|
||||
exc=HassioAPIError,
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
with pytest.raises(
|
||||
HomeAssistantError, match=r"^Error updating Home Assistant Core:"
|
||||
):
|
||||
assert not await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
@ -613,9 +622,12 @@ async def test_no_os_entity(hass: HomeAssistant) -> None:
|
||||
|
||||
|
||||
async def test_setting_up_core_update_when_addon_fails(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
addon_installed: AsyncMock,
|
||||
) -> None:
|
||||
"""Test setting up core update when single addon fails."""
|
||||
addon_installed.side_effect = SupervisorBadRequestError("Addon Test does not exist")
|
||||
with (
|
||||
patch.dict(os.environ, MOCK_ENVIRON),
|
||||
patch(
|
||||
@ -626,10 +638,6 @@ async def test_setting_up_core_update_when_addon_fails(
|
||||
"homeassistant.components.hassio.HassIO.get_addon_changelog",
|
||||
side_effect=HassioAPIError("add-on is not running"),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.hassio.HassIO.get_addon_info",
|
||||
side_effect=HassioAPIError("add-on is not running"),
|
||||
),
|
||||
):
|
||||
result = await async_setup_component(
|
||||
hass,
|
||||
|
@ -418,7 +418,7 @@ async def test_option_flow_install_multi_pan_addon_zha_other_radio(
|
||||
await hass.async_block_till_done()
|
||||
install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol")
|
||||
|
||||
addon_info.return_value["hostname"] = "core-silabs-multiprotocol"
|
||||
addon_info.return_value.hostname = "core-silabs-multiprotocol"
|
||||
result = await hass.config_entries.options.async_configure(result["flow_id"])
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
assert result["step_id"] == "start_addon"
|
||||
@ -513,7 +513,7 @@ async def test_option_flow_addon_installed_same_device_reconfigure_unexpected_us
|
||||
) -> None:
|
||||
"""Test reconfiguring the multi pan addon."""
|
||||
|
||||
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
|
||||
addon_info.return_value.options["device"] = "/dev/ttyTEST123"
|
||||
|
||||
multipan_manager = await silabs_multiprotocol_addon.get_multiprotocol_addon_manager(
|
||||
hass
|
||||
@ -572,7 +572,7 @@ async def test_option_flow_addon_installed_same_device_reconfigure_expected_user
|
||||
) -> None:
|
||||
"""Test reconfiguring the multi pan addon."""
|
||||
|
||||
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
|
||||
addon_info.return_value.options["device"] = "/dev/ttyTEST123"
|
||||
|
||||
multipan_manager = await silabs_multiprotocol_addon.get_multiprotocol_addon_manager(
|
||||
hass
|
||||
@ -643,7 +643,7 @@ async def test_option_flow_addon_installed_same_device_uninstall(
|
||||
) -> None:
|
||||
"""Test uninstalling the multi pan addon."""
|
||||
|
||||
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
|
||||
addon_info.return_value.options["device"] = "/dev/ttyTEST123"
|
||||
|
||||
# Setup the config entry
|
||||
config_entry = MockConfigEntry(
|
||||
@ -738,7 +738,7 @@ async def test_option_flow_addon_installed_same_device_do_not_uninstall_multi_pa
|
||||
) -> None:
|
||||
"""Test uninstalling the multi pan addon."""
|
||||
|
||||
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
|
||||
addon_info.return_value.options["device"] = "/dev/ttyTEST123"
|
||||
|
||||
# Setup the config entry
|
||||
config_entry = MockConfigEntry(
|
||||
@ -781,7 +781,7 @@ async def test_option_flow_flasher_already_running_failure(
|
||||
) -> None:
|
||||
"""Test uninstalling the multi pan addon but with the flasher addon running."""
|
||||
|
||||
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
|
||||
addon_info.return_value.options["device"] = "/dev/ttyTEST123"
|
||||
|
||||
# Setup the config entry
|
||||
config_entry = MockConfigEntry(
|
||||
@ -805,7 +805,7 @@ async def test_option_flow_flasher_already_running_failure(
|
||||
|
||||
# The flasher addon is already installed and running, this is bad
|
||||
addon_store_info.return_value["installed"] = True
|
||||
addon_info.return_value["state"] = "started"
|
||||
addon_info.return_value.state = "started"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], {silabs_multiprotocol_addon.CONF_DISABLE_MULTI_PAN: True}
|
||||
@ -828,7 +828,7 @@ async def test_option_flow_addon_installed_same_device_flasher_already_installed
|
||||
) -> None:
|
||||
"""Test uninstalling the multi pan addon."""
|
||||
|
||||
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
|
||||
addon_info.return_value.options["device"] = "/dev/ttyTEST123"
|
||||
|
||||
# Setup the config entry
|
||||
config_entry = MockConfigEntry(
|
||||
@ -898,7 +898,7 @@ async def test_option_flow_flasher_install_failure(
|
||||
) -> None:
|
||||
"""Test uninstalling the multi pan addon, case where flasher addon fails."""
|
||||
|
||||
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
|
||||
addon_info.return_value.options["device"] = "/dev/ttyTEST123"
|
||||
|
||||
# Setup the config entry
|
||||
config_entry = MockConfigEntry(
|
||||
@ -967,7 +967,7 @@ async def test_option_flow_flasher_addon_flash_failure(
|
||||
) -> None:
|
||||
"""Test where flasher addon fails to flash Zigbee firmware."""
|
||||
|
||||
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
|
||||
addon_info.return_value.options["device"] = "/dev/ttyTEST123"
|
||||
|
||||
# Setup the config entry
|
||||
config_entry = MockConfigEntry(
|
||||
@ -1034,7 +1034,7 @@ async def test_option_flow_uninstall_migration_initiate_failure(
|
||||
) -> None:
|
||||
"""Test uninstalling the multi pan addon, case where ZHA migration init fails."""
|
||||
|
||||
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
|
||||
addon_info.return_value.options["device"] = "/dev/ttyTEST123"
|
||||
|
||||
# Setup the config entry
|
||||
config_entry = MockConfigEntry(
|
||||
@ -1095,7 +1095,7 @@ async def test_option_flow_uninstall_migration_finish_failure(
|
||||
) -> None:
|
||||
"""Test uninstalling the multi pan addon, case where ZHA migration init fails."""
|
||||
|
||||
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
|
||||
addon_info.return_value.options["device"] = "/dev/ttyTEST123"
|
||||
|
||||
# Setup the config entry
|
||||
config_entry = MockConfigEntry(
|
||||
@ -1667,7 +1667,7 @@ async def test_check_multi_pan_addon_auto_start(
|
||||
) -> None:
|
||||
"""Test `check_multi_pan_addon` auto starting the addon."""
|
||||
|
||||
addon_info.return_value["state"] = "not_running"
|
||||
addon_info.return_value.state = "not_running"
|
||||
addon_store_info.return_value = {
|
||||
"installed": True,
|
||||
"available": True,
|
||||
@ -1686,7 +1686,7 @@ async def test_check_multi_pan_addon(
|
||||
) -> None:
|
||||
"""Test `check_multi_pan_addon`."""
|
||||
|
||||
addon_info.return_value["state"] = "started"
|
||||
addon_info.return_value.state = "started"
|
||||
addon_store_info.return_value = {
|
||||
"installed": True,
|
||||
"available": True,
|
||||
@ -1717,7 +1717,7 @@ async def test_multi_pan_addon_using_device_not_running(
|
||||
) -> None:
|
||||
"""Test `multi_pan_addon_using_device` when the addon isn't running."""
|
||||
|
||||
addon_info.return_value["state"] = "not_running"
|
||||
addon_info.return_value.state = "not_running"
|
||||
addon_store_info.return_value = {
|
||||
"installed": True,
|
||||
"available": True,
|
||||
@ -1745,8 +1745,8 @@ async def test_multi_pan_addon_using_device(
|
||||
) -> None:
|
||||
"""Test `multi_pan_addon_using_device` when the addon isn't running."""
|
||||
|
||||
addon_info.return_value["state"] = "started"
|
||||
addon_info.return_value["options"] = {
|
||||
addon_info.return_value.state = "started"
|
||||
addon_info.return_value.options = {
|
||||
"autoflash_firmware": True,
|
||||
"device": options_device,
|
||||
"baudrate": "115200",
|
||||
|
@ -13,6 +13,7 @@ from tests.common import MockConfigEntry, MockModule, mock_integration
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_hardware_info(
|
||||
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, addon_store_info
|
||||
) -> None:
|
||||
@ -65,6 +66,7 @@ async def test_hardware_info(
|
||||
|
||||
|
||||
@pytest.mark.parametrize("os_info", [None, {"board": None}, {"board": "other"}])
|
||||
@pytest.mark.usefixtures("supervisor_client")
|
||||
async def test_hardware_info_fail(
|
||||
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, os_info, addon_store_info
|
||||
) -> None:
|
||||
|
@ -411,8 +411,8 @@ async def test_update_addon(
|
||||
connect_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
|
||||
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 = connect_side_effect
|
||||
|
@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
from http import HTTPStatus
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
import aiohttp
|
||||
import pytest
|
||||
@ -32,21 +32,16 @@ HASSIO_DATA_2 = hassio.HassioServiceInfo(
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="addon_info")
|
||||
def addon_info_fixture():
|
||||
"""Mock Supervisor add-on info."""
|
||||
with patch(
|
||||
"homeassistant.components.otbr.config_flow.async_get_addon_info",
|
||||
) as addon_info:
|
||||
addon_info.return_value = {
|
||||
"available": True,
|
||||
"hostname": None,
|
||||
"options": {},
|
||||
"state": None,
|
||||
"update_available": False,
|
||||
"version": None,
|
||||
}
|
||||
yield addon_info
|
||||
@pytest.fixture(name="otbr_addon_info")
|
||||
def otbr_addon_info_fixture(addon_info: AsyncMock, addon_installed) -> AsyncMock:
|
||||
"""Mock Supervisor otbr add-on info."""
|
||||
addon_info.return_value.available = True
|
||||
addon_info.return_value.hostname = ""
|
||||
addon_info.return_value.options = {}
|
||||
addon_info.return_value.state = "unknown"
|
||||
addon_info.return_value.update_available = False
|
||||
addon_info.return_value.version = None
|
||||
return addon_info
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -360,7 +355,7 @@ async def _test_user_flow_connect_error(hass: HomeAssistant, func, error) -> Non
|
||||
|
||||
@pytest.mark.usefixtures("get_border_agent_id")
|
||||
async def test_hassio_discovery_flow(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, addon_info
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_addon_info
|
||||
) -> None:
|
||||
"""Test the hassio discovery flow."""
|
||||
url = "http://core-silabs-multiprotocol:8081"
|
||||
@ -393,20 +388,14 @@ async def test_hassio_discovery_flow(
|
||||
|
||||
@pytest.mark.usefixtures("get_border_agent_id")
|
||||
async def test_hassio_discovery_flow_yellow(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, addon_info
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_addon_info
|
||||
) -> None:
|
||||
"""Test the hassio discovery flow."""
|
||||
url = "http://core-silabs-multiprotocol:8081"
|
||||
aioclient_mock.get(f"{url}/node/dataset/active", text="aa")
|
||||
|
||||
addon_info.return_value = {
|
||||
"available": True,
|
||||
"hostname": None,
|
||||
"options": {"device": "/dev/ttyAMA1"},
|
||||
"state": None,
|
||||
"update_available": False,
|
||||
"version": None,
|
||||
}
|
||||
otbr_addon_info.return_value.available = True
|
||||
otbr_addon_info.return_value.options = {"device": "/dev/ttyAMA1"}
|
||||
|
||||
with (
|
||||
patch(
|
||||
@ -455,20 +444,14 @@ async def test_hassio_discovery_flow_sky_connect(
|
||||
title: str,
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
addon_info,
|
||||
otbr_addon_info,
|
||||
) -> None:
|
||||
"""Test the hassio discovery flow."""
|
||||
url = "http://core-silabs-multiprotocol:8081"
|
||||
aioclient_mock.get(f"{url}/node/dataset/active", text="aa")
|
||||
|
||||
addon_info.return_value = {
|
||||
"available": True,
|
||||
"hostname": None,
|
||||
"options": {"device": device},
|
||||
"state": None,
|
||||
"update_available": False,
|
||||
"version": None,
|
||||
}
|
||||
otbr_addon_info.return_value.available = True
|
||||
otbr_addon_info.return_value.options = {"device": device}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.otbr.async_setup_entry",
|
||||
@ -497,7 +480,7 @@ async def test_hassio_discovery_flow_sky_connect(
|
||||
|
||||
@pytest.mark.usefixtures("get_active_dataset_tlvs", "get_extended_address")
|
||||
async def test_hassio_discovery_flow_2x_addons(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, addon_info
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_addon_info
|
||||
) -> None:
|
||||
"""Test the hassio discovery flow when the user has 2 addons with otbr support."""
|
||||
url1 = "http://core-silabs-multiprotocol:8081"
|
||||
@ -507,37 +490,28 @@ async def test_hassio_discovery_flow_2x_addons(
|
||||
aioclient_mock.get(f"{url1}/node/ba-id", json=TEST_BORDER_AGENT_ID.hex())
|
||||
aioclient_mock.get(f"{url2}/node/ba-id", json=TEST_BORDER_AGENT_ID_2.hex())
|
||||
|
||||
async def _addon_info(hass: HomeAssistant, slug: str) -> dict[str, Any]:
|
||||
async def _addon_info(slug: str) -> Mock:
|
||||
await asyncio.sleep(0)
|
||||
if slug == "otbr":
|
||||
return {
|
||||
"available": True,
|
||||
"hostname": None,
|
||||
"options": {
|
||||
"device": (
|
||||
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_"
|
||||
"9e2adbd75b8beb119fe564a0f320645d-if00-port0"
|
||||
)
|
||||
},
|
||||
"state": None,
|
||||
"update_available": False,
|
||||
"version": None,
|
||||
}
|
||||
return {
|
||||
"available": True,
|
||||
"hostname": None,
|
||||
"options": {
|
||||
"device": (
|
||||
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_"
|
||||
"9e2adbd75b8beb119fe564a0f320645d-if00-port1"
|
||||
)
|
||||
},
|
||||
"state": None,
|
||||
"update_available": False,
|
||||
"version": None,
|
||||
}
|
||||
device = (
|
||||
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_"
|
||||
"9e2adbd75b8beb119fe564a0f320645d-if00-port0"
|
||||
)
|
||||
else:
|
||||
device = (
|
||||
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_"
|
||||
"9e2adbd75b8beb119fe564a0f320645d-if00-port1"
|
||||
)
|
||||
return Mock(
|
||||
available=True,
|
||||
hostname=otbr_addon_info.return_value.hostname,
|
||||
options={"device": device},
|
||||
state=otbr_addon_info.return_value.state,
|
||||
update_available=otbr_addon_info.return_value.update_available,
|
||||
version=otbr_addon_info.return_value.version,
|
||||
)
|
||||
|
||||
addon_info.side_effect = _addon_info
|
||||
otbr_addon_info.side_effect = _addon_info
|
||||
|
||||
result1 = await hass.config_entries.flow.async_init(
|
||||
otbr.DOMAIN, context={"source": "hassio"}, data=HASSIO_DATA
|
||||
@ -590,7 +564,7 @@ async def test_hassio_discovery_flow_2x_addons(
|
||||
|
||||
@pytest.mark.usefixtures("get_active_dataset_tlvs", "get_extended_address")
|
||||
async def test_hassio_discovery_flow_2x_addons_same_ext_address(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, addon_info
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_addon_info
|
||||
) -> None:
|
||||
"""Test the hassio discovery flow when the user has 2 addons with otbr support."""
|
||||
url1 = "http://core-silabs-multiprotocol:8081"
|
||||
@ -600,37 +574,28 @@ async def test_hassio_discovery_flow_2x_addons_same_ext_address(
|
||||
aioclient_mock.get(f"{url1}/node/ba-id", json=TEST_BORDER_AGENT_ID.hex())
|
||||
aioclient_mock.get(f"{url2}/node/ba-id", json=TEST_BORDER_AGENT_ID.hex())
|
||||
|
||||
async def _addon_info(hass: HomeAssistant, slug: str) -> dict[str, Any]:
|
||||
async def _addon_info(slug: str) -> Mock:
|
||||
await asyncio.sleep(0)
|
||||
if slug == "otbr":
|
||||
return {
|
||||
"available": True,
|
||||
"hostname": None,
|
||||
"options": {
|
||||
"device": (
|
||||
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_"
|
||||
"9e2adbd75b8beb119fe564a0f320645d-if00-port0"
|
||||
)
|
||||
},
|
||||
"state": None,
|
||||
"update_available": False,
|
||||
"version": None,
|
||||
}
|
||||
return {
|
||||
"available": True,
|
||||
"hostname": None,
|
||||
"options": {
|
||||
"device": (
|
||||
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_"
|
||||
"9e2adbd75b8beb119fe564a0f320645d-if00-port1"
|
||||
)
|
||||
},
|
||||
"state": None,
|
||||
"update_available": False,
|
||||
"version": None,
|
||||
}
|
||||
device = (
|
||||
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_"
|
||||
"9e2adbd75b8beb119fe564a0f320645d-if00-port0"
|
||||
)
|
||||
else:
|
||||
device = (
|
||||
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_"
|
||||
"9e2adbd75b8beb119fe564a0f320645d-if00-port1"
|
||||
)
|
||||
return Mock(
|
||||
available=True,
|
||||
hostname=otbr_addon_info.return_value.hostname,
|
||||
options={"device": device},
|
||||
state=otbr_addon_info.return_value.state,
|
||||
update_available=otbr_addon_info.return_value.update_available,
|
||||
version=otbr_addon_info.return_value.version,
|
||||
)
|
||||
|
||||
addon_info.side_effect = _addon_info
|
||||
otbr_addon_info.side_effect = _addon_info
|
||||
|
||||
result1 = await hass.config_entries.flow.async_init(
|
||||
otbr.DOMAIN, context={"source": "hassio"}, data=HASSIO_DATA
|
||||
@ -666,7 +631,7 @@ async def test_hassio_discovery_flow_2x_addons_same_ext_address(
|
||||
|
||||
@pytest.mark.usefixtures("get_border_agent_id")
|
||||
async def test_hassio_discovery_flow_router_not_setup(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, addon_info
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_addon_info
|
||||
) -> None:
|
||||
"""Test the hassio discovery flow when the border router has no dataset.
|
||||
|
||||
@ -724,7 +689,7 @@ async def test_hassio_discovery_flow_router_not_setup(
|
||||
|
||||
@pytest.mark.usefixtures("get_border_agent_id")
|
||||
async def test_hassio_discovery_flow_router_not_setup_has_preferred(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, addon_info
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_addon_info
|
||||
) -> None:
|
||||
"""Test the hassio discovery flow when the border router has no dataset.
|
||||
|
||||
@ -780,7 +745,7 @@ async def test_hassio_discovery_flow_router_not_setup_has_preferred_2(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
multiprotocol_addon_manager_mock,
|
||||
addon_info,
|
||||
otbr_addon_info,
|
||||
) -> None:
|
||||
"""Test the hassio discovery flow when the border router has no dataset.
|
||||
|
||||
@ -920,7 +885,7 @@ async def test_hassio_discovery_flow_new_port(hass: HomeAssistant) -> None:
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(
|
||||
"addon_info",
|
||||
"otbr_addon_info",
|
||||
"get_active_dataset_tlvs",
|
||||
"get_border_agent_id",
|
||||
"get_extended_address",
|
||||
@ -962,7 +927,7 @@ async def test_hassio_discovery_flow_new_port_other_addon(hass: HomeAssistant) -
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures(
|
||||
"addon_info",
|
||||
"otbr_addon_info",
|
||||
"get_active_dataset_tlvs",
|
||||
"get_border_agent_id",
|
||||
"get_extended_address",
|
||||
|
@ -772,8 +772,8 @@ async def test_update_addon(
|
||||
network_key = "abc123"
|
||||
addon_options["device"] = device
|
||||
addon_options["network_key"] = network_key
|
||||
addon_info.return_value["version"] = addon_version
|
||||
addon_info.return_value["update_available"] = update_available
|
||||
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
|
||||
client.connect.side_effect = InvalidServerVersion(
|
||||
|
Loading…
x
Reference in New Issue
Block a user