mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-12-20 02:18:59 +00:00
Add availability API for addons (#6140)
* Add availability API for addons * Add cast back and test for latest version of installed addon * Make error responses more translation/client library friendly * Add test cases for install/update APIs
This commit is contained in:
@@ -67,9 +67,9 @@ from ..docker.monitor import DockerContainerStateEvent
|
||||
from ..docker.stats import DockerStats
|
||||
from ..exceptions import (
|
||||
AddonConfigurationError,
|
||||
AddonNotSupportedError,
|
||||
AddonsError,
|
||||
AddonsJobError,
|
||||
AddonsNotSupportedError,
|
||||
ConfigurationFileError,
|
||||
DockerError,
|
||||
HomeAssistantAPIError,
|
||||
@@ -1172,7 +1172,7 @@ class Addon(AddonModel):
|
||||
async def write_stdin(self, data) -> None:
|
||||
"""Write data to add-on stdin."""
|
||||
if not self.with_stdin:
|
||||
raise AddonsNotSupportedError(
|
||||
raise AddonNotSupportedError(
|
||||
f"Add-on {self.slug} does not support writing to stdin!", _LOGGER.error
|
||||
)
|
||||
|
||||
@@ -1419,7 +1419,7 @@ class Addon(AddonModel):
|
||||
|
||||
# If available
|
||||
if not self._available(data[ATTR_SYSTEM]):
|
||||
raise AddonsNotSupportedError(
|
||||
raise AddonNotSupportedError(
|
||||
f"Add-on {self.slug} is not available for this platform",
|
||||
_LOGGER.error,
|
||||
)
|
||||
|
||||
@@ -14,9 +14,9 @@ from supervisor.jobs.const import JobConcurrency
|
||||
from ..const import AddonBoot, AddonStartup, AddonState
|
||||
from ..coresys import CoreSys, CoreSysAttributes
|
||||
from ..exceptions import (
|
||||
AddonNotSupportedError,
|
||||
AddonsError,
|
||||
AddonsJobError,
|
||||
AddonsNotSupportedError,
|
||||
CoreDNSError,
|
||||
DockerError,
|
||||
HassioError,
|
||||
@@ -307,7 +307,7 @@ class AddonManager(CoreSysAttributes):
|
||||
"Version changed, use Update instead Rebuild", _LOGGER.error
|
||||
)
|
||||
if not force and not addon.need_build:
|
||||
raise AddonsNotSupportedError(
|
||||
raise AddonNotSupportedError(
|
||||
"Can't rebuild a image based add-on", _LOGGER.error
|
||||
)
|
||||
|
||||
|
||||
@@ -89,7 +89,12 @@ from ..const import (
|
||||
)
|
||||
from ..coresys import CoreSys
|
||||
from ..docker.const import Capabilities
|
||||
from ..exceptions import AddonsNotSupportedError
|
||||
from ..exceptions import (
|
||||
AddonNotSupportedArchitectureError,
|
||||
AddonNotSupportedError,
|
||||
AddonNotSupportedHomeAssistantVersionError,
|
||||
AddonNotSupportedMachineTypeError,
|
||||
)
|
||||
from ..jobs.const import JOB_GROUP_ADDON
|
||||
from ..jobs.job_group import JobGroup
|
||||
from ..utils import version_is_new_enough
|
||||
@@ -680,9 +685,8 @@ class AddonModel(JobGroup, ABC):
|
||||
"""Validate if addon is available for current system."""
|
||||
# Architecture
|
||||
if not self.sys_arch.is_supported(config[ATTR_ARCH]):
|
||||
raise AddonsNotSupportedError(
|
||||
f"Add-on {self.slug} not supported on this platform, supported architectures: {', '.join(config[ATTR_ARCH])}",
|
||||
logger,
|
||||
raise AddonNotSupportedArchitectureError(
|
||||
logger, slug=self.slug, architectures=config[ATTR_ARCH]
|
||||
)
|
||||
|
||||
# Machine / Hardware
|
||||
@@ -690,9 +694,8 @@ class AddonModel(JobGroup, ABC):
|
||||
if machine and (
|
||||
f"!{self.sys_machine}" in machine or self.sys_machine not in machine
|
||||
):
|
||||
raise AddonsNotSupportedError(
|
||||
f"Add-on {self.slug} not supported on this machine, supported machine types: {', '.join(machine)}",
|
||||
logger,
|
||||
raise AddonNotSupportedMachineTypeError(
|
||||
logger, slug=self.slug, machine_types=machine
|
||||
)
|
||||
|
||||
# Home Assistant
|
||||
@@ -701,16 +704,15 @@ class AddonModel(JobGroup, ABC):
|
||||
if version and not version_is_new_enough(
|
||||
self.sys_homeassistant.version, version
|
||||
):
|
||||
raise AddonsNotSupportedError(
|
||||
f"Add-on {self.slug} not supported on this system, requires Home Assistant version {version} or greater",
|
||||
logger,
|
||||
raise AddonNotSupportedHomeAssistantVersionError(
|
||||
logger, slug=self.slug, version=str(version)
|
||||
)
|
||||
|
||||
def _available(self, config) -> bool:
|
||||
"""Return True if this add-on is available on this platform."""
|
||||
try:
|
||||
self._validate_availability(config)
|
||||
except AddonsNotSupportedError:
|
||||
except AddonNotSupportedError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -735,6 +735,10 @@ class RestAPI(CoreSysAttributes):
|
||||
"/store/addons/{addon}/documentation",
|
||||
api_store.addons_addon_documentation,
|
||||
),
|
||||
web.get(
|
||||
"/store/addons/{addon}/availability",
|
||||
api_store.addons_addon_availability,
|
||||
),
|
||||
web.post(
|
||||
"/store/addons/{addon}/install", api_store.addons_addon_install
|
||||
),
|
||||
|
||||
@@ -326,6 +326,12 @@ class APIStore(CoreSysAttributes):
|
||||
_read_static_text_file, addon.path_documentation
|
||||
)
|
||||
|
||||
@api_process
|
||||
async def addons_addon_availability(self, request: web.Request) -> None:
|
||||
"""Check add-on availability for current system."""
|
||||
addon = cast(AddonStore, self._extract_addon(request))
|
||||
addon.validate_availability()
|
||||
|
||||
@api_process
|
||||
async def repositories_list(self, request: web.Request) -> list[dict[str, Any]]:
|
||||
"""Return all repositories."""
|
||||
|
||||
@@ -16,8 +16,11 @@ from ..const import (
|
||||
HEADER_TOKEN,
|
||||
HEADER_TOKEN_OLD,
|
||||
JSON_DATA,
|
||||
JSON_ERROR_KEY,
|
||||
JSON_EXTRA_FIELDS,
|
||||
JSON_JOB_ID,
|
||||
JSON_MESSAGE,
|
||||
JSON_MESSAGE_TEMPLATE,
|
||||
JSON_RESULT,
|
||||
REQUEST_FROM,
|
||||
RESULT_ERROR,
|
||||
@@ -136,10 +139,11 @@ def api_process_raw(content, *, error_type=None):
|
||||
|
||||
|
||||
def api_return_error(
|
||||
error: Exception | None = None,
|
||||
error: HassioError | None = None,
|
||||
message: str | None = None,
|
||||
error_type: str | None = None,
|
||||
status: int = 400,
|
||||
*,
|
||||
job_id: str | None = None,
|
||||
) -> web.Response:
|
||||
"""Return an API error message."""
|
||||
@@ -158,12 +162,18 @@ def api_return_error(
|
||||
body=message.encode(), content_type=error_type, status=status
|
||||
)
|
||||
case _:
|
||||
result = {
|
||||
result: dict[str, Any] = {
|
||||
JSON_RESULT: RESULT_ERROR,
|
||||
JSON_MESSAGE: message,
|
||||
}
|
||||
if job_id:
|
||||
result[JSON_JOB_ID] = job_id
|
||||
if error and error.error_key:
|
||||
result[JSON_ERROR_KEY] = error.error_key
|
||||
if error and error.message_template:
|
||||
result[JSON_MESSAGE_TEMPLATE] = error.message_template
|
||||
if error and error.extra_fields:
|
||||
result[JSON_EXTRA_FIELDS] = error.extra_fields
|
||||
|
||||
return web.json_response(
|
||||
result,
|
||||
|
||||
@@ -76,6 +76,9 @@ JSON_DATA = "data"
|
||||
JSON_MESSAGE = "message"
|
||||
JSON_RESULT = "result"
|
||||
JSON_JOB_ID = "job_id"
|
||||
JSON_ERROR_KEY = "error_key"
|
||||
JSON_MESSAGE_TEMPLATE = "message_template"
|
||||
JSON_EXTRA_FIELDS = "extra_fields"
|
||||
|
||||
RESULT_ERROR = "error"
|
||||
RESULT_OK = "ok"
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
"""Core Exceptions."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
|
||||
class HassioError(Exception):
|
||||
"""Root exception."""
|
||||
|
||||
error_key: str | None = None
|
||||
message_template: str | None = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str | None = None,
|
||||
logger: Callable[..., None] | None = None,
|
||||
*,
|
||||
extra_fields: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
"""Raise & log."""
|
||||
self.extra_fields = extra_fields or {}
|
||||
|
||||
if not message and self.message_template:
|
||||
message = (
|
||||
self.message_template.format(**self.extra_fields)
|
||||
if self.extra_fields
|
||||
else self.message_template
|
||||
)
|
||||
|
||||
if logger is not None and message is not None:
|
||||
logger(message)
|
||||
|
||||
@@ -235,8 +250,71 @@ class AddonConfigurationError(AddonsError):
|
||||
"""Error with add-on configuration."""
|
||||
|
||||
|
||||
class AddonsNotSupportedError(HassioNotSupportedError):
|
||||
"""Addons don't support a function."""
|
||||
class AddonNotSupportedError(HassioNotSupportedError):
|
||||
"""Addon doesn't support a function."""
|
||||
|
||||
|
||||
class AddonNotSupportedArchitectureError(AddonNotSupportedError):
|
||||
"""Addon does not support system due to architecture."""
|
||||
|
||||
error_key = "addon_not_supported_architecture_error"
|
||||
message_template = "Add-on {slug} not supported on this platform, supported architectures: {architectures}"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: Callable[..., None] | None = None,
|
||||
*,
|
||||
slug: str,
|
||||
architectures: list[str],
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
super().__init__(
|
||||
None,
|
||||
logger,
|
||||
extra_fields={"slug": slug, "architectures": ", ".join(architectures)},
|
||||
)
|
||||
|
||||
|
||||
class AddonNotSupportedMachineTypeError(AddonNotSupportedError):
|
||||
"""Addon does not support system due to machine type."""
|
||||
|
||||
error_key = "addon_not_supported_machine_type_error"
|
||||
message_template = "Add-on {slug} not supported on this machine, supported machine types: {machine_types}"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: Callable[..., None] | None = None,
|
||||
*,
|
||||
slug: str,
|
||||
machine_types: list[str],
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
super().__init__(
|
||||
None,
|
||||
logger,
|
||||
extra_fields={"slug": slug, "machine_types": ", ".join(machine_types)},
|
||||
)
|
||||
|
||||
|
||||
class AddonNotSupportedHomeAssistantVersionError(AddonNotSupportedError):
|
||||
"""Addon does not support system due to Home Assistant version."""
|
||||
|
||||
error_key = "addon_not_supported_home_assistant_version_error"
|
||||
message_template = "Add-on {slug} not supported on this system, requires Home Assistant version {version} or greater"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: Callable[..., None] | None = None,
|
||||
*,
|
||||
slug: str,
|
||||
version: str,
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
super().__init__(
|
||||
None,
|
||||
logger,
|
||||
extra_fields={"slug": slug, "version": version},
|
||||
)
|
||||
|
||||
|
||||
class AddonsJobError(AddonsError, JobException):
|
||||
@@ -319,10 +397,17 @@ class APIError(HassioError, RuntimeError):
|
||||
self,
|
||||
message: str | None = None,
|
||||
logger: Callable[..., None] | None = None,
|
||||
*,
|
||||
job_id: str | None = None,
|
||||
error: HassioError | None = None,
|
||||
) -> None:
|
||||
"""Raise & log, optionally with job."""
|
||||
super().__init__(message, logger)
|
||||
# Allow these to be set from another error here since APIErrors essentially wrap others to add a status
|
||||
self.error_key = error.error_key if error else None
|
||||
self.message_template = error.message_template if error else None
|
||||
super().__init__(
|
||||
message, logger, extra_fields=error.extra_fields if error else None
|
||||
)
|
||||
self.job_id = job_id
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch
|
||||
|
||||
from aiohttp import ClientResponse
|
||||
from aiohttp.test_utils import TestClient
|
||||
from awesomeversion import AwesomeVersion
|
||||
import pytest
|
||||
|
||||
from supervisor.addons.addon import Addon
|
||||
@@ -18,6 +19,7 @@ from supervisor.docker.addon import DockerAddon
|
||||
from supervisor.docker.const import ContainerState
|
||||
from supervisor.docker.interface import DockerInterface
|
||||
from supervisor.docker.monitor import DockerContainerStateEvent
|
||||
from supervisor.homeassistant.module import HomeAssistant
|
||||
from supervisor.store.addon import AddonStore
|
||||
from supervisor.store.repository import Repository
|
||||
|
||||
@@ -306,6 +308,7 @@ async def get_message(resp: ClientResponse, json_expected: bool) -> str:
|
||||
("post", "/store/addons/bad/install/1", True),
|
||||
("post", "/store/addons/bad/update", True),
|
||||
("post", "/store/addons/bad/update/1", True),
|
||||
("get", "/store/addons/bad/availability", True),
|
||||
# Legacy paths
|
||||
("get", "/addons/bad/icon", False),
|
||||
("get", "/addons/bad/logo", False),
|
||||
@@ -492,3 +495,226 @@ async def test_background_addon_update_fails_fast(
|
||||
assert resp.status == 400
|
||||
body = await resp.json()
|
||||
assert body["message"] == "No update available for add-on local_ssh"
|
||||
|
||||
|
||||
async def test_api_store_addons_addon_availability_success(
|
||||
api_client: TestClient, store_addon: AddonStore
|
||||
):
|
||||
"""Test /store/addons/{addon}/availability REST API - success case."""
|
||||
resp = await api_client.get(f"/store/addons/{store_addon.slug}/availability")
|
||||
assert resp.status == 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("supported_architectures", "api_action", "api_method", "installed"),
|
||||
[
|
||||
(["i386"], "availability", "get", False),
|
||||
(["i386", "aarch64"], "availability", "get", False),
|
||||
(["i386"], "install", "post", False),
|
||||
(["i386", "aarch64"], "install", "post", False),
|
||||
(["i386"], "update", "post", True),
|
||||
(["i386", "aarch64"], "update", "post", True),
|
||||
],
|
||||
)
|
||||
async def test_api_store_addons_addon_availability_arch_not_supported(
|
||||
api_client: TestClient,
|
||||
coresys: CoreSys,
|
||||
supported_architectures: list[str],
|
||||
api_action: str,
|
||||
api_method: str,
|
||||
installed: bool,
|
||||
):
|
||||
"""Test availability errors for /store/addons/{addon}/* REST APIs - architecture not supported."""
|
||||
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
||||
# Create an addon with unsupported architecture
|
||||
addon_obj = AddonStore(coresys, "test_arch_addon")
|
||||
coresys.addons.store[addon_obj.slug] = addon_obj
|
||||
|
||||
# Set addon config with unsupported architecture
|
||||
addon_config = {
|
||||
"advanced": False,
|
||||
"arch": supported_architectures,
|
||||
"slug": "test_arch_addon",
|
||||
"description": "Test arch add-on",
|
||||
"name": "Test Arch Add-on",
|
||||
"repository": "test",
|
||||
"stage": "stable",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
coresys.store.data.addons[addon_obj.slug] = addon_config
|
||||
if installed:
|
||||
coresys.addons.local[addon_obj.slug] = Addon(coresys, addon_obj.slug)
|
||||
coresys.addons.data.user[addon_obj.slug] = {"version": AwesomeVersion("0.0.1")}
|
||||
|
||||
# Mock the system architecture to be different
|
||||
with patch.object(CpuArch, "supported", new=PropertyMock(return_value=["amd64"])):
|
||||
resp = await api_client.request(
|
||||
api_method, f"/store/addons/{addon_obj.slug}/{api_action}"
|
||||
)
|
||||
assert resp.status == 400
|
||||
result = await resp.json()
|
||||
assert result["error_key"] == "addon_not_supported_architecture_error"
|
||||
assert (
|
||||
result["message_template"]
|
||||
== "Add-on {slug} not supported on this platform, supported architectures: {architectures}"
|
||||
)
|
||||
assert result["extra_fields"] == {
|
||||
"slug": "test_arch_addon",
|
||||
"architectures": ", ".join(supported_architectures),
|
||||
}
|
||||
assert result["message"] == result["message_template"].format(
|
||||
**result["extra_fields"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("supported_machines", "api_action", "api_method", "installed"),
|
||||
[
|
||||
(["odroid-n2"], "availability", "get", False),
|
||||
(["!qemux86-64"], "availability", "get", False),
|
||||
(["a", "b"], "availability", "get", False),
|
||||
(["odroid-n2"], "install", "post", False),
|
||||
(["!qemux86-64"], "install", "post", False),
|
||||
(["a", "b"], "install", "post", False),
|
||||
(["odroid-n2"], "update", "post", True),
|
||||
(["!qemux86-64"], "update", "post", True),
|
||||
(["a", "b"], "update", "post", True),
|
||||
],
|
||||
)
|
||||
async def test_api_store_addons_addon_availability_machine_not_supported(
|
||||
api_client: TestClient,
|
||||
coresys: CoreSys,
|
||||
supported_machines: list[str],
|
||||
api_action: str,
|
||||
api_method: str,
|
||||
installed: bool,
|
||||
):
|
||||
"""Test availability errors for /store/addons/{addon}/* REST APIs - machine not supported."""
|
||||
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
||||
# Create an addon with unsupported machine type
|
||||
addon_obj = AddonStore(coresys, "test_machine_addon")
|
||||
coresys.addons.store[addon_obj.slug] = addon_obj
|
||||
|
||||
# Set addon config with unsupported machine
|
||||
addon_config = {
|
||||
"advanced": False,
|
||||
"arch": ["amd64"],
|
||||
"machine": supported_machines,
|
||||
"slug": "test_machine_addon",
|
||||
"description": "Test machine add-on",
|
||||
"name": "Test Machine Add-on",
|
||||
"repository": "test",
|
||||
"stage": "stable",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
coresys.store.data.addons[addon_obj.slug] = addon_config
|
||||
if installed:
|
||||
coresys.addons.local[addon_obj.slug] = Addon(coresys, addon_obj.slug)
|
||||
coresys.addons.data.user[addon_obj.slug] = {"version": AwesomeVersion("0.0.1")}
|
||||
|
||||
# Mock the system machine to be different
|
||||
with patch.object(CoreSys, "machine", new=PropertyMock(return_value="qemux86-64")):
|
||||
resp = await api_client.request(
|
||||
api_method, f"/store/addons/{addon_obj.slug}/{api_action}"
|
||||
)
|
||||
assert resp.status == 400
|
||||
result = await resp.json()
|
||||
assert result["error_key"] == "addon_not_supported_machine_type_error"
|
||||
assert (
|
||||
result["message_template"]
|
||||
== "Add-on {slug} not supported on this machine, supported machine types: {machine_types}"
|
||||
)
|
||||
assert result["extra_fields"] == {
|
||||
"slug": "test_machine_addon",
|
||||
"machine_types": ", ".join(supported_machines),
|
||||
}
|
||||
assert result["message"] == result["message_template"].format(
|
||||
**result["extra_fields"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("api_action", "api_method", "installed"),
|
||||
[
|
||||
("availability", "get", False),
|
||||
("install", "post", False),
|
||||
("update", "post", True),
|
||||
],
|
||||
)
|
||||
async def test_api_store_addons_addon_availability_homeassistant_version_too_old(
|
||||
api_client: TestClient,
|
||||
coresys: CoreSys,
|
||||
api_action: str,
|
||||
api_method: str,
|
||||
installed: bool,
|
||||
):
|
||||
"""Test availability errors for /store/addons/{addon}/* REST APIs - Home Assistant version too old."""
|
||||
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
||||
# Create an addon that requires newer Home Assistant version
|
||||
addon_obj = AddonStore(coresys, "test_version_addon")
|
||||
coresys.addons.store[addon_obj.slug] = addon_obj
|
||||
|
||||
# Set addon config with minimum Home Assistant version requirement
|
||||
addon_config = {
|
||||
"advanced": False,
|
||||
"arch": ["amd64"],
|
||||
"homeassistant": "2023.1.1", # Requires newer version than current
|
||||
"slug": "test_version_addon",
|
||||
"description": "Test version add-on",
|
||||
"name": "Test Version Add-on",
|
||||
"repository": "test",
|
||||
"stage": "stable",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
coresys.store.data.addons[addon_obj.slug] = addon_config
|
||||
if installed:
|
||||
coresys.addons.local[addon_obj.slug] = Addon(coresys, addon_obj.slug)
|
||||
coresys.addons.data.user[addon_obj.slug] = {"version": AwesomeVersion("0.0.1")}
|
||||
|
||||
# Mock the Home Assistant version to be older
|
||||
with patch.object(
|
||||
HomeAssistant,
|
||||
"version",
|
||||
new=PropertyMock(return_value=AwesomeVersion("2022.1.1")),
|
||||
):
|
||||
resp = await api_client.request(
|
||||
api_method, f"/store/addons/{addon_obj.slug}/{api_action}"
|
||||
)
|
||||
assert resp.status == 400
|
||||
result = await resp.json()
|
||||
assert result["error_key"] == "addon_not_supported_home_assistant_version_error"
|
||||
assert (
|
||||
result["message_template"]
|
||||
== "Add-on {slug} not supported on this system, requires Home Assistant version {version} or greater"
|
||||
)
|
||||
assert result["extra_fields"] == {
|
||||
"slug": "test_version_addon",
|
||||
"version": "2023.1.1",
|
||||
}
|
||||
assert result["message"] == result["message_template"].format(
|
||||
**result["extra_fields"]
|
||||
)
|
||||
|
||||
|
||||
async def test_api_store_addons_addon_availability_installed_addon(
|
||||
api_client: TestClient, install_addon_ssh: Addon
|
||||
):
|
||||
"""Test /store/addons/{addon}/availability REST API - installed addon checks against latest version."""
|
||||
resp = await api_client.get("/store/addons/local_ssh/availability")
|
||||
assert resp.status == 200
|
||||
|
||||
install_addon_ssh.data_store["version"] = AwesomeVersion("10.0.0")
|
||||
install_addon_ssh.data_store["homeassistant"] = AwesomeVersion("2023.1.1")
|
||||
|
||||
# Mock the Home Assistant version to be older
|
||||
with patch.object(
|
||||
HomeAssistant,
|
||||
"version",
|
||||
new=PropertyMock(return_value=AwesomeVersion("2022.1.1")),
|
||||
):
|
||||
resp = await api_client.get("/store/addons/local_ssh/availability")
|
||||
assert resp.status == 400
|
||||
result = await resp.json()
|
||||
assert (
|
||||
"requires Home Assistant version 2023.1.1 or greater" in result["message"]
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ from supervisor.addons.addon import Addon
|
||||
from supervisor.arch import CpuArch
|
||||
from supervisor.backups.manager import BackupManager
|
||||
from supervisor.coresys import CoreSys
|
||||
from supervisor.exceptions import AddonsNotSupportedError, StoreJobError
|
||||
from supervisor.exceptions import AddonNotSupportedError, StoreJobError
|
||||
from supervisor.homeassistant.module import HomeAssistant
|
||||
from supervisor.store import StoreManager
|
||||
from supervisor.store.addon import AddonStore
|
||||
@@ -172,7 +172,7 @@ async def test_update_unavailable_addon(
|
||||
),
|
||||
patch("shutil.disk_usage", return_value=(42, 42, (1024.0**3))),
|
||||
):
|
||||
with pytest.raises(AddonsNotSupportedError):
|
||||
with pytest.raises(AddonNotSupportedError):
|
||||
await coresys.addons.update("local_ssh", backup=True)
|
||||
|
||||
backup.assert_not_called()
|
||||
@@ -227,7 +227,7 @@ async def test_install_unavailable_addon(
|
||||
new=PropertyMock(return_value=AwesomeVersion("2022.1.1")),
|
||||
),
|
||||
patch("shutil.disk_usage", return_value=(42, 42, (1024.0**3))),
|
||||
pytest.raises(AddonsNotSupportedError),
|
||||
pytest.raises(AddonNotSupportedError),
|
||||
):
|
||||
await coresys.addons.install("local_ssh")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user