Files
supervisor/tests/resolution/fixup/test_fixup.py
Stefan AgnerandClaude Fable 5 b9f0ff7c02 Drop ResolutionFixupError, let fixup failures bubble
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>
2026-08-20 23:31:53 +02:00

87 lines
3.0 KiB
Python

"""Test check."""
# pylint: disable=import-error, protected-access
from unittest.mock import AsyncMock, PropertyMock, patch
from supervisor.const import CoreState
from supervisor.coresys import CoreSys
from supervisor.exceptions import HassioError
from supervisor.resolution.const import ContextType, SuggestionType
from supervisor.resolution.data import Suggestion
from supervisor.resolution.fixup import ResolutionFixup
from supervisor.resolution.validate import get_valid_modules
async def test_check_autofix(coresys: CoreSys):
"""Test check for setup."""
await coresys.core.set_state(CoreState.RUNNING)
coresys.resolution.fixup._fixups[
"system_create_full_backup"
].process_fixup = AsyncMock()
with patch(
"supervisor.resolution.fixups.system_create_full_backup.FixupSystemCreateFullBackup.auto",
return_value=True,
):
await coresys.resolution.fixup.run_autofix()
coresys.resolution.fixup._fixups[
"system_create_full_backup"
].process_fixup.assert_not_called()
coresys.resolution.add_suggestion(
Suggestion(SuggestionType.CREATE_FULL_BACKUP, ContextType.SYSTEM)
)
with patch(
"supervisor.resolution.fixups.system_create_full_backup.FixupSystemCreateFullBackup.auto",
return_value=True,
):
await coresys.resolution.fixup.run_autofix()
coresys.resolution.fixup._fixups[
"system_create_full_backup"
].process_fixup.assert_called_once()
assert len(coresys.resolution.suggestions) == 0
async def test_autofix_error_handling(coresys: CoreSys):
"""Test autofix continues on errors and only captures unexpected ones."""
await coresys.core.set_state(CoreState.RUNNING)
fix_hassio_error = AsyncMock(side_effect=HassioError("fail"))
fix_hassio_error.auto = True
fix_hassio_error.suggestion = SuggestionType.EXECUTE_RELOAD
unexpected = RuntimeError("boom")
fix_unexpected_error = AsyncMock(side_effect=unexpected)
fix_unexpected_error.auto = True
fix_unexpected_error.suggestion = SuggestionType.EXECUTE_RESET
with (
patch.object(
ResolutionFixup,
"all_fixes",
new_callable=PropertyMock,
return_value=[fix_hassio_error, fix_unexpected_error],
),
patch(
"supervisor.resolution.fixup.async_capture_exception",
new_callable=AsyncMock,
) as capture,
):
await coresys.resolution.fixup.run_autofix()
# The HassioError did not abort the loop, the next fixup still ran
fix_hassio_error.assert_awaited_once()
fix_unexpected_error.assert_awaited_once()
# Only the unexpected error is reported to Sentry
capture.assert_awaited_once_with(unexpected)
async def test_dynamic_fixup_loader(coresys: CoreSys):
"""Test dynamic fixup loader, this ensures that all fixups have defined a setup function."""
for fixup in await coresys.run_in_executor(get_valid_modules, "fixups"):
assert fixup in coresys.resolution.fixup._fixups