Skip to content

Commit 46c2ac8

Browse files
committed
Add availability API for addons
1 parent 3c39f2f commit 46c2ac8

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
@@ -297,6 +297,12 @@ async def addons_addon_documentation(self, request: web.Request) -> str:
297297
_read_static_text_file, addon.path_documentation
298298
)
299299

300+
@api_process
301+
async def addons_addon_availability(self, request: web.Request) -> None:
302+
"""Check add-on availability for current system."""
303+
addon = self._extract_addon(request)
304+
addon.validate_availability()
305+
300306
@api_process
301307
async def repositories_list(self, request: web.Request) -> list[dict[str, Any]]:
302308
"""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
@@ -17,6 +18,7 @@
1718
from supervisor.docker.const import ContainerState
1819
from supervisor.docker.interface import DockerInterface
1920
from supervisor.docker.monitor import DockerContainerStateEvent
21+
from supervisor.homeassistant.module import HomeAssistant
2022
from supervisor.store.addon import AddonStore
2123
from supervisor.store.repository import Repository
2224

@@ -305,6 +307,7 @@ async def get_message(resp: ClientResponse, json_expected: bool) -> str:
305307
("post", "/store/addons/bad/install/1", True),
306308
("post", "/store/addons/bad/update", True),
307309
("post", "/store/addons/bad/update/1", True),
310+
("get", "/store/addons/bad/availability", True),
308311
# Legacy paths
309312
("get", "/addons/bad/icon", False),
310313
("get", "/addons/bad/logo", False),
@@ -390,3 +393,116 @@ async def test_api_store_addons_changelog_corrupted(
390393
assert resp.status == 200
391394
result = await resp.text()
392395
assert result == "Text with an invalid UTF-8 char: �"
396+
397+
398+
async def test_api_store_addons_addon_availability_success(
399+
api_client: TestClient, store_addon: AddonStore
400+
):
401+
"""Test /store/addons/{addon}/availability REST API - success case."""
402+
resp = await api_client.get(f"/store/addons/{store_addon.slug}/availability")
403+
assert resp.status == 200
404+
405+
406+
@pytest.mark.usefixtures("install_addon_ssh")
407+
async def test_api_store_addons_addon_availability_installed_addon(
408+
api_client: TestClient,
409+
):
410+
"""Test /store/addons/{addon}/availability REST API - installed addon."""
411+
resp = await api_client.get("/store/addons/local_ssh/availability")
412+
assert resp.status == 200
413+
414+
415+
async def test_api_store_addons_addon_availability_arch_not_supported(
416+
api_client: TestClient, coresys: CoreSys
417+
):
418+
"""Test /store/addons/{addon}/availability REST API - architecture not supported."""
419+
# Create an addon with unsupported architecture
420+
addon_obj = AddonStore(coresys, "test_arch_addon")
421+
coresys.addons.store[addon_obj.slug] = addon_obj
422+
423+
# Set addon config with unsupported architecture
424+
addon_config = {
425+
"advanced": False,
426+
"arch": ["i386"], # Not supported on current system
427+
"slug": "test_arch",
428+
"description": "Test arch add-on",
429+
"name": "Test Arch Add-on",
430+
"repository": "test",
431+
"stage": "stable",
432+
"version": "1.0.0",
433+
}
434+
coresys.store.data.addons[addon_obj.slug] = addon_config
435+
436+
# Mock the system architecture to be different
437+
with patch.object(CpuArch, "supported", new=PropertyMock(return_value=["amd64"])):
438+
resp = await api_client.get(f"/store/addons/{addon_obj.slug}/availability")
439+
assert resp.status == 400
440+
result = await resp.json()
441+
assert "not supported on this platform" in result["message"]
442+
443+
444+
@pytest.mark.parametrize("supported_machines", [["odroid-n2"], ["!qemux86-64"]])
445+
async def test_api_store_addons_addon_availability_machine_not_supported(
446+
api_client: TestClient, coresys: CoreSys, supported_machines: list[str]
447+
):
448+
"""Test /store/addons/{addon}/availability REST API - machine not supported."""
449+
# Create an addon with unsupported machine type
450+
addon_obj = AddonStore(coresys, "test_machine_addon")
451+
coresys.addons.store[addon_obj.slug] = addon_obj
452+
453+
# Set addon config with unsupported machine
454+
addon_config = {
455+
"advanced": False,
456+
"arch": ["amd64"],
457+
"machine": supported_machines,
458+
"slug": "test_machine",
459+
"description": "Test machine add-on",
460+
"name": "Test Machine Add-on",
461+
"repository": "test",
462+
"stage": "stable",
463+
"version": "1.0.0",
464+
}
465+
coresys.store.data.addons[addon_obj.slug] = addon_config
466+
467+
# Mock the system machine to be different
468+
with patch.object(CoreSys, "machine", new=PropertyMock(return_value="qemux86-64")):
469+
resp = await api_client.get(f"/store/addons/{addon_obj.slug}/availability")
470+
assert resp.status == 400
471+
result = await resp.json()
472+
assert "not supported on this machine" in result["message"]
473+
474+
475+
async def test_api_store_addons_addon_availability_homeassistant_version_too_old(
476+
api_client: TestClient, coresys: CoreSys, test_repository: Repository
477+
):
478+
"""Test /store/addons/{addon}/availability REST API - Home Assistant version too old."""
479+
# Create an addon that requires newer Home Assistant version
480+
addon_obj = AddonStore(coresys, "test_version_addon")
481+
coresys.addons.store[addon_obj.slug] = addon_obj
482+
483+
# Set addon config with minimum Home Assistant version requirement
484+
addon_config = {
485+
"advanced": False,
486+
"arch": ["amd64"],
487+
"homeassistant": "2023.1.1", # Requires newer version than current
488+
"slug": "test_version",
489+
"description": "Test version add-on",
490+
"name": "Test Version Add-on",
491+
"repository": "test",
492+
"stage": "stable",
493+
"version": "1.0.0",
494+
}
495+
coresys.store.data.addons[addon_obj.slug] = addon_config
496+
497+
# Mock the Home Assistant version to be older
498+
with patch.object(
499+
HomeAssistant,
500+
"version",
501+
new=PropertyMock(return_value=AwesomeVersion("2022.1.1")),
502+
):
503+
resp = await api_client.get(f"/store/addons/{addon_obj.slug}/availability")
504+
assert resp.status == 400
505+
result = await resp.json()
506+
assert (
507+
"requires Home Assistant version 2023.1.1 or greater" in result["message"]
508+
)

0 commit comments

Comments
 (0)