Skip to content

Commit 29b882f

Browse files
committed
Add availability API for addons
1 parent 3d62c9a commit 29b882f

File tree

3 files changed

+126
-0
lines changed

3 files changed

+126
-0
lines changed

supervisor/api/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,10 @@ def _register_store(self) -> None:
735735
"/store/addons/{addon}/documentation",
736736
api_store.addons_addon_documentation,
737737
),
738+
web.get(
739+
"/store/addons/{addon}/availability",
740+
api_store.addons_addon_availability,
741+
),
738742
web.post(
739743
"/store/addons/{addon}/install", api_store.addons_addon_install
740744
),

supervisor/api/store.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,12 @@ async def addons_addon_documentation(self, request: web.Request) -> str:
326326
_read_static_text_file, addon.path_documentation
327327
)
328328

329+
@api_process
330+
async def addons_addon_availability(self, request: web.Request) -> None:
331+
"""Check add-on availability for current system."""
332+
addon = self._extract_addon(request)
333+
addon.validate_availability()
334+
329335
@api_process
330336
async def repositories_list(self, request: web.Request) -> list[dict[str, Any]]:
331337
"""Return all repositories."""

tests/api/test_store.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from aiohttp import ClientResponse
88
from aiohttp.test_utils import TestClient
9+
from awesomeversion import AwesomeVersion
910
import pytest
1011

1112
from supervisor.addons.addon import Addon
@@ -18,6 +19,7 @@
1819
from supervisor.docker.const import ContainerState
1920
from supervisor.docker.interface import DockerInterface
2021
from supervisor.docker.monitor import DockerContainerStateEvent
22+
from supervisor.homeassistant.module import HomeAssistant
2123
from supervisor.store.addon import AddonStore
2224
from supervisor.store.repository import Repository
2325

@@ -306,6 +308,7 @@ async def get_message(resp: ClientResponse, json_expected: bool) -> str:
306308
("post", "/store/addons/bad/install/1", True),
307309
("post", "/store/addons/bad/update", True),
308310
("post", "/store/addons/bad/update/1", True),
311+
("get", "/store/addons/bad/availability", True),
309312
# Legacy paths
310313
("get", "/addons/bad/icon", False),
311314
("get", "/addons/bad/logo", False),
@@ -492,3 +495,116 @@ async def test_background_addon_update_fails_fast(
492495
assert resp.status == 400
493496
body = await resp.json()
494497
assert body["message"] == "No update available for add-on local_ssh"
498+
499+
500+
async def test_api_store_addons_addon_availability_success(
501+
api_client: TestClient, store_addon: AddonStore
502+
):
503+
"""Test /store/addons/{addon}/availability REST API - success case."""
504+
resp = await api_client.get(f"/store/addons/{store_addon.slug}/availability")
505+
assert resp.status == 200
506+
507+
508+
@pytest.mark.usefixtures("install_addon_ssh")
509+
async def test_api_store_addons_addon_availability_installed_addon(
510+
api_client: TestClient,
511+
):
512+
"""Test /store/addons/{addon}/availability REST API - installed addon."""
513+
resp = await api_client.get("/store/addons/local_ssh/availability")
514+
assert resp.status == 200
515+
516+
517+
async def test_api_store_addons_addon_availability_arch_not_supported(
518+
api_client: TestClient, coresys: CoreSys
519+
):
520+
"""Test /store/addons/{addon}/availability REST API - architecture not supported."""
521+
# Create an addon with unsupported architecture
522+
addon_obj = AddonStore(coresys, "test_arch_addon")
523+
coresys.addons.store[addon_obj.slug] = addon_obj
524+
525+
# Set addon config with unsupported architecture
526+
addon_config = {
527+
"advanced": False,
528+
"arch": ["i386"], # Not supported on current system
529+
"slug": "test_arch",
530+
"description": "Test arch add-on",
531+
"name": "Test Arch Add-on",
532+
"repository": "test",
533+
"stage": "stable",
534+
"version": "1.0.0",
535+
}
536+
coresys.store.data.addons[addon_obj.slug] = addon_config
537+
538+
# Mock the system architecture to be different
539+
with patch.object(CpuArch, "supported", new=PropertyMock(return_value=["amd64"])):
540+
resp = await api_client.get(f"/store/addons/{addon_obj.slug}/availability")
541+
assert resp.status == 400
542+
result = await resp.json()
543+
assert "not supported on this platform" in result["message"]
544+
545+
546+
@pytest.mark.parametrize("supported_machines", [["odroid-n2"], ["!qemux86-64"]])
547+
async def test_api_store_addons_addon_availability_machine_not_supported(
548+
api_client: TestClient, coresys: CoreSys, supported_machines: list[str]
549+
):
550+
"""Test /store/addons/{addon}/availability REST API - machine not supported."""
551+
# Create an addon with unsupported machine type
552+
addon_obj = AddonStore(coresys, "test_machine_addon")
553+
coresys.addons.store[addon_obj.slug] = addon_obj
554+
555+
# Set addon config with unsupported machine
556+
addon_config = {
557+
"advanced": False,
558+
"arch": ["amd64"],
559+
"machine": supported_machines,
560+
"slug": "test_machine",
561+
"description": "Test machine add-on",
562+
"name": "Test Machine Add-on",
563+
"repository": "test",
564+
"stage": "stable",
565+
"version": "1.0.0",
566+
}
567+
coresys.store.data.addons[addon_obj.slug] = addon_config
568+
569+
# Mock the system machine to be different
570+
with patch.object(CoreSys, "machine", new=PropertyMock(return_value="qemux86-64")):
571+
resp = await api_client.get(f"/store/addons/{addon_obj.slug}/availability")
572+
assert resp.status == 400
573+
result = await resp.json()
574+
assert "not supported on this machine" in result["message"]
575+
576+
577+
async def test_api_store_addons_addon_availability_homeassistant_version_too_old(
578+
api_client: TestClient, coresys: CoreSys, test_repository: Repository
579+
):
580+
"""Test /store/addons/{addon}/availability REST API - Home Assistant version too old."""
581+
# Create an addon that requires newer Home Assistant version
582+
addon_obj = AddonStore(coresys, "test_version_addon")
583+
coresys.addons.store[addon_obj.slug] = addon_obj
584+
585+
# Set addon config with minimum Home Assistant version requirement
586+
addon_config = {
587+
"advanced": False,
588+
"arch": ["amd64"],
589+
"homeassistant": "2023.1.1", # Requires newer version than current
590+
"slug": "test_version",
591+
"description": "Test version add-on",
592+
"name": "Test Version Add-on",
593+
"repository": "test",
594+
"stage": "stable",
595+
"version": "1.0.0",
596+
}
597+
coresys.store.data.addons[addon_obj.slug] = addon_config
598+
599+
# Mock the Home Assistant version to be older
600+
with patch.object(
601+
HomeAssistant,
602+
"version",
603+
new=PropertyMock(return_value=AwesomeVersion("2022.1.1")),
604+
):
605+
resp = await api_client.get(f"/store/addons/{addon_obj.slug}/availability")
606+
assert resp.status == 400
607+
result = await resp.json()
608+
assert (
609+
"requires Home Assistant version 2023.1.1 or greater" in result["message"]
610+
)

0 commit comments

Comments
 (0)