Continue migration of methods from handler to aiohasupervisor (#129183)

This commit is contained in:
Mike Degatano 2024-10-29 09:33:21 -04:00 committed by GitHub
parent 79c602f59c
commit 673f0224c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 906 additions and 667 deletions

View File

@ -10,6 +10,7 @@ import os
import re import re
from typing import Any, NamedTuple from typing import Any, NamedTuple
from aiohasupervisor import SupervisorError
import voluptuous as vol import voluptuous as vol
from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.auth.const import GROUP_ID_ADMIN
@ -101,16 +102,12 @@ from .handler import ( # noqa: F401
HassIO, HassIO,
HassioAPIError, HassioAPIError,
async_create_backup, async_create_backup,
async_get_addon_discovery_info,
async_get_green_settings, async_get_green_settings,
async_get_yellow_settings, async_get_yellow_settings,
async_reboot_host, async_reboot_host,
async_set_green_settings, async_set_green_settings,
async_set_yellow_settings, async_set_yellow_settings,
async_update_core,
async_update_diagnostics, async_update_diagnostics,
async_update_os,
async_update_supervisor,
get_supervisor_client, get_supervisor_client,
) )
from .http import HassIOView from .http import HassIOView
@ -310,8 +307,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
host = os.environ["SUPERVISOR"] host = os.environ["SUPERVISOR"]
websession = async_get_clientsession(hass) websession = async_get_clientsession(hass)
hass.data[DOMAIN] = hassio = HassIO(hass.loop, websession, host) hass.data[DOMAIN] = hassio = HassIO(hass.loop, websession, host)
supervisor_client = get_supervisor_client(hass)
if not await hassio.is_connected(): try:
await supervisor_client.supervisor.ping()
except SupervisorError:
_LOGGER.warning("Not connected with the supervisor / system too busy!") _LOGGER.warning("Not connected with the supervisor / system too busy!")
store = Store[dict[str, str]](hass, STORAGE_VERSION, STORAGE_KEY) store = Store[dict[str, str]](hass, STORAGE_VERSION, STORAGE_KEY)
@ -468,9 +468,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
async def _async_stop(hass: HomeAssistant, restart: bool) -> None: async def _async_stop(hass: HomeAssistant, restart: bool) -> None:
"""Stop or restart home assistant.""" """Stop or restart home assistant."""
if restart: if restart:
await hassio.restart_homeassistant() await supervisor_client.homeassistant.restart()
else: else:
await hassio.stop_homeassistant() await supervisor_client.homeassistant.stop()
# Set a custom handler for the homeassistant.restart and homeassistant.stop services # Set a custom handler for the homeassistant.restart and homeassistant.stop services
async_set_stop_handler(hass, _async_stop) async_set_stop_handler(hass, _async_stop)

View File

@ -21,12 +21,7 @@ from aiohasupervisor.models import (
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from .handler import ( from .handler import HassioAPIError, async_create_backup, get_supervisor_client
HassioAPIError,
async_create_backup,
async_get_addon_discovery_info,
get_supervisor_client,
)
type _FuncType[_T, **_P, _R] = Callable[Concatenate[_T, _P], Awaitable[_R]] type _FuncType[_T, **_P, _R] = Callable[Concatenate[_T, _P], Awaitable[_R]]
type _ReturnFuncType[_T, **_P, _R] = Callable[ type _ReturnFuncType[_T, **_P, _R] = Callable[
@ -128,18 +123,25 @@ class AddonManager:
) )
) )
@api_error("Failed to get the {addon_name} add-on discovery info") @api_error(
"Failed to get the {addon_name} add-on discovery info",
expected_error_type=SupervisorError,
)
async def async_get_addon_discovery_info(self) -> dict: async def async_get_addon_discovery_info(self) -> dict:
"""Return add-on discovery info.""" """Return add-on discovery info."""
discovery_info = await async_get_addon_discovery_info( discovery_info = next(
self._hass, self.addon_slug (
msg
for msg in await self._supervisor_client.discovery.list()
if msg.addon == self.addon_slug
),
None,
) )
if not discovery_info: if not discovery_info:
raise AddonError(f"Failed to get {self.addon_name} add-on discovery info") raise AddonError(f"Failed to get {self.addon_name} add-on discovery info")
discovery_info_config: dict = discovery_info["config"] return discovery_info.config
return discovery_info_config
@api_error( @api_error(
"Failed to get the {addon_name} add-on info", "Failed to get the {addon_name} add-on info",

View File

@ -563,8 +563,8 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
# updates if this is not a scheduled refresh and # updates if this is not a scheduled refresh and
# we are not doing the first refresh. # we are not doing the first refresh.
try: try:
await self.hassio.refresh_updates() await self.supervisor_client.refresh_updates()
except HassioAPIError as err: except SupervisorError as err:
_LOGGER.warning("Error on Supervisor API: %s", err) _LOGGER.warning("Error on Supervisor API: %s", err)
await super()._async_refresh( await super()._async_refresh(

View File

@ -8,6 +8,7 @@ import logging
from typing import Any from typing import Any
from aiohasupervisor import SupervisorError from aiohasupervisor import SupervisorError
from aiohasupervisor.models import Discovery
from aiohttp import web from aiohttp import web
from aiohttp.web_exceptions import HTTPServiceUnavailable from aiohttp.web_exceptions import HTTPServiceUnavailable
@ -19,8 +20,8 @@ from homeassistant.data_entry_flow import BaseServiceInfo
from homeassistant.helpers import discovery_flow from homeassistant.helpers import discovery_flow
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .const import ATTR_ADDON, ATTR_CONFIG, ATTR_DISCOVERY, ATTR_UUID, DOMAIN from .const import ATTR_ADDON, ATTR_UUID, DOMAIN
from .handler import HassIO, HassioAPIError, get_supervisor_client from .handler import HassIO, get_supervisor_client
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -39,20 +40,21 @@ class HassioServiceInfo(BaseServiceInfo):
def async_setup_discovery_view(hass: HomeAssistant, hassio: HassIO) -> None: def async_setup_discovery_view(hass: HomeAssistant, hassio: HassIO) -> None:
"""Discovery setup.""" """Discovery setup."""
hassio_discovery = HassIODiscovery(hass, hassio) hassio_discovery = HassIODiscovery(hass, hassio)
supervisor_client = get_supervisor_client(hass)
hass.http.register_view(hassio_discovery) hass.http.register_view(hassio_discovery)
# Handle exists discovery messages # Handle exists discovery messages
async def _async_discovery_start_handler(event: Event) -> None: async def _async_discovery_start_handler(event: Event) -> None:
"""Process all exists discovery on startup.""" """Process all exists discovery on startup."""
try: try:
data = await hassio.retrieve_discovery_messages() data = await supervisor_client.discovery.list()
except HassioAPIError as err: except SupervisorError as err:
_LOGGER.error("Can't read discover info: %s", err) _LOGGER.error("Can't read discover info: %s", err)
return return
jobs = [ jobs = [
asyncio.create_task(hassio_discovery.async_process_new(discovery)) asyncio.create_task(hassio_discovery.async_process_new(discovery))
for discovery in data[ATTR_DISCOVERY] for discovery in data
] ]
if jobs: if jobs:
await asyncio.wait(jobs) await asyncio.wait(jobs)
@ -95,8 +97,8 @@ class HassIODiscovery(HomeAssistantView):
"""Handle new discovery requests.""" """Handle new discovery requests."""
# Fetch discovery data and prevent injections # Fetch discovery data and prevent injections
try: try:
data = await self.hassio.get_discovery_message(uuid) data = await self._supervisor_client.discovery.get(uuid)
except HassioAPIError as err: except SupervisorError as err:
_LOGGER.error("Can't read discovery data: %s", err) _LOGGER.error("Can't read discovery data: %s", err)
raise HTTPServiceUnavailable from None raise HTTPServiceUnavailable from None
@ -113,52 +115,50 @@ class HassIODiscovery(HomeAssistantView):
async def async_rediscover(self, uuid: str) -> None: async def async_rediscover(self, uuid: str) -> None:
"""Rediscover add-on when config entry is removed.""" """Rediscover add-on when config entry is removed."""
try: try:
data = await self.hassio.get_discovery_message(uuid) data = await self._supervisor_client.discovery.get(uuid)
except HassioAPIError as err: except SupervisorError as err:
_LOGGER.debug("Can't read discovery data: %s", err) _LOGGER.debug("Can't read discovery data: %s", err)
else: else:
await self.async_process_new(data) await self.async_process_new(data)
async def async_process_new(self, data: dict[str, Any]) -> None: async def async_process_new(self, data: Discovery) -> None:
"""Process add discovery entry.""" """Process add discovery entry."""
service: str = data[ATTR_SERVICE]
config_data: dict[str, Any] = data[ATTR_CONFIG]
slug: str = data[ATTR_ADDON]
uuid: str = data[ATTR_UUID]
# Read additional Add-on info # Read additional Add-on info
try: try:
addon_info = await self._supervisor_client.addons.addon_info(slug) addon_info = await self._supervisor_client.addons.addon_info(data.addon)
except SupervisorError as err: except SupervisorError as err:
_LOGGER.error("Can't read add-on info: %s", err) _LOGGER.error("Can't read add-on info: %s", err)
return return
config_data[ATTR_ADDON] = addon_info.name data.config[ATTR_ADDON] = addon_info.name
# Use config flow # Use config flow
discovery_flow.async_create_flow( discovery_flow.async_create_flow(
self.hass, self.hass,
service, data.service,
context={"source": config_entries.SOURCE_HASSIO}, context={"source": config_entries.SOURCE_HASSIO},
data=HassioServiceInfo( data=HassioServiceInfo(
config=config_data, name=addon_info.name, slug=slug, uuid=uuid config=data.config,
name=addon_info.name,
slug=data.addon,
uuid=data.uuid,
), ),
discovery_key=discovery_flow.DiscoveryKey( discovery_key=discovery_flow.DiscoveryKey(
domain=DOMAIN, domain=DOMAIN,
key=data[ATTR_UUID], key=data.uuid,
version=1, version=1,
), ),
) )
async def async_process_del(self, data: dict[str, Any]) -> None: async def async_process_del(self, data: dict[str, Any]) -> None:
"""Process remove discovery entry.""" """Process remove discovery entry."""
service = data[ATTR_SERVICE] service: str = data[ATTR_SERVICE]
uuid = data[ATTR_UUID] uuid: str = data[ATTR_UUID]
# Check if really deletet / prevent injections # Check if really deletet / prevent injections
try: try:
data = await self.hassio.get_discovery_message(uuid) data = await self._supervisor_client.discovery.get(uuid)
except HassioAPIError: except SupervisorError:
pass pass
else: else:
_LOGGER.warning("Retrieve wrong unload for %s", service) _LOGGER.warning("Retrieve wrong unload for %s", service)

View File

@ -24,7 +24,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.singleton import singleton from homeassistant.helpers.singleton import singleton
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
from .const import ATTR_DISCOVERY, ATTR_MESSAGE, ATTR_RESULT, DOMAIN, X_HASS_SOURCE from .const import ATTR_MESSAGE, ATTR_RESULT, DOMAIN, X_HASS_SOURCE
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -76,15 +76,6 @@ async def async_update_diagnostics(hass: HomeAssistant, diagnostics: bool) -> bo
return await hassio.update_diagnostics(diagnostics) return await hassio.update_diagnostics(diagnostics)
@bind_hass
async def async_get_addon_discovery_info(hass: HomeAssistant, slug: str) -> dict | None:
"""Return discovery data for an add-on."""
hassio: HassIO = hass.data[DOMAIN]
data = await hassio.retrieve_discovery_messages()
discovered_addons = data[ATTR_DISCOVERY]
return next((addon for addon in discovered_addons if addon["addon"] == slug), None)
@bind_hass @bind_hass
@api_data @api_data
async def async_create_backup( async def async_create_backup(
@ -100,52 +91,6 @@ async def async_create_backup(
return await hassio.send_command(command, payload=payload, timeout=None) return await hassio.send_command(command, payload=payload, timeout=None)
@bind_hass
@api_data
async def async_update_os(hass: HomeAssistant, version: str | None = None) -> dict:
"""Update Home Assistant Operating System.
The caller of the function should handle HassioAPIError.
"""
hassio: HassIO = hass.data[DOMAIN]
command = "/os/update"
return await hassio.send_command(
command,
payload={"version": version},
timeout=None,
)
@bind_hass
@api_data
async def async_update_supervisor(hass: HomeAssistant) -> dict:
"""Update Home Assistant Supervisor.
The caller of the function should handle HassioAPIError.
"""
hassio: HassIO = hass.data[DOMAIN]
command = "/supervisor/update"
return await hassio.send_command(command, timeout=None)
@bind_hass
@api_data
async def async_update_core(
hass: HomeAssistant, version: str | None = None, backup: bool = False
) -> dict:
"""Update Home Assistant Core.
The caller of the function should handle HassioAPIError.
"""
hassio: HassIO = hass.data[DOMAIN]
command = "/core/update"
return await hassio.send_command(
command,
payload={"version": version, "backup": backup},
timeout=None,
)
@bind_hass @bind_hass
@_api_bool @_api_bool
async def async_apply_suggestion(hass: HomeAssistant, suggestion_uuid: str) -> dict: async def async_apply_suggestion(hass: HomeAssistant, suggestion_uuid: str) -> dict:
@ -228,14 +173,6 @@ class HassIO:
"""Return base url for Supervisor.""" """Return base url for Supervisor."""
return self._base_url return self._base_url
@_api_bool
def is_connected(self) -> Coroutine:
"""Return true if it connected to Hass.io supervisor.
This method returns a coroutine.
"""
return self.send_command("/supervisor/ping", method="get", timeout=15)
@api_data @api_data
def get_info(self) -> Coroutine: def get_info(self) -> Coroutine:
"""Return generic Supervisor information. """Return generic Supervisor information.
@ -308,46 +245,6 @@ class HassIO:
""" """
return self.send_command("/ingress/panels", method="get") return self.send_command("/ingress/panels", method="get")
@_api_bool
def restart_homeassistant(self) -> Coroutine:
"""Restart Home-Assistant container.
This method returns a coroutine.
"""
return self.send_command("/homeassistant/restart")
@_api_bool
def stop_homeassistant(self) -> Coroutine:
"""Stop Home-Assistant container.
This method returns a coroutine.
"""
return self.send_command("/homeassistant/stop")
@_api_bool
def refresh_updates(self) -> Coroutine:
"""Refresh available updates.
This method returns a coroutine.
"""
return self.send_command("/refresh_updates", timeout=300)
@api_data
def retrieve_discovery_messages(self) -> Coroutine:
"""Return all discovery data from Hass.io API.
This method returns a coroutine.
"""
return self.send_command("/discovery", method="get", timeout=60)
@api_data
def get_discovery_message(self, uuid: str) -> Coroutine:
"""Return a single discovery data message.
This method returns a coroutine.
"""
return self.send_command(f"/discovery/{uuid}", method="get")
@api_data @api_data
def get_resolution_info(self) -> Coroutine: def get_resolution_info(self) -> Coroutine:
"""Return data for Supervisor resolution center. """Return data for Supervisor resolution center.

View File

@ -5,7 +5,11 @@ from __future__ import annotations
from typing import Any from typing import Any
from aiohasupervisor import SupervisorError from aiohasupervisor import SupervisorError
from aiohasupervisor.models import StoreAddonUpdate from aiohasupervisor.models import (
HomeAssistantUpdateOptions,
OSUpdate,
StoreAddonUpdate,
)
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy from awesomeversion import AwesomeVersion, AwesomeVersionStrategy
from homeassistant.components.update import ( from homeassistant.components.update import (
@ -36,12 +40,6 @@ from .entity import (
HassioOSEntity, HassioOSEntity,
HassioSupervisorEntity, HassioSupervisorEntity,
) )
from .handler import (
HassioAPIError,
async_update_core,
async_update_os,
async_update_supervisor,
)
ENTITY_DESCRIPTION = UpdateEntityDescription( ENTITY_DESCRIPTION = UpdateEntityDescription(
name="Update", name="Update",
@ -213,8 +211,10 @@ class SupervisorOSUpdateEntity(HassioOSEntity, UpdateEntity):
) -> None: ) -> None:
"""Install an update.""" """Install an update."""
try: try:
await async_update_os(self.hass, version) await self.coordinator.supervisor_client.os.update(
except HassioAPIError as err: OSUpdate(version=version)
)
except SupervisorError as err:
raise HomeAssistantError( raise HomeAssistantError(
f"Error updating Home Assistant Operating System: {err}" f"Error updating Home Assistant Operating System: {err}"
) from err ) from err
@ -259,8 +259,8 @@ class SupervisorSupervisorUpdateEntity(HassioSupervisorEntity, UpdateEntity):
) -> None: ) -> None:
"""Install an update.""" """Install an update."""
try: try:
await async_update_supervisor(self.hass) await self.coordinator.supervisor_client.supervisor.update()
except HassioAPIError as err: except SupervisorError as err:
raise HomeAssistantError( raise HomeAssistantError(
f"Error updating Home Assistant Supervisor: {err}" f"Error updating Home Assistant Supervisor: {err}"
) from err ) from err
@ -304,8 +304,10 @@ class SupervisorCoreUpdateEntity(HassioCoreEntity, UpdateEntity):
) -> None: ) -> None:
"""Install an update.""" """Install an update."""
try: try:
await async_update_core(self.hass, version=version, backup=backup) await self.coordinator.supervisor_client.homeassistant.update(
except HassioAPIError as err: HomeAssistantUpdateOptions(version=version, backup=backup)
)
except SupervisorError as err:
raise HomeAssistantError( raise HomeAssistantError(
f"Error updating Home Assistant Core: {err}" f"Error updating Home Assistant Core: {err}"
) from err ) from err

View File

@ -8,7 +8,7 @@ from pathlib import Path
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
from aiohasupervisor.models import Repository, StoreAddon, StoreInfo from aiohasupervisor.models import Discovery, Repository, StoreAddon, StoreInfo
import pytest import pytest
from homeassistant.config_entries import ( from homeassistant.config_entries import (
@ -205,12 +205,9 @@ def addon_manager_fixture(
@pytest.fixture(name="discovery_info") @pytest.fixture(name="discovery_info")
def discovery_info_fixture() -> Any: def discovery_info_fixture() -> list[Discovery]:
"""Return the discovery info from the supervisor.""" """Return the discovery info from the supervisor."""
# pylint: disable-next=import-outside-toplevel return []
from .hassio.common import mock_discovery_info
return mock_discovery_info()
@pytest.fixture(name="discovery_info_side_effect") @pytest.fixture(name="discovery_info_side_effect")
@ -221,13 +218,29 @@ def discovery_info_side_effect_fixture() -> Any | None:
@pytest.fixture(name="get_addon_discovery_info") @pytest.fixture(name="get_addon_discovery_info")
def get_addon_discovery_info_fixture( def get_addon_discovery_info_fixture(
discovery_info: dict[str, Any], discovery_info_side_effect: Any | None supervisor_client: AsyncMock,
) -> Generator[AsyncMock]: discovery_info: list[Discovery],
discovery_info_side_effect: Any | None,
) -> AsyncMock:
"""Mock get add-on discovery info.""" """Mock get add-on discovery info."""
# pylint: disable-next=import-outside-toplevel supervisor_client.discovery.list.return_value = discovery_info
from .hassio.common import mock_get_addon_discovery_info supervisor_client.discovery.list.side_effect = discovery_info_side_effect
return supervisor_client.discovery.list
yield from mock_get_addon_discovery_info(discovery_info, discovery_info_side_effect)
@pytest.fixture(name="get_discovery_message_side_effect")
def get_discovery_message_side_effect_fixture() -> Any | None:
"""Side effect for getting a discovery message by uuid."""
return None
@pytest.fixture(name="get_discovery_message")
def get_discovery_message_fixture(
supervisor_client: AsyncMock, get_discovery_message_side_effect: Any | None
) -> AsyncMock:
"""Mock getting a discovery message by uuid."""
supervisor_client.discovery.get.side_effect = get_discovery_message_side_effect
return supervisor_client.discovery.get
@pytest.fixture(name="addon_store_info_side_effect") @pytest.fixture(name="addon_store_info_side_effect")
@ -453,11 +466,22 @@ def addon_changelog_fixture(supervisor_client: AsyncMock) -> AsyncMock:
return supervisor_client.store.addon_changelog return supervisor_client.store.addon_changelog
@pytest.fixture(name="supervisor_is_connected")
def supervisor_is_connected_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock supervisor is connected."""
supervisor_client.supervisor.ping.return_value = None
return supervisor_client.supervisor.ping
@pytest.fixture(name="supervisor_client") @pytest.fixture(name="supervisor_client")
def supervisor_client() -> Generator[AsyncMock]: def supervisor_client() -> Generator[AsyncMock]:
"""Mock the supervisor client.""" """Mock the supervisor client."""
supervisor_client = AsyncMock() supervisor_client = AsyncMock()
supervisor_client.addons = AsyncMock() supervisor_client.addons = AsyncMock()
supervisor_client.discovery = AsyncMock()
supervisor_client.homeassistant = AsyncMock()
supervisor_client.os = AsyncMock()
supervisor_client.supervisor = AsyncMock()
with ( with (
patch( patch(
"homeassistant.components.hassio.get_supervisor_client", "homeassistant.components.hassio.get_supervisor_client",

View File

@ -7,7 +7,7 @@ from dataclasses import fields
import logging import logging
from types import MethodType from types import MethodType
from typing import Any from typing import Any
from unittest.mock import DEFAULT, AsyncMock, Mock, patch from unittest.mock import AsyncMock, Mock, patch
from aiohasupervisor.models import ( from aiohasupervisor.models import (
AddonsOptions, AddonsOptions,
@ -75,23 +75,6 @@ def mock_addon_manager(hass: HomeAssistant) -> AddonManager:
return AddonManager(hass, LOGGER, "Test", "test_addon") return AddonManager(hass, LOGGER, "Test", "test_addon")
def mock_discovery_info() -> Any:
"""Return the discovery info from the supervisor."""
return DEFAULT
def mock_get_addon_discovery_info(
discovery_info: dict[str, Any], discovery_info_side_effect: Any | None
) -> Generator[AsyncMock]:
"""Mock get add-on discovery info."""
with patch(
"homeassistant.components.hassio.addon_manager.async_get_addon_discovery_info",
side_effect=discovery_info_side_effect,
return_value=discovery_info,
) as get_addon_discovery_info:
yield get_addon_discovery_info
def mock_addon_store_info( def mock_addon_store_info(
supervisor_client: AsyncMock, supervisor_client: AsyncMock,
addon_store_info_side_effect: Any | None, addon_store_info_side_effect: Any | None,

View File

@ -32,14 +32,10 @@ def disable_security_filter() -> Generator[None]:
@pytest.fixture @pytest.fixture
def hassio_env() -> Generator[None]: def hassio_env(supervisor_is_connected: AsyncMock) -> Generator[None]:
"""Fixture to inject hassio env.""" """Fixture to inject hassio env."""
with ( with (
patch.dict(os.environ, {"SUPERVISOR": "127.0.0.1"}), patch.dict(os.environ, {"SUPERVISOR": "127.0.0.1"}),
patch(
"homeassistant.components.hassio.HassIO.is_connected",
return_value={"result": "ok", "data": {}},
),
patch.dict(os.environ, {"SUPERVISOR_TOKEN": SUPERVISOR_TOKEN}), patch.dict(os.environ, {"SUPERVISOR_TOKEN": SUPERVISOR_TOKEN}),
patch( patch(
"homeassistant.components.hassio.HassIO.get_info", "homeassistant.components.hassio.HassIO.get_info",
@ -78,9 +74,6 @@ def hassio_stubs(
patch( patch(
"homeassistant.components.hassio.issues.SupervisorIssues.setup", "homeassistant.components.hassio.issues.SupervisorIssues.setup",
), ),
patch(
"homeassistant.components.hassio.HassIO.refresh_updates",
),
): ):
hass.set_state(CoreState.starting) hass.set_state(CoreState.starting)
hass.loop.run_until_complete(async_setup_component(hass, "hassio", {})) hass.loop.run_until_complete(async_setup_component(hass, "hassio", {}))
@ -144,7 +137,6 @@ def all_setup_requests(
) )
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) 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"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/info", "http://127.0.0.1/info",
@ -225,7 +217,6 @@ def all_setup_requests(
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
) )
aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"})
addon_installed.return_value.update_available = False addon_installed.return_value.update_available = False
addon_installed.return_value.version = "1.0.0" addon_installed.return_value.version = "1.0.0"

View File

@ -5,9 +5,10 @@ from __future__ import annotations
import asyncio import asyncio
from typing import Any from typing import Any
from unittest.mock import AsyncMock, call from unittest.mock import AsyncMock, call
from uuid import uuid4
from aiohasupervisor import SupervisorError from aiohasupervisor import SupervisorError
from aiohasupervisor.models import AddonsOptions from aiohasupervisor.models import AddonsOptions, Discovery
import pytest import pytest
from homeassistant.components.hassio.addon_manager import ( from homeassistant.components.hassio.addon_manager import (
@ -62,7 +63,11 @@ async def test_get_addon_discovery_info(
addon_manager: AddonManager, get_addon_discovery_info: AsyncMock addon_manager: AddonManager, get_addon_discovery_info: AsyncMock
) -> None: ) -> None:
"""Test get addon discovery info.""" """Test get addon discovery info."""
get_addon_discovery_info.return_value = {"config": {"test_key": "test"}} get_addon_discovery_info.return_value = [
Discovery(
addon="test_addon", service="", uuid=uuid4(), config={"test_key": "test"}
)
]
assert await addon_manager.async_get_addon_discovery_info() == {"test_key": "test"} assert await addon_manager.async_get_addon_discovery_info() == {"test_key": "test"}
@ -73,8 +78,6 @@ async def test_missing_addon_discovery_info(
addon_manager: AddonManager, get_addon_discovery_info: AsyncMock addon_manager: AddonManager, get_addon_discovery_info: AsyncMock
) -> None: ) -> None:
"""Test missing addon discovery info.""" """Test missing addon discovery info."""
get_addon_discovery_info.return_value = None
with pytest.raises(AddonError): with pytest.raises(AddonError):
await addon_manager.async_get_addon_discovery_info() await addon_manager.async_get_addon_discovery_info()
@ -85,7 +88,7 @@ async def test_get_addon_discovery_info_error(
addon_manager: AddonManager, get_addon_discovery_info: AsyncMock addon_manager: AddonManager, get_addon_discovery_info: AsyncMock
) -> None: ) -> None:
"""Test get addon discovery info raises error.""" """Test get addon discovery info raises error."""
get_addon_discovery_info.side_effect = HassioAPIError("Boom") get_addon_discovery_info.side_effect = SupervisorError("Boom")
with pytest.raises(AddonError) as err: with pytest.raises(AddonError) as err:
assert await addon_manager.async_get_addon_discovery_info() assert await addon_manager.async_get_addon_discovery_info()

View File

@ -1,7 +1,7 @@
"""Test add-on panel.""" """Test add-on panel."""
from http import HTTPStatus from http import HTTPStatus
from unittest.mock import patch from unittest.mock import AsyncMock, patch
import pytest import pytest
@ -13,10 +13,11 @@ from tests.typing import ClientSessionGenerator
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def mock_all(aioclient_mock: AiohttpClientMocker) -> None: def mock_all(
aioclient_mock: AiohttpClientMocker, supervisor_is_connected: AsyncMock
) -> None:
"""Mock all setup requests.""" """Mock all setup requests."""
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) 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"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/homeassistant/info", "http://127.0.0.1/homeassistant/info",

View File

@ -28,7 +28,6 @@ def mock_all(
) -> None: ) -> None:
"""Mock all setup requests.""" """Mock all setup requests."""
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) 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"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/info", "http://127.0.0.1/info",
@ -141,7 +140,6 @@ def mock_all(
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
) )
aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/resolution/info", "http://127.0.0.1/resolution/info",
json={ json={

View File

@ -27,7 +27,6 @@ def mock_all(
) -> None: ) -> None:
"""Mock all setup requests.""" """Mock all setup requests."""
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) 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"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/info", "http://127.0.0.1/info",
@ -144,7 +143,6 @@ def mock_all(
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
) )
aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/resolution/info", "http://127.0.0.1/resolution/info",
json={ json={

View File

@ -3,7 +3,9 @@
from collections.abc import Generator from collections.abc import Generator
from http import HTTPStatus from http import HTTPStatus
from unittest.mock import AsyncMock, Mock, patch from unittest.mock import AsyncMock, Mock, patch
from uuid import uuid4
from aiohasupervisor.models import Discovery
from aiohttp.test_utils import TestClient from aiohttp.test_utils import TestClient
import pytest import pytest
@ -48,42 +50,34 @@ def mock_mqtt_fixture(
@pytest.mark.usefixtures("hassio_client") @pytest.mark.usefixtures("hassio_client")
async def test_hassio_discovery_startup( async def test_hassio_discovery_startup(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_mqtt: type[config_entries.ConfigFlow], mock_mqtt: type[config_entries.ConfigFlow],
addon_installed: AsyncMock, addon_installed: AsyncMock,
get_addon_discovery_info: AsyncMock,
) -> None: ) -> None:
"""Test startup and discovery after event.""" """Test startup and discovery after event."""
aioclient_mock.get( get_addon_discovery_info.return_value = [
"http://127.0.0.1/discovery", Discovery(
json={ addon="mosquitto",
"result": "ok", service="mqtt",
"data": { uuid=(uuid := uuid4()),
"discovery": [ config={
{ "broker": "mock-broker",
"service": "mqtt", "port": 1883,
"uuid": "test", "username": "mock-user",
"addon": "mosquitto", "password": "mock-pass",
"config": { "protocol": "3.1.1",
"broker": "mock-broker",
"port": 1883,
"username": "mock-user",
"password": "mock-pass",
"protocol": "3.1.1",
},
}
]
}, },
}, )
) ]
addon_installed.return_value.name = "Mosquitto Test" addon_installed.return_value.name = "Mosquitto Test"
assert aioclient_mock.call_count == 0 assert get_addon_discovery_info.call_count == 0
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
await hass.async_block_till_done() await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count == 1 assert get_addon_discovery_info.call_count == 1
assert mock_mqtt.async_step_hassio.called assert mock_mqtt.async_step_hassio.called
mock_mqtt.async_step_hassio.assert_called_with( mock_mqtt.async_step_hassio.assert_called_with(
HassioServiceInfo( HassioServiceInfo(
@ -97,7 +91,7 @@ async def test_hassio_discovery_startup(
}, },
name="Mosquitto Test", name="Mosquitto Test",
slug="mosquitto", slug="mosquitto",
uuid="test", uuid=uuid,
) )
) )
@ -108,34 +102,27 @@ async def test_hassio_discovery_startup_done(
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
mock_mqtt: type[config_entries.ConfigFlow], mock_mqtt: type[config_entries.ConfigFlow],
addon_installed: AsyncMock, addon_installed: AsyncMock,
get_addon_discovery_info: AsyncMock,
) -> None: ) -> None:
"""Test startup and discovery with hass discovery.""" """Test startup and discovery with hass discovery."""
aioclient_mock.post( aioclient_mock.post(
"http://127.0.0.1/supervisor/options", "http://127.0.0.1/supervisor/options",
json={"result": "ok", "data": {}}, json={"result": "ok", "data": {}},
) )
aioclient_mock.get( get_addon_discovery_info.return_value = [
"http://127.0.0.1/discovery", Discovery(
json={ addon="mosquitto",
"result": "ok", service="mqtt",
"data": { uuid=(uuid := uuid4()),
"discovery": [ config={
{ "broker": "mock-broker",
"service": "mqtt", "port": 1883,
"uuid": "test", "username": "mock-user",
"addon": "mosquitto", "password": "mock-pass",
"config": { "protocol": "3.1.1",
"broker": "mock-broker",
"port": 1883,
"username": "mock-user",
"password": "mock-pass",
"protocol": "3.1.1",
},
}
]
}, },
}, )
) ]
addon_installed.return_value.name = "Mosquitto Test" addon_installed.return_value.name = "Mosquitto Test"
with ( with (
@ -152,7 +139,7 @@ async def test_hassio_discovery_startup_done(
await async_setup_component(hass, "hassio", {}) await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count == 1 assert get_addon_discovery_info.call_count == 1
assert mock_mqtt.async_step_hassio.called assert mock_mqtt.async_step_hassio.called
mock_mqtt.async_step_hassio.assert_called_with( mock_mqtt.async_step_hassio.assert_called_with(
HassioServiceInfo( HassioServiceInfo(
@ -166,35 +153,29 @@ async def test_hassio_discovery_startup_done(
}, },
name="Mosquitto Test", name="Mosquitto Test",
slug="mosquitto", slug="mosquitto",
uuid="test", uuid=uuid,
) )
) )
async def test_hassio_discovery_webhook( async def test_hassio_discovery_webhook(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hassio_client: TestClient, hassio_client: TestClient,
mock_mqtt: type[config_entries.ConfigFlow], mock_mqtt: type[config_entries.ConfigFlow],
addon_installed: AsyncMock, addon_installed: AsyncMock,
get_discovery_message: AsyncMock,
) -> None: ) -> None:
"""Test discovery webhook.""" """Test discovery webhook."""
aioclient_mock.get( get_discovery_message.return_value = Discovery(
"http://127.0.0.1/discovery/testuuid", addon="mosquitto",
json={ service="mqtt",
"result": "ok", uuid=(uuid := uuid4()),
"data": { config={
"service": "mqtt", "broker": "mock-broker",
"uuid": "test", "port": 1883,
"addon": "mosquitto", "username": "mock-user",
"config": { "password": "mock-pass",
"broker": "mock-broker", "protocol": "3.1.1",
"port": 1883,
"username": "mock-user",
"password": "mock-pass",
"protocol": "3.1.1",
},
},
}, },
) )
addon_installed.return_value.name = "Mosquitto Test" addon_installed.return_value.name = "Mosquitto Test"
@ -208,7 +189,7 @@ async def test_hassio_discovery_webhook(
await hass.async_block_till_done() await hass.async_block_till_done()
assert resp.status == HTTPStatus.OK assert resp.status == HTTPStatus.OK
assert aioclient_mock.call_count == 1 assert get_discovery_message.call_count == 1
assert mock_mqtt.async_step_hassio.called assert mock_mqtt.async_step_hassio.called
mock_mqtt.async_step_hassio.assert_called_with( mock_mqtt.async_step_hassio.assert_called_with(
HassioServiceInfo( HassioServiceInfo(
@ -222,7 +203,7 @@ async def test_hassio_discovery_webhook(
}, },
name="Mosquitto Test", name="Mosquitto Test",
slug="mosquitto", slug="mosquitto",
uuid="test", uuid=uuid,
) )
) )
@ -271,6 +252,8 @@ async def test_hassio_rediscover(
entry_domain: str, entry_domain: str,
entry_discovery_keys: dict[str, tuple[DiscoveryKey, ...]], entry_discovery_keys: dict[str, tuple[DiscoveryKey, ...]],
entry_source: str, entry_source: str,
get_addon_discovery_info: AsyncMock,
get_discovery_message: AsyncMock,
) -> None: ) -> None:
"""Test we reinitiate flows when an ignored config entry is removed.""" """Test we reinitiate flows when an ignored config entry is removed."""
@ -286,30 +269,21 @@ async def test_hassio_rediscover(
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
aioclient_mock.get( get_discovery_message.return_value = Discovery(
"http://127.0.0.1/discovery/test", addon="mosquitto",
json={ service="mqtt",
"result": "ok", uuid=(uuid := uuid4()),
"data": { config={
"service": "mqtt", "broker": "mock-broker",
"uuid": "test", "port": 1883,
"addon": "mosquitto", "username": "mock-user",
"config": { "password": "mock-pass",
"broker": "mock-broker", "protocol": "3.1.1",
"port": 1883,
"username": "mock-user",
"password": "mock-pass",
"protocol": "3.1.1",
},
},
}, },
) )
aioclient_mock.get(
"http://127.0.0.1/discovery", json={"result": "ok", "data": {"discovery": []}}
)
expected_context = { expected_context = {
"discovery_key": DiscoveryKey(domain="hassio", key="test", version=1), "discovery_key": DiscoveryKey(domain="hassio", key=uuid, version=1),
"source": config_entries.SOURCE_HASSIO, "source": config_entries.SOURCE_HASSIO,
} }

View File

@ -4,7 +4,6 @@ from __future__ import annotations
from typing import Any, Literal from typing import Any, Literal
import aiohttp
from aiohttp import hdrs, web from aiohttp import hdrs, web
import pytest import pytest
@ -16,36 +15,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
async def test_api_ping(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API ping."""
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={"result": "ok"})
assert await hassio_handler.is_connected()
assert aioclient_mock.call_count == 1
async def test_api_ping_error(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API ping error."""
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={"result": "error"})
assert not (await hassio_handler.is_connected())
assert aioclient_mock.call_count == 1
async def test_api_ping_exeption(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API ping exception."""
aioclient_mock.get("http://127.0.0.1/supervisor/ping", exc=aiohttp.ClientError())
assert not (await hassio_handler.is_connected())
assert aioclient_mock.call_count == 1
async def test_api_info( async def test_api_info(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None: ) -> None:
@ -181,26 +150,6 @@ async def test_api_core_info_error(
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
async def test_api_homeassistant_stop(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Home Assistant stop."""
aioclient_mock.post("http://127.0.0.1/homeassistant/stop", json={"result": "ok"})
assert await hassio_handler.stop_homeassistant()
assert aioclient_mock.call_count == 1
async def test_api_homeassistant_restart(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Home Assistant restart."""
aioclient_mock.post("http://127.0.0.1/homeassistant/restart", json={"result": "ok"})
assert await hassio_handler.restart_homeassistant()
assert aioclient_mock.call_count == 1
async def test_api_core_stats( async def test_api_core_stats(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None: ) -> None:
@ -229,34 +178,6 @@ async def test_api_supervisor_stats(
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
async def test_api_discovery_message(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API discovery message."""
aioclient_mock.get(
"http://127.0.0.1/discovery/test",
json={"result": "ok", "data": {"service": "mqtt"}},
)
data = await hassio_handler.get_discovery_message("test")
assert data["service"] == "mqtt"
assert aioclient_mock.call_count == 1
async def test_api_retrieve_discovery(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API discovery message."""
aioclient_mock.get(
"http://127.0.0.1/discovery",
json={"result": "ok", "data": {"discovery": [{"service": "mqtt"}]}},
)
data = await hassio_handler.retrieve_discovery_messages()
assert data["discovery"][-1]["service"] == "mqtt"
assert aioclient_mock.call_count == 1
async def test_api_ingress_panels( async def test_api_ingress_panels(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None: ) -> None:
@ -287,8 +208,7 @@ async def test_api_ingress_panels(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("api_call", "method", "payload"), ("api_call", "method", "payload"),
[ [
("retrieve_discovery_messages", "GET", None), ("get_resolution_info", "GET", None),
("refresh_updates", "POST", None),
("update_diagnostics", "POST", True), ("update_diagnostics", "POST", True),
], ],
) )

View File

@ -5,6 +5,7 @@ import os
from typing import Any from typing import Any
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import AddonsStats from aiohasupervisor.models import AddonsStats
import pytest import pytest
from voluptuous import Invalid from voluptuous import Invalid
@ -21,7 +22,6 @@ from homeassistant.components.hassio import (
is_hassio, is_hassio,
) )
from homeassistant.components.hassio.const import REQUEST_REFRESH_DELAY from homeassistant.components.hassio.const import REQUEST_REFRESH_DELAY
from homeassistant.components.hassio.handler import HassioAPIError
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, issue_registry as ir from homeassistant.helpers import device_registry as dr, issue_registry as ir
@ -62,7 +62,6 @@ def mock_all(
) -> None: ) -> None:
"""Mock all setup requests.""" """Mock all setup requests."""
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) 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"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/info", "http://127.0.0.1/info",
@ -197,7 +196,6 @@ def mock_all(
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
) )
aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/resolution/info", "http://127.0.0.1/resolution/info",
json={ json={
@ -282,9 +280,9 @@ async def test_setup_api_push_api_data(
assert result assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20
assert not aioclient_mock.mock_calls[1][2]["ssl"] assert not aioclient_mock.mock_calls[0][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 9999 assert aioclient_mock.mock_calls[0][2]["port"] == 9999
assert "watchdog" not in aioclient_mock.mock_calls[1][2] assert "watchdog" not in aioclient_mock.mock_calls[0][2]
async def test_setup_api_push_api_data_server_host( async def test_setup_api_push_api_data_server_host(
@ -303,9 +301,9 @@ async def test_setup_api_push_api_data_server_host(
assert result assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20
assert not aioclient_mock.mock_calls[1][2]["ssl"] assert not aioclient_mock.mock_calls[0][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 9999 assert aioclient_mock.mock_calls[0][2]["port"] == 9999
assert not aioclient_mock.mock_calls[1][2]["watchdog"] assert not aioclient_mock.mock_calls[0][2]["watchdog"]
async def test_setup_api_push_api_data_default( async def test_setup_api_push_api_data_default(
@ -321,9 +319,9 @@ async def test_setup_api_push_api_data_default(
assert result assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20
assert not aioclient_mock.mock_calls[1][2]["ssl"] assert not aioclient_mock.mock_calls[0][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 8123 assert aioclient_mock.mock_calls[0][2]["port"] == 8123
refresh_token = aioclient_mock.mock_calls[1][2]["refresh_token"] refresh_token = aioclient_mock.mock_calls[0][2]["refresh_token"]
hassio_user = await hass.auth.async_get_user( hassio_user = await hass.auth.async_get_user(
hass_storage[STORAGE_KEY]["data"]["hassio_user"] hass_storage[STORAGE_KEY]["data"]["hassio_user"]
) )
@ -402,9 +400,9 @@ async def test_setup_api_existing_hassio_user(
assert result assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20
assert not aioclient_mock.mock_calls[1][2]["ssl"] assert not aioclient_mock.mock_calls[0][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 8123 assert aioclient_mock.mock_calls[0][2]["port"] == 8123
assert aioclient_mock.mock_calls[1][2]["refresh_token"] == token.token assert aioclient_mock.mock_calls[0][2]["refresh_token"] == token.token
async def test_setup_core_push_timezone( async def test_setup_core_push_timezone(
@ -421,7 +419,7 @@ async def test_setup_core_push_timezone(
assert result assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20
assert aioclient_mock.mock_calls[2][2]["timezone"] == "testzone" assert aioclient_mock.mock_calls[1][2]["timezone"] == "testzone"
with patch("homeassistant.util.dt.set_default_time_zone"): with patch("homeassistant.util.dt.set_default_time_zone"):
await hass.config.async_update(time_zone="America/New_York") await hass.config.async_update(time_zone="America/New_York")
@ -455,16 +453,13 @@ async def test_fail_setup_without_environ_var(hass: HomeAssistant) -> None:
async def test_warn_when_cannot_connect( async def test_warn_when_cannot_connect(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
supervisor_is_connected: AsyncMock,
) -> None: ) -> None:
"""Fail warn when we cannot connect.""" """Fail warn when we cannot connect."""
with ( supervisor_is_connected.side_effect = SupervisorError
patch.dict(os.environ, MOCK_ENVIRON), with patch.dict(os.environ, MOCK_ENVIRON):
patch(
"homeassistant.components.hassio.HassIO.is_connected",
return_value=None,
),
):
result = await async_setup_component(hass, "hassio", {}) result = await async_setup_component(hass, "hassio", {})
assert result assert result
@ -496,17 +491,13 @@ async def test_service_calls(
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
supervisor_client: AsyncMock, supervisor_client: AsyncMock,
addon_installed, addon_installed: AsyncMock,
supervisor_is_connected: AsyncMock,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
) -> None: ) -> None:
"""Call service and check the API calls behind that.""" """Call service and check the API calls behind that."""
with ( supervisor_is_connected.side_effect = SupervisorError
patch.dict(os.environ, MOCK_ENVIRON), with patch.dict(os.environ, MOCK_ENVIRON):
patch(
"homeassistant.components.hassio.HassIO.is_connected",
return_value=None,
),
):
assert await async_setup_component(hass, "hassio", {}) assert await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done() await hass.async_block_till_done()
@ -536,14 +527,14 @@ async def test_service_calls(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 24 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 25
assert aioclient_mock.mock_calls[-1][2] == "test" assert aioclient_mock.mock_calls[-1][2] == "test"
await hass.services.async_call("hassio", "host_shutdown", {}) await hass.services.async_call("hassio", "host_shutdown", {})
await hass.services.async_call("hassio", "host_reboot", {}) await hass.services.async_call("hassio", "host_reboot", {})
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 26 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 27
await hass.services.async_call("hassio", "backup_full", {}) await hass.services.async_call("hassio", "backup_full", {})
await hass.services.async_call( await hass.services.async_call(
@ -558,7 +549,7 @@ async def test_service_calls(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 28 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 29
assert aioclient_mock.mock_calls[-1][2] == { assert aioclient_mock.mock_calls[-1][2] == {
"name": "2021-11-13 03:48:00", "name": "2021-11-13 03:48:00",
"homeassistant": True, "homeassistant": True,
@ -583,7 +574,7 @@ async def test_service_calls(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 30 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 31
assert aioclient_mock.mock_calls[-1][2] == { assert aioclient_mock.mock_calls[-1][2] == {
"addons": ["test"], "addons": ["test"],
"folders": ["ssl"], "folders": ["ssl"],
@ -602,7 +593,7 @@ async def test_service_calls(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 31 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 32
assert aioclient_mock.mock_calls[-1][2] == { assert aioclient_mock.mock_calls[-1][2] == {
"name": "backup_name", "name": "backup_name",
"location": "backup_share", "location": "backup_share",
@ -618,7 +609,7 @@ async def test_service_calls(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 32 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 33
assert aioclient_mock.mock_calls[-1][2] == { assert aioclient_mock.mock_calls[-1][2] == {
"name": "2021-11-13 03:48:00", "name": "2021-11-13 03:48:00",
"location": None, "location": None,
@ -637,7 +628,7 @@ async def test_service_calls(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 34 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 35
assert aioclient_mock.mock_calls[-1][2] == { assert aioclient_mock.mock_calls[-1][2] == {
"name": "2021-11-13 11:48:00", "name": "2021-11-13 11:48:00",
"location": None, "location": None,
@ -647,15 +638,11 @@ async def test_service_calls(
async def test_invalid_service_calls( async def test_invalid_service_calls(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
supervisor_is_connected: AsyncMock,
) -> None: ) -> None:
"""Call service with invalid input and check that it raises.""" """Call service with invalid input and check that it raises."""
with ( supervisor_is_connected.side_effect = SupervisorError
patch.dict(os.environ, MOCK_ENVIRON), with patch.dict(os.environ, MOCK_ENVIRON):
patch(
"homeassistant.components.hassio.HassIO.is_connected",
return_value=None,
),
):
assert await async_setup_component(hass, "hassio", {}) assert await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done() await hass.async_block_till_done()
@ -672,6 +659,7 @@ async def test_invalid_service_calls(
async def test_addon_service_call_with_complex_slug( async def test_addon_service_call_with_complex_slug(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
supervisor_is_connected: AsyncMock,
) -> None: ) -> None:
"""Addon slugs can have ., - and _, confirm that passes validation.""" """Addon slugs can have ., - and _, confirm that passes validation."""
supervisor_mock_data = { supervisor_mock_data = {
@ -691,12 +679,9 @@ async def test_addon_service_call_with_complex_slug(
}, },
], ],
} }
supervisor_is_connected.side_effect = SupervisorError
with ( with (
patch.dict(os.environ, MOCK_ENVIRON), patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.hassio.HassIO.is_connected",
return_value=None,
),
patch( patch(
"homeassistant.components.hassio.HassIO.get_supervisor_info", "homeassistant.components.hassio.HassIO.get_supervisor_info",
return_value=supervisor_mock_data, return_value=supervisor_mock_data,
@ -724,12 +709,12 @@ async def test_service_calls_core(
await hass.services.async_call("homeassistant", "stop") await hass.services.async_call("homeassistant", "stop")
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 5 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 6
await hass.services.async_call("homeassistant", "check_config") await hass.services.async_call("homeassistant", "check_config")
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 5 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 6
with patch( with patch(
"homeassistant.config.async_check_ha_config_file", return_value=None "homeassistant.config.async_check_ha_config_file", return_value=None
@ -738,7 +723,7 @@ async def test_service_calls_core(
await hass.async_block_till_done() await hass.async_block_till_done()
assert mock_check_config.called assert mock_check_config.called
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 6 assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 7
@pytest.mark.usefixtures("addon_installed") @pytest.mark.usefixtures("addon_installed")
@ -923,129 +908,108 @@ async def test_device_registry_calls(
@pytest.mark.usefixtures("addon_installed") @pytest.mark.usefixtures("addon_installed")
async def test_coordinator_updates( async def test_coordinator_updates(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture hass: HomeAssistant, caplog: pytest.LogCaptureFixture, supervisor_client: AsyncMock
) -> None: ) -> None:
"""Test coordinator updates.""" """Test coordinator updates."""
await async_setup_component(hass, "homeassistant", {}) await async_setup_component(hass, "homeassistant", {})
with ( with patch.dict(os.environ, MOCK_ENVIRON):
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.hassio.HassIO.refresh_updates"
) as refresh_updates_mock,
):
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
# Initial refresh, no update refresh call # Initial refresh, no update refresh call
assert refresh_updates_mock.call_count == 0 supervisor_client.refresh_updates.assert_not_called()
with patch( async_fire_time_changed(hass, dt_util.now() + timedelta(minutes=20))
"homeassistant.components.hassio.HassIO.refresh_updates", await hass.async_block_till_done()
) as refresh_updates_mock:
async_fire_time_changed(hass, dt_util.now() + timedelta(minutes=20))
await hass.async_block_till_done()
# Scheduled refresh, no update refresh call # Scheduled refresh, no update refresh call
assert refresh_updates_mock.call_count == 0 supervisor_client.refresh_updates.assert_not_called()
with patch( await hass.services.async_call(
"homeassistant.components.hassio.HassIO.refresh_updates", "homeassistant",
) as refresh_updates_mock: "update_entity",
await hass.services.async_call( {
"homeassistant", "entity_id": [
"update_entity", "update.home_assistant_core_update",
{ "update.home_assistant_supervisor_update",
"entity_id": [ ]
"update.home_assistant_core_update", },
"update.home_assistant_supervisor_update", blocking=True,
] )
},
blocking=True,
)
# There is a REQUEST_REFRESH_DELAYs cooldown on the debouncer # There is a REQUEST_REFRESH_DELAYs cooldown on the debouncer
assert refresh_updates_mock.call_count == 0 supervisor_client.refresh_updates.assert_not_called()
async_fire_time_changed( async_fire_time_changed(
hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY) hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY)
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert refresh_updates_mock.call_count == 1 supervisor_client.refresh_updates.assert_called_once()
with patch( supervisor_client.refresh_updates.reset_mock()
"homeassistant.components.hassio.HassIO.refresh_updates", supervisor_client.refresh_updates.side_effect = SupervisorError("Unknown")
side_effect=HassioAPIError("Unknown"), await hass.services.async_call(
) as refresh_updates_mock: "homeassistant",
await hass.services.async_call( "update_entity",
"homeassistant", {
"update_entity", "entity_id": [
{ "update.home_assistant_core_update",
"entity_id": [ "update.home_assistant_supervisor_update",
"update.home_assistant_core_update", ]
"update.home_assistant_supervisor_update", },
] blocking=True,
}, )
blocking=True, # There is a REQUEST_REFRESH_DELAYs cooldown on the debouncer
) async_fire_time_changed(
# There is a REQUEST_REFRESH_DELAYs cooldown on the debouncer hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY)
async_fire_time_changed( )
hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY) await hass.async_block_till_done()
) supervisor_client.refresh_updates.assert_called_once()
await hass.async_block_till_done() assert "Error on Supervisor API: Unknown" in caplog.text
assert refresh_updates_mock.call_count == 1
assert "Error on Supervisor API: Unknown" in caplog.text
@pytest.mark.usefixtures("entity_registry_enabled_by_default", "addon_installed") @pytest.mark.usefixtures("entity_registry_enabled_by_default", "addon_installed")
async def test_coordinator_updates_stats_entities_enabled( async def test_coordinator_updates_stats_entities_enabled(
hass: HomeAssistant, hass: HomeAssistant,
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
supervisor_client: AsyncMock,
) -> None: ) -> None:
"""Test coordinator updates with stats entities enabled.""" """Test coordinator updates with stats entities enabled."""
await async_setup_component(hass, "homeassistant", {}) await async_setup_component(hass, "homeassistant", {})
with ( with patch.dict(os.environ, MOCK_ENVIRON):
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.hassio.HassIO.refresh_updates"
) as refresh_updates_mock,
):
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
# Initial refresh without stats # Initial refresh without stats
assert refresh_updates_mock.call_count == 0 supervisor_client.refresh_updates.assert_not_called()
# Refresh with stats once we know which ones are needed # Refresh with stats once we know which ones are needed
async_fire_time_changed( async_fire_time_changed(
hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY) hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY)
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert refresh_updates_mock.call_count == 1
with patch( supervisor_client.refresh_updates.assert_called_once()
"homeassistant.components.hassio.HassIO.refresh_updates",
) as refresh_updates_mock:
async_fire_time_changed(hass, dt_util.now() + timedelta(minutes=20))
await hass.async_block_till_done()
assert refresh_updates_mock.call_count == 0
with patch( supervisor_client.refresh_updates.reset_mock()
"homeassistant.components.hassio.HassIO.refresh_updates", async_fire_time_changed(hass, dt_util.now() + timedelta(minutes=20))
) as refresh_updates_mock: await hass.async_block_till_done()
await hass.services.async_call( supervisor_client.refresh_updates.assert_not_called()
"homeassistant",
"update_entity", await hass.services.async_call(
{ "homeassistant",
"entity_id": [ "update_entity",
"update.home_assistant_core_update", {
"update.home_assistant_supervisor_update", "entity_id": [
] "update.home_assistant_core_update",
}, "update.home_assistant_supervisor_update",
blocking=True, ]
) },
assert refresh_updates_mock.call_count == 0 blocking=True,
)
supervisor_client.refresh_updates.assert_not_called()
# There is a REQUEST_REFRESH_DELAYs cooldown on the debouncer # There is a REQUEST_REFRESH_DELAYs cooldown on the debouncer
async_fire_time_changed( async_fire_time_changed(
@ -1053,28 +1017,26 @@ async def test_coordinator_updates_stats_entities_enabled(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
with patch( supervisor_client.refresh_updates.reset_mock()
"homeassistant.components.hassio.HassIO.refresh_updates", supervisor_client.refresh_updates.side_effect = SupervisorError("Unknown")
side_effect=HassioAPIError("Unknown"), await hass.services.async_call(
) as refresh_updates_mock: "homeassistant",
await hass.services.async_call( "update_entity",
"homeassistant", {
"update_entity", "entity_id": [
{ "update.home_assistant_core_update",
"entity_id": [ "update.home_assistant_supervisor_update",
"update.home_assistant_core_update", ]
"update.home_assistant_supervisor_update", },
] blocking=True,
}, )
blocking=True, # There is a REQUEST_REFRESH_DELAYs cooldown on the debouncer
) async_fire_time_changed(
# There is a REQUEST_REFRESH_DELAYs cooldown on the debouncer hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY)
async_fire_time_changed( )
hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY) await hass.async_block_till_done()
) supervisor_client.refresh_updates.assert_called_once()
await hass.async_block_till_done() assert "Error on Supervisor API: Unknown" in caplog.text
assert refresh_updates_mock.call_count == 1
assert "Error on Supervisor API: Unknown" in caplog.text
@pytest.mark.parametrize( @pytest.mark.parametrize(

View File

@ -41,7 +41,6 @@ def mock_all(
def _install_default_mocks(aioclient_mock: AiohttpClientMocker): def _install_default_mocks(aioclient_mock: AiohttpClientMocker):
"""Install default mocks.""" """Install default mocks."""
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) 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"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/info", "http://127.0.0.1/info",
@ -147,7 +146,6 @@ def _install_default_mocks(aioclient_mock: AiohttpClientMocker):
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
) )
aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/resolution/info", "http://127.0.0.1/resolution/info",
json={ json={

View File

@ -8,7 +8,7 @@ from aiohasupervisor import SupervisorBadRequestError, SupervisorError
from aiohasupervisor.models import StoreAddonUpdate from aiohasupervisor.models import StoreAddonUpdate
import pytest import pytest
from homeassistant.components.hassio import DOMAIN, HassioAPIError from homeassistant.components.hassio import DOMAIN
from homeassistant.components.hassio.const import REQUEST_REFRESH_DELAY from homeassistant.components.hassio.const import REQUEST_REFRESH_DELAY
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
@ -32,7 +32,6 @@ def mock_all(
) -> None: ) -> None:
"""Mock all setup requests.""" """Mock all setup requests."""
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) 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"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/info", "http://127.0.0.1/info",
@ -150,7 +149,6 @@ def mock_all(
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
) )
aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/resolution/info", "http://127.0.0.1/resolution/info",
json={ json={
@ -239,9 +237,7 @@ async def test_update_addon(hass: HomeAssistant, update_addon: AsyncMock) -> Non
update_addon.assert_called_once_with("test", StoreAddonUpdate(backup=False)) update_addon.assert_called_once_with("test", StoreAddonUpdate(backup=False))
async def test_update_os( async def test_update_os(hass: HomeAssistant, supervisor_client: AsyncMock) -> None:
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test updating OS update entity.""" """Test updating OS update entity."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
@ -255,22 +251,17 @@ async def test_update_os(
assert result assert result
await hass.async_block_till_done() await hass.async_block_till_done()
aioclient_mock.post( supervisor_client.os.update.return_value = None
"http://127.0.0.1/os/update",
json={"result": "ok", "data": {}},
)
await hass.services.async_call( await hass.services.async_call(
"update", "update",
"install", "install",
{"entity_id": "update.home_assistant_operating_system_update"}, {"entity_id": "update.home_assistant_operating_system_update"},
blocking=True, blocking=True,
) )
supervisor_client.os.update.assert_called_once()
async def test_update_core( async def test_update_core(hass: HomeAssistant, supervisor_client: AsyncMock) -> None:
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test updating core update entity.""" """Test updating core update entity."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
@ -284,21 +275,18 @@ async def test_update_core(
assert result assert result
await hass.async_block_till_done() await hass.async_block_till_done()
aioclient_mock.post( supervisor_client.homeassistant.update.return_value = None
"http://127.0.0.1/core/update",
json={"result": "ok", "data": {}},
)
await hass.services.async_call( await hass.services.async_call(
"update", "update",
"install", "install",
{"entity_id": "update.home_assistant_os_update"}, {"entity_id": "update.home_assistant_core_update"},
blocking=True, blocking=True,
) )
supervisor_client.homeassistant.update.assert_called_once()
async def test_update_supervisor( async def test_update_supervisor(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker hass: HomeAssistant, supervisor_client: AsyncMock
) -> None: ) -> None:
"""Test updating supervisor update entity.""" """Test updating supervisor update entity."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
@ -313,17 +301,14 @@ async def test_update_supervisor(
assert result assert result
await hass.async_block_till_done() await hass.async_block_till_done()
aioclient_mock.post( supervisor_client.supervisor.update.return_value = None
"http://127.0.0.1/supervisor/update",
json={"result": "ok", "data": {}},
)
await hass.services.async_call( await hass.services.async_call(
"update", "update",
"install", "install",
{"entity_id": "update.home_assistant_supervisor_update"}, {"entity_id": "update.home_assistant_supervisor_update"},
blocking=True, blocking=True,
) )
supervisor_client.supervisor.update.assert_called_once()
async def test_update_addon_with_error( async def test_update_addon_with_error(
@ -353,7 +338,7 @@ async def test_update_addon_with_error(
async def test_update_os_with_error( async def test_update_os_with_error(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker hass: HomeAssistant, supervisor_client: AsyncMock
) -> None: ) -> None:
"""Test updating OS update entity with error.""" """Test updating OS update entity with error."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
@ -367,11 +352,7 @@ async def test_update_os_with_error(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
aioclient_mock.post( supervisor_client.os.update.side_effect = SupervisorError
"http://127.0.0.1/os/update",
exc=HassioAPIError,
)
with pytest.raises( with pytest.raises(
HomeAssistantError, match=r"^Error updating Home Assistant Operating System:" HomeAssistantError, match=r"^Error updating Home Assistant Operating System:"
): ):
@ -384,7 +365,7 @@ async def test_update_os_with_error(
async def test_update_supervisor_with_error( async def test_update_supervisor_with_error(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker hass: HomeAssistant, supervisor_client: AsyncMock
) -> None: ) -> None:
"""Test updating supervisor update entity with error.""" """Test updating supervisor update entity with error."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
@ -398,11 +379,7 @@ async def test_update_supervisor_with_error(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
aioclient_mock.post( supervisor_client.supervisor.update.side_effect = SupervisorError
"http://127.0.0.1/supervisor/update",
exc=HassioAPIError,
)
with pytest.raises( with pytest.raises(
HomeAssistantError, match=r"^Error updating Home Assistant Supervisor:" HomeAssistantError, match=r"^Error updating Home Assistant Supervisor:"
): ):
@ -415,7 +392,7 @@ async def test_update_supervisor_with_error(
async def test_update_core_with_error( async def test_update_core_with_error(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker hass: HomeAssistant, supervisor_client: AsyncMock
) -> None: ) -> None:
"""Test updating core update entity with error.""" """Test updating core update entity with error."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
@ -429,11 +406,7 @@ async def test_update_core_with_error(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
aioclient_mock.post( supervisor_client.homeassistant.update.side_effect = SupervisorError
"http://127.0.0.1/core/update",
exc=HassioAPIError,
)
with pytest.raises( with pytest.raises(
HomeAssistantError, match=r"^Error updating Home Assistant Core:" HomeAssistantError, match=r"^Error updating Home Assistant Core:"
): ):

View File

@ -1,5 +1,7 @@
"""Test websocket API.""" """Test websocket API."""
from unittest.mock import AsyncMock
import pytest import pytest
from homeassistant.components.hassio.const import ( from homeassistant.components.hassio.const import (
@ -23,10 +25,11 @@ from tests.typing import WebSocketGenerator
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def mock_all(aioclient_mock: AiohttpClientMocker) -> None: def mock_all(
aioclient_mock: AiohttpClientMocker, supervisor_is_connected: AsyncMock
) -> None:
"""Mock all setup requests.""" """Mock all setup requests."""
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) 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"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/info", "http://127.0.0.1/info",

View File

@ -3,7 +3,7 @@
from http import HTTPStatus from http import HTTPStatus
from ipaddress import ip_address from ipaddress import ip_address
import os import os
from unittest.mock import Mock, mock_open, patch from unittest.mock import AsyncMock, Mock, mock_open, patch
from aiohttp import web from aiohttp import web
from aiohttp.web_exceptions import HTTPUnauthorized from aiohttp.web_exceptions import HTTPUnauthorized
@ -34,14 +34,10 @@ BANNED_IPS_WITH_SUPERVISOR = [*BANNED_IPS, SUPERVISOR_IP]
@pytest.fixture(name="hassio_env") @pytest.fixture(name="hassio_env")
def hassio_env_fixture(): def hassio_env_fixture(supervisor_is_connected: AsyncMock):
"""Fixture to inject hassio env.""" """Fixture to inject hassio env."""
with ( with (
patch.dict(os.environ, {"SUPERVISOR": "127.0.0.1"}), patch.dict(os.environ, {"SUPERVISOR": "127.0.0.1"}),
patch(
"homeassistant.components.hassio.HassIO.is_connected",
return_value={"result": "ok", "data": {}},
),
patch.dict(os.environ, {"SUPERVISOR_TOKEN": "123456"}), patch.dict(os.environ, {"SUPERVISOR_TOKEN": "123456"}),
): ):
yield yield

View File

@ -5,13 +5,15 @@ from __future__ import annotations
from collections.abc import Generator from collections.abc import Generator
from ipaddress import ip_address from ipaddress import ip_address
from unittest.mock import AsyncMock, MagicMock, call, patch from unittest.mock import AsyncMock, MagicMock, call, patch
from uuid import uuid4
from aiohasupervisor import SupervisorError from aiohasupervisor import SupervisorError
from aiohasupervisor.models import Discovery
from matter_server.client.exceptions import CannotConnect, InvalidServerVersion from matter_server.client.exceptions import CannotConnect, InvalidServerVersion
import pytest import pytest
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.hassio import HassioAPIError, HassioServiceInfo from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.components.matter.const import ADDON_SLUG, DOMAIN from homeassistant.components.matter.const import ADDON_SLUG, DOMAIN
from homeassistant.components.zeroconf import ZeroconfServiceInfo from homeassistant.components.zeroconf import ZeroconfServiceInfo
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -290,7 +292,19 @@ async def test_zeroconf_discovery_not_onboarded_not_supervisor(
@pytest.mark.parametrize("zeroconf_info", [ZEROCONF_INFO_TCP, ZEROCONF_INFO_UDP]) @pytest.mark.parametrize("zeroconf_info", [ZEROCONF_INFO_TCP, ZEROCONF_INFO_UDP])
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_zeroconf_not_onboarded_already_discovered( async def test_zeroconf_not_onboarded_already_discovered(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -328,7 +342,19 @@ async def test_zeroconf_not_onboarded_already_discovered(
@pytest.mark.parametrize("zeroconf_info", [ZEROCONF_INFO_TCP, ZEROCONF_INFO_UDP]) @pytest.mark.parametrize("zeroconf_info", [ZEROCONF_INFO_TCP, ZEROCONF_INFO_UDP])
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_zeroconf_not_onboarded_running( async def test_zeroconf_not_onboarded_running(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -360,7 +386,19 @@ async def test_zeroconf_not_onboarded_running(
@pytest.mark.parametrize("zeroconf_info", [ZEROCONF_INFO_TCP, ZEROCONF_INFO_UDP]) @pytest.mark.parametrize("zeroconf_info", [ZEROCONF_INFO_TCP, ZEROCONF_INFO_UDP])
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_zeroconf_not_onboarded_installed( async def test_zeroconf_not_onboarded_installed(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -394,7 +432,19 @@ async def test_zeroconf_not_onboarded_installed(
@pytest.mark.parametrize("zeroconf_info", [ZEROCONF_INFO_TCP, ZEROCONF_INFO_UDP]) @pytest.mark.parametrize("zeroconf_info", [ZEROCONF_INFO_TCP, ZEROCONF_INFO_UDP])
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_zeroconf_not_onboarded_not_installed( async def test_zeroconf_not_onboarded_not_installed(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -431,7 +481,19 @@ async def test_zeroconf_not_onboarded_not_installed(
assert setup_entry.call_count == 1 assert setup_entry.call_count == 1
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_supervisor_discovery( async def test_supervisor_discovery(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -469,7 +531,19 @@ async def test_supervisor_discovery(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("discovery_info", "error"), ("discovery_info", "error"),
[({"config": ADDON_DISCOVERY_INFO}, SupervisorError())], [
(
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
SupervisorError(),
)
],
) )
async def test_supervisor_discovery_addon_info_failed( async def test_supervisor_discovery_addon_info_failed(
hass: HomeAssistant, hass: HomeAssistant,
@ -502,7 +576,19 @@ async def test_supervisor_discovery_addon_info_failed(
assert result["reason"] == "addon_info_failed" assert result["reason"] == "addon_info_failed"
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_clean_supervisor_discovery_on_user_create( async def test_clean_supervisor_discovery_on_user_create(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -793,7 +879,19 @@ async def test_not_addon(
assert setup_entry.call_count == 1 assert setup_entry.call_count == 1
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_running( async def test_addon_running(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -839,8 +937,15 @@ async def test_addon_running(
), ),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
HassioAPIError(), Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
SupervisorError(),
None, None,
None, None,
"addon_get_discovery_info_failed", "addon_get_discovery_info_failed",
@ -848,7 +953,14 @@ async def test_addon_running(
False, False,
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
None, None,
CannotConnect(Exception("Boom")), CannotConnect(Exception("Boom")),
None, None,
@ -857,7 +969,7 @@ async def test_addon_running(
True, True,
), ),
( (
None, [],
None, None,
None, None,
None, None,
@ -866,7 +978,14 @@ async def test_addon_running(
False, False,
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
None, None,
None, None,
SupervisorError(), SupervisorError(),
@ -925,8 +1044,15 @@ async def test_addon_running_failures(
), ),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
HassioAPIError(), Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
SupervisorError(),
None, None,
None, None,
"addon_get_discovery_info_failed", "addon_get_discovery_info_failed",
@ -934,7 +1060,14 @@ async def test_addon_running_failures(
False, False,
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
None, None,
CannotConnect(Exception("Boom")), CannotConnect(Exception("Boom")),
None, None,
@ -943,7 +1076,7 @@ async def test_addon_running_failures(
True, True,
), ),
( (
None, [],
None, None,
None, None,
None, None,
@ -952,7 +1085,14 @@ async def test_addon_running_failures(
False, False,
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
None, None,
None, None,
SupervisorError(), SupervisorError(),
@ -996,7 +1136,19 @@ async def test_addon_running_failures_zeroconf(
assert result["reason"] == abort_reason assert result["reason"] == abort_reason
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_running_already_configured( async def test_addon_running_already_configured(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -1034,7 +1186,19 @@ async def test_addon_running_already_configured(
assert setup_entry.call_count == 1 assert setup_entry.call_count == 1
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_installed( async def test_addon_installed(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -1084,21 +1248,35 @@ async def test_addon_installed(
), ),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
SupervisorError(), SupervisorError(),
None, None,
False, False,
False, False,
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
None, None,
CannotConnect(Exception("Boom")), CannotConnect(Exception("Boom")),
True, True,
True, True,
), ),
( (
None, [],
None, None,
None, None,
True, True,
@ -1159,21 +1337,35 @@ async def test_addon_installed_failures(
), ),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
SupervisorError(), SupervisorError(),
None, None,
False, False,
False, False,
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
None, None,
CannotConnect(Exception("Boom")), CannotConnect(Exception("Boom")),
True, True,
True, True,
), ),
( (
None, [],
None, None,
None, None,
True, True,
@ -1213,7 +1405,19 @@ async def test_addon_installed_failures_zeroconf(
assert result["reason"] == "addon_start_failed" assert result["reason"] == "addon_start_failed"
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_installed_already_configured( async def test_addon_installed_already_configured(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -1259,7 +1463,19 @@ async def test_addon_installed_already_configured(
assert setup_entry.call_count == 1 assert setup_entry.call_count == 1
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_not_installed( async def test_addon_not_installed(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,
@ -1368,7 +1584,19 @@ async def test_addon_not_installed_failures_zeroconf(
assert result["reason"] == "addon_install_failed" assert result["reason"] == "addon_install_failed"
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_matter_server",
service="matter",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_not_installed_already_configured( async def test_addon_not_installed_already_configured(
hass: HomeAssistant, hass: HomeAssistant,
supervisor: MagicMock, supervisor: MagicMock,

View File

@ -9,6 +9,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
from uuid import uuid4 from uuid import uuid4
from aiohasupervisor import SupervisorError from aiohasupervisor import SupervisorError
from aiohasupervisor.models import Discovery
import pytest import pytest
import voluptuous as vol import voluptuous as vol
@ -528,7 +529,19 @@ async def test_hassio_cannot_connect(
@pytest.mark.usefixtures( @pytest.mark.usefixtures(
"mqtt_client_mock", "supervisor", "addon_info", "addon_running" "mqtt_client_mock", "supervisor", "addon_info", "addon_running"
) )
@pytest.mark.parametrize("discovery_info", [{"config": ADD_ON_DISCOVERY_INFO.copy()}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_mosquitto",
service="mqtt",
uuid=uuid4(),
config=ADD_ON_DISCOVERY_INFO.copy(),
)
]
],
)
async def test_addon_flow_with_supervisor_addon_running( async def test_addon_flow_with_supervisor_addon_running(
hass: HomeAssistant, hass: HomeAssistant,
mock_try_connection_success: MagicMock, mock_try_connection_success: MagicMock,
@ -570,7 +583,19 @@ async def test_addon_flow_with_supervisor_addon_running(
@pytest.mark.usefixtures( @pytest.mark.usefixtures(
"mqtt_client_mock", "supervisor", "addon_info", "addon_installed", "start_addon" "mqtt_client_mock", "supervisor", "addon_info", "addon_installed", "start_addon"
) )
@pytest.mark.parametrize("discovery_info", [{"config": ADD_ON_DISCOVERY_INFO.copy()}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_mosquitto",
service="mqtt",
uuid=uuid4(),
config=ADD_ON_DISCOVERY_INFO.copy(),
)
]
],
)
async def test_addon_flow_with_supervisor_addon_installed( async def test_addon_flow_with_supervisor_addon_installed(
hass: HomeAssistant, hass: HomeAssistant,
mock_try_connection_success: MagicMock, mock_try_connection_success: MagicMock,
@ -625,7 +650,19 @@ async def test_addon_flow_with_supervisor_addon_installed(
@pytest.mark.usefixtures( @pytest.mark.usefixtures(
"mqtt_client_mock", "supervisor", "addon_info", "addon_running" "mqtt_client_mock", "supervisor", "addon_info", "addon_running"
) )
@pytest.mark.parametrize("discovery_info", [{"config": ADD_ON_DISCOVERY_INFO.copy()}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_mosquitto",
service="mqtt",
uuid=uuid4(),
config=ADD_ON_DISCOVERY_INFO.copy(),
)
]
],
)
async def test_addon_flow_with_supervisor_addon_running_connection_fails( async def test_addon_flow_with_supervisor_addon_running_connection_fails(
hass: HomeAssistant, hass: HomeAssistant,
mock_try_connection: MagicMock, mock_try_connection: MagicMock,
@ -780,7 +817,19 @@ async def test_addon_info_error(
"install_addon", "install_addon",
"start_addon", "start_addon",
) )
@pytest.mark.parametrize("discovery_info", [{"config": ADD_ON_DISCOVERY_INFO.copy()}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_mosquitto",
service="mqtt",
uuid=uuid4(),
config=ADD_ON_DISCOVERY_INFO.copy(),
)
]
],
)
async def test_addon_flow_with_supervisor_addon_not_installed( async def test_addon_flow_with_supervisor_addon_not_installed(
hass: HomeAssistant, hass: HomeAssistant,
mock_try_connection_success: MagicMock, mock_try_connection_success: MagicMock,
@ -1576,7 +1625,19 @@ async def test_step_reauth(
await hass.async_block_till_done() await hass.async_block_till_done()
@pytest.mark.parametrize("discovery_info", [{"config": ADD_ON_DISCOVERY_INFO.copy()}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_mosquitto",
service="mqtt",
uuid=uuid4(),
config=ADD_ON_DISCOVERY_INFO.copy(),
)
]
],
)
@pytest.mark.usefixtures( @pytest.mark.usefixtures(
"mqtt_client_mock", "mock_reload_after_entry_update", "supervisor", "addon_running" "mqtt_client_mock", "mock_reload_after_entry_update", "supervisor", "addon_running"
) )
@ -1625,8 +1686,30 @@ async def test_step_hassio_reauth(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("discovery_info", "discovery_info_side_effect", "broker"), ("discovery_info", "discovery_info_side_effect", "broker"),
[ [
({"config": ADD_ON_DISCOVERY_INFO.copy()}, AddonError, "core-mosquitto"), (
({"config": ADD_ON_DISCOVERY_INFO.copy()}, None, "broker-not-addon"), [
Discovery(
addon="core_mosquitto",
service="mqtt",
uuid=uuid4(),
config=ADD_ON_DISCOVERY_INFO.copy(),
)
],
AddonError,
"core-mosquitto",
),
(
[
Discovery(
addon="core_mosquitto",
service="mqtt",
uuid=uuid4(),
config=ADD_ON_DISCOVERY_INFO.copy(),
)
],
None,
"broker-not-addon",
),
], ],
) )
@pytest.mark.usefixtures( @pytest.mark.usefixtures(

View File

@ -5,7 +5,7 @@ from collections.abc import AsyncGenerator
from http import HTTPStatus from http import HTTPStatus
import os import os
from typing import Any from typing import Any
from unittest.mock import Mock, patch from unittest.mock import AsyncMock, Mock, patch
import pytest import pytest
@ -69,7 +69,9 @@ async def no_rpi_fixture(
@pytest.fixture(name="mock_supervisor") @pytest.fixture(name="mock_supervisor")
async def mock_supervisor_fixture( async def mock_supervisor_fixture(
aioclient_mock: AiohttpClientMocker, store_info aioclient_mock: AiohttpClientMocker,
store_info: AsyncMock,
supervisor_is_connected: AsyncMock,
) -> AsyncGenerator[None]: ) -> AsyncGenerator[None]:
"""Mock supervisor.""" """Mock supervisor."""
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"})
@ -99,10 +101,6 @@ async def mock_supervisor_fixture(
) )
with ( with (
patch.dict(os.environ, {"SUPERVISOR": "127.0.0.1"}), patch.dict(os.environ, {"SUPERVISOR": "127.0.0.1"}),
patch(
"homeassistant.components.hassio.HassIO.is_connected",
return_value=True,
),
patch( patch(
"homeassistant.components.hassio.HassIO.get_info", "homeassistant.components.hassio.HassIO.get_info",
return_value={}, return_value={},

View File

@ -6,9 +6,10 @@ from copy import copy
from ipaddress import ip_address from ipaddress import ip_address
from typing import Any from typing import Any
from unittest.mock import AsyncMock, MagicMock, call, patch from unittest.mock import AsyncMock, MagicMock, call, patch
from uuid import uuid4
from aiohasupervisor import SupervisorError from aiohasupervisor import SupervisorError
from aiohasupervisor.models import AddonsOptions from aiohasupervisor.models import AddonsOptions, Discovery
import aiohttp import aiohttp
import pytest import pytest
from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_common import ListPortInfo
@ -16,7 +17,7 @@ from zwave_js_server.version import VersionInfo
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components import usb from homeassistant.components import usb
from homeassistant.components.hassio import HassioAPIError, HassioServiceInfo from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.components.zeroconf import ZeroconfServiceInfo from homeassistant.components.zeroconf import ZeroconfServiceInfo
from homeassistant.components.zwave_js.config_flow import SERVER_VERSION_TIMEOUT, TITLE from homeassistant.components.zwave_js.config_flow import SERVER_VERSION_TIMEOUT, TITLE
from homeassistant.components.zwave_js.const import ADDON_SLUG, DOMAIN from homeassistant.components.zwave_js.const import ADDON_SLUG, DOMAIN
@ -555,7 +556,19 @@ async def test_abort_hassio_discovery_for_other_addon(
assert result2["reason"] == "not_zwave_js_addon" assert result2["reason"] == "not_zwave_js_addon"
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_usb_discovery( async def test_usb_discovery(
hass: HomeAssistant, hass: HomeAssistant,
supervisor, supervisor,
@ -653,7 +666,19 @@ async def test_usb_discovery(
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_usb_discovery_addon_not_running( async def test_usb_discovery_addon_not_running(
hass: HomeAssistant, hass: HomeAssistant,
supervisor, supervisor,
@ -1090,7 +1115,19 @@ async def test_not_addon(hass: HomeAssistant, supervisor) -> None:
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_running( async def test_addon_running(
hass: HomeAssistant, hass: HomeAssistant,
supervisor, supervisor,
@ -1156,28 +1193,49 @@ async def test_addon_running(
), ),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
HassioAPIError(), Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
SupervisorError(),
None, None,
None, None,
"addon_get_discovery_info_failed", "addon_get_discovery_info_failed",
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
None, None,
TimeoutError, TimeoutError,
None, None,
"cannot_connect", "cannot_connect",
), ),
( (
None, [],
None, None,
None, None,
None, None,
"addon_get_discovery_info_failed", "addon_get_discovery_info_failed",
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
None, None,
None, None,
SupervisorError(), SupervisorError(),
@ -1212,7 +1270,19 @@ async def test_addon_running_failures(
assert result["reason"] == abort_reason assert result["reason"] == abort_reason
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_running_already_configured( async def test_addon_running_already_configured(
hass: HomeAssistant, hass: HomeAssistant,
supervisor, supervisor,
@ -1271,7 +1341,19 @@ async def test_addon_running_already_configured(
assert entry.data["lr_s2_authenticated_key"] == "new321" assert entry.data["lr_s2_authenticated_key"] == "new321"
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_installed( async def test_addon_installed(
hass: HomeAssistant, hass: HomeAssistant,
supervisor, supervisor,
@ -1363,7 +1445,17 @@ async def test_addon_installed(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("discovery_info", "start_addon_side_effect"), ("discovery_info", "start_addon_side_effect"),
[({"config": ADDON_DISCOVERY_INFO}, SupervisorError())], [
(
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
),
SupervisorError(),
)
],
) )
async def test_addon_installed_start_failure( async def test_addon_installed_start_failure(
hass: HomeAssistant, hass: HomeAssistant,
@ -1434,11 +1526,18 @@ async def test_addon_installed_start_failure(
("discovery_info", "server_version_side_effect"), ("discovery_info", "server_version_side_effect"),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
TimeoutError, TimeoutError,
), ),
( (
None, [],
None, None,
), ),
], ],
@ -1510,7 +1609,19 @@ async def test_addon_installed_failures(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("set_addon_options_side_effect", "discovery_info"), ("set_addon_options_side_effect", "discovery_info"),
[(SupervisorError(), {"config": ADDON_DISCOVERY_INFO})], [
(
SupervisorError(),
[
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
)
],
) )
async def test_addon_installed_set_options_failure( async def test_addon_installed_set_options_failure(
hass: HomeAssistant, hass: HomeAssistant,
@ -1571,7 +1682,19 @@ async def test_addon_installed_set_options_failure(
assert start_addon.call_count == 0 assert start_addon.call_count == 0
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_installed_already_configured( async def test_addon_installed_already_configured(
hass: HomeAssistant, hass: HomeAssistant,
supervisor, supervisor,
@ -1662,7 +1785,19 @@ async def test_addon_installed_already_configured(
assert entry.data["lr_s2_authenticated_key"] == "new321" assert entry.data["lr_s2_authenticated_key"] == "new321"
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) @pytest.mark.parametrize(
"discovery_info",
[
[
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
]
],
)
async def test_addon_not_installed( async def test_addon_not_installed(
hass: HomeAssistant, hass: HomeAssistant,
supervisor, supervisor,
@ -1887,7 +2022,14 @@ async def test_options_not_addon(
), ),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
{}, {},
{ {
"device": "/test", "device": "/test",
@ -1913,7 +2055,14 @@ async def test_options_not_addon(
0, 0,
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
{"use_addon": True}, {"use_addon": True},
{ {
"device": "/test", "device": "/test",
@ -2033,7 +2182,14 @@ async def test_options_addon_running(
("discovery_info", "entry_data", "old_addon_options", "new_addon_options"), ("discovery_info", "entry_data", "old_addon_options", "new_addon_options"),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
{}, {},
{ {
"device": "/test", "device": "/test",
@ -2160,7 +2316,14 @@ async def different_device_server_version(*args):
), ),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
{}, {},
{ {
"device": "/test", "device": "/test",
@ -2189,7 +2352,14 @@ async def different_device_server_version(*args):
different_device_server_version, different_device_server_version,
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
{}, {},
{ {
"device": "/test", "device": "/test",
@ -2318,7 +2488,14 @@ async def test_options_different_device(
), ),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
{}, {},
{ {
"device": "/test", "device": "/test",
@ -2347,7 +2524,14 @@ async def test_options_different_device(
[SupervisorError(), None], [SupervisorError(), None],
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
{}, {},
{ {
"device": "/test", "device": "/test",
@ -2477,7 +2661,14 @@ async def test_options_addon_restart_failed(
), ),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
{}, {},
{ {
"device": "/test", "device": "/test",
@ -2570,7 +2761,14 @@ async def test_options_addon_running_server_info_failure(
), ),
[ [
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
{}, {},
{ {
"device": "/test", "device": "/test",
@ -2596,7 +2794,14 @@ async def test_options_addon_running_server_info_failure(
0, 0,
), ),
( (
{"config": ADDON_DISCOVERY_INFO}, [
Discovery(
addon="core_zwave_js",
service="zwave_js",
uuid=uuid4(),
config=ADDON_DISCOVERY_INFO,
)
],
{"use_addon": True}, {"use_addon": True},
{ {
"device": "/test", "device": "/test",