mirror of
https://github.com/home-assistant/supervisor.git
synced 2026-08-22 14:17:36 +01:00
Per review: the generic wrapper existed for a time when suggestions were only applied by autofix and the one job was separating fixup failures from real bugs for Sentry. The suggestion API is in regular use now and the wrapper actively hurts it — well-defined errors from the underlying operations were caught and replaced with a generic message. Remove the exception type entirely and let the original errors reach the caller. The autofix loop and the bus-event fixup path treat any HassioError as an environmental/config failure: log and continue without Sentry capture (the raise site reports to Sentry where warranted); everything else is still captured. Direct raises in the data disk fixups become HassOSDataDiskError, the could-not-start check in the app start fixup becomes AppsError. ResolutionFixupJobError now derives from ResolutionError and JobException. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
123 lines
4.2 KiB
Python
123 lines
4.2 KiB
Python
"""Test fixup app execute restart."""
|
|
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from supervisor.apps.app import App
|
|
from supervisor.const import AppState
|
|
from supervisor.coresys import CoreSys
|
|
from supervisor.docker.app import DockerApp
|
|
from supervisor.docker.interface import DockerInterface
|
|
from supervisor.exceptions import AppUnknownError, DockerError
|
|
from supervisor.resolution.const import ContextType, IssueType, SuggestionType
|
|
from supervisor.resolution.data import Issue, Suggestion
|
|
from supervisor.resolution.fixups.app_execute_restart import FixupAppExecuteRestart
|
|
|
|
from tests.common import force_app_state
|
|
from tests.const import TEST_ADDON_SLUG
|
|
|
|
DEVICE_ACCESS_MISSING_ISSUE = Issue(
|
|
IssueType.DEVICE_ACCESS_MISSING,
|
|
ContextType.ADDON,
|
|
reference=TEST_ADDON_SLUG,
|
|
)
|
|
EXECUTE_RESTART_SUGGESTION = Suggestion(
|
|
SuggestionType.EXECUTE_RESTART, ContextType.ADDON, reference="local_ssh"
|
|
)
|
|
|
|
|
|
@pytest.mark.usefixtures("path_extern")
|
|
async def test_fixup(coresys: CoreSys, install_app_ssh: App):
|
|
"""Test fixup restarts app."""
|
|
force_app_state(install_app_ssh, AppState.STARTED)
|
|
app_execute_restart = FixupAppExecuteRestart(coresys)
|
|
assert app_execute_restart.auto is False
|
|
|
|
async def mock_stop(*args, **kwargs):
|
|
force_app_state(install_app_ssh, AppState.STOPPED)
|
|
|
|
coresys.resolution.add_issue(
|
|
DEVICE_ACCESS_MISSING_ISSUE,
|
|
suggestions=[SuggestionType.EXECUTE_RESTART],
|
|
)
|
|
with (
|
|
patch.object(DockerInterface, "stop") as stop,
|
|
patch.object(DockerApp, "run") as run,
|
|
patch.object(App, "_wait_for_startup"),
|
|
patch.object(App, "write_options"),
|
|
):
|
|
await app_execute_restart()
|
|
stop.assert_called_once()
|
|
run.assert_called_once()
|
|
|
|
assert not coresys.resolution.issues
|
|
assert not coresys.resolution.suggestions
|
|
|
|
|
|
@pytest.mark.usefixtures("path_extern")
|
|
async def test_fixup_stop_error(
|
|
coresys: CoreSys, install_app_ssh: App, caplog: pytest.LogCaptureFixture
|
|
):
|
|
"""Test fixup fails on stop app failure."""
|
|
force_app_state(install_app_ssh, AppState.STARTED)
|
|
app_execute_start = FixupAppExecuteRestart(coresys)
|
|
|
|
coresys.resolution.add_issue(
|
|
DEVICE_ACCESS_MISSING_ISSUE,
|
|
suggestions=[SuggestionType.EXECUTE_RESTART],
|
|
)
|
|
with (
|
|
patch.object(DockerInterface, "stop", side_effect=DockerError),
|
|
patch.object(DockerApp, "run") as run,
|
|
):
|
|
with pytest.raises(AppUnknownError):
|
|
await app_execute_start()
|
|
run.assert_not_called()
|
|
|
|
assert DEVICE_ACCESS_MISSING_ISSUE in coresys.resolution.issues
|
|
assert EXECUTE_RESTART_SUGGESTION in coresys.resolution.suggestions
|
|
assert "Could not stop container for app local_ssh" in caplog.text
|
|
|
|
|
|
@pytest.mark.usefixtures("path_extern")
|
|
async def test_fixup_start_error(
|
|
coresys: CoreSys, install_app_ssh: App, caplog: pytest.LogCaptureFixture
|
|
):
|
|
"""Test fixup logs a start app failure."""
|
|
force_app_state(install_app_ssh, AppState.STARTED)
|
|
app_execute_start = FixupAppExecuteRestart(coresys)
|
|
|
|
coresys.resolution.add_issue(
|
|
DEVICE_ACCESS_MISSING_ISSUE,
|
|
suggestions=[SuggestionType.EXECUTE_RESTART],
|
|
)
|
|
with (
|
|
patch.object(DockerInterface, "stop") as stop,
|
|
patch.object(DockerApp, "run", side_effect=DockerError),
|
|
patch.object(App, "write_options"),
|
|
):
|
|
await app_execute_start()
|
|
stop.assert_called_once()
|
|
|
|
assert DEVICE_ACCESS_MISSING_ISSUE not in coresys.resolution.issues
|
|
assert EXECUTE_RESTART_SUGGESTION not in coresys.resolution.suggestions
|
|
assert "Could not restart local_ssh" in caplog.text
|
|
|
|
|
|
async def test_fixup_no_app(coresys: CoreSys, caplog: pytest.LogCaptureFixture):
|
|
"""Test fixup dismisses if app is missing."""
|
|
app_execute_start = FixupAppExecuteRestart(coresys)
|
|
|
|
coresys.resolution.add_issue(
|
|
DEVICE_ACCESS_MISSING_ISSUE,
|
|
suggestions=[SuggestionType.EXECUTE_RESTART],
|
|
)
|
|
with patch.object(DockerApp, "stop") as stop:
|
|
await app_execute_start()
|
|
stop.assert_not_called()
|
|
|
|
assert not coresys.resolution.issues
|
|
assert not coresys.resolution.suggestions
|
|
assert "Cannot restart app local_ssh as it does not exist" in caplog.text
|