Replace calls to ingress panels API with aiohasupervisor (#166400)

This commit is contained in:
Mike Degatano
2026-03-25 16:42:32 +01:00
committed by GitHub
parent 71be2073eb
commit 15d0a01833
19 changed files with 119 additions and 353 deletions
+1 -1
View File
@@ -666,7 +666,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
# Init add-on ingress panels
panels_task = hass.async_create_task(
async_setup_addon_panel(hass, hassio), eager_start=True
async_setup_addon_panel(hass), eager_start=True
)
# Make sure to await the update_info task before
+19 -25
View File
@@ -2,24 +2,23 @@
from http import HTTPStatus
import logging
from typing import Any
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import IngressPanel
from aiohttp import web
from homeassistant.components import frontend
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import ATTR_ICON
from homeassistant.core import HomeAssistant
from .const import ATTR_ADMIN, ATTR_ENABLE, ATTR_PANELS, ATTR_TITLE
from .handler import HassIO, HassioAPIError
from .handler import get_supervisor_client
_LOGGER = logging.getLogger(__name__)
async def async_setup_addon_panel(hass: HomeAssistant, hassio: HassIO) -> None:
async def async_setup_addon_panel(hass: HomeAssistant) -> None:
"""Add-on Ingress Panel setup."""
hassio_addon_panel = HassIOAddonPanel(hass, hassio)
hassio_addon_panel = HassIOAddonPanel(hass)
hass.http.register_view(hassio_addon_panel)
# If panels are exists
@@ -28,11 +27,8 @@ async def async_setup_addon_panel(hass: HomeAssistant, hassio: HassIO) -> None:
# Register available panels
for addon, data in panels.items():
if not data[ATTR_ENABLE]:
if not data.enable:
continue
# _register_panel never suspends and is only
# a coroutine because it would be a breaking change
# to make it a normal function
_register_panel(hass, addon, data)
@@ -42,23 +38,22 @@ class HassIOAddonPanel(HomeAssistantView):
name = "api:hassio_push:panel"
url = "/api/hassio_push/panel/{addon}"
def __init__(self, hass: HomeAssistant, hassio: HassIO) -> None:
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize WebView."""
self.hass = hass
self.hassio = hassio
self.client = get_supervisor_client(hass)
async def post(self, request: web.Request, addon: str) -> web.Response:
"""Handle new add-on panel requests."""
panels = await self.get_panels()
# Panel exists for add-on slug
if addon not in panels or not panels[addon][ATTR_ENABLE]:
_LOGGER.error("Panel is not enable for %s", addon)
if addon not in panels or not panels[addon].enable:
_LOGGER.error("Panel is not enabled for %s", addon)
return web.Response(status=HTTPStatus.BAD_REQUEST)
data = panels[addon]
# Register panel
_register_panel(self.hass, addon, data)
_register_panel(self.hass, addon, panels[addon])
return web.Response()
async def delete(self, request: web.Request, addon: str) -> web.Response:
@@ -66,24 +61,23 @@ class HassIOAddonPanel(HomeAssistantView):
frontend.async_remove_panel(self.hass, addon)
return web.Response()
async def get_panels(self) -> dict:
async def get_panels(self) -> dict[str, IngressPanel]:
"""Return panels add-on info data."""
try:
data = await self.hassio.get_ingress_panels()
return data[ATTR_PANELS]
except HassioAPIError as err:
return await self.client.ingress.panels()
except SupervisorError as err:
_LOGGER.error("Can't read panel info: %s", err)
return {}
def _register_panel(hass: HomeAssistant, addon: str, data: dict[str, Any]):
"""Init coroutine to register the panel."""
def _register_panel(hass: HomeAssistant, addon: str, data: IngressPanel):
"""Helper to register the panel."""
frontend.async_register_built_in_panel(
hass,
"app",
frontend_url_path=addon,
sidebar_title=data[ATTR_TITLE],
sidebar_icon=data[ATTR_ICON],
require_admin=data[ATTR_ADMIN],
sidebar_title=data.title,
sidebar_icon=data.icon,
require_admin=data.admin,
config={"addon": addon},
)
@@ -3,7 +3,6 @@
from __future__ import annotations
import asyncio
from collections.abc import Callable, Coroutine
from http import HTTPStatus
import logging
import os
@@ -28,21 +27,6 @@ class HassioAPIError(RuntimeError):
"""Return if a API trow a error."""
def api_data[**_P](
funct: Callable[_P, Coroutine[Any, Any, dict[str, Any]]],
) -> Callable[_P, Coroutine[Any, Any, Any]]:
"""Return data of an api."""
async def _wrapper(*argv: _P.args, **kwargs: _P.kwargs) -> Any:
"""Wrap function."""
data = await funct(*argv, **kwargs)
if data["result"] == "ok":
return data["data"]
raise HassioAPIError(data["message"])
return _wrapper
class HassIO:
"""Small API wrapper for Hass.io."""
@@ -64,14 +48,6 @@ class HassIO:
"""Return base url for Supervisor."""
return self._base_url
@api_data
def get_ingress_panels(self) -> Coroutine:
"""Return data for Add-on ingress panels.
This method returns a coroutine.
"""
return self.send_command("/ingress/panels", method="get")
async def send_command(
self,
command: str,
+13
View File
@@ -20,6 +20,7 @@ from aiohasupervisor.backups import BackupsClient
from aiohasupervisor.discovery import DiscoveryClient
from aiohasupervisor.homeassistant import HomeAssistantClient
from aiohasupervisor.host import HostClient
from aiohasupervisor.ingress import IngressClient
from aiohasupervisor.jobs import JobsClient
from aiohasupervisor.models import (
AddonStage,
@@ -781,6 +782,13 @@ def supervisor_stats_fixture(supervisor_client: AsyncMock) -> AsyncMock:
return supervisor_client.supervisor.stats
@pytest.fixture(name="ingress_panels")
def ingress_panels_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock ingress panels API from supervisor."""
supervisor_client.ingress.panels.return_value = {}
return supervisor_client.ingress.panels
@pytest.fixture(name="supervisor_client")
def supervisor_client() -> Generator[AsyncMock]:
"""Mock the supervisor client."""
@@ -790,6 +798,7 @@ def supervisor_client() -> Generator[AsyncMock]:
supervisor_client.discovery = AsyncMock(spec=DiscoveryClient)
supervisor_client.homeassistant = AsyncMock(spec=HomeAssistantClient)
supervisor_client.host = AsyncMock(spec=HostClient)
supervisor_client.ingress = AsyncMock(spec=IngressClient)
supervisor_client.jobs = AsyncMock(spec=JobsClient)
supervisor_client.jobs.info.return_value = JobsInfo(ignore_conditions=[], jobs=[])
supervisor_client.mounts = AsyncMock(spec=MountsClient)
@@ -815,6 +824,10 @@ def supervisor_client() -> Generator[AsyncMock]:
"homeassistant.components.hassio.addon_manager.get_supervisor_client",
return_value=supervisor_client,
),
patch(
"homeassistant.components.hassio.addon_panel.get_supervisor_client",
return_value=supervisor_client,
),
patch(
"homeassistant.components.hassio.backup.get_supervisor_client",
return_value=supervisor_client,
+3 -17
View File
@@ -1,6 +1,6 @@
"""Fixtures for Hass.io."""
from collections.abc import Generator
from collections.abc import AsyncGenerator, Generator
from dataclasses import replace
import os
import re
@@ -17,7 +17,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from . import SUPERVISOR_TOKEN
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import ClientSessionGenerator
@@ -67,9 +66,7 @@ async def hassio_client_supervisor(
@pytest.fixture
async def hassio_handler(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> Generator[HassIO]:
async def hassio_handler(hass: HomeAssistant) -> AsyncGenerator[HassIO]:
"""Create mock hassio handler."""
with patch.dict(os.environ, {"SUPERVISOR_TOKEN": SUPERVISOR_TOKEN}):
yield HassIO(hass.loop, async_get_clientsession(hass), "127.0.0.1")
@@ -77,7 +74,6 @@ async def hassio_handler(
@pytest.fixture
def all_setup_requests(
aioclient_mock: AiohttpClientMocker,
request: pytest.FixtureRequest,
addon_installed: AsyncMock,
store_info: AsyncMock,
@@ -93,18 +89,13 @@ def all_setup_requests(
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""Mock all setup requests."""
include_addons = hasattr(request, "param") and request.param.get(
"include_addons", False
)
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
if include_addons:
addons_list.return_value[0] = replace(
addons_list.return_value[0],
@@ -174,8 +165,3 @@ def all_setup_requests(
)
addon_stats.side_effect = mock_addon_stats
aioclient_mock.get(
"http://127.0.0.1/jobs/info",
json={"result": "ok", "data": {"ignore_conditions": [], "jobs": []}},
)
+29 -82
View File
@@ -3,57 +3,35 @@
from http import HTTPStatus
from unittest.mock import AsyncMock, patch
from aiohasupervisor.models import IngressPanel
import pytest
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import ClientSessionGenerator
@pytest.fixture(autouse=True)
def mock_all(
aioclient_mock: AiohttpClientMocker, supervisor_is_connected: AsyncMock
supervisor_is_connected: AsyncMock,
homeassistant_info: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""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/supervisor/options", json={"result": "ok"})
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info",
json={"result": "ok", "data": {"last_version": "10.0"}},
)
@pytest.mark.usefixtures("hassio_env")
async def test_hassio_addon_panel_startup(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant, ingress_panels: AsyncMock
) -> None:
"""Test startup and panel setup after event."""
aioclient_mock.get(
"http://127.0.0.1/ingress/panels",
json={
"result": "ok",
"data": {
"panels": {
"test1": {
"enable": True,
"title": "Test",
"icon": "mdi:test",
"admin": False,
},
"test2": {
"enable": False,
"title": "Test 2",
"icon": "mdi:test2",
"admin": True,
},
}
},
},
)
assert aioclient_mock.call_count == 0
ingress_panels.return_value = {
"test1": IngressPanel(enable=True, title="Test", icon="mdi:test", admin=False),
"test2": IngressPanel(
enable=False, title="Test 2", icon="mdi:test2", admin=True
),
}
with patch(
"homeassistant.components.hassio.addon_panel._register_panel",
@@ -61,46 +39,26 @@ async def test_hassio_addon_panel_startup(
await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done()
assert aioclient_mock.call_count == 1
ingress_panels.assert_called_once()
assert mock_panel.called
mock_panel.assert_called_with(
hass,
"test1",
{"enable": True, "title": "Test", "icon": "mdi:test", "admin": False},
IngressPanel(enable=True, title="Test", icon="mdi:test", admin=False),
)
@pytest.mark.usefixtures("hassio_env")
async def test_hassio_addon_panel_api(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hass_client: ClientSessionGenerator,
hass: HomeAssistant, hass_client: ClientSessionGenerator, ingress_panels: AsyncMock
) -> None:
"""Test panel api after event."""
aioclient_mock.get(
"http://127.0.0.1/ingress/panels",
json={
"result": "ok",
"data": {
"panels": {
"test1": {
"enable": True,
"title": "Test",
"icon": "mdi:test",
"admin": False,
},
"test2": {
"enable": False,
"title": "Test 2",
"icon": "mdi:test2",
"admin": True,
},
}
},
},
)
assert aioclient_mock.call_count == 0
ingress_panels.return_value = {
"test1": IngressPanel(enable=True, title="Test", icon="mdi:test", admin=False),
"test2": IngressPanel(
enable=False, title="Test 2", icon="mdi:test2", admin=True
),
}
with patch(
"homeassistant.components.hassio.addon_panel._register_panel",
@@ -108,12 +66,12 @@ async def test_hassio_addon_panel_api(
await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done()
assert aioclient_mock.call_count == 1
ingress_panels.assert_called_once()
assert mock_panel.called
mock_panel.assert_called_with(
hass,
"test1",
{"enable": True, "title": "Test", "icon": "mdi:test", "admin": False},
IngressPanel(enable=True, title="Test", icon="mdi:test", admin=False),
)
hass_client = await hass_client()
@@ -128,31 +86,20 @@ async def test_hassio_addon_panel_api(
mock_panel.assert_called_with(
hass,
"test1",
{"enable": True, "title": "Test", "icon": "mdi:test", "admin": False},
IngressPanel(enable=True, title="Test", icon="mdi:test", admin=False),
)
@pytest.mark.usefixtures("hassio_env")
async def test_hassio_addon_panel_registration(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant, ingress_panels: AsyncMock
) -> None:
"""Test panel registration calls frontend.async_register_built_in_panel."""
aioclient_mock.get(
"http://127.0.0.1/ingress/panels",
json={
"result": "ok",
"data": {
"panels": {
"test_addon": {
"enable": True,
"title": "Test Addon",
"icon": "mdi:test-tube",
"admin": True,
},
}
},
},
)
ingress_panels.return_value = {
"test_addon": IngressPanel(
enable=True, title="Test Addon", icon="mdi:test-tube", admin=True
),
}
with patch(
"homeassistant.components.hassio.addon_panel.frontend.async_register_built_in_panel"
@@ -27,7 +27,6 @@ from homeassistant.util import dt as dt_util
from .common import MOCK_REPOSITORIES, MOCK_STORE_ADDONS
from tests.common import MockConfigEntry, async_fire_time_changed
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import WebSocketGenerator
MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@@ -35,7 +34,6 @@ MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@pytest.fixture(autouse=True)
def mock_all(
aioclient_mock: AiohttpClientMocker,
addon_installed: AsyncMock,
store_info: AsyncMock,
addon_changelog: AsyncMock,
@@ -51,13 +49,9 @@ def mock_all(
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""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/supervisor/options", json={"result": "ok"})
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
def mock_addon_info(slug: str):
addon = Mock(
@@ -104,7 +98,6 @@ async def test_binary_sensor(
entity_id: str,
expected: str,
addon_state: str,
aioclient_mock: AiohttpClientMocker,
entity_registry: er.EntityRegistry,
addon_installed: AsyncMock,
) -> None:
+1 -7
View File
@@ -17,7 +17,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.common import MockUser
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import WebSocketGenerator
MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@@ -25,7 +24,6 @@ MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@pytest.fixture(autouse=True)
def mock_all(
aioclient_mock: AiohttpClientMocker,
supervisor_is_connected: AsyncMock,
resolution_info: AsyncMock,
addon_info: AsyncMock,
@@ -36,17 +34,13 @@ def mock_all(
addons_list: AsyncMock,
network_info: AsyncMock,
os_info: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""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/supervisor/options", json={"result": "ok"})
supervisor_root_info.return_value = replace(
supervisor_root_info.return_value, hassos=None
)
addons_list.return_value.pop(1)
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
@pytest.fixture
+1 -7
View File
@@ -13,7 +13,6 @@ from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import ClientSessionGenerator
MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@@ -21,7 +20,6 @@ MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@pytest.fixture(autouse=True)
def mock_all(
aioclient_mock: AiohttpClientMocker,
addon_installed: AsyncMock,
store_info: AsyncMock,
addon_stats: AsyncMock,
@@ -37,10 +35,9 @@ def mock_all(
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""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/supervisor/options", json={"result": "ok"})
homeassistant_info.return_value = replace(
homeassistant_info.return_value,
version="1.0.0dev221",
@@ -58,9 +55,6 @@ def mock_all(
version_latest="1.0.1dev222",
update_available=True,
)
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
def mock_addon_info(slug: str):
addon = Mock(
+1 -10
View File
@@ -25,7 +25,6 @@ from tests.common import (
mock_integration,
mock_platform,
)
from tests.test_util.aiohttp import AiohttpClientMocker
@pytest.fixture(name="mock_mqtt")
@@ -99,17 +98,12 @@ async def test_hassio_discovery_startup(
@pytest.mark.usefixtures("hassio_client")
async def test_hassio_discovery_startup_done(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_mqtt: type[config_entries.ConfigFlow],
addon_installed: AsyncMock,
get_addon_discovery_info: AsyncMock,
supervisor_root_info: AsyncMock,
) -> None:
"""Test startup and discovery with hass discovery."""
aioclient_mock.post(
"http://127.0.0.1/supervisor/options",
json={"result": "ok", "data": {}},
)
get_addon_discovery_info.return_value = [
Discovery(
addon="mosquitto",
@@ -239,15 +233,12 @@ TEST_UUID = str(uuid4())
config_entries.SOURCE_USER,
],
)
@pytest.mark.usefixtures("hassio_client", "addon_installed", "get_addon_discovery_info")
async def test_hassio_rediscover(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hassio_client: TestClient,
addon_installed: AsyncMock,
entry_domain: str,
entry_discovery_keys: dict[str, tuple[DiscoveryKey, ...]],
entry_source: str,
get_addon_discovery_info: AsyncMock,
get_discovery_message: AsyncMock,
) -> None:
"""Test we reinitiate flows when an ignored config entry is removed."""
+4 -35
View File
@@ -11,40 +11,13 @@ from homeassistant.components.hassio.handler import HassIO, HassioAPIError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from tests.test_util.aiohttp import AiohttpClientMocker
async def test_api_ingress_panels(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Ingress panels."""
aioclient_mock.get(
"http://127.0.0.1/ingress/panels",
json={
"result": "ok",
"data": {
"panels": {
"slug": {
"enable": True,
"title": "Test",
"icon": "mdi:test",
"admin": False,
}
}
},
},
)
data = await hassio_handler.get_ingress_panels()
assert aioclient_mock.call_count == 1
assert data["panels"]
assert "slug" in data["panels"]
@pytest.mark.parametrize(
("api_call", "method", "payload"),
[
("get_ingress_panels", "GET", None),
("/ingress/panels", "GET", None),
("/supervisor/options", "POST", {"diagnostics": True}),
("/supervisor/update", "POST", None),
],
)
@pytest.mark.usefixtures("socket_enabled")
@@ -71,11 +44,7 @@ async def test_api_headers(
f"{server.host}:{server.port}",
)
api_func = getattr(hassio_handler, api_call)
if payload:
await api_func(payload)
else:
await api_func()
await hassio_handler.send_command(api_call, method, payload)
assert received_request is not None
assert received_request.method == method
+24 -62
View File
@@ -61,7 +61,6 @@ MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@pytest.fixture(autouse=True)
def mock_all(
aioclient_mock: AiohttpClientMocker,
store_info: AsyncMock,
addon_info: AsyncMock,
addon_stats: AsyncMock,
@@ -78,10 +77,9 @@ def mock_all(
homeassistant_stats: AsyncMock,
supervisor_stats: AsyncMock,
addon_installed: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""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/supervisor/options", json={"result": "ok"})
addons_list.return_value[0] = replace(
addons_list.return_value[0],
version="1.0.0",
@@ -142,15 +140,10 @@ def mock_all(
return addon
addon_info.side_effect = mock_addon_info
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
async def test_setup_api_ping(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
hass: HomeAssistant, supervisor_client: AsyncMock
) -> None:
"""Test setup with API ping."""
with patch.dict(os.environ, MOCK_ENVIRON):
@@ -158,14 +151,12 @@ async def test_setup_api_ping(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert len(supervisor_client.mock_calls) == 23
assert get_core_info(hass)["version_latest"] == "1.0.0"
assert is_hassio(hass)
async def test_setup_api_panel(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
async def test_setup_api_panel(hass: HomeAssistant) -> None:
"""Test setup with API ping."""
assert await async_setup_component(hass, "frontend", {})
with patch.dict(os.environ, MOCK_ENVIRON):
@@ -217,9 +208,7 @@ async def test_setup_app_panel(hass: HomeAssistant) -> None:
async def test_setup_api_push_api_data(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
hass: HomeAssistant, supervisor_client: AsyncMock
) -> None:
"""Test setup with API push."""
with patch.dict(os.environ, MOCK_ENVIRON):
@@ -229,17 +218,14 @@ async def test_setup_api_push_api_data(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert len(supervisor_client.mock_calls) == 23
supervisor_client.homeassistant.set_options.assert_called_once_with(
HomeAssistantOptions(ssl=False, port=9999, refresh_token=ANY)
)
async def test_setup_api_push_api_data_error(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
caplog: pytest.LogCaptureFixture,
hass: HomeAssistant, supervisor_client: AsyncMock, caplog: pytest.LogCaptureFixture
) -> None:
"""Test setup with error while pushing core config data to API."""
supervisor_client.homeassistant.set_options.side_effect = SupervisorError("boom")
@@ -248,14 +234,12 @@ async def test_setup_api_push_api_data_error(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert len(supervisor_client.mock_calls) == 23
assert "Failed to update Home Assistant options in Supervisor: boom" in caplog.text
async def test_setup_api_push_api_data_server_host(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
hass: HomeAssistant, supervisor_client: AsyncMock
) -> None:
"""Test setup with API push with active server host."""
with patch.dict(os.environ, MOCK_ENVIRON):
@@ -267,17 +251,14 @@ async def test_setup_api_push_api_data_server_host(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert len(supervisor_client.mock_calls) == 23
supervisor_client.homeassistant.set_options.assert_called_once_with(
HomeAssistantOptions(ssl=False, port=9999, refresh_token=ANY, watchdog=False)
)
async def test_setup_api_push_api_data_default(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hass_storage: dict[str, Any],
supervisor_client: AsyncMock,
hass: HomeAssistant, hass_storage: dict[str, Any], supervisor_client: AsyncMock
) -> None:
"""Test setup with API push default data."""
with (
@@ -288,7 +269,7 @@ async def test_setup_api_push_api_data_default(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert len(supervisor_client.mock_calls) == 23
supervisor_client.homeassistant.set_options.assert_called_once_with(
HomeAssistantOptions(ssl=False, port=8123, refresh_token=ANY)
)
@@ -311,9 +292,7 @@ async def test_setup_api_push_api_data_default(
async def test_setup_adds_admin_group_to_user(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hass_storage: dict[str, Any],
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
"""Test setup with API push default data."""
# Create user without admin
@@ -335,9 +314,7 @@ async def test_setup_adds_admin_group_to_user(
async def test_setup_migrate_user_name(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hass_storage: dict[str, Any],
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
"""Test setup with migrating the user name."""
# Create user with old name
@@ -358,10 +335,7 @@ async def test_setup_migrate_user_name(
async def test_setup_api_existing_hassio_user(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hass_storage: dict[str, Any],
supervisor_client: AsyncMock,
hass: HomeAssistant, hass_storage: dict[str, Any], supervisor_client: AsyncMock
) -> None:
"""Test setup with API push default data."""
user = await hass.auth.async_create_system_user("Hass.io test")
@@ -372,16 +346,14 @@ async def test_setup_api_existing_hassio_user(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert len(supervisor_client.mock_calls) == 23
supervisor_client.homeassistant.set_options.assert_called_once_with(
HomeAssistantOptions(ssl=False, port=8123, refresh_token=token.token)
)
async def test_setup_core_push_config(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
hass: HomeAssistant, supervisor_client: AsyncMock
) -> None:
"""Test setup with API push default data."""
hass.config.time_zone = "testzone"
@@ -391,7 +363,7 @@ async def test_setup_core_push_config(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert len(supervisor_client.mock_calls) == 23
supervisor_client.supervisor.set_options.assert_called_once_with(
SupervisorOptions(timezone="testzone")
)
@@ -405,10 +377,7 @@ async def test_setup_core_push_config(
async def test_setup_core_push_config_error(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
caplog: pytest.LogCaptureFixture,
hass: HomeAssistant, supervisor_client: AsyncMock, caplog: pytest.LogCaptureFixture
) -> None:
"""Test setup with error while pushing supervisor config data to API."""
hass.config.time_zone = "testzone"
@@ -419,14 +388,12 @@ async def test_setup_core_push_config_error(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert len(supervisor_client.mock_calls) == 23
assert "Failed to update Supervisor options: boom" in caplog.text
async def test_setup_hassio_no_additional_data(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
hass: HomeAssistant, supervisor_client: AsyncMock
) -> None:
"""Test setup with API push default data."""
with (
@@ -437,8 +404,7 @@ async def test_setup_hassio_no_additional_data(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert aioclient_mock.mock_calls[-1][3]["Authorization"] == "Bearer 123456"
assert len(supervisor_client.mock_calls) == 23
async def test_fail_setup_without_environ_var(hass: HomeAssistant) -> None:
@@ -651,10 +617,7 @@ async def test_service_calls(
["app", "addon"],
)
async def test_invalid_service_calls(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_is_connected: AsyncMock,
app_or_addon: str,
hass: HomeAssistant, supervisor_is_connected: AsyncMock, app_or_addon: str
) -> None:
"""Call service with invalid input and check that it raises."""
supervisor_is_connected.side_effect = SupervisorError
@@ -1035,7 +998,6 @@ async def test_coordinator_updates_stats_entities_enabled(
)
async def test_setup_hardware_integration(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
os_info: AsyncMock,
board: str,
@@ -1059,7 +1021,7 @@ async def test_setup_hardware_integration(
await hass.async_block_till_done(wait_background_tasks=True)
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert len(supervisor_client.mock_calls) == 23
assert len(mock_setup_entry.mock_calls) == 1
+1 -11
View File
@@ -23,14 +23,12 @@ from homeassistant.util import dt as dt_util
from .common import MOCK_REPOSITORIES, MOCK_STORE_ADDONS
from tests.common import MockConfigEntry, async_fire_time_changed
from tests.test_util.aiohttp import AiohttpClientMocker
MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@pytest.fixture(autouse=True)
def mock_all(
aioclient_mock: AiohttpClientMocker,
addon_installed: AsyncMock,
store_info: AsyncMock,
addon_stats: AsyncMock,
@@ -46,14 +44,9 @@ def mock_all(
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""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/supervisor/options", json={"result": "ok"})
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
host_info.return_value = replace(host_info.return_value, agent_version="1.0.0")
addons_list.return_value[1] = replace(
addons_list.return_value[1], version_latest="3.2.0", update_available=True
@@ -114,9 +107,7 @@ async def test_sensor(
hass: HomeAssistant,
entity_id,
expected,
aioclient_mock: AiohttpClientMocker,
entity_registry: er.EntityRegistry,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test hassio OS and addons sensor."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
@@ -165,7 +156,6 @@ async def test_stats_addon_sensor(
hass: HomeAssistant,
entity_id,
expected,
aioclient_mock: AiohttpClientMocker,
entity_registry: er.EntityRegistry,
caplog: pytest.LogCaptureFixture,
freezer: FrozenDateTimeFactory,
+1 -6
View File
@@ -56,7 +56,6 @@ async def enable_entity(
@pytest.fixture(autouse=True)
def mock_all(
aioclient_mock: AiohttpClientMocker,
addon_installed: AsyncMock,
store_info: AsyncMock,
addon_changelog: AsyncMock,
@@ -72,13 +71,9 @@ def mock_all(
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""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/supervisor/options", json={"result": "ok"})
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
addons_list.return_value[1] = replace(
addons_list.return_value[1], name="test-two", slug="test-two"
)
+4 -20
View File
@@ -36,7 +36,6 @@ from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
from tests.common import MockConfigEntry, async_fire_time_changed
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import WebSocketGenerator
MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@@ -44,7 +43,6 @@ MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@pytest.fixture(autouse=True)
def mock_all(
aioclient_mock: AiohttpClientMocker,
addon_installed: AsyncMock,
store_info: AsyncMock,
addon_stats: AsyncMock,
@@ -60,10 +58,9 @@ def mock_all(
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""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/supervisor/options", json={"result": "ok"})
homeassistant_info.return_value = replace(
homeassistant_info.return_value,
version="1.0.0dev221",
@@ -81,9 +78,6 @@ def mock_all(
version_latest="1.0.1dev222",
update_available=True,
)
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
def mock_addon_info(slug: str):
addon = Mock(
@@ -130,7 +124,6 @@ async def test_update_entities(
entity_id,
expected_state,
auto_update,
aioclient_mock: AiohttpClientMocker,
addon_installed: AsyncMock,
) -> None:
"""Test update entities."""
@@ -1399,10 +1392,7 @@ async def test_update_core_with_backup_and_error(
async def test_release_notes_between_versions(
hass: HomeAssistant,
addon_changelog: AsyncMock,
aioclient_mock: AiohttpClientMocker,
hass_ws_client: WebSocketGenerator,
hass: HomeAssistant, addon_changelog: AsyncMock, hass_ws_client: WebSocketGenerator
) -> None:
"""Test release notes between versions."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
@@ -1437,10 +1427,7 @@ async def test_release_notes_between_versions(
async def test_release_notes_full(
hass: HomeAssistant,
addon_changelog: AsyncMock,
aioclient_mock: AiohttpClientMocker,
hass_ws_client: WebSocketGenerator,
hass: HomeAssistant, addon_changelog: AsyncMock, hass_ws_client: WebSocketGenerator
) -> None:
"""Test release notes no match."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
@@ -1487,10 +1474,7 @@ async def test_release_notes_full(
async def test_not_release_notes(
hass: HomeAssistant,
addon_changelog: AsyncMock,
aioclient_mock: AiohttpClientMocker,
hass_ws_client: WebSocketGenerator,
hass: HomeAssistant, addon_changelog: AsyncMock, hass_ws_client: WebSocketGenerator
) -> None:
"""Test handling where there are no release notes."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
@@ -53,10 +53,9 @@ def mock_all(
network_info: AsyncMock,
os_info: AsyncMock,
store_info: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""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/supervisor/options", json={"result": "ok"})
supervisor_root_info.return_value = replace(
supervisor_root_info.return_value, hassos=None
)
@@ -64,9 +63,6 @@ def mock_all(
addon_info.return_value.version = "2.0.0"
addon_info.return_value.version_latest = "2.0.1"
addon_info.return_value.update_available = True
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
# The websocket API still relies on HassIO.send_command for all Supervisor API calls
# So must keep some aioclient mocks normally covered by aiohasupervisor in component
+6 -1
View File
@@ -246,7 +246,12 @@ async def test_ip_ban_manager_never_started(
),
)
@pytest.mark.usefixtures(
"hassio_env", "resolution_info", "os_info", "store_info", "supervisor_info"
"hassio_env",
"resolution_info",
"os_info",
"store_info",
"supervisor_info",
"ingress_panels",
)
async def test_access_from_supervisor_ip(
remote_addr,
+6 -24
View File
@@ -27,7 +27,6 @@ from tests.common import (
mock_platform,
register_auth_provider,
)
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import ClientSessionGenerator
@@ -39,15 +38,11 @@ async def auth_active(hass: HomeAssistant) -> None:
@pytest.fixture(name="rpi")
async def rpi_fixture(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, mock_supervisor
hass: HomeAssistant, homeassistant_info: AsyncMock, mock_supervisor: None
) -> None:
"""Mock core info with rpi."""
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={
"result": "ok",
"data": {"version_latest": "1.0.0", "machine": "raspberrypi3"},
},
homeassistant_info.return_value = replace(
homeassistant_info.return_value, machine="raspberrypi3"
)
assert await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done()
@@ -55,10 +50,7 @@ async def rpi_fixture(
@pytest.fixture(name="no_rpi")
async def no_rpi_fixture(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
homeassistant_info: AsyncMock,
mock_supervisor,
hass: HomeAssistant, homeassistant_info: AsyncMock, mock_supervisor: None
) -> None:
"""Mock core info with rpi."""
homeassistant_info.return_value = replace(
@@ -70,7 +62,6 @@ async def no_rpi_fixture(
@pytest.fixture(name="mock_supervisor")
async def mock_supervisor_fixture(
aioclient_mock: AiohttpClientMocker,
store_info: AsyncMock,
supervisor_is_connected: AsyncMock,
resolution_info: AsyncMock,
@@ -79,23 +70,15 @@ async def mock_supervisor_fixture(
supervisor_info: AsyncMock,
network_info: AsyncMock,
os_info: AsyncMock,
ingress_panels: AsyncMock,
) -> AsyncGenerator[None]:
"""Mock supervisor."""
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
supervisor_info.return_value = replace(
supervisor_info.return_value, diagnostics=True
)
with (
patch.dict(os.environ, {"SUPERVISOR": "127.0.0.1"}),
patch(
"homeassistant.components.hassio.HassIO.get_ingress_panels",
return_value={"panels": {}},
),
patch.dict(
os.environ,
{"SUPERVISOR_TOKEN": "123456"},
),
patch.dict(os.environ, {"SUPERVISOR_TOKEN": "123456"}),
):
yield
@@ -513,7 +496,6 @@ async def test_onboarding_core_no_rpi_power(
hass: HomeAssistant,
hass_storage: dict[str, Any],
hass_client: ClientSessionGenerator,
aioclient_mock: AiohttpClientMocker,
no_rpi,
mock_default_integrations,
) -> None:
+3 -8
View File
@@ -2008,16 +2008,11 @@ async def hassio_stubs(
hass_client: ClientSessionGenerator,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
ingress_panels: AsyncMock,
) -> None:
"""Create mock hassio http client."""
with (
patch(
"homeassistant.components.hassio.HassIO.get_ingress_panels",
return_value={"panels": []},
),
patch(
"homeassistant.components.hassio.issues.SupervisorIssues.setup",
),
with patch(
"homeassistant.components.hassio.issues.SupervisorIssues.setup",
):
await async_setup_component(hass, "hassio", {})