Compare commits

...

7 Commits

Author SHA1 Message Date
Erik Montnemery
9bbd9d8bcd Deduplicate trigger tests checking labs flag (#165760)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-17 08:19:10 +01:00
mettolen
5ff2cac077 Set parallel updates for Huum integration (#165749) 2026-03-17 07:54:37 +01:00
mettolen
74b0d058ec Fix issues in Huum unit test (#165753) 2026-03-17 07:54:01 +01:00
mettolen
29f96e3f9c Move _async_abort_entries_match before the try block in Huum (#165752) 2026-03-17 07:47:47 +01:00
Mike Degatano
39b44445ec Use aiohasupervisor for all calls from hassio/coordinator (#164413)
Co-authored-by: Stefan Agner <stefan@agner.ch>
2026-03-17 01:06:56 +01:00
Robert Resch
589622c05a Fix pterodactyl tests (#165745) 2026-03-16 23:44:26 +01:00
Brett Adams
6abe576ec9 Platinum quality for Teslemetry (#165727) 2026-03-16 22:31:17 +00:00
69 changed files with 1145 additions and 1915 deletions

View File

@@ -338,6 +338,7 @@ class Analytics:
hass = self._hass
supervisor_info = None
addons_info: dict[str, Any] | None = None
operating_system_info: dict[str, Any] = {}
if self._data.uuid is None:
@@ -347,6 +348,7 @@ class Analytics:
if self.supervisor:
supervisor_info = hassio.get_supervisor_info(hass)
operating_system_info = hassio.get_os_info(hass) or {}
addons_info = hassio.get_addons_info(hass) or {}
system_info = await async_get_system_info(hass)
integrations = []
@@ -419,13 +421,10 @@ class Analytics:
integrations.append(integration.domain)
if supervisor_info is not None:
if addons_info is not None:
supervisor_client = hassio.get_supervisor_client(hass)
installed_addons = await asyncio.gather(
*(
supervisor_client.addons.addon_info(addon[ATTR_SLUG])
for addon in supervisor_info[ATTR_ADDONS]
)
*(supervisor_client.addons.addon_info(slug) for slug in addons_info)
)
addons.extend(
{

View File

@@ -9,10 +9,21 @@ import logging
import os
import re
import struct
from typing import Any, NamedTuple
from typing import Any, NamedTuple, cast
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import GreenOptions, YellowOptions # noqa: F401
from aiohasupervisor.models import (
GreenOptions,
HomeAssistantInfo,
HostInfo,
InstalledAddon,
NetworkInfo,
OSInfo,
RootInfo,
StoreInfo,
SupervisorInfo,
YellowOptions,
)
import voluptuous as vol
from homeassistant.auth.const import GROUP_ID_ADMIN
@@ -65,7 +76,7 @@ from . import ( # noqa: F401
system_health,
update,
)
from .addon_manager import AddonError, AddonInfo, AddonManager, AddonState # noqa: F401
from .addon_manager import AddonError, AddonInfo, AddonManager, AddonState
from .addon_panel import async_setup_addon_panel
from .auth import async_setup_auth_view
from .config import HassioConfig
@@ -82,7 +93,9 @@ from .const import (
ATTR_INPUT,
ATTR_LOCATION,
ATTR_PASSWORD,
ATTR_REPOSITORIES,
ATTR_SLUG,
DATA_ADDONS_LIST,
DATA_COMPONENT,
DATA_CONFIG_STORE,
DATA_CORE_INFO,
@@ -100,18 +113,21 @@ from .const import (
from .coordinator import (
HassioDataUpdateCoordinator,
get_addons_info,
get_addons_stats, # noqa: F401
get_core_info, # noqa: F401
get_core_stats, # noqa: F401
get_host_info, # noqa: F401
get_addons_list,
get_addons_stats,
get_core_info,
get_core_stats,
get_host_info,
get_info,
get_issues_info, # noqa: F401
get_issues_info,
get_network_info,
get_os_info,
get_supervisor_info, # noqa: F401
get_supervisor_stats, # noqa: F401
get_store,
get_supervisor_info,
get_supervisor_stats,
)
from .discovery import async_setup_discovery_view
from .handler import ( # noqa: F401
from .handler import (
HassIO,
HassioAPIError,
async_update_diagnostics,
@@ -122,6 +138,35 @@ from .ingress import async_setup_ingress_view
from .issues import SupervisorIssues
from .websocket_api import async_load_websocket_api
# Expose the future safe name now so integrations can use it
# All references to addons will eventually be refactored and deprecated
get_apps_list = get_addons_list
__all__ = [
"AddonError",
"AddonInfo",
"AddonManager",
"AddonState",
"GreenOptions",
"SupervisorError",
"YellowOptions",
"async_update_diagnostics",
"get_addons_info",
"get_addons_list",
"get_addons_stats",
"get_apps_list",
"get_core_info",
"get_core_stats",
"get_host_info",
"get_info",
"get_issues_info",
"get_network_info",
"get_os_info",
"get_store",
"get_supervisor_client",
"get_supervisor_info",
"get_supervisor_stats",
]
_LOGGER = logging.getLogger(__name__)
@@ -504,27 +549,55 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
try:
(
hass.data[DATA_INFO],
hass.data[DATA_HOST_INFO],
root_info,
host_info,
store_info,
hass.data[DATA_CORE_INFO],
hass.data[DATA_SUPERVISOR_INFO],
hass.data[DATA_OS_INFO],
hass.data[DATA_NETWORK_INFO],
) = await asyncio.gather(
create_eager_task(hassio.get_info()),
create_eager_task(hassio.get_host_info()),
create_eager_task(supervisor_client.store.info()),
create_eager_task(hassio.get_core_info()),
create_eager_task(hassio.get_supervisor_info()),
create_eager_task(hassio.get_os_info()),
create_eager_task(hassio.get_network_info()),
homeassistant_info,
supervisor_info,
os_info,
network_info,
addons_list,
) = cast(
tuple[
RootInfo,
HostInfo,
StoreInfo,
HomeAssistantInfo,
SupervisorInfo,
OSInfo,
NetworkInfo,
list[InstalledAddon],
],
await asyncio.gather(
create_eager_task(supervisor_client.info()),
create_eager_task(supervisor_client.host.info()),
create_eager_task(supervisor_client.store.info()),
create_eager_task(supervisor_client.homeassistant.info()),
create_eager_task(supervisor_client.supervisor.info()),
create_eager_task(supervisor_client.os.info()),
create_eager_task(supervisor_client.network.info()),
create_eager_task(supervisor_client.addons.list()),
),
)
except HassioAPIError as err:
except SupervisorError as err:
_LOGGER.warning("Can't read Supervisor data: %s", err)
else:
hass.data[DATA_INFO] = root_info.to_dict()
hass.data[DATA_HOST_INFO] = host_info.to_dict()
hass.data[DATA_STORE] = store_info.to_dict()
hass.data[DATA_CORE_INFO] = homeassistant_info.to_dict()
hass.data[DATA_SUPERVISOR_INFO] = supervisor_info.to_dict()
hass.data[DATA_OS_INFO] = os_info.to_dict()
hass.data[DATA_NETWORK_INFO] = network_info.to_dict()
hass.data[DATA_ADDONS_LIST] = [addon.to_dict() for addon in addons_list]
# Deprecated 2026.4.0: Folding repositories and addons.list results into supervisor_info for compatibility
# Can drop this after removal period
hass.data[DATA_SUPERVISOR_INFO]["repositories"] = hass.data[DATA_STORE][
ATTR_REPOSITORIES
]
hass.data[DATA_SUPERVISOR_INFO]["addons"] = hass.data[DATA_ADDONS_LIST]
async_call_later(
hass,

View File

@@ -93,6 +93,7 @@ DATA_SUPERVISOR_INFO = "hassio_supervisor_info"
DATA_SUPERVISOR_STATS = "hassio_supervisor_stats"
DATA_ADDONS_INFO = "hassio_addons_info"
DATA_ADDONS_STATS = "hassio_addons_stats"
DATA_ADDONS_LIST = "hassio_addons_list"
HASSIO_UPDATE_INTERVAL = timedelta(minutes=5)
ATTR_AUTO_UPDATE = "auto_update"
@@ -106,6 +107,7 @@ ATTR_STATE = "state"
ATTR_STARTED = "started"
ATTR_URL = "url"
ATTR_REPOSITORY = "repository"
ATTR_REPOSITORIES = "repositories"
DATA_KEY_ADDONS = "addons"
DATA_KEY_OS = "os"

View File

@@ -4,13 +4,20 @@ from __future__ import annotations
import asyncio
from collections import defaultdict
from collections.abc import Awaitable
from copy import deepcopy
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, cast
from aiohasupervisor import SupervisorError, SupervisorNotFoundError
from aiohasupervisor.models import StoreInfo
from aiohasupervisor.models.mounts import CIFSMountResponse, NFSMountResponse
from aiohasupervisor.models import (
AddonState,
CIFSMountResponse,
InstalledAddon,
NFSMountResponse,
StoreInfo,
)
from aiohasupervisor.models.base import ResponseData
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_MANUFACTURER, ATTR_NAME
@@ -23,16 +30,16 @@ from homeassistant.loader import bind_hass
from .const import (
ATTR_AUTO_UPDATE,
ATTR_REPOSITORIES,
ATTR_REPOSITORY,
ATTR_SLUG,
ATTR_STARTED,
ATTR_STATE,
ATTR_URL,
ATTR_VERSION,
CONTAINER_INFO,
CONTAINER_STATS,
CORE_CONTAINER,
DATA_ADDONS_INFO,
DATA_ADDONS_LIST,
DATA_ADDONS_STATS,
DATA_COMPONENT,
DATA_CORE_INFO,
@@ -57,7 +64,7 @@ from .const import (
SUPERVISOR_CONTAINER,
SupervisorEntityModel,
)
from .handler import HassioAPIError, get_supervisor_client
from .handler import get_supervisor_client
from .jobs import SupervisorJobs
if TYPE_CHECKING:
@@ -118,7 +125,7 @@ def get_network_info(hass: HomeAssistant) -> dict[str, Any] | None:
@callback
@bind_hass
def get_addons_info(hass: HomeAssistant) -> dict[str, dict[str, Any]] | None:
def get_addons_info(hass: HomeAssistant) -> dict[str, dict[str, Any] | None] | None:
"""Return Addons info.
Async friendly.
@@ -126,9 +133,18 @@ def get_addons_info(hass: HomeAssistant) -> dict[str, dict[str, Any]] | None:
return hass.data.get(DATA_ADDONS_INFO)
@callback
def get_addons_list(hass: HomeAssistant) -> list[dict[str, Any]] | None:
"""Return list of installed addons and subset of details for each.
Async friendly.
"""
return hass.data.get(DATA_ADDONS_LIST)
@callback
@bind_hass
def get_addons_stats(hass: HomeAssistant) -> dict[str, Any]:
def get_addons_stats(hass: HomeAssistant) -> dict[str, dict[str, Any] | None]:
"""Return Addons stats.
Async friendly.
@@ -341,7 +357,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
try:
await self.force_data_refresh(is_first_update)
except HassioAPIError as err:
except SupervisorError as err:
raise UpdateFailed(f"Error on Supervisor API: {err}") from err
new_data: dict[str, Any] = {}
@@ -350,6 +366,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
addons_stats = get_addons_stats(self.hass)
store_data = get_store(self.hass)
mounts_info = await self.supervisor_client.mounts.info()
addons_list = get_addons_list(self.hass) or []
if store_data:
repositories = {
@@ -360,17 +377,17 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
repositories = {}
new_data[DATA_KEY_ADDONS] = {
addon[ATTR_SLUG]: {
(slug := addon[ATTR_SLUG]): {
**addon,
**((addons_stats or {}).get(addon[ATTR_SLUG]) or {}),
ATTR_AUTO_UPDATE: (addons_info.get(addon[ATTR_SLUG]) or {}).get(
**(addons_stats.get(slug) or {}),
ATTR_AUTO_UPDATE: (addons_info.get(slug) or {}).get(
ATTR_AUTO_UPDATE, False
),
ATTR_REPOSITORY: repositories.get(
addon.get(ATTR_REPOSITORY), addon.get(ATTR_REPOSITORY, "")
repo_slug := addon.get(ATTR_REPOSITORY, ""), repo_slug
),
}
for addon in supervisor_info.get("addons", [])
for addon in addons_list
}
if self.is_hass_os:
new_data[DATA_KEY_OS] = get_os_info(self.hass)
@@ -462,32 +479,48 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
container_updates = self._container_updates
data = self.hass.data
hassio = self.hassio
updates = {
DATA_INFO: hassio.get_info(),
DATA_CORE_INFO: hassio.get_core_info(),
DATA_SUPERVISOR_INFO: hassio.get_supervisor_info(),
DATA_OS_INFO: hassio.get_os_info(),
client = self.supervisor_client
updates: dict[str, Awaitable[ResponseData]] = {
DATA_INFO: client.info(),
DATA_CORE_INFO: client.homeassistant.info(),
DATA_SUPERVISOR_INFO: client.supervisor.info(),
DATA_OS_INFO: client.os.info(),
DATA_STORE: client.store.info(),
}
if CONTAINER_STATS in container_updates[CORE_CONTAINER]:
updates[DATA_CORE_STATS] = hassio.get_core_stats()
updates[DATA_CORE_STATS] = client.homeassistant.stats()
if CONTAINER_STATS in container_updates[SUPERVISOR_CONTAINER]:
updates[DATA_SUPERVISOR_STATS] = hassio.get_supervisor_stats()
updates[DATA_SUPERVISOR_STATS] = client.supervisor.stats()
results = await asyncio.gather(*updates.values())
for key, result in zip(updates, results, strict=False):
data[key] = result
# Pull off addons.list results for further processing before caching
addons_list, *results = await asyncio.gather(
client.addons.list(), *updates.values()
)
for key, result in zip(updates, cast(list[ResponseData], results), strict=True):
data[key] = result.to_dict()
installed_addons = cast(list[InstalledAddon], addons_list)
data[DATA_ADDONS_LIST] = [addon.to_dict() for addon in installed_addons]
# Deprecated 2026.4.0: Folding repositories and addons.list results into supervisor_info for compatibility
# Can drop this after removal period
data[DATA_SUPERVISOR_INFO].update(
{
"repositories": data[DATA_STORE][ATTR_REPOSITORIES],
"addons": [addon.to_dict() for addon in installed_addons],
}
)
all_addons = {addon.slug for addon in installed_addons}
started_addons = {
addon.slug
for addon in installed_addons
if addon.state in {AddonState.STARTED, AddonState.STARTUP}
}
_addon_data = data[DATA_SUPERVISOR_INFO].get("addons", [])
all_addons: list[str] = []
started_addons: list[str] = []
for addon in _addon_data:
slug = addon[ATTR_SLUG]
all_addons.append(slug)
if addon[ATTR_STATE] == ATTR_STARTED:
started_addons.append(slug)
#
# Update add-on info if its the first update or
# Update addon info if its the first update or
# there is at least one entity that needs the data.
#
# When entities are added they call async_enable_container_updates
@@ -514,6 +547,12 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
),
):
container_data: dict[str, Any] = data.setdefault(data_key, {})
# Clean up cache
for slug in container_data.keys() - wanted_addons:
del container_data[slug]
# Update cache from API
container_data.update(
dict(
await asyncio.gather(
@@ -540,7 +579,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
return (slug, stats.to_dict())
async def _update_addon_info(self, slug: str) -> tuple[str, dict[str, Any] | None]:
"""Return the info for an add-on."""
"""Return the info for an addon."""
try:
info = await self.supervisor_client.addons.addon_info(slug)
except SupervisorError as err:

View File

@@ -87,70 +87,6 @@ class HassIO:
"""Return base url for Supervisor."""
return self._base_url
@api_data
def get_info(self) -> Coroutine:
"""Return generic Supervisor information.
This method returns a coroutine.
"""
return self.send_command("/info", method="get")
@api_data
def get_host_info(self) -> Coroutine:
"""Return data for Host.
This method returns a coroutine.
"""
return self.send_command("/host/info", method="get")
@api_data
def get_os_info(self) -> Coroutine:
"""Return data for the OS.
This method returns a coroutine.
"""
return self.send_command("/os/info", method="get")
@api_data
def get_core_info(self) -> Coroutine:
"""Return data for Home Asssistant Core.
This method returns a coroutine.
"""
return self.send_command("/core/info", method="get")
@api_data
def get_supervisor_info(self) -> Coroutine:
"""Return data for the Supervisor.
This method returns a coroutine.
"""
return self.send_command("/supervisor/info", method="get")
@api_data
def get_network_info(self) -> Coroutine:
"""Return data for the Host Network.
This method returns a coroutine.
"""
return self.send_command("/network/info", method="get")
@api_data
def get_core_stats(self) -> Coroutine:
"""Return stats for the core.
This method returns a coroutine.
"""
return self.send_command("/core/stats", method="get")
@api_data
def get_supervisor_stats(self) -> Coroutine:
"""Return stats for the supervisor.
This method returns a coroutine.
"""
return self.send_command("/supervisor/stats", method="get")
@api_data
def get_ingress_panels(self) -> Coroutine:
"""Return data for Add-on ingress panels.

View File

@@ -17,6 +17,7 @@ from aiohasupervisor.models import (
UnsupportedReason,
)
from homeassistant.const import ATTR_NAME
from homeassistant.core import HassJob, HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.event import async_call_later
@@ -30,6 +31,7 @@ from .const import (
ADDONS_COORDINATOR,
ATTR_DATA,
ATTR_HEALTHY,
ATTR_SLUG,
ATTR_STARTUP,
ATTR_SUPPORTED,
ATTR_UNHEALTHY_REASONS,
@@ -59,7 +61,7 @@ from .const import (
STARTUP_COMPLETE,
UPDATE_KEY_SUPERVISOR,
)
from .coordinator import HassioDataUpdateCoordinator, get_addons_info, get_host_info
from .coordinator import HassioDataUpdateCoordinator, get_addons_list, get_host_info
from .handler import HassIO, get_supervisor_client
ISSUE_KEY_UNHEALTHY = "unhealthy"
@@ -265,23 +267,18 @@ class SupervisorIssues:
placeholders[PLACEHOLDER_KEY_ADDON_URL] = (
f"/hassio/addon/{issue.reference}"
)
addons = get_addons_info(self._hass)
if addons and issue.reference in addons:
placeholders[PLACEHOLDER_KEY_ADDON] = addons[issue.reference][
"name"
]
else:
placeholders[PLACEHOLDER_KEY_ADDON] = issue.reference
addons_list = get_addons_list(self._hass) or []
placeholders[PLACEHOLDER_KEY_ADDON] = issue.reference
for addon in addons_list:
if addon[ATTR_SLUG] == issue.reference:
placeholders[PLACEHOLDER_KEY_ADDON] = addon[ATTR_NAME]
break
elif issue.key == ISSUE_KEY_SYSTEM_FREE_SPACE:
host_info = get_host_info(self._hass)
if (
host_info
and "data" in host_info
and "disk_free" in host_info["data"]
):
if host_info and "disk_free" in host_info:
placeholders[PLACEHOLDER_KEY_FREE_SPACE] = str(
host_info["data"]["disk_free"]
host_info["disk_free"]
)
else:
placeholders[PLACEHOLDER_KEY_FREE_SPACE] = "<2"

View File

@@ -11,11 +11,13 @@ from aiohasupervisor.models import ContextType
import voluptuous as vol
from homeassistant.components.repairs import RepairsFlow
from homeassistant.const import ATTR_NAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult
from . import get_addons_info, get_issues_info
from . import get_addons_list, get_issues_info
from .const import (
ATTR_SLUG,
EXTRA_PLACEHOLDERS,
ISSUE_KEY_ADDON_BOOT_FAIL,
ISSUE_KEY_ADDON_DEPRECATED,
@@ -154,7 +156,7 @@ class DockerConfigIssueRepairFlow(SupervisorIssueRepairFlow):
placeholders = {PLACEHOLDER_KEY_COMPONENTS: ""}
supervisor_issues = get_issues_info(self.hass)
if supervisor_issues and self.issue:
addons = get_addons_info(self.hass) or {}
addons_list = get_addons_list(self.hass) or []
components: list[str] = []
for issue in supervisor_issues.issues:
if issue.key == self.issue.key or issue.type != self.issue.type:
@@ -166,9 +168,9 @@ class DockerConfigIssueRepairFlow(SupervisorIssueRepairFlow):
components.append(
next(
(
info["name"]
for slug, info in addons.items()
if slug == issue.reference
addon[ATTR_NAME]
for addon in addons_list
if addon[ATTR_SLUG] == issue.reference
),
issue.reference or "",
)
@@ -187,13 +189,12 @@ class AddonIssueRepairFlow(SupervisorIssueRepairFlow):
"""Get description placeholders for steps."""
placeholders: dict[str, str] = super().description_placeholders or {}
if self.issue and self.issue.reference:
addons = get_addons_info(self.hass)
if addons and self.issue.reference in addons:
placeholders[PLACEHOLDER_KEY_ADDON] = addons[self.issue.reference][
"name"
]
else:
placeholders[PLACEHOLDER_KEY_ADDON] = self.issue.reference
addons_list = get_addons_list(self.hass) or []
placeholders[PLACEHOLDER_KEY_ADDON] = self.issue.reference
for addon in addons_list:
if addon[ATTR_SLUG] == self.issue.reference:
placeholders[PLACEHOLDER_KEY_ADDON] = addon[ATTR_NAME]
break
return placeholders or None

View File

@@ -9,6 +9,7 @@ from homeassistant.components import system_health
from homeassistant.core import HomeAssistant, callback
from .coordinator import (
get_addons_list,
get_host_info,
get_info,
get_network_info,
@@ -35,6 +36,7 @@ async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
host_info = get_host_info(hass) or {}
supervisor_info = get_supervisor_info(hass)
network_info = get_network_info(hass) or {}
addons_list = get_addons_list(hass) or []
healthy: bool | dict[str, str]
if supervisor_info is not None and supervisor_info.get("healthy"):
@@ -84,6 +86,8 @@ async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
os_info = get_os_info(hass) or {}
information["board"] = os_info.get("board")
# Not using aiohasupervisor for ping call below intentionally. Given system health
# context, it seems preferable to do this check with minimal dependencies
information["supervisor_api"] = system_health.async_check_can_reach_url(
hass,
SUPERVISOR_PING.format(ip_address=ip_address),
@@ -95,8 +99,7 @@ async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
)
information["installed_addons"] = ", ".join(
f"{addon['name']} ({addon['version']})"
for addon in (supervisor_info or {}).get("addons", [])
f"{addon['name']} ({addon['version']})" for addon in addons_list
)
return information

View File

@@ -39,7 +39,7 @@ from .const import (
WS_TYPE_EVENT,
WS_TYPE_SUBSCRIBE,
)
from .coordinator import get_supervisor_info
from .coordinator import get_addons_list
from .update_helper import update_addon, update_core
SCHEMA_WEBSOCKET_EVENT = vol.Schema(
@@ -168,8 +168,8 @@ async def websocket_update_addon(
"""Websocket handler to update an addon."""
addon_name: str | None = None
addon_version: str | None = None
addons: list = (get_supervisor_info(hass) or {}).get("addons", [])
for addon in addons:
addons_list: list[dict[str, Any]] = get_addons_list(hass) or []
for addon in addons_list:
if addon[ATTR_SLUG] == msg["addon"]:
addon_name = addon[ATTR_NAME]
addon_version = addon[ATTR_VERSION]

View File

@@ -12,6 +12,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import HuumConfigEntry, HuumDataUpdateCoordinator
from .entity import HuumBaseEntity
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -24,6 +24,8 @@ from .entity import HuumBaseEntity
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 1
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -36,6 +36,7 @@ class HuumConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle the initial step."""
errors = {}
if user_input is not None:
self._async_abort_entries_match({CONF_USERNAME: user_input[CONF_USERNAME]})
try:
huum = Huum(
user_input[CONF_USERNAME],
@@ -51,9 +52,6 @@ class HuumConfigFlow(ConfigFlow, domain=DOMAIN):
_LOGGER.exception("Unknown error")
errors["base"] = "unknown"
else:
self._async_abort_entries_match(
{CONF_USERNAME: user_input[CONF_USERNAME]}
)
return self.async_create_entry(
title=user_input[CONF_USERNAME], data=user_input
)

View File

@@ -15,6 +15,8 @@ from .entity import HuumBaseEntity
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 1
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -16,6 +16,8 @@ from .entity import HuumBaseEntity
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 1
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -6,17 +6,12 @@ rules:
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage:
status: done
comment: |
PLANNED: Rename result2 -> result throughout test_config_flow.py.
config-flow-test-coverage: done
config-flow:
status: done
comment: |
PLANNED: Move _async_abort_entries_match before the try block so duplicate
entries are rejected before credentials are validated. Remove _LOGGER.error
call from config_flow.py — the error message is redundant with the errors
dict entry.
PLANNED: Remove _LOGGER.error call from config_flow.py — the error
message is redundant with the errors dict entry.
dependency-transparency: done
docs-actions:
status: exempt
@@ -32,12 +27,7 @@ rules:
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry:
status: done
comment: |
PLANNED: Move _async_abort_entries_match before the try block in
config_flow.py so duplicate entries are rejected before credentials are
validated.
unique-config-entry: done
# Silver
action-exceptions:
@@ -55,14 +45,12 @@ rules:
comment: |
PLANNED: Remove _LOGGER.error from coordinator.py — the message is already
passed to UpdateFailed, so logging it separately is redundant.
parallel-updates: todo
parallel-updates: done
reauthentication-flow: todo
test-coverage:
status: todo
comment: |
PLANNED: Remove unique_id from mock config entry in conftest.py. Use
freezer-based time advancement instead of directly calling async_refresh().
Rename result2 -> result in test files.
PLANNED: Use freezer-based time advancement instead of directly calling async_refresh().
# Gold
devices: done

View File

@@ -14,6 +14,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import HuumConfigEntry, HuumDataUpdateCoordinator
from .entity import HuumBaseEntity
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -8,6 +8,6 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["tesla-fleet-api"],
"quality_scale": "silver",
"quality_scale": "platinum",
"requirements": ["tesla-fleet-api==1.4.3", "teslemetry-stream==0.9.0"]
}

View File

@@ -850,3 +850,17 @@ async def assert_condition_gated_by_labs_flag(
"conditions' feature to be enabled in Home Assistant Labs settings "
"(feature flag: 'new_triggers_conditions')"
) in caplog.text
async def assert_trigger_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger: str
) -> None:
"""Helper to check that a trigger is gated by the labs flag."""
await arm_trigger(hass, trigger, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text

View File

@@ -8,12 +8,13 @@ from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntityFeature,
AlarmControlPanelState,
)
from homeassistant.const import ATTR_LABEL_ID, ATTR_SUPPORTED_FEATURES, CONF_ENTITY_ID
from homeassistant.const import ATTR_SUPPORTED_FEATURES, CONF_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
other_states,
parametrize_target_entities,
parametrize_trigger_states,
@@ -44,13 +45,7 @@ async def test_alarm_control_panel_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the ACP triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -359,10 +359,13 @@ async def test_send_usage_with_supervisor(
"healthy": True,
"supported": True,
"arch": "amd64",
"addons": [{"slug": "test_addon"}],
}
),
),
patch(
"homeassistant.components.hassio.get_addons_info",
side_effect=Mock(return_value={"test_addon": {}}),
),
patch(
"homeassistant.components.hassio.get_os_info",
side_effect=Mock(return_value={}),
@@ -578,10 +581,13 @@ async def test_send_statistics_with_supervisor(
"healthy": True,
"supported": True,
"arch": "amd64",
"addons": [{"slug": "test_addon"}],
}
),
),
patch(
"homeassistant.components.hassio.get_addons_info",
side_effect=Mock(return_value={"test_addon": {}}),
),
patch(
"homeassistant.components.hassio.get_os_info",
side_effect=Mock(return_value={}),

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.assist_satellite.entity import AssistSatelliteState
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID
from homeassistant.const import CONF_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
other_states,
parametrize_target_entities,
parametrize_trigger_states,
@@ -38,13 +39,7 @@ async def test_assist_satellite_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the Assist satellite triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -2,17 +2,13 @@
import pytest
from homeassistant.const import (
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.const import CONF_ENTITY_ID, STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
set_or_remove_state,
target_entities,
@@ -30,13 +26,7 @@ async def test_button_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the button triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -14,7 +14,6 @@ from homeassistant.components.climate.const import (
)
from homeassistant.components.climate.trigger import CONF_HVAC_MODE
from homeassistant.const import (
ATTR_LABEL_ID,
ATTR_TEMPERATURE,
CONF_ENTITY_ID,
CONF_OPTIONS,
@@ -26,6 +25,7 @@ from homeassistant.helpers.trigger import async_validate_trigger_config
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
other_states,
parametrize_numerical_attribute_changed_trigger_states,
parametrize_numerical_attribute_crossed_threshold_trigger_states,
@@ -59,13 +59,7 @@ async def test_climate_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the climate triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -7,23 +7,52 @@ from collections.abc import AsyncGenerator, Callable, Coroutine, Generator, Mapp
from functools import lru_cache
from importlib.util import find_spec
import inspect
from ipaddress import IPv4Address, IPv4Network
from pathlib import Path
import re
import string
from typing import TYPE_CHECKING, Any
from unittest.mock import AsyncMock, MagicMock, patch
from aiohasupervisor import SupervisorNotFoundError
from aiohasupervisor import SupervisorClient, SupervisorNotFoundError
from aiohasupervisor.addons import AddonsClient
from aiohasupervisor.backups import BackupsClient
from aiohasupervisor.discovery import DiscoveryClient
from aiohasupervisor.homeassistant import HomeAssistantClient
from aiohasupervisor.host import HostClient
from aiohasupervisor.jobs import JobsClient
from aiohasupervisor.models import (
AddonStage,
AddonState,
Discovery,
DockerNetwork,
GreenInfo,
HomeAssistantInfo,
HomeAssistantStats,
HostInfo,
InstalledAddon,
JobsInfo,
LogLevel,
MountsInfo,
NetworkInfo,
OSInfo,
Repository,
ResolutionInfo,
RootInfo,
StoreAddon,
StoreInfo,
SupervisorInfo,
SupervisorState,
SupervisorStats,
UpdateChannel,
YellowInfo,
)
from aiohasupervisor.mounts import MountsClient
from aiohasupervisor.network import NetworkClient
from aiohasupervisor.os import OSClient
from aiohasupervisor.resolution import ResolutionClient
from aiohasupervisor.store import StoreClient
from aiohasupervisor.supervisor import SupervisorManagementClient
import pytest
import voluptuous as vol
@@ -538,23 +567,239 @@ def os_green_info_fixture(supervisor_client: AsyncMock) -> AsyncMock:
return supervisor_client.os.green_info
@pytest.fixture(name="supervisor_root_info")
def supervisor_root_info_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock root info API from supervisor."""
supervisor_client.info.return_value = RootInfo(
supervisor="222",
homeassistant="0.110.0",
hassos="1.2.3",
docker="",
hostname=None,
operating_system=None,
features=[],
machine=None,
machine_id=None,
arch="",
state=SupervisorState.RUNNING,
supported_arch=[],
supported=True,
channel=UpdateChannel.STABLE,
logging=LogLevel.INFO,
timezone="Etc/UTC",
)
return supervisor_client.info
@pytest.fixture(name="host_info")
def host_info_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock host info API from supervisor."""
supervisor_client.host.info.return_value = HostInfo(
agent_version=None,
apparmor_version=None,
chassis="vm",
virtualization=None,
cpe=None,
deployment=None,
disk_free=1.6,
disk_total=100.0,
disk_used=98.4,
disk_life_time=None,
features=[],
hostname=None,
llmnr_hostname=None,
kernel="4.19.0-6-amd64",
operating_system="Debian GNU/Linux 10 (buster)",
timezone=None,
dt_utc=None,
dt_synchronized=None,
use_ntp=None,
startup_time=None,
boot_timestamp=None,
broadcast_llmnr=None,
broadcast_mdns=None,
)
return supervisor_client.host.info
@pytest.fixture(name="homeassistant_info")
def homeassistant_info_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock Home Assistant info API from supervisor."""
supervisor_client.homeassistant.info.return_value = HomeAssistantInfo(
version="1.0.0",
version_latest="1.0.0",
update_available=False,
machine=None,
ip_address=IPv4Address("172.30.32.1"),
arch=None,
image="homeassistant",
boot=True,
port=8123,
ssl=False,
watchdog=True,
audio_input=None,
audio_output=None,
backups_exclude_database=False,
duplicate_log_file=False,
)
return supervisor_client.homeassistant.info
@pytest.fixture(name="supervisor_info")
def supervisor_info_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock supervisor info API from supervisor."""
supervisor_client.supervisor.info.return_value = SupervisorInfo(
version="1.0.0",
version_latest="1.0.0",
update_available=False,
channel=UpdateChannel.STABLE,
arch="",
supported=True,
healthy=True,
ip_address=IPv4Address("172.30.32.2"),
timezone=None,
logging=LogLevel.INFO,
debug=False,
debug_block=False,
diagnostics=None,
auto_update=True,
country=None,
detect_blocking_io=False,
)
return supervisor_client.supervisor.info
@pytest.fixture(name="addons_list")
def addons_list_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock addons list API from supervisor."""
supervisor_client.addons.list.return_value = [
InstalledAddon(
detached=False,
advanced=False,
available=True,
build=False,
description="",
homeassistant=None,
icon=False,
logo=False,
name="test",
repository="core",
slug="test",
stage=AddonStage.STABLE,
update_available=True,
url="https://github.com/home-assistant/addons/test",
version_latest="2.0.1",
version="2.0.0",
state=AddonState.STARTED,
),
InstalledAddon(
detached=False,
advanced=False,
available=True,
build=False,
description="",
homeassistant=None,
icon=False,
logo=False,
name="test2",
repository="core",
slug="test2",
stage=AddonStage.STABLE,
update_available=False,
url="https://github.com",
version_latest="3.1.0",
version="3.1.0",
state=AddonState.STOPPED,
),
]
return supervisor_client.addons.list
@pytest.fixture(name="network_info")
def network_info_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock network info API from supervisor."""
supervisor_client.network.info.return_value = NetworkInfo(
interfaces=[],
docker=DockerNetwork(
interface="hassio",
address=IPv4Network("172.30.32.0/23"),
gateway=IPv4Address("172.30.32.1"),
dns=IPv4Address("172.30.32.3"),
),
host_internet=True,
supervisor_internet=True,
)
return supervisor_client.network.info
@pytest.fixture(name="os_info")
def os_info_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock os info API from supervisor."""
supervisor_client.os.info.return_value = OSInfo(
version="1.0.0",
version_latest="1.0.0",
update_available=False,
board=None,
boot=None,
data_disk=None,
boot_slots={},
)
return supervisor_client.os.info
@pytest.fixture(name="homeassistant_stats")
def homeassistant_stats_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock Home Assistant stats API from supervisor."""
supervisor_client.homeassistant.stats.return_value = HomeAssistantStats(
cpu_percent=0.99,
memory_usage=182611968,
memory_limit=3977146368,
memory_percent=4.59,
network_rx=362570232,
network_tx=82374138,
blk_read=46010945536,
blk_write=15051526144,
)
return supervisor_client.homeassistant.stats
@pytest.fixture(name="supervisor_stats")
def supervisor_stats_fixture(supervisor_client: AsyncMock) -> AsyncMock:
"""Mock supervisor stats API from supervisor."""
supervisor_client.supervisor.stats.return_value = SupervisorStats(
cpu_percent=0.99,
memory_usage=182611968,
memory_limit=3977146368,
memory_percent=4.59,
network_rx=362570232,
network_tx=82374138,
blk_read=46010945536,
blk_write=15051526144,
)
return supervisor_client.supervisor.stats
@pytest.fixture(name="supervisor_client")
def supervisor_client() -> Generator[AsyncMock]:
"""Mock the supervisor client."""
mounts_info_mock = AsyncMock(spec_set=["default_backup_mount", "mounts"])
mounts_info_mock.default_backup_mount = None
mounts_info_mock.mounts = []
supervisor_client = AsyncMock()
supervisor_client.addons = AsyncMock()
supervisor_client.discovery = AsyncMock()
supervisor_client.homeassistant = AsyncMock()
supervisor_client.host = AsyncMock()
supervisor_client.jobs = AsyncMock()
supervisor_client.jobs.info.return_value = MagicMock()
supervisor_client.mounts.info.return_value = mounts_info_mock
supervisor_client.os = AsyncMock()
supervisor_client.resolution = AsyncMock()
supervisor_client.supervisor = AsyncMock()
supervisor_client = AsyncMock(spec=SupervisorClient)
supervisor_client.addons = AsyncMock(spec=AddonsClient)
supervisor_client.backups = AsyncMock(spec=BackupsClient)
supervisor_client.discovery = AsyncMock(spec=DiscoveryClient)
supervisor_client.homeassistant = AsyncMock(spec=HomeAssistantClient)
supervisor_client.host = AsyncMock(spec=HostClient)
supervisor_client.jobs = AsyncMock(spec=JobsClient)
supervisor_client.jobs.info.return_value = JobsInfo(ignore_conditions=[], jobs=[])
supervisor_client.mounts = AsyncMock(spec=MountsClient)
supervisor_client.mounts.info.return_value = MagicMock(
spec=MountsInfo, default_backup_mount=None, mounts=[]
)
supervisor_client.network = AsyncMock(spec=NetworkClient)
supervisor_client.os = AsyncMock(spec=OSClient)
supervisor_client.resolution = AsyncMock(spec=ResolutionClient)
supervisor_client.supervisor = AsyncMock(spec=SupervisorManagementClient)
supervisor_client.store = AsyncMock(spec=StoreClient)
with (
patch(
"homeassistant.components.hassio.get_supervisor_client",

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.cover import ATTR_IS_CLOSED, CoverState
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_LABEL_ID, CONF_ENTITY_ID
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -44,13 +45,7 @@ async def test_cover_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the cover triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -4,17 +4,13 @@ from typing import Any
import pytest
from homeassistant.const import (
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_HOME,
STATE_NOT_HOME,
)
from homeassistant.const import CONF_ENTITY_ID, STATE_HOME, STATE_NOT_HOME
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -38,13 +34,7 @@ async def test_device_tracker_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the device_tracker triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,18 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.cover import ATTR_IS_CLOSED, CoverState
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_OFF,
STATE_ON,
)
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -47,13 +42,7 @@ async def test_door_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the door triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -4,12 +4,13 @@ from typing import Any
import pytest
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.const import CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -34,13 +35,7 @@ async def test_fan_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the fan triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,18 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.cover import ATTR_IS_CLOSED, CoverState
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_OFF,
STATE_ON,
)
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -47,13 +42,7 @@ async def test_garage_door_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the garage door triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.cover import ATTR_IS_CLOSED, CoverState
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_LABEL_ID, CONF_ENTITY_ID
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -35,13 +36,7 @@ async def test_gate_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the gate triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -84,6 +84,7 @@ def mock_addon_store_info(
supervisor_client.store.addon_info.return_value = addon_info = Mock(
spec=StoreAddonComplete,
slug="test",
name="test",
repository="core",
available=True,
installed=False,
@@ -109,15 +110,18 @@ def mock_addon_info(
supervisor_client.addons.addon_info.return_value = addon_info = Mock(
spec=InstalledAddonComplete,
slug="test",
name="test",
repository="core",
available=False,
hostname="",
options={},
state="unknown",
update_available=False,
version=None,
version="1.0.0",
version_latest="1.0.0",
supervisor_api=False,
supervisor_role="default",
icon=False,
)
addon_info.name = "test"
addon_info.to_dict = MethodType(

View File

@@ -1,11 +1,12 @@
"""Fixtures for Hass.io."""
from collections.abc import Generator
from dataclasses import replace
import os
import re
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, Mock, patch
from aiohasupervisor.models import AddonsStats, AddonState
from aiohasupervisor.models import AddonsStats, AddonState, InstalledAddonComplete
from aiohttp.test_utils import TestClient
import pytest
@@ -80,6 +81,15 @@ def all_setup_requests(
addon_changelog: AsyncMock,
addon_stats: AsyncMock,
jobs_info: AsyncMock,
host_info: AsyncMock,
supervisor_root_info: AsyncMock,
homeassistant_info: AsyncMock,
supervisor_info: AsyncMock,
addons_list: AsyncMock,
network_info: AsyncMock,
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: AsyncMock,
) -> None:
"""Mock all setup requests."""
include_addons = hasattr(request, "param") and request.param.get(
@@ -88,87 +98,26 @@ 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/supervisor/options", json={"result": "ok"})
aioclient_mock.get(
"http://127.0.0.1/info",
json={
"result": "ok",
"data": {
"supervisor": "222",
"homeassistant": "0.110.0",
"hassos": "1.2.3",
},
},
)
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
"disk_free": 1.6,
},
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
)
aioclient_mock.get(
"http://127.0.0.1/os/info",
json={
"result": "ok",
"data": {
"version_latest": "1.0.0",
"version": "1.0.0",
"update_available": False,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={
"result": "ok",
"data": {
"result": "ok",
"version": "1.0.0",
"version_latest": "1.0.0",
"auto_update": True,
"addons": [
{
"name": "test",
"slug": "test",
"update_available": False,
"version": "1.0.0",
"version_latest": "1.0.0",
"repository": "core",
"state": "started",
"icon": False,
},
{
"name": "test2",
"slug": "test2",
"update_available": False,
"version": "1.0.0",
"version_latest": "1.0.0",
"repository": "core",
"state": "started",
"icon": False,
},
]
if include_addons
else [],
},
},
)
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],
version="1.0.0",
version_latest="1.0.0",
update_available=False,
)
addons_list.return_value[1] = replace(
addons_list.return_value[1],
version="1.0.0",
version_latest="1.0.0",
state=AddonState.STARTED,
)
else:
addons_list.return_value = []
addon_installed.return_value.update_available = False
addon_installed.return_value.version = "1.0.0"
addon_installed.return_value.version_latest = "1.0.0"
@@ -177,56 +126,26 @@ def all_setup_requests(
addon_installed.return_value.icon = False
def mock_addon_info(slug: str):
addon = Mock(
spec=InstalledAddonComplete,
to_dict=addon_installed.return_value.to_dict,
**addon_installed.return_value.to_dict(),
)
if slug == "test":
addon_installed.return_value.name = "test"
addon_installed.return_value.slug = "test"
addon_installed.return_value.url = (
"https://github.com/home-assistant/addons/test"
)
addon_installed.return_value.auto_update = True
addon.name = "test"
addon.slug = "test"
addon.url = "https://github.com/home-assistant/addons/test"
addon.auto_update = True
else:
addon_installed.return_value.name = "test2"
addon_installed.return_value.slug = "test2"
addon_installed.return_value.url = "https://github.com"
addon_installed.return_value.auto_update = False
addon.name = "test2"
addon.slug = "test2"
addon.url = "https://github.com"
addon.auto_update = False
return addon_installed.return_value
return addon
addon_installed.side_effect = mock_addon_info
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
async def mock_addon_stats(addon: str) -> AddonsStats:
"""Mock addon stats for test and test2."""
if addon == "test2":
@@ -252,16 +171,6 @@ def all_setup_requests(
)
addon_stats.side_effect = mock_addon_stats
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/jobs/info",

View File

@@ -4,9 +4,10 @@ from dataclasses import replace
from datetime import timedelta
import os
from pathlib import PurePath
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, Mock, patch
from uuid import uuid4
from aiohasupervisor.models import AddonState, InstalledAddonComplete
from aiohasupervisor.models.mounts import (
CIFSMountResponse,
MountsInfo,
@@ -41,133 +42,51 @@ def mock_all(
addon_stats: AsyncMock,
resolution_info: AsyncMock,
jobs_info: AsyncMock,
host_info: AsyncMock,
supervisor_root_info: AsyncMock,
homeassistant_info: AsyncMock,
supervisor_info: AsyncMock,
addons_list: AsyncMock,
network_info: AsyncMock,
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: 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/info",
json={
"result": "ok",
"data": {
"supervisor": "222",
"homeassistant": "0.110.0",
"hassos": "1.2.3",
},
},
)
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
)
aioclient_mock.get(
"http://127.0.0.1/os/info",
json={
"result": "ok",
"data": {
"version_latest": "1.0.0",
"version": "1.0.0",
"update_available": False,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={
"result": "ok",
"data": {
"result": "ok",
"version": "1.0.0",
"version_latest": "1.0.0",
"auto_update": True,
"addons": [
{
"name": "test",
"state": "started",
"slug": "test",
"installed": True,
"update_available": True,
"version": "2.0.0",
"version_latest": "2.0.1",
"repository": "core",
"url": "https://github.com/home-assistant/addons/test",
"icon": False,
},
{
"name": "test2",
"state": "stopped",
"slug": "test2",
"installed": True,
"update_available": False,
"version": "3.1.0",
"version_latest": "3.1.0",
"repository": "core",
"url": "https://github.com",
"icon": False,
},
],
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
def mock_addon_info(slug: str):
addon = Mock(
spec=InstalledAddonComplete,
to_dict=addon_installed.return_value.to_dict,
**addon_installed.return_value.to_dict(),
)
if slug == "test":
addon.name = "test"
addon.slug = "test"
addon.version = "2.0.0"
addon.version_latest = "2.0.1"
addon.update_available = True
addon.state = AddonState.STARTED
addon.url = "https://github.com/home-assistant/addons/test"
addon.auto_update = True
else:
addon.name = "test2"
addon.slug = "test2"
addon.version = "3.1.0"
addon.version_latest = "3.1.0"
addon.update_available = False
addon.state = AddonState.STOPPED
addon.url = "https://github.com"
addon.auto_update = False
return addon
addon_installed.side_effect = mock_addon_info
@pytest.mark.parametrize(

View File

@@ -1,6 +1,7 @@
"""Test websocket API."""
from collections.abc import Generator
from dataclasses import replace
from typing import Any
from unittest.mock import AsyncMock, patch
from uuid import UUID, uuid4
@@ -28,77 +29,24 @@ def mock_all(
supervisor_is_connected: AsyncMock,
resolution_info: AsyncMock,
addon_info: AsyncMock,
host_info: AsyncMock,
supervisor_root_info: AsyncMock,
homeassistant_info: AsyncMock,
supervisor_info: AsyncMock,
addons_list: AsyncMock,
network_info: AsyncMock,
os_info: 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/info",
json={
"result": "ok",
"data": {"supervisor": "222", "homeassistant": "0.110.0", "hassos": None},
},
)
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
)
aioclient_mock.get(
"http://127.0.0.1/os/info",
json={"result": "ok", "data": {"version_latest": "1.0.0"}},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={
"result": "ok",
"data": {
"version": "1.0.0",
"version_latest": "1.0.0",
"auto_update": True,
"addons": [
{
"name": "test",
"state": "started",
"slug": "test",
"installed": True,
"update_available": True,
"icon": False,
"version": "2.0.0",
"version_latest": "2.0.1",
"repository": "core",
"url": "https://github.com/home-assistant/addons/test",
},
],
},
},
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": {}}}
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
@pytest.fixture

View File

@@ -1,8 +1,10 @@
"""Test Supervisor diagnostics."""
from dataclasses import replace
import os
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, Mock, patch
from aiohasupervisor.models import AddonState, InstalledAddonComplete
import pytest
from homeassistant.components.hassio import DOMAIN
@@ -26,135 +28,68 @@ def mock_all(
addon_changelog: AsyncMock,
resolution_info: AsyncMock,
jobs_info: AsyncMock,
host_info: AsyncMock,
supervisor_root_info: AsyncMock,
homeassistant_info: AsyncMock,
supervisor_info: AsyncMock,
addons_list: AsyncMock,
network_info: AsyncMock,
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: 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/info",
json={
"result": "ok",
"data": {
"supervisor": "222",
"homeassistant": "0.110.0",
"hassos": "1.2.3",
},
},
homeassistant_info.return_value = replace(
homeassistant_info.return_value,
version="1.0.0dev221",
version_latest="1.0.0dev222",
update_available=True,
)
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
},
os_info.return_value = replace(
os_info.return_value,
version="1.0.0dev2221",
version_latest="1.0.0dev2222",
update_available=True,
)
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={
"result": "ok",
"data": {"version_latest": "1.0.0dev222", "version": "1.0.0dev221"},
},
)
aioclient_mock.get(
"http://127.0.0.1/os/info",
json={
"result": "ok",
"data": {
"version_latest": "1.0.0dev2222",
"version": "1.0.0dev2221",
"update_available": False,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={
"result": "ok",
"data": {
"result": "ok",
"version": "1.0.0",
"version_latest": "1.0.1dev222",
"addons": [
{
"name": "test",
"state": "started",
"slug": "test",
"installed": True,
"update_available": True,
"icon": False,
"version": "2.0.0",
"version_latest": "2.0.1",
"repository": "core",
"url": "https://github.com/home-assistant/addons/test",
},
{
"name": "test2",
"state": "stopped",
"slug": "test2",
"installed": True,
"update_available": False,
"icon": True,
"version": "3.1.0",
"version_latest": "3.1.0",
"repository": "core",
"url": "https://github.com",
},
],
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
supervisor_info.return_value = replace(
supervisor_info.return_value,
version_latest="1.0.1dev222",
update_available=True,
)
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
def mock_addon_info(slug: str):
addon = Mock(
spec=InstalledAddonComplete,
to_dict=addon_installed.return_value.to_dict,
**addon_installed.return_value.to_dict(),
)
if slug == "test":
addon.name = "test"
addon.slug = "test"
addon.version = "2.0.0"
addon.version_latest = "2.0.1"
addon.update_available = True
addon.state = AddonState.STARTED
addon.url = "https://github.com/home-assistant/addons/test"
addon.auto_update = True
else:
addon.name = "test2"
addon.slug = "test2"
addon.version = "3.1.0"
addon.version_latest = "3.1.0"
addon.update_available = False
addon.state = AddonState.STOPPED
addon.url = "https://github.com"
addon.auto_update = False
return addon
addon_installed.side_effect = mock_addon_info
async def test_diagnostics(

View File

@@ -2,15 +2,15 @@
from collections.abc import Generator
from http import HTTPStatus
from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import AsyncMock, patch
from uuid import uuid4
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import Discovery
from aiohttp.test_utils import TestClient
import pytest
from homeassistant import config_entries
from homeassistant.components.hassio.handler import HassioAPIError
from homeassistant.components.mqtt import DOMAIN as MQTT_DOMAIN
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STARTED
from homeassistant.core import HomeAssistant
@@ -103,6 +103,7 @@ async def test_hassio_discovery_startup_done(
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(
@@ -125,15 +126,12 @@ async def test_hassio_discovery_startup_done(
]
addon_installed.return_value.name = "Mosquitto Test"
supervisor_root_info.side_effect = SupervisorError()
with (
patch(
"homeassistant.components.hassio.HassIO.update_hass_api",
return_value={"result": "ok"},
),
patch(
"homeassistant.components.hassio.HassIO.get_info",
Mock(side_effect=HassioAPIError()),
),
):
await hass.async_start()
await async_setup_component(hass, "hassio", {})

View File

@@ -14,169 +14,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from tests.test_util.aiohttp import AiohttpClientMocker
async def test_api_info(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API generic info."""
aioclient_mock.get(
"http://127.0.0.1/info",
json={
"result": "ok",
"data": {"supervisor": "222", "homeassistant": "0.110.0", "hassos": None},
},
)
data = await hassio_handler.get_info()
assert aioclient_mock.call_count == 1
assert data["hassos"] is None
assert data["homeassistant"] == "0.110.0"
assert data["supervisor"] == "222"
async def test_api_info_error(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Home Assistant info error."""
aioclient_mock.get(
"http://127.0.0.1/info", json={"result": "error", "message": None}
)
with pytest.raises(HassioAPIError):
await hassio_handler.get_info()
assert aioclient_mock.call_count == 1
async def test_api_host_info(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Host info."""
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
)
data = await hassio_handler.get_host_info()
assert aioclient_mock.call_count == 1
assert data["chassis"] == "vm"
assert data["kernel"] == "4.19.0-6-amd64"
assert data["operating_system"] == "Debian GNU/Linux 10 (buster)"
async def test_api_supervisor_info(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Supervisor info."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={
"result": "ok",
"data": {"supported": True, "version": "2020.11.1", "channel": "stable"},
},
)
data = await hassio_handler.get_supervisor_info()
assert aioclient_mock.call_count == 1
assert data["supported"]
assert data["version"] == "2020.11.1"
assert data["channel"] == "stable"
async def test_api_os_info(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API OS info."""
aioclient_mock.get(
"http://127.0.0.1/os/info",
json={
"result": "ok",
"data": {"board": "odroid-n2", "version": "2020.11.1"},
},
)
data = await hassio_handler.get_os_info()
assert aioclient_mock.call_count == 1
assert data["board"] == "odroid-n2"
assert data["version"] == "2020.11.1"
async def test_api_host_info_error(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Home Assistant info error."""
aioclient_mock.get(
"http://127.0.0.1/host/info", json={"result": "error", "message": None}
)
with pytest.raises(HassioAPIError):
await hassio_handler.get_host_info()
assert aioclient_mock.call_count == 1
async def test_api_core_info(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Home Assistant Core info."""
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={"result": "ok", "data": {"version_latest": "1.0.0"}},
)
data = await hassio_handler.get_core_info()
assert aioclient_mock.call_count == 1
assert data["version_latest"] == "1.0.0"
async def test_api_core_info_error(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Home Assistant Core info error."""
aioclient_mock.get(
"http://127.0.0.1/core/info", json={"result": "error", "message": None}
)
with pytest.raises(HassioAPIError):
await hassio_handler.get_core_info()
assert aioclient_mock.call_count == 1
async def test_api_core_stats(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Add-on stats."""
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={"result": "ok", "data": {"memory_percent": 0.01}},
)
data = await hassio_handler.get_core_stats()
assert data["memory_percent"] == 0.01
assert aioclient_mock.call_count == 1
async def test_api_supervisor_stats(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Add-on stats."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={"result": "ok", "data": {"memory_percent": 0.01}},
)
data = await hassio_handler.get_supervisor_stats()
assert data["memory_percent"] == 0.01
assert aioclient_mock.call_count == 1
async def test_api_ingress_panels(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
@@ -207,7 +44,7 @@ async def test_api_ingress_panels(
@pytest.mark.parametrize(
("api_call", "method", "payload"),
[
("get_network_info", "GET", None),
("get_ingress_panels", "GET", None),
],
)
@pytest.mark.usefixtures("socket_enabled")

View File

@@ -1,15 +1,20 @@
"""The tests for the hassio component."""
from dataclasses import replace
from datetime import timedelta
import os
from pathlib import PurePath
from typing import Any
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, Mock, patch
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import AddonsStats
from aiohasupervisor.models.mounts import (
from aiohasupervisor.models import (
AddonsStats,
AddonStage,
AddonState,
CIFSMountResponse,
InstalledAddon,
InstalledAddonComplete,
MountsInfo,
MountState,
MountType,
@@ -52,135 +57,42 @@ from tests.test_util.aiohttp import AiohttpClientMocker
MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@pytest.fixture
def extra_os_info():
"""Extra os/info."""
return {}
@pytest.fixture
def os_info(extra_os_info):
"""Mock os/info."""
return {
"json": {
"result": "ok",
"data": {"version_latest": "1.0.0", "version": "1.0.0", **extra_os_info},
}
}
@pytest.fixture(autouse=True)
def mock_all(
aioclient_mock: AiohttpClientMocker,
os_info: AsyncMock,
store_info: AsyncMock,
addon_info: AsyncMock,
addon_stats: AsyncMock,
addon_changelog: AsyncMock,
resolution_info: AsyncMock,
jobs_info: AsyncMock,
host_info: AsyncMock,
supervisor_root_info: AsyncMock,
homeassistant_info: AsyncMock,
supervisor_info: AsyncMock,
addons_list: AsyncMock,
network_info: AsyncMock,
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: AsyncMock,
addon_installed: 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/info",
json={
"result": "ok",
"data": {
"supervisor": "222",
"homeassistant": "0.110.0",
"hassos": "1.2.3",
},
},
addons_list.return_value[0] = replace(
addons_list.return_value[0],
version="1.0.0",
version_latest="1.0.0",
update_available=False,
state=AddonState.STOPPED,
)
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
)
aioclient_mock.get(
"http://127.0.0.1/os/info",
**os_info,
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={
"result": "ok",
"data": {
"version_latest": "1.0.0",
"version": "1.0.0",
"auto_update": True,
"addons": [
{
"name": "test",
"slug": "test",
"state": "stopped",
"update_available": False,
"version": "1.0.0",
"version_latest": "1.0.0",
"repository": "core",
"icon": False,
},
{
"name": "test2",
"slug": "test2",
"state": "stopped",
"update_available": False,
"version": "1.0.0",
"version_latest": "1.0.0",
"repository": "core",
"icon": False,
},
],
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
addons_list.return_value[1] = replace(
addons_list.return_value[1],
version="1.0.0",
version_latest="1.0.0",
)
addon_installed.return_value.state = AddonState.STOPPED
async def mock_addon_stats(addon: str) -> AddonsStats:
"""Mock addon stats for test and test2."""
@@ -209,23 +121,28 @@ def mock_all(
addon_stats.side_effect = mock_addon_stats
def mock_addon_info(slug: str):
addon_info.return_value.auto_update = slug == "test"
return addon_info.return_value
addon = Mock(
spec=InstalledAddonComplete,
to_dict=addon_installed.return_value.to_dict,
**addon_installed.return_value.to_dict(),
)
if slug == "test":
addon.name = "test"
addon.slug = "test"
addon.url = "https://github.com/home-assistant/addons/test"
addon.auto_update = True
else:
addon.name = "test2"
addon.slug = "test2"
addon.url = "https://github.com"
addon.auto_update = False
return addon
addon_info.side_effect = mock_addon_info
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
async def test_setup_api_ping(
@@ -239,7 +156,7 @@ async def test_setup_api_ping(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert get_core_info(hass)["version_latest"] == "1.0.0"
assert is_hassio(hass)
@@ -310,7 +227,7 @@ 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) == 20
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert not aioclient_mock.mock_calls[0][2]["ssl"]
assert aioclient_mock.mock_calls[0][2]["port"] == 9999
assert "watchdog" not in aioclient_mock.mock_calls[0][2]
@@ -331,7 +248,7 @@ 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) == 20
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert not aioclient_mock.mock_calls[0][2]["ssl"]
assert aioclient_mock.mock_calls[0][2]["port"] == 9999
assert not aioclient_mock.mock_calls[0][2]["watchdog"]
@@ -352,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) == 20
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert not aioclient_mock.mock_calls[0][2]["ssl"]
assert aioclient_mock.mock_calls[0][2]["port"] == 8123
refresh_token = aioclient_mock.mock_calls[0][2]["refresh_token"]
@@ -433,7 +350,7 @@ 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) == 20
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert not aioclient_mock.mock_calls[0][2]["ssl"]
assert aioclient_mock.mock_calls[0][2]["port"] == 8123
assert aioclient_mock.mock_calls[0][2]["refresh_token"] == token.token
@@ -452,7 +369,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) == 20
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert aioclient_mock.mock_calls[1][2]["timezone"] == "testzone"
with patch("homeassistant.util.dt.set_default_time_zone"):
@@ -476,7 +393,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) == 20
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert aioclient_mock.mock_calls[-1][3]["Authorization"] == "Bearer 123456"
@@ -536,7 +453,6 @@ async def test_service_calls(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
addon_installed: AsyncMock,
supervisor_is_connected: AsyncMock,
app_or_addon: str,
) -> None:
@@ -576,14 +492,14 @@ async def test_service_calls(
)
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) == 27
assert aioclient_mock.mock_calls[-1][2] == "test"
await hass.services.async_call("hassio", "host_shutdown", {})
await hass.services.async_call("hassio", "host_reboot", {})
await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 26
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 29
await hass.services.async_call("hassio", "backup_full", {})
await hass.services.async_call(
@@ -598,7 +514,7 @@ async def test_service_calls(
)
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) == 31
# API receives "addons" even when we pass "apps"
assert aioclient_mock.mock_calls[-1][2] == {
"name": "2021-11-13 03:48:00",
@@ -624,7 +540,7 @@ async def test_service_calls(
)
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) == 33
# API receives "addons" even when we pass "apps"
assert aioclient_mock.mock_calls[-1][2] == {
"addons": ["test"],
@@ -644,7 +560,7 @@ async def test_service_calls(
)
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) == 34
assert aioclient_mock.mock_calls[-1][2] == {
"name": "backup_name",
"location": "backup_share",
@@ -660,7 +576,7 @@ async def test_service_calls(
)
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) == 35
assert aioclient_mock.mock_calls[-1][2] == {
"name": "2021-11-13 03:48:00",
"location": None,
@@ -679,7 +595,7 @@ async def test_service_calls(
)
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) == 37
assert aioclient_mock.mock_calls[-1][2] == {
"name": "2021-11-13 11:48:00",
"location": None,
@@ -750,38 +666,37 @@ async def test_service_calls_apps_addons_exclusive(
"app_or_addon",
["app", "addon"],
)
@pytest.mark.usefixtures("aioclient_mock")
async def test_addon_service_call_with_complex_slug(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_is_connected: AsyncMock,
app_or_addon: str,
addons_list: AsyncMock,
) -> None:
"""Addon slugs can have ., - and _, confirm that passes validation."""
supervisor_mock_data = {
"version_latest": "1.0.0",
"version": "1.0.0",
"auto_update": True,
"addons": [
{
"name": "test.a_1-2",
"slug": "test.a_1-2",
"state": "stopped",
"update_available": False,
"version": "1.0.0",
"version_latest": "1.0.0",
"repository": "core",
"icon": False,
},
],
}
addons_list.return_value = [
InstalledAddon(
detached=False,
advanced=False,
available=True,
build=False,
description="",
homeassistant=None,
icon=False,
logo=False,
name="test.a_1-2",
repository="core",
slug="test.a_1-2",
stage=AddonStage.STABLE,
update_available=False,
url="https://github.com",
version_latest="1.0.0",
version="1.0.0",
state=AddonState.STOPPED,
)
]
supervisor_is_connected.side_effect = SupervisorError
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.hassio.HassIO.get_supervisor_info",
return_value=supervisor_mock_data,
),
):
with patch.dict(os.environ, MOCK_ENVIRON):
assert await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done()
@@ -806,12 +721,12 @@ async def test_service_calls_core(
await hass.services.async_call("homeassistant", "stop")
await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 6
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20
await hass.services.async_call("homeassistant", "check_config")
await hass.async_block_till_done()
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 6
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 20
with patch(
"homeassistant.config.async_check_ha_config_file", return_value=None
@@ -820,7 +735,7 @@ async def test_service_calls_core(
await hass.async_block_till_done()
assert mock_check_config.called
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 7
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 21
@pytest.mark.usefixtures("addon_installed")
@@ -850,157 +765,79 @@ async def test_migration_off_hassio(hass: HomeAssistant) -> None:
assert hass.config_entries.async_entries(DOMAIN) == []
@pytest.mark.usefixtures("addon_installed")
@pytest.mark.usefixtures("addon_installed", "supervisor_info")
async def test_device_registry_calls(
hass: HomeAssistant, device_registry: dr.DeviceRegistry
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
addons_list: AsyncMock,
os_info: AsyncMock,
) -> None:
"""Test device registry entries for hassio."""
supervisor_mock_data = {
"version": "1.0.0",
"version_latest": "1.0.0",
"auto_update": True,
"addons": [
{
"name": "test",
"state": "started",
"slug": "test",
"installed": True,
"icon": False,
"update_available": False,
"version": "1.0.0",
"version_latest": "1.0.0",
"repository": "test",
"url": "https://github.com/home-assistant/addons/test",
},
{
"name": "test2",
"state": "started",
"slug": "test2",
"installed": True,
"icon": False,
"update_available": False,
"version": "1.0.0",
"version_latest": "1.0.0",
"url": "https://github.com",
},
],
}
os_mock_data = {
"board": "odroid-n2",
"boot": "A",
"update_available": False,
"version": "5.12",
"version_latest": "5.12",
}
addons_list.return_value[0] = replace(
addons_list.return_value[0],
version="1.0.0",
version_latest="1.0.0",
update_available=False,
)
addons_list.return_value[1] = replace(
addons_list.return_value[1],
version="1.0.0",
version_latest="1.0.0",
state=AddonState.STARTED,
)
os_info.return_value = replace(
os_info.return_value,
board="odroid-n2",
boot="A",
version="5.12",
version_latest="5.12",
)
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.hassio.HassIO.get_supervisor_info",
return_value=supervisor_mock_data,
),
patch(
"homeassistant.components.hassio.HassIO.get_os_info",
return_value=os_mock_data,
),
):
with patch.dict(os.environ, MOCK_ENVIRON):
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done(wait_background_tasks=True)
assert len(device_registry.devices) == 6
supervisor_mock_data = {
"version": "1.0.0",
"version_latest": "1.0.0",
"auto_update": True,
"addons": [
{
"name": "test2",
"state": "started",
"slug": "test2",
"installed": True,
"icon": False,
"update_available": False,
"version": "1.0.0",
"version_latest": "1.0.0",
"url": "https://github.com",
},
],
}
addons_list.return_value.pop(0)
# Test that when addon is removed, next update will remove the add-on and subsequent updates won't
with (
patch(
"homeassistant.components.hassio.HassIO.get_supervisor_info",
return_value=supervisor_mock_data,
),
patch(
"homeassistant.components.hassio.HassIO.get_os_info",
return_value=os_mock_data,
),
):
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=1))
await hass.async_block_till_done(wait_background_tasks=True)
assert len(device_registry.devices) == 5
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=1))
await hass.async_block_till_done(wait_background_tasks=True)
assert len(device_registry.devices) == 5
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=2))
await hass.async_block_till_done(wait_background_tasks=True)
assert len(device_registry.devices) == 5
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=2))
await hass.async_block_till_done(wait_background_tasks=True)
assert len(device_registry.devices) == 5
supervisor_mock_data = {
"version": "1.0.0",
"version_latest": "1.0.0",
"auto_update": True,
"addons": [
{
"name": "test2",
"slug": "test2",
"state": "started",
"installed": True,
"icon": False,
"update_available": False,
"version": "1.0.0",
"version_latest": "1.0.0",
"url": "https://github.com",
},
{
"name": "test3",
"slug": "test3",
"state": "stopped",
"installed": True,
"icon": False,
"update_available": False,
"version": "1.0.0",
"version_latest": "1.0.0",
"url": "https://github.com",
},
],
}
addons_list.return_value.append(
InstalledAddon(
detached=False,
advanced=False,
available=True,
build=False,
description="",
homeassistant=None,
icon=False,
logo=False,
name="test3",
repository="core",
slug="test3",
stage=AddonStage.STABLE,
update_available=False,
url="https://github.com",
version_latest="1.0.0",
version="1.0.0",
state=AddonState.STOPPED,
)
)
# Test that when addon is added, next update will reload the entry so we register
# a new device
with (
patch(
"homeassistant.components.hassio.HassIO.get_supervisor_info",
return_value=supervisor_mock_data,
),
patch(
"homeassistant.components.hassio.HassIO.get_os_info",
return_value=os_mock_data,
),
patch(
"homeassistant.components.hassio.HassIO.get_info",
return_value={
"supervisor": "222",
"homeassistant": "0.110.0",
"hassos": None,
},
),
):
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=3))
await hass.async_block_till_done()
assert len(device_registry.devices) == 5
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=3))
await hass.async_block_till_done()
assert len(device_registry.devices) == 5
@pytest.mark.usefixtures("addon_installed")
@@ -1137,28 +974,31 @@ async def test_coordinator_updates_stats_entities_enabled(
@pytest.mark.parametrize(
("extra_os_info", "integration"),
("board", "integration"),
[
({"board": "green"}, "homeassistant_green"),
({"board": "odroid-c2"}, "hardkernel"),
({"board": "odroid-c4"}, "hardkernel"),
({"board": "odroid-n2"}, "hardkernel"),
({"board": "odroid-xu4"}, "hardkernel"),
({"board": "rpi2"}, "raspberry_pi"),
({"board": "rpi3"}, "raspberry_pi"),
({"board": "rpi3-64"}, "raspberry_pi"),
({"board": "rpi4"}, "raspberry_pi"),
({"board": "rpi4-64"}, "raspberry_pi"),
({"board": "yellow"}, "homeassistant_yellow"),
("green", "homeassistant_green"),
("odroid-c2", "hardkernel"),
("odroid-c4", "hardkernel"),
("odroid-n2", "hardkernel"),
("odroid-xu4", "hardkernel"),
("rpi2", "raspberry_pi"),
("rpi3", "raspberry_pi"),
("rpi3-64", "raspberry_pi"),
("rpi4", "raspberry_pi"),
("rpi4-64", "raspberry_pi"),
("yellow", "homeassistant_yellow"),
],
)
async def test_setup_hardware_integration(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
integration,
os_info: AsyncMock,
board: str,
integration: str,
) -> None:
"""Test setup initiates hardware integration."""
os_info.return_value = replace(os_info.return_value, board=board)
with (
patch.dict(os.environ, MOCK_ENVIRON),
@@ -1175,7 +1015,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) == 20
assert aioclient_mock.call_count + len(supervisor_client.mock_calls) == 23
assert len(mock_setup_entry.mock_calls) == 1

View File

@@ -1047,13 +1047,16 @@ async def test_supervisor_issues_free_space(
)
@pytest.mark.usefixtures("all_setup_requests")
async def test_supervisor_issues_free_space_host_info_fail(
hass: HomeAssistant,
supervisor_client: AsyncMock,
hass_ws_client: WebSocketGenerator,
host_info: AsyncMock,
) -> None:
"""Test supervisor issue for too little free space remaining without host info."""
mock_resolution_info(supervisor_client)
host_info.side_effect = SupervisorError()
result = await async_setup_component(hass, "hassio", {})
assert result

View File

@@ -1,10 +1,12 @@
"""The tests for the hassio sensors."""
from dataclasses import replace
from datetime import timedelta
import os
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, Mock, patch
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import AddonState, InstalledAddonComplete
from freezegun.api import FrozenDateTimeFactory
import pytest
@@ -35,130 +37,57 @@ def mock_all(
addon_changelog: AsyncMock,
resolution_info: AsyncMock,
jobs_info: AsyncMock,
host_info: AsyncMock,
supervisor_root_info: AsyncMock,
homeassistant_info: AsyncMock,
supervisor_info: AsyncMock,
addons_list: AsyncMock,
network_info: AsyncMock,
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: AsyncMock,
) -> None:
"""Mock all setup requests."""
_install_default_mocks(aioclient_mock)
def _install_default_mocks(aioclient_mock: AiohttpClientMocker):
"""Install default mocks."""
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/info",
json={
"result": "ok",
"data": {
"supervisor": "222",
"homeassistant": "0.110.0",
"hassos": "1.2.3",
},
},
)
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"agent_version": "1.0.0",
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
)
aioclient_mock.get(
"http://127.0.0.1/os/info",
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={
"result": "ok",
"data": {
"result": "ok",
"version": "1.0.0",
"version_latest": "1.0.0",
"auto_update": True,
"addons": [
{
"name": "test",
"state": "started",
"slug": "test",
"installed": True,
"update_available": False,
"version": "2.0.0",
"version_latest": "2.0.1",
"repository": "core",
"url": "https://github.com/home-assistant/addons/test",
"icon": False,
},
{
"name": "test2",
"state": "stopped",
"slug": "test2",
"installed": True,
"update_available": False,
"version": "3.1.0",
"version_latest": "3.2.0",
"repository": "core",
"url": "https://github.com",
"icon": False,
},
],
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
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
)
def mock_addon_info(slug: str):
addon = Mock(
spec=InstalledAddonComplete,
to_dict=addon_installed.return_value.to_dict,
**addon_installed.return_value.to_dict(),
)
if slug == "test":
addon.name = "test"
addon.slug = "test"
addon.version = "2.0.0"
addon.version_latest = "2.0.1"
addon.update_available = True
addon.state = AddonState.STARTED
addon.url = "https://github.com/home-assistant/addons/test"
addon.auto_update = True
else:
addon.name = "test2"
addon.slug = "test2"
addon.version = "3.1.0"
addon.version_latest = "3.2.0"
addon.update_available = True
addon.state = AddonState.STOPPED
addon.url = "https://github.com"
addon.auto_update = False
return addon
addon_installed.side_effect = mock_addon_info
@pytest.mark.parametrize(
("store_addons", "store_repositories"), [(MOCK_STORE_ADDONS, MOCK_REPOSITORIES)]
@@ -256,20 +185,14 @@ async def test_stats_addon_sensor(
# Verify that the entity is disabled by default.
assert hass.states.get(entity_id) is None
aioclient_mock.clear_requests()
_install_default_mocks(aioclient_mock)
addon_stats.side_effect = SupervisorError
freezer.tick(HASSIO_UPDATE_INTERVAL + timedelta(seconds=1))
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
assert "Could not fetch stats" not in caplog.text
aioclient_mock.clear_requests()
_install_default_mocks(aioclient_mock)
addon_stats.side_effect = None
freezer.tick(HASSIO_UPDATE_INTERVAL + timedelta(seconds=1))
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
@@ -299,10 +222,7 @@ async def test_stats_addon_sensor(
state = hass.states.get(entity_id)
assert state.state == expected
aioclient_mock.clear_requests()
_install_default_mocks(aioclient_mock)
addon_stats.side_effect = SupervisorError
freezer.tick(HASSIO_UPDATE_INTERVAL + timedelta(seconds=1))
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)

View File

@@ -1,9 +1,11 @@
"""The tests for the hassio switch."""
from collections.abc import AsyncGenerator
from dataclasses import replace
import os
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, Mock, patch
from aiohasupervisor.models import AddonState, InstalledAddonComplete
import pytest
from homeassistant.components.hassio import DOMAIN
@@ -61,134 +63,55 @@ def mock_all(
addon_stats: AsyncMock,
resolution_info: AsyncMock,
jobs_info: AsyncMock,
host_info: AsyncMock,
supervisor_root_info: AsyncMock,
homeassistant_info: AsyncMock,
supervisor_info: AsyncMock,
addons_list: AsyncMock,
network_info: AsyncMock,
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: 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/info",
json={
"result": "ok",
"data": {
"supervisor": "222",
"homeassistant": "0.110.0",
"hassos": "1.2.3",
},
},
)
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
)
aioclient_mock.get(
"http://127.0.0.1/os/info",
json={
"result": "ok",
"data": {
"version_latest": "1.0.0",
"version": "1.0.0",
"update_available": False,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={
"result": "ok",
"data": {
"result": "ok",
"version": "1.0.0",
"version_latest": "1.0.0",
"auto_update": True,
"addons": [
{
"name": "test",
"state": "started",
"slug": "test",
"installed": True,
"update_available": True,
"icon": False,
"version": "2.0.0",
"version_latest": "2.0.1",
"repository": "core",
"url": "https://github.com/home-assistant/addons/test",
},
{
"name": "test-two",
"state": "stopped",
"slug": "test-two",
"installed": True,
"update_available": False,
"icon": True,
"version": "3.1.0",
"version_latest": "3.1.0",
"repository": "core",
"url": "https://github.com",
},
],
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
addons_list.return_value[1] = replace(
addons_list.return_value[1], name="test-two", slug="test-two"
)
def mock_addon_info(slug: str):
addon = Mock(
spec=InstalledAddonComplete,
to_dict=addon_installed.return_value.to_dict,
**addon_installed.return_value.to_dict(),
)
if slug == "test":
addon.name = "test"
addon.slug = "test"
addon.version = "2.0.0"
addon.version_latest = "2.0.1"
addon.update_available = True
addon.state = AddonState.STARTED
addon.url = "https://github.com/home-assistant/addons/test"
addon.auto_update = True
else:
addon.name = "test-two"
addon.slug = "test-two"
addon.version = "3.1.0"
addon.version_latest = "3.1.0"
addon.update_available = False
addon.state = AddonState.STOPPED
addon.url = "https://github.com"
addon.auto_update = False
return addon
addon_installed.side_effect = mock_addon_info
@pytest.mark.parametrize(
("store_addons", "store_repositories"), [(MOCK_STORE_ADDONS, MOCK_REPOSITORIES)]

View File

@@ -5,6 +5,7 @@ import os
from unittest.mock import patch
from aiohttp import ClientError
import pytest
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
@@ -15,18 +16,15 @@ from tests.common import get_system_health_info
from tests.test_util.aiohttp import AiohttpClientMocker
@pytest.mark.usefixtures(
"supervisor_root_info", "host_info", "os_info", "supervisor_info"
)
async def test_hassio_system_health(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test hassio system health."""
aioclient_mock.get("http://127.0.0.1/info", json={"result": "ok", "data": {}})
aioclient_mock.get("http://127.0.0.1/host/info", json={"result": "ok", "data": {}})
aioclient_mock.get("http://127.0.0.1/os/info", json={"result": "ok", "data": {}})
aioclient_mock.get("http://127.0.0.1/supervisor/ping", text="")
aioclient_mock.get("https://version.home-assistant.io/stable.json", text="")
aioclient_mock.get(
"http://127.0.0.1/supervisor/info", json={"result": "ok", "data": {}}
)
hass.config.components.add("hassio")
assert await async_setup_component(hass, "system_health", {})
@@ -50,8 +48,21 @@ async def test_hassio_system_health(
hass.data["hassio_supervisor_info"] = {
"healthy": True,
"supported": True,
"addons": [{"name": "Awesome Addon", "version": "1.0.0"}],
}
hass.data["hassio_addons_info"] = {
"test": {
"name": "Awesome Addon",
"slug": "test",
"version": "1.0.0",
}
}
hass.data["hassio_addons_list"] = [
{
"slug": "test",
"name": "Awesome Addon",
"version": "1.0.0",
}
]
hass.data["hassio_network_info"] = {
"host_internet": True,
"supervisor_internet": True,
@@ -90,18 +101,15 @@ async def test_hassio_system_health(
}
@pytest.mark.usefixtures(
"supervisor_root_info", "host_info", "os_info", "supervisor_info"
)
async def test_hassio_system_health_with_issues(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test hassio system health."""
aioclient_mock.get("http://127.0.0.1/info", json={"result": "ok", "data": {}})
aioclient_mock.get("http://127.0.0.1/host/info", json={"result": "ok", "data": {}})
aioclient_mock.get("http://127.0.0.1/os/info", json={"result": "ok", "data": {}})
aioclient_mock.get("http://127.0.0.1/supervisor/ping", text="")
aioclient_mock.get("https://version.home-assistant.io/stable.json", exc=ClientError)
aioclient_mock.get(
"http://127.0.0.1/supervisor/info", json={"result": "ok", "data": {}}
)
hass.config.components.add("hassio")
assert await async_setup_component(hass, "system_health", {})

View File

@@ -1,9 +1,10 @@
"""The tests for the hassio update entities."""
from dataclasses import replace
from datetime import datetime, timedelta
import os
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch
from unittest.mock import AsyncMock, MagicMock, Mock, patch
from uuid import uuid4
from aiohasupervisor import (
@@ -12,7 +13,9 @@ from aiohasupervisor import (
SupervisorNotFoundError,
)
from aiohasupervisor.models import (
AddonState,
HomeAssistantUpdateOptions,
InstalledAddonComplete,
Job,
JobsInfo,
OSUpdate,
@@ -48,136 +51,68 @@ def mock_all(
addon_changelog: AsyncMock,
resolution_info: AsyncMock,
jobs_info: AsyncMock,
host_info: AsyncMock,
supervisor_root_info: AsyncMock,
homeassistant_info: AsyncMock,
supervisor_info: AsyncMock,
addons_list: AsyncMock,
network_info: AsyncMock,
os_info: AsyncMock,
homeassistant_stats: AsyncMock,
supervisor_stats: 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/info",
json={
"result": "ok",
"data": {
"supervisor": "222",
"homeassistant": "0.110.0",
"hassos": "1.2.3",
},
},
homeassistant_info.return_value = replace(
homeassistant_info.return_value,
version="1.0.0dev221",
version_latest="1.0.0dev222",
update_available=True,
)
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
},
os_info.return_value = replace(
os_info.return_value,
version="1.0.0dev2221",
version_latest="1.0.0dev2222",
update_available=True,
)
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={
"result": "ok",
"data": {"version_latest": "1.0.0dev222", "version": "1.0.0dev221"},
},
)
aioclient_mock.get(
"http://127.0.0.1/os/info",
json={
"result": "ok",
"data": {
"version_latest": "1.0.0dev2222",
"version": "1.0.0dev2221",
"update_available": False,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={
"result": "ok",
"data": {
"result": "ok",
"version": "1.0.0",
"version_latest": "1.0.1dev222",
"auto_update": True,
"addons": [
{
"name": "test",
"state": "started",
"slug": "test",
"installed": True,
"update_available": True,
"icon": False,
"version": "2.0.0",
"version_latest": "2.0.1",
"repository": "core",
"url": "https://github.com/home-assistant/addons/test",
},
{
"name": "test2",
"state": "stopped",
"slug": "test2",
"installed": True,
"update_available": False,
"icon": True,
"version": "3.1.0",
"version_latest": "3.1.0",
"repository": "core",
"url": "https://github.com",
},
],
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
supervisor_info.return_value = replace(
supervisor_info.return_value,
version_latest="1.0.1dev222",
update_available=True,
)
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
def mock_addon_info(slug: str):
addon = Mock(
spec=InstalledAddonComplete,
to_dict=addon_installed.return_value.to_dict,
**addon_installed.return_value.to_dict(),
)
if slug == "test":
addon.name = "test"
addon.slug = "test"
addon.version = "2.0.0"
addon.version_latest = "2.0.1"
addon.update_available = True
addon.state = AddonState.STARTED
addon.url = "https://github.com/home-assistant/addons/test"
addon.auto_update = True
else:
addon.name = "test2"
addon.slug = "test2"
addon.version = "3.1.0"
addon.version_latest = "3.1.0"
addon.update_available = False
addon.state = AddonState.STOPPED
addon.url = "https://github.com"
addon.auto_update = False
return addon
addon_installed.side_effect = mock_addon_info
@pytest.mark.parametrize(
@@ -1588,19 +1523,14 @@ async def test_not_release_notes(
assert result["result"] is None
async def test_no_os_entity(hass: HomeAssistant) -> None:
async def test_no_os_entity(
hass: HomeAssistant, supervisor_root_info: AsyncMock
) -> None:
"""Test handling where there is no os entity."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.hassio.HassIO.get_info",
return_value={
"supervisor": "222",
"homeassistant": "0.110.0",
"hassos": None,
},
),
):
supervisor_root_info.return_value = replace(
supervisor_root_info.return_value, hassos=None
)
with patch.dict(os.environ, MOCK_ENVIRON):
result = await async_setup_component(
hass,
"hassio",
@@ -1624,9 +1554,7 @@ async def test_setting_up_core_update_when_addon_fails(
addon_installed.side_effect = SupervisorBadRequestError("Addon Test does not exist")
addon_stats.side_effect = SupervisorBadRequestError("add-on is not running")
addon_changelog.side_effect = SupervisorBadRequestError("add-on is not running")
with (
patch.dict(os.environ, MOCK_ENVIRON),
):
with patch.dict(os.environ, MOCK_ENVIRON):
result = await async_setup_component(
hass,
"hassio",

View File

@@ -1,5 +1,6 @@
"""Test websocket API."""
from dataclasses import replace
import os
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch
@@ -44,39 +45,31 @@ def mock_all(
supervisor_is_connected: AsyncMock,
resolution_info: AsyncMock,
addon_info: AsyncMock,
host_info: AsyncMock,
supervisor_root_info: AsyncMock,
homeassistant_info: AsyncMock,
supervisor_info: AsyncMock,
addons_list: AsyncMock,
network_info: AsyncMock,
os_info: AsyncMock,
store_info: 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/info",
json={
"result": "ok",
"data": {"supervisor": "222", "homeassistant": "0.110.0", "hassos": None},
},
supervisor_root_info.return_value = replace(
supervisor_root_info.return_value, hassos=None
)
addons_list.return_value.pop(1)
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/host/info",
json={
"result": "ok",
"data": {
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
)
aioclient_mock.get(
"http://127.0.0.1/os/info",
json={"result": "ok", "data": {"version_latest": "1.0.0"}},
"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
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={
@@ -102,19 +95,6 @@ def mock_all(
},
},
)
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
@pytest.mark.usefixtures("hassio_env")

View File

@@ -245,14 +245,15 @@ async def test_ip_ban_manager_never_started(
)
),
)
@pytest.mark.usefixtures(
"hassio_env", "resolution_info", "os_info", "store_info", "supervisor_info"
)
async def test_access_from_supervisor_ip(
remote_addr,
bans,
status,
hass: HomeAssistant,
aiohttp_client: ClientSessionGenerator,
hassio_env,
resolution_info: AsyncMock,
) -> None:
"""Test accessing to server from supervisor IP."""
app = web.Application()

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.humidifier.const import ATTR_ACTION, HumidifierAction
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.const import CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -37,13 +38,7 @@ async def test_humidifier_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the humidifier triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -12,17 +12,13 @@ from homeassistant.components.humidifier import (
ATTR_CURRENT_HUMIDITY as HUMIDIFIER_ATTR_CURRENT_HUMIDITY,
)
from homeassistant.components.weather import ATTR_WEATHER_HUMIDITY
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_ON,
)
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_numerical_attribute_changed_trigger_states,
parametrize_numerical_attribute_crossed_threshold_trigger_states,
parametrize_numerical_state_value_changed_trigger_states,
@@ -68,13 +64,7 @@ async def test_humidity_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the humidity triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
# --- Sensor domain tests (value in state.state) ---

View File

@@ -74,6 +74,5 @@ def mock_config_entry() -> MockConfigEntry:
CONF_USERNAME: "huum@sauna.org",
CONF_PASSWORD: "ukuuku",
},
unique_id="123456",
entry_id="AABBCC112233",
)

View File

@@ -28,7 +28,7 @@ async def test_form(
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
result2 = await hass.config_entries.flow.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: TEST_USERNAME,
@@ -37,9 +37,9 @@ async def test_form(
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == TEST_USERNAME
assert result2["data"] == {
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == TEST_USERNAME
assert result["data"] == {
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
}
@@ -59,7 +59,7 @@ async def test_signup_flow_already_set_up(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
result2 = await hass.config_entries.flow.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: TEST_USERNAME,
@@ -67,7 +67,7 @@ async def test_signup_flow_already_set_up(
},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.ABORT
assert result["type"] is FlowResultType.ABORT
@pytest.mark.parametrize(
@@ -96,7 +96,7 @@ async def test_huum_errors(
"homeassistant.components.huum.config_flow.Huum.status",
side_effect=raises,
):
result2 = await hass.config_entries.flow.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: TEST_USERNAME,
@@ -104,14 +104,14 @@ async def test_huum_errors(
},
)
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": error_base}
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": error_base}
result2 = await hass.config_entries.flow.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
},
)
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result["type"] is FlowResultType.CREATE_ENTRY

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.input_boolean import DOMAIN
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.const import CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -35,13 +36,7 @@ async def test_input_boolean_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the input_boolean triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.lawn_mower import LawnMowerActivity
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID
from homeassistant.const import CONF_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
other_states,
parametrize_target_entities,
parametrize_trigger_states,
@@ -39,13 +40,7 @@ async def test_lawn_mower_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the lawn mower triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -6,7 +6,6 @@ import pytest
from homeassistant.components.light import ATTR_BRIGHTNESS
from homeassistant.const import (
ATTR_LABEL_ID,
CONF_ABOVE,
CONF_BELOW,
CONF_ENTITY_ID,
@@ -24,6 +23,7 @@ from homeassistant.helpers.trigger import (
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -175,13 +175,7 @@ async def test_light_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the light triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.lock import DOMAIN, LockState
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID
from homeassistant.const import CONF_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
other_states,
parametrize_target_entities,
parametrize_trigger_states,
@@ -38,13 +39,7 @@ async def test_lock_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the lock triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.media_player import MediaPlayerState
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID
from homeassistant.const import CONF_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -34,13 +35,7 @@ async def test_media_player_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the media player triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -4,18 +4,13 @@ from typing import Any
import pytest
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_OFF,
STATE_ON,
)
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -40,13 +35,7 @@ async def test_motion_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the motion triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -4,18 +4,13 @@ from typing import Any
import pytest
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_OFF,
STATE_ON,
)
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -40,13 +35,7 @@ async def test_occupancy_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the occupancy triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -2,6 +2,7 @@
import asyncio
from collections.abc import AsyncGenerator
from dataclasses import replace
from http import HTTPStatus
import os
from typing import Any
@@ -54,15 +55,14 @@ async def rpi_fixture(
@pytest.fixture(name="no_rpi")
async def no_rpi_fixture(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, mock_supervisor
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
homeassistant_info: AsyncMock,
mock_supervisor,
) -> 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": "odroid-n2"},
},
homeassistant_info.return_value = replace(
homeassistant_info.return_value, machine="odroid-n2"
)
assert await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done()
@@ -74,38 +74,20 @@ async def mock_supervisor_fixture(
store_info: AsyncMock,
supervisor_is_connected: AsyncMock,
resolution_info: AsyncMock,
supervisor_root_info: AsyncMock,
host_info: AsyncMock,
supervisor_info: AsyncMock,
network_info: AsyncMock,
os_info: 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"})
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
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_info",
return_value={},
),
patch(
"homeassistant.components.hassio.HassIO.get_host_info",
return_value={},
),
patch(
"homeassistant.components.hassio.HassIO.get_supervisor_info",
return_value={"diagnostics": True},
),
patch(
"homeassistant.components.hassio.HassIO.get_os_info",
return_value={},
),
patch(
"homeassistant.components.hassio.HassIO.get_ingress_panels",
return_value={"panels": {}},

View File

@@ -5,17 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.person.const import DOMAIN
from homeassistant.const import (
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_HOME,
STATE_NOT_HOME,
)
from homeassistant.const import CONF_ENTITY_ID, STATE_HOME, STATE_NOT_HOME
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -39,13 +35,7 @@ async def test_person_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the person triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -53,10 +53,10 @@ def mock_pterodactyl() -> Generator[AsyncMock]:
mock.return_value.client.servers.list_servers.return_value = PaginatedResponse(
mock.return_value, "client", server_list_data
)
mock.return_value.client.servers.get_server.side_effect = [
server_1_data,
server_2_data,
]
server_data = {"1": server_1_data, "2": server_2_data}
mock.return_value.client.servers.get_server.side_effect = lambda identifier: (
server_data[identifier]
)
mock.return_value.client.servers.get_server_utilization.return_value = (
utilization_data
)

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.remote import DOMAIN
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.const import CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -32,13 +33,7 @@ async def test_remote_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the remote triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -2,17 +2,13 @@
import pytest
from homeassistant.const import (
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.const import CONF_ENTITY_ID, STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
set_or_remove_state,
target_entities,
@@ -30,13 +26,7 @@ async def test_scene_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the scene triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -14,7 +14,6 @@ from homeassistant.components.schedule.const import (
DOMAIN,
)
from homeassistant.const import (
ATTR_LABEL_ID,
CONF_ENTITY_ID,
CONF_ICON,
CONF_NAME,
@@ -27,6 +26,7 @@ from tests.common import async_fire_time_changed
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -51,13 +51,7 @@ async def test_schedule_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the schedule triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.siren import DOMAIN
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.const import CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -35,13 +36,7 @@ async def test_siren_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the siren triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.switch import DOMAIN
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.const import CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -35,13 +36,7 @@ async def test_switch_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the switch triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -2,17 +2,13 @@
import pytest
from homeassistant.const import (
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.const import CONF_ENTITY_ID, STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
set_or_remove_state,
target_entities,
@@ -30,13 +26,7 @@ async def test_text_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the text triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.update import DOMAIN
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.const import CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -34,13 +35,7 @@ async def test_update_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the update triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,12 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.vacuum import VacuumActivity
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID
from homeassistant.const import CONF_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
other_states,
parametrize_target_entities,
parametrize_trigger_states,
@@ -39,13 +40,7 @@ async def test_vacuum_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the vacuum triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -5,18 +5,13 @@ from typing import Any
import pytest
from homeassistant.components.cover import ATTR_IS_CLOSED, CoverState
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_LABEL_ID,
CONF_ENTITY_ID,
STATE_OFF,
STATE_ON,
)
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
TriggerStateDescription,
arm_trigger,
assert_trigger_gated_by_labs_flag,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -47,13 +42,7 @@ async def test_window_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the window triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
"feature to be enabled in Home Assistant Labs settings (feature flag: "
"'new_triggers_conditions')"
) in caplog.text
await assert_trigger_gated_by_labs_flag(hass, caplog, trigger_key)
@pytest.mark.usefixtures("enable_labs_preview_features")

View File

@@ -1986,19 +1986,18 @@ def mock_bleak_scanner_start() -> Generator[MagicMock]:
@pytest.fixture
def hassio_env(supervisor_is_connected: AsyncMock) -> Generator[None]:
def hassio_env(
supervisor_is_connected: AsyncMock, supervisor_root_info: AsyncMock
) -> Generator[None]:
"""Fixture to inject hassio env."""
from homeassistant.components.hassio import HassioAPIError # noqa: PLC0415
from aiohasupervisor import SupervisorError # noqa: PLC0415
from .components.hassio import SUPERVISOR_TOKEN # noqa: PLC0415
supervisor_root_info.side_effect = SupervisorError()
with (
patch.dict(os.environ, {"SUPERVISOR": "127.0.0.1"}),
patch.dict(os.environ, {"SUPERVISOR_TOKEN": SUPERVISOR_TOKEN}),
patch(
"homeassistant.components.hassio.HassIO.get_info",
Mock(side_effect=HassioAPIError()),
),
):
yield
@@ -2012,8 +2011,6 @@ async def hassio_stubs(
supervisor_client: AsyncMock,
) -> RefreshToken:
"""Create mock hassio http client."""
from homeassistant.components.hassio import HassioAPIError # noqa: PLC0415
with (
patch(
"homeassistant.components.hassio.HassIO.update_hass_api",
@@ -2023,10 +2020,6 @@ async def hassio_stubs(
"homeassistant.components.hassio.HassIO.update_hass_config",
return_value={"result": "ok"},
),
patch(
"homeassistant.components.hassio.HassIO.get_info",
side_effect=HassioAPIError(),
),
patch(
"homeassistant.components.hassio.HassIO.get_ingress_panels",
return_value={"panels": []},