Handle not available add-on in hassio add-on manager (#84943)

* Handle not available add-on in hassio add-on manager

* Fix zwave_js tests

* Fix sky connect tests

* Fix matter tests

* Fix yellow tests

* Update hardware tests
This commit is contained in:
Martin Hjelmare 2023-01-03 02:28:21 +01:00 committed by GitHub
parent 240e1fd8f3
commit 94b80db968
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 81 additions and 3 deletions

View file

@ -70,6 +70,7 @@ def api_error(
class AddonInfo:
"""Represent the current add-on info state."""
available: bool
hostname: str | None
options: dict[str, Any]
state: AddonState
@ -144,6 +145,7 @@ class AddonManager:
self._logger.debug("Add-on store info: %s", addon_store_info)
if not addon_store_info["installed"]:
return AddonInfo(
available=addon_store_info["available"],
hostname=None,
options={},
state=AddonState.NOT_INSTALLED,
@ -154,6 +156,7 @@ class AddonManager:
addon_info = await async_get_addon_info(self._hass, self.addon_slug)
addon_state = self.async_get_addon_state(addon_info)
return AddonInfo(
available=addon_info["available"],
hostname=addon_info["hostname"],
options=addon_info["options"],
state=addon_state,
@ -184,6 +187,11 @@ class AddonManager:
@api_error("Failed to install the {addon_name} add-on")
async def async_install_addon(self) -> None:
"""Install the managed add-on."""
addon_info = await self.async_get_addon_info()
if not addon_info.available:
raise AddonError(f"{self.addon_name} add-on is not available anymore")
await async_install_addon(self._hass, self.addon_slug)
@api_error("Failed to uninstall the {addon_name} add-on")
@ -196,6 +204,9 @@ class AddonManager:
"""Update the managed add-on if needed."""
addon_info = await self.async_get_addon_info()
if not addon_info.available:
raise AddonError(f"{self.addon_name} add-on is not available anymore")
if addon_info.state is AddonState.NOT_INSTALLED:
raise AddonError(f"{self.addon_name} add-on is not installed")

View file

@ -32,6 +32,7 @@ def addon_not_installed_fixture(
addon_store_info: AsyncMock, addon_info: AsyncMock
) -> AsyncMock:
"""Mock add-on not installed."""
addon_store_info.return_value["available"] = True
return addon_info
@ -41,10 +42,12 @@ def mock_addon_installed(
) -> AsyncMock:
"""Mock add-on already installed but not running."""
addon_store_info.return_value = {
"available": True,
"installed": "1.0.0",
"state": "stopped",
"version": "1.0.0",
}
addon_info.return_value["available"] = True
addon_info.return_value["hostname"] = "core-test-addon"
addon_info.return_value["state"] = "stopped"
addon_info.return_value["version"] = "1.0.0"
@ -67,6 +70,7 @@ def addon_store_info_fixture() -> Generator[AsyncMock, None, None]:
"homeassistant.components.hassio.addon_manager.async_get_addon_store_info"
) as addon_store_info:
addon_store_info.return_value = {
"available": False,
"installed": None,
"state": None,
"version": "1.0.0",
@ -81,6 +85,7 @@ def addon_info_fixture() -> Generator[AsyncMock, None, None]:
"homeassistant.components.hassio.addon_manager.async_get_addon_info",
) as addon_info:
addon_info.return_value = {
"available": False,
"hostname": None,
"options": {},
"state": None,
@ -180,6 +185,26 @@ async def test_not_installed_raises_exception(
assert str(err.value) == "Test add-on is not installed"
async def test_not_available_raises_exception(
addon_manager: AddonManager,
addon_store_info: AsyncMock,
addon_info: AsyncMock,
) -> None:
"""Test addon not available raises exception."""
addon_store_info.return_value["available"] = False
addon_info.return_value["available"] = False
with pytest.raises(AddonError) as err:
await addon_manager.async_install_addon()
assert str(err.value) == "Test add-on is not available anymore"
with pytest.raises(AddonError) as err:
await addon_manager.async_update_addon()
assert str(err.value) == "Test add-on is not available anymore"
async def test_get_addon_discovery_info(
addon_manager: AddonManager, get_addon_discovery_info: AsyncMock
) -> None:
@ -222,6 +247,7 @@ async def test_get_addon_info_not_installed(
) -> None:
"""Test get addon info when addon is not installed.."""
assert await addon_manager.async_get_addon_info() == AddonInfo(
available=True,
hostname=None,
options={},
state=AddonState.NOT_INSTALLED,
@ -243,6 +269,7 @@ async def test_get_addon_info(
"""Test get addon info when addon is installed."""
addon_installed.return_value["state"] = addon_info_state
assert await addon_manager.async_get_addon_info() == AddonInfo(
available=True,
hostname="core-test-addon",
options={},
state=addon_state,
@ -308,18 +335,29 @@ async def test_set_addon_options_error(
async def test_install_addon(
addon_manager: AddonManager, install_addon: AsyncMock
addon_manager: AddonManager,
install_addon: AsyncMock,
addon_store_info: AsyncMock,
addon_info: AsyncMock,
) -> None:
"""Test install addon."""
addon_store_info.return_value["available"] = True
addon_info.return_value["available"] = True
await addon_manager.async_install_addon()
assert install_addon.call_count == 1
async def test_install_addon_error(
addon_manager: AddonManager, install_addon: AsyncMock
addon_manager: AddonManager,
install_addon: AsyncMock,
addon_store_info: AsyncMock,
addon_info: AsyncMock,
) -> None:
"""Test install addon raises error."""
addon_store_info.return_value["available"] = True
addon_info.return_value["available"] = True
install_addon.side_effect = HassioAPIError("Boom")
with pytest.raises(AddonError) as err:
@ -341,6 +379,7 @@ async def test_schedule_install_addon(
assert addon_manager.task_in_progress() is True
assert await addon_manager.async_get_addon_info() == AddonInfo(
available=True,
hostname="core-test-addon",
options={},
state=AddonState.INSTALLING,
@ -676,6 +715,7 @@ async def test_schedule_update_addon(
assert addon_manager.task_in_progress() is True
assert await addon_manager.async_get_addon_info() == AddonInfo(
available=True,
hostname="core-test-addon",
options={},
state=AddonState.UPDATING,

View file

@ -67,6 +67,7 @@ def addon_store_info_fixture():
"homeassistant.components.hassio.addon_manager.async_get_addon_store_info"
) as addon_store_info:
addon_store_info.return_value = {
"available": True,
"installed": None,
"state": None,
"version": "1.0.0",
@ -81,6 +82,7 @@ def addon_info_fixture():
"homeassistant.components.hassio.addon_manager.async_get_addon_info",
) as addon_info:
addon_info.return_value = {
"available": True,
"hostname": None,
"options": {},
"state": None,

View file

@ -69,6 +69,7 @@ def addon_store_info_fixture():
"homeassistant.components.hassio.addon_manager.async_get_addon_store_info"
) as addon_store_info:
addon_store_info.return_value = {
"available": True,
"installed": None,
"state": None,
"version": "1.0.0",
@ -83,6 +84,7 @@ def addon_info_fixture():
"homeassistant.components.hassio.addon_manager.async_get_addon_info",
) as addon_info:
addon_info.return_value = {
"available": True,
"hostname": None,
"options": {},
"state": None,

View file

@ -67,6 +67,7 @@ def addon_store_info_fixture():
"homeassistant.components.hassio.addon_manager.async_get_addon_store_info"
) as addon_store_info:
addon_store_info.return_value = {
"available": True,
"installed": None,
"state": None,
"version": "1.0.0",
@ -81,6 +82,7 @@ def addon_info_fixture():
"homeassistant.components.hassio.addon_manager.async_get_addon_info",
) as addon_info:
addon_info.return_value = {
"available": True,
"hostname": None,
"options": {},
"state": None,

View file

@ -80,6 +80,7 @@ def addon_store_info_fixture() -> Generator[AsyncMock, None, None]:
"homeassistant.components.hassio.addon_manager.async_get_addon_store_info"
) as addon_store_info:
addon_store_info.return_value = {
"available": False,
"installed": None,
"state": None,
"version": "1.0.0",
@ -94,6 +95,7 @@ def addon_info_fixture() -> Generator[AsyncMock, None, None]:
"homeassistant.components.hassio.addon_manager.async_get_addon_info",
) as addon_info:
addon_info.return_value = {
"available": False,
"hostname": None,
"options": {},
"state": None,
@ -108,6 +110,7 @@ def addon_not_installed_fixture(
addon_store_info: AsyncMock, addon_info: AsyncMock
) -> AsyncMock:
"""Mock add-on not installed."""
addon_store_info.return_value["available"] = True
return addon_info
@ -117,10 +120,12 @@ def addon_installed_fixture(
) -> AsyncMock:
"""Mock add-on already installed but not running."""
addon_store_info.return_value = {
"available": True,
"installed": "1.0.0",
"state": "stopped",
"version": "1.0.0",
}
addon_info.return_value["available"] = True
addon_info.return_value["hostname"] = "core-matter-server"
addon_info.return_value["state"] = "stopped"
addon_info.return_value["version"] = "1.0.0"
@ -133,10 +138,12 @@ def addon_running_fixture(
) -> AsyncMock:
"""Mock add-on already running."""
addon_store_info.return_value = {
"available": True,
"installed": "1.0.0",
"state": "started",
"version": "1.0.0",
}
addon_info.return_value["available"] = True
addon_info.return_value["hostname"] = "core-matter-server"
addon_info.return_value["state"] = "started"
addon_info.return_value["version"] = "1.0.0"
@ -152,10 +159,12 @@ def install_addon_fixture(
async def install_addon_side_effect(hass: HomeAssistant, slug: str) -> None:
"""Mock install add-on."""
addon_store_info.return_value = {
"available": True,
"installed": "1.0.0",
"state": "stopped",
"version": "1.0.0",
}
addon_info.return_value["available"] = True
addon_info.return_value["state"] = "stopped"
addon_info.return_value["version"] = "1.0.0"

View file

@ -307,7 +307,7 @@ async def test_install_addon(
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.SETUP_RETRY
assert addon_store_info.call_count == 2
assert addon_store_info.call_count == 3
assert install_addon.call_count == 1
assert install_addon.call_args == call(hass, "core_matter_server")
assert start_addon.call_count == 1

View file

@ -30,6 +30,7 @@ def mock_addon_info(addon_info_side_effect):
side_effect=addon_info_side_effect,
) as addon_info:
addon_info.return_value = {
"available": False,
"hostname": None,
"options": {},
"state": None,
@ -53,6 +54,7 @@ def mock_addon_store_info(addon_store_info_side_effect):
side_effect=addon_store_info_side_effect,
) as addon_store_info:
addon_store_info.return_value = {
"available": False,
"installed": None,
"state": None,
"version": "1.0.0",
@ -64,10 +66,12 @@ def mock_addon_store_info(addon_store_info_side_effect):
def mock_addon_running(addon_store_info, addon_info):
"""Mock add-on already running."""
addon_store_info.return_value = {
"available": True,
"installed": "1.0.0",
"state": "started",
"version": "1.0.0",
}
addon_info.return_value["available"] = True
addon_info.return_value["state"] = "started"
addon_info.return_value["version"] = "1.0.0"
return addon_info
@ -77,10 +81,12 @@ def mock_addon_running(addon_store_info, addon_info):
def mock_addon_installed(addon_store_info, addon_info):
"""Mock add-on already installed but not running."""
addon_store_info.return_value = {
"available": True,
"installed": "1.0.0",
"state": "stopped",
"version": "1.0.0",
}
addon_info.return_value["available"] = True
addon_info.return_value["state"] = "stopped"
addon_info.return_value["version"] = "1.0.0"
return addon_info
@ -89,6 +95,7 @@ def mock_addon_installed(addon_store_info, addon_info):
@pytest.fixture(name="addon_not_installed")
def mock_addon_not_installed(addon_store_info, addon_info):
"""Mock add-on not installed."""
addon_store_info.return_value["available"] = True
return addon_info
@ -126,10 +133,12 @@ def install_addon_side_effect_fixture(addon_store_info, addon_info):
async def install_addon(hass, slug):
"""Mock install add-on."""
addon_store_info.return_value = {
"available": True,
"installed": "1.0.0",
"state": "stopped",
"version": "1.0.0",
}
addon_info.return_value["available"] = True
addon_info.return_value["state"] = "stopped"
addon_info.return_value["version"] = "1.0.0"
@ -162,10 +171,12 @@ def start_addon_side_effect_fixture(addon_store_info, addon_info):
async def start_addon(hass, slug):
"""Mock start add-on."""
addon_store_info.return_value = {
"available": True,
"installed": "1.0.0",
"state": "started",
"version": "1.0.0",
}
addon_info.return_value["available"] = True
addon_info.return_value["state"] = "started"
return start_addon

View file

@ -536,6 +536,7 @@ async def test_abort_hassio_discovery_for_other_addon(
async def test_usb_discovery(
hass,
supervisor,
addon_not_installed,
install_addon,
addon_options,
get_addon_discovery_info,