mirror of
https://github.com/home-assistant/supervisor.git
synced 2026-05-21 07:08:53 +01:00
9923b8580b
Follow-up on #6739: with HassioError now logged and captured by Sentry in api_process, hostname rejections from systemd-hostnamed surfaced as "unexpected" 400s with noisy log entries and a Sentry event, even though the user had simply submitted an invalid hostname. Map this through properly so the API returns a clean, structured 400: - Split ErrorType.INVALID_ARGS out of DBusInterfaceMethodError into its own DBusInvalidArgsError. The two cases collapsed there before are semantically different: UNKNOWN_METHOD / INVALID_SIGNATURE mean the call is broken (method missing or types wrong); INVALID_ARGS means the call is valid but the service rejected an argument's value. - Add HostInvalidHostnameError(HostError, APIError) with error_key and extra_fields so clients get a normalized message and a stable key rather than systemd's raw "Invalid static hostname '...'" text. - Translate DBusInvalidArgsError to HostInvalidHostnameError in SystemControl.set_hostname. @api_process turns the result into a 400 without logging or Sentry capture, since this is now a modeled client-input error rather than an unexpected one. Validation continues to live in hostnamed (hostname_is_valid() in systemd's src/basic/hostname-util.c); Supervisor only translates the rejection. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
76 lines
2.7 KiB
Python
76 lines
2.7 KiB
Python
"""Test host control."""
|
|
|
|
from dbus_fast import DBusError, ErrorType
|
|
import pytest
|
|
|
|
from supervisor.coresys import CoreSys
|
|
from supervisor.exceptions import HostInvalidHostnameError
|
|
|
|
from tests.dbus_service_mocks.base import DBusServiceMock
|
|
from tests.dbus_service_mocks.hostname import Hostname as HostnameService
|
|
from tests.dbus_service_mocks.timedate import TimeDate as TimeDateService
|
|
|
|
|
|
async def test_set_hostname(
|
|
coresys: CoreSys,
|
|
all_dbus_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]],
|
|
):
|
|
"""Test set hostname."""
|
|
hostname_service: HostnameService = all_dbus_services["hostname"]
|
|
hostname_service.SetStaticHostname.calls.clear()
|
|
|
|
assert coresys.dbus.hostname.hostname == "homeassistant-n2"
|
|
|
|
await coresys.host.control.set_hostname("test")
|
|
assert hostname_service.SetStaticHostname.calls == [("test", False)]
|
|
await hostname_service.ping()
|
|
assert coresys.dbus.hostname.hostname == "test"
|
|
|
|
|
|
async def test_set_hostname_rejected_by_host(
|
|
coresys: CoreSys,
|
|
all_dbus_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]],
|
|
):
|
|
"""A hostname rejected by hostnamed surfaces as HostInvalidHostnameError."""
|
|
hostname_service: HostnameService = all_dbus_services["hostname"]
|
|
hostname_service.response_set_static_hostname = DBusError(
|
|
ErrorType.INVALID_ARGS, "Invalid static hostname 'bad name'"
|
|
)
|
|
|
|
with pytest.raises(HostInvalidHostnameError) as exc_info:
|
|
await coresys.host.control.set_hostname("bad name")
|
|
|
|
assert exc_info.value.error_key == "host_invalid_hostname"
|
|
assert exc_info.value.extra_fields == {"hostname": "bad name"}
|
|
assert str(exc_info.value) == "Invalid hostname 'bad name'"
|
|
|
|
|
|
@pytest.mark.parametrize("os_available", ["16.2"], indirect=True)
|
|
async def test_set_timezone(
|
|
coresys: CoreSys,
|
|
all_dbus_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]],
|
|
os_available: str,
|
|
):
|
|
"""Test set timezone."""
|
|
timedate_service: TimeDateService = all_dbus_services["timedate"]
|
|
timedate_service.SetTimezone.calls.clear()
|
|
|
|
assert coresys.dbus.timedate.timezone == "Etc/UTC"
|
|
|
|
await coresys.host.control.set_timezone("Europe/Prague")
|
|
assert timedate_service.SetTimezone.calls == [("Europe/Prague", False)]
|
|
|
|
|
|
@pytest.mark.parametrize("os_available", ["16.1"], indirect=True)
|
|
async def test_set_timezone_unsupported(
|
|
coresys: CoreSys,
|
|
all_dbus_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]],
|
|
os_available: str,
|
|
):
|
|
"""Test DBus call is not made when OS doesn't support it."""
|
|
timedate_service: TimeDateService = all_dbus_services["timedate"]
|
|
timedate_service.SetTimezone.calls.clear()
|
|
|
|
await coresys.host.control.set_timezone("Europe/Prague")
|
|
assert timedate_service.SetTimezone.calls == []
|