1
0
mirror of https://github.com/home-assistant/supervisor.git synced 2026-07-04 12:25:02 +01:00
Files
supervisor/tests/api/test_mounts.py
T
Stefan Agner dae48c62e4 Treat mount errors as API errors instead of unexpected failures (#6946)
Mount failures generally reflect user configuration or host conditions
(unreachable server, wrong credentials, ...) rather than a Supervisor
bug. As a plain HassioError, MountError reached the generic error branch
of api_process, which logged a full traceback as an "Unexpected error
during API call" and captured the exception to Sentry. The mount reload
path already encoded the opposite intent by explicitly skipping Sentry
for MountError.

Make MountError an APIError so mount failures are surfaced as client-side
errors with their explicit message, without a traceback or Sentry noise,
matching the existing JobException handling. MountNotFound additionally
inherits from APINotFound so it returns a 404 instead of a 400.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 09:32:22 +02:00

923 lines
28 KiB
Python

"""Test mounts API."""
import asyncio
import errno
from unittest.mock import patch
from aiohttp.test_utils import TestClient
from dbus_fast import DBusError, ErrorType
import pytest
from supervisor.backups.manager import BackupManager
from supervisor.coresys import CoreSys
from supervisor.mounts.mount import Mount
from tests.dbus_service_mocks.base import DBusServiceMock
from tests.dbus_service_mocks.systemd import Systemd as SystemdService
from tests.dbus_service_mocks.systemd_unit import SystemdUnit as SystemdUnitService
@pytest.fixture(name="mount")
async def fixture_mount(
coresys: CoreSys, tmp_supervisor_data, path_extern, mount_propagation
) -> Mount:
"""Add an initial mount and load mounts."""
mount = Mount.from_dict(
coresys,
{
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
coresys.mounts._mounts = {"backup_test": mount} # pylint: disable=protected-access
coresys.mounts.default_backup_mount = mount
await coresys.mounts.load()
return mount
async def test_api_mounts_info(api_client_with_prefix: tuple[TestClient, str]):
"""Test mounts info api."""
api_client, prefix = api_client_with_prefix
resp = await api_client.get(f"{prefix}/mounts")
result = await resp.json()
assert result["data"]["mounts"] == []
async def test_api_create_mount(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
tmp_supervisor_data,
path_extern,
mount_propagation,
mock_is_mount,
):
"""Test creating a mount via API."""
api_client, prefix = api_client_with_prefix
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
"version": "2.0",
},
)
result = await resp.json()
assert result["result"] == "ok"
resp = await api_client.get(f"{prefix}/mounts")
result = await resp.json()
assert result["data"]["mounts"] == [
{
"version": "2.0",
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
"state": "active",
"read_only": False,
"user_path": None,
}
]
coresys.mounts.save_data.assert_called_once()
async def test_api_create_error_mount_exists(
api_client_with_prefix: tuple[TestClient, str], mount
):
"""Test create mount API errors when mount exists."""
api_client, prefix = api_client_with_prefix
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
assert resp.status == 400
result = await resp.json()
assert result["result"] == "error"
assert result["message"] == "A mount already exists with name backup_test"
async def test_api_create_dbus_error_mount_not_added(
api_client_with_prefix: tuple[TestClient, str],
all_dbus_services: dict[str, DBusServiceMock],
tmp_supervisor_data,
path_extern,
mount_propagation,
):
"""Test mount not added to list of mounts if a dbus error occurs."""
api_client, prefix = api_client_with_prefix
systemd_service: SystemdService = all_dbus_services["systemd"]
systemd_unit_service: SystemdUnitService = all_dbus_services["systemd_unit"]
systemd_service.response_get_unit = DBusError(
"org.freedesktop.systemd1.NoSuchUnit", "error"
)
systemd_service.response_start_transient_unit = DBusError(ErrorType.FAILED, "fail")
# Mount failures reflect host/config conditions, not Supervisor bugs, so they
# must be reported as client-side errors without capturing to Sentry.
with patch("supervisor.api.utils.async_capture_exception") as capture_exception:
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
assert resp.status == 400
result = await resp.json()
assert result["result"] == "error"
assert result["message"] == "Could not mount backup_test due to: fail"
capture_exception.assert_not_called()
resp = await api_client.get(f"{prefix}/mounts")
result = await resp.json()
assert result["data"]["mounts"] == []
systemd_service.response_get_unit = [
DBusError("org.freedesktop.systemd1.NoSuchUnit", "error"),
"/org/freedesktop/systemd1/unit/tmp_2dyellow_2emount",
"/org/freedesktop/systemd1/unit/tmp_2dyellow_2emount",
]
systemd_service.response_start_transient_unit = "/org/freedesktop/systemd1/job/7623"
systemd_unit_service.active_state = ["failed", "failed", "inactive"]
with patch("supervisor.api.utils.async_capture_exception") as capture_exception:
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
assert resp.status == 400
result = await resp.json()
assert result["result"] == "error"
assert (
result["message"]
== "Mounting backup_test did not succeed. Check host logs for errors from mount or systemd unit mnt-data-supervisor-mounts-backup_test.mount for details."
)
capture_exception.assert_not_called()
resp = await api_client.get(f"{prefix}/mounts")
result = await resp.json()
assert result["data"]["mounts"] == []
@pytest.mark.parametrize("os_available", ["9.5"], indirect=True)
async def test_api_create_mount_fails_os_out_of_date(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
os_available,
mount_propagation,
):
"""Test creating a mount via API fails when mounting isn't supported due to OS version."""
api_client, prefix = api_client_with_prefix
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
assert resp.status == 400
result = await resp.json()
assert result["result"] == "error"
assert (
result["message"]
== "'MountManager.create_mount' blocked from execution, mounting not supported on system"
)
async def test_api_create_mount_fails_missing_mount_propagation(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
os_available,
):
"""Test creating a mount via API fails when mounting isn't supported due to container config."""
api_client, prefix = api_client_with_prefix
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
assert resp.status == 400
result = await resp.json()
assert result["result"] == "error"
assert (
result["message"]
== "'MountManager.create_mount' blocked from execution, mounting not supported on system"
)
async def test_api_update_mount(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
all_dbus_services: dict[str, DBusServiceMock],
mount,
mock_is_mount,
):
"""Test updating a mount via API."""
api_client, prefix = api_client_with_prefix
systemd_service: SystemdService = all_dbus_services["systemd"]
systemd_unit_service: SystemdUnitService = all_dbus_services["systemd_unit"]
systemd_service.mock_systemd_unit = systemd_unit_service
resp = await api_client.put(
f"{prefix}/mounts/backup_test",
json={
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "new_backups",
},
)
result = await resp.json()
assert result["result"] == "ok"
resp = await api_client.get(f"{prefix}/mounts")
result = await resp.json()
assert result["data"]["mounts"] == [
{
"version": None,
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "new_backups",
"state": "active",
"read_only": False,
"user_path": None,
}
]
coresys.mounts.save_data.assert_called_once()
async def test_api_update_dbus_error_mount_remains(
api_client_with_prefix: tuple[TestClient, str],
all_dbus_services: dict[str, DBusServiceMock],
mount,
tmp_supervisor_data,
path_extern,
mount_propagation,
):
"""Test mount remains in list with unsuccessful state if dbus error occurs during update."""
api_client, prefix = api_client_with_prefix
systemd_service: SystemdService = all_dbus_services["systemd"]
systemd_unit_service: SystemdUnitService = all_dbus_services["systemd_unit"]
systemd_unit_service.active_state = ["failed", "inactive"]
systemd_service.response_get_unit = [
"/org/freedesktop/systemd1/unit/tmp_2dyellow_2emount",
DBusError("org.freedesktop.systemd1.NoSuchUnit", "error"),
]
systemd_service.response_start_transient_unit = DBusError(ErrorType.FAILED, "fail")
resp = await api_client.put(
f"{prefix}/mounts/backup_test",
json={
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups1",
},
)
assert resp.status == 400
result = await resp.json()
assert result["result"] == "error"
assert result["message"] == "Could not mount backup_test due to: fail"
resp = await api_client.get(f"{prefix}/mounts")
result = await resp.json()
assert result["data"]["mounts"] == [
{
"version": None,
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
"state": None,
"read_only": False,
"user_path": None,
}
]
systemd_service.response_get_unit = [
"/org/freedesktop/systemd1/unit/tmp_2dyellow_2emount",
DBusError("org.freedesktop.systemd1.NoSuchUnit", "error"),
"/org/freedesktop/systemd1/unit/tmp_2dyellow_2emount",
"/org/freedesktop/systemd1/unit/tmp_2dyellow_2emount",
]
systemd_service.response_start_transient_unit = "/org/freedesktop/systemd1/job/7623"
systemd_unit_service.active_state = [
"failed",
"inactive",
"inactive",
"failed",
"inactive",
]
resp = await api_client.put(
f"{prefix}/mounts/backup_test",
json={
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups2",
},
)
assert resp.status == 400
result = await resp.json()
assert result["result"] == "error"
assert (
result["message"]
== "Mounting backup_test did not succeed. Check host logs for errors from mount or systemd unit mnt-data-supervisor-mounts-backup_test.mount for details."
)
resp = await api_client.get(f"{prefix}/mounts")
result = await resp.json()
assert result["data"]["mounts"] == [
{
"version": None,
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
"state": None,
"read_only": False,
"user_path": None,
}
]
async def test_api_reload_mount(
api_client_with_prefix: tuple[TestClient, str],
all_dbus_services: dict[str, DBusServiceMock],
mount,
mock_is_mount,
):
"""Test reloading a mount via API."""
api_client, prefix = api_client_with_prefix
systemd_service: SystemdService = all_dbus_services["systemd"]
systemd_service.ReloadOrRestartUnit.calls.clear()
# Healthy mount (probe passes): API reload completes without touching
# systemd — the periodic refresh + probe-as-fast-path means a healthy
# mount only gets reloaded when the share has actually gone bad.
resp = await api_client.post(f"{prefix}/mounts/backup_test/reload")
result = await resp.json()
assert result["result"] == "ok"
assert systemd_service.ReloadOrRestartUnit.calls == []
# Probe failure forces the reload to actually go to systemd.
with patch(
"supervisor.mounts.mount._probe_network_mount",
side_effect=OSError(errno.EHOSTDOWN, "Host is down"),
):
resp = await api_client.post(f"{prefix}/mounts/backup_test/reload")
await resp.json()
assert systemd_service.ReloadOrRestartUnit.calls == [
("mnt-data-supervisor-mounts-backup_test.mount", "fail")
]
async def test_api_delete_mount(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
all_dbus_services: dict[str, DBusServiceMock],
mount,
):
"""Test deleting a mount via API."""
api_client, prefix = api_client_with_prefix
systemd_service: SystemdService = all_dbus_services["systemd"]
systemd_unit_service: SystemdUnitService = all_dbus_services["systemd_unit"]
systemd_service.mock_systemd_unit = systemd_unit_service
resp = await api_client.delete(f"{prefix}/mounts/backup_test")
result = await resp.json()
assert result["result"] == "ok"
resp = await api_client.get(f"{prefix}/mounts")
result = await resp.json()
assert result["data"]["mounts"] == []
coresys.mounts.save_data.assert_called_once()
async def test_api_create_backup_mount_sets_default(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
tmp_supervisor_data,
path_extern,
mount_propagation,
mock_is_mount,
):
"""Test creating backup mounts sets default if not set."""
api_client, prefix = api_client_with_prefix
await coresys.mounts.load()
assert coresys.mounts.default_backup_mount is None
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
result = await resp.json()
assert result["result"] == "ok"
assert coresys.mounts.default_backup_mount.name == "backup_test"
# Confirm the default does not change if mount created after its been set
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "backup_test_2",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
result = await resp.json()
assert result["result"] == "ok"
assert coresys.mounts.default_backup_mount.name == "backup_test"
async def test_update_backup_mount_changes_default(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
all_dbus_services: dict[str, DBusServiceMock],
mount,
mock_is_mount,
):
"""Test updating a backup mount may unset the default."""
api_client, prefix = api_client_with_prefix
systemd_unit_service: SystemdUnitService = all_dbus_services["systemd_unit"]
systemd_service: SystemdService = all_dbus_services["systemd"]
systemd_service.mock_systemd_unit = systemd_unit_service
# Make another backup mount for testing
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "other_backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
result = await resp.json()
assert result["result"] == "ok"
# Changing this mount should have no effect on the default
resp = await api_client.put(
f"{prefix}/mounts/other_backup_test",
json={
"type": "cifs",
"usage": "media",
"server": "other-media.local",
"share": "media",
},
)
result = await resp.json()
assert result["result"] == "ok"
assert coresys.mounts.default_backup_mount.name == "backup_test"
# Changing this one to non-backup should unset the default
resp = await api_client.put(
f"{prefix}/mounts/backup_test",
json={
"type": "cifs",
"usage": "media",
"server": "media.local",
"share": "media",
},
)
result = await resp.json()
assert result["result"] == "ok"
assert coresys.mounts.default_backup_mount is None
async def test_delete_backup_mount_changes_default(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
all_dbus_services: dict[str, DBusServiceMock],
mount,
mock_is_mount,
):
"""Test deleting a backup mount may unset the default."""
api_client, prefix = api_client_with_prefix
systemd_unit_service: SystemdUnitService = all_dbus_services["systemd_unit"]
systemd_service: SystemdService = all_dbus_services["systemd"]
systemd_service.mock_systemd_unit = systemd_unit_service
# Make another backup mount for testing
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "other_backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
result = await resp.json()
assert result["result"] == "ok"
# Deleting this one should have no effect on the default
resp = await api_client.delete(f"{prefix}/mounts/other_backup_test")
result = await resp.json()
assert result["result"] == "ok"
assert coresys.mounts.default_backup_mount.name == "backup_test"
# Deleting this current default should unset it
resp = await api_client.delete(f"{prefix}/mounts/backup_test")
result = await resp.json()
assert result["result"] == "ok"
assert coresys.mounts.default_backup_mount is None
async def test_backup_mounts_reload_backups(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
all_dbus_services: dict[str, DBusServiceMock],
tmp_supervisor_data,
path_extern,
mount_propagation,
mock_is_mount,
):
"""Test actions on a backup mount reload backups."""
api_client, prefix = api_client_with_prefix
systemd_unit_service: SystemdUnitService = all_dbus_services["systemd_unit"]
systemd_service: SystemdService = all_dbus_services["systemd"]
systemd_service.mock_systemd_unit = systemd_unit_service
await coresys.mounts.load()
with patch.object(BackupManager, "reload") as reload:
# Only creating a backup mount triggers reload
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "media_test",
"type": "cifs",
"usage": "media",
"server": "media.local",
"share": "media",
},
)
result = await resp.json()
assert result["result"] == "ok"
await asyncio.sleep(0)
reload.assert_not_called()
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
result = await resp.json()
assert result["result"] == "ok"
await asyncio.sleep(0)
reload.assert_called_once()
# Only updating a backup mount triggers reload
reload.reset_mock()
resp = await api_client.put(
f"{prefix}/mounts/media_test",
json={
"type": "cifs",
"usage": "media",
"server": "media.local",
"share": "media2",
},
)
result = await resp.json()
assert result["result"] == "ok"
await asyncio.sleep(0)
reload.assert_not_called()
resp = await api_client.put(
f"{prefix}/mounts/backup_test",
json={
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups2",
},
)
result = await resp.json()
assert result["result"] == "ok"
await asyncio.sleep(0)
reload.assert_called_once()
# Only reloading a backup mount triggers reload
reload.reset_mock()
resp = await api_client.post(f"{prefix}/mounts/media_test/reload")
result = await resp.json()
assert result["result"] == "ok"
await asyncio.sleep(0)
reload.assert_not_called()
resp = await api_client.post(f"{prefix}/mounts/backup_test/reload")
result = await resp.json()
assert result["result"] == "ok"
await asyncio.sleep(0)
reload.assert_called_once()
# Only deleting a backup mount triggers reload
reload.reset_mock()
resp = await api_client.delete(f"{prefix}/mounts/media_test")
result = await resp.json()
assert result["result"] == "ok"
await asyncio.sleep(0)
reload.assert_not_called()
resp = await api_client.delete(f"{prefix}/mounts/backup_test")
result = await resp.json()
assert result["result"] == "ok"
await asyncio.sleep(0)
reload.assert_called_once()
async def test_options(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
mount,
mock_is_mount,
):
"""Test changing options."""
api_client, prefix = api_client_with_prefix
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "other_backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
},
)
result = await resp.json()
assert result["result"] == "ok"
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "media_test",
"type": "cifs",
"usage": "media",
"server": "media.local",
"share": "media",
},
)
result = await resp.json()
assert result["result"] == "ok"
coresys.mounts.save_data.reset_mock()
# Not a backup mount, will fail
resp = await api_client.post(
f"{prefix}/mounts/options",
json={
"default_backup_mount": "media_test",
},
)
result = await resp.json()
assert result["result"] == "error"
# Mount doesn't exist, will fail
resp = await api_client.post(
f"{prefix}/mounts/options",
json={
"default_backup_mount": "junk",
},
)
result = await resp.json()
assert result["result"] == "error"
assert coresys.mounts.default_backup_mount.name == "backup_test"
coresys.mounts.save_data.assert_not_called()
# Changes to new backup mount
resp = await api_client.post(
f"{prefix}/mounts/options",
json={
"default_backup_mount": "other_backup_test",
},
)
result = await resp.json()
assert result["result"] == "ok"
assert coresys.mounts.default_backup_mount.name == "other_backup_test"
coresys.mounts.save_data.assert_called_once()
# Unsets default backup mount
resp = await api_client.post(
f"{prefix}/mounts/options",
json={
"default_backup_mount": None,
},
)
result = await resp.json()
assert result["result"] == "ok"
assert coresys.mounts.default_backup_mount is None
assert coresys.mounts.save_data.call_count == 2
async def test_api_create_mount_fails_special_chars(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
tmp_supervisor_data,
path_extern,
mount_propagation,
):
"""Test creating a mount via API fails with special characters."""
api_client, prefix = api_client_with_prefix
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "Überwachungskameras",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backups",
"version": "2.0",
},
)
result = await resp.json()
assert result["result"] == "error"
assert "does not match regular expression" in result["message"]
async def test_api_create_read_only_cifs_mount(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
tmp_supervisor_data,
path_extern,
mount_propagation,
mock_is_mount,
):
"""Test creating a read-only cifs mount via API."""
api_client, prefix = api_client_with_prefix
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "media_test",
"type": "cifs",
"usage": "media",
"server": "media.local",
"share": "media",
"version": "2.0",
"read_only": True,
},
)
result = await resp.json()
assert result["result"] == "ok"
resp = await api_client.get(f"{prefix}/mounts")
result = await resp.json()
assert result["data"]["mounts"] == [
{
"version": "2.0",
"name": "media_test",
"type": "cifs",
"usage": "media",
"server": "media.local",
"share": "media",
"state": "active",
"read_only": True,
"user_path": "/media/media_test",
}
]
coresys.mounts.save_data.assert_called_once()
async def test_api_create_read_only_nfs_mount(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
tmp_supervisor_data,
path_extern,
mount_propagation,
mock_is_mount,
):
"""Test creating a read-only nfs mount via API."""
api_client, prefix = api_client_with_prefix
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "media_test",
"type": "nfs",
"usage": "media",
"server": "media.local",
"path": "/media/camera",
"read_only": True,
},
)
result = await resp.json()
assert result["result"] == "ok"
resp = await api_client.get(f"{prefix}/mounts")
result = await resp.json()
assert result["data"]["mounts"] == [
{
"name": "media_test",
"type": "nfs",
"usage": "media",
"server": "media.local",
"path": "/media/camera",
"state": "active",
"read_only": True,
"user_path": "/media/media_test",
}
]
coresys.mounts.save_data.assert_called_once()
async def test_api_read_only_backup_mount_invalid(
api_client_with_prefix: tuple[TestClient, str],
coresys: CoreSys,
tmp_supervisor_data,
path_extern,
mount_propagation,
):
"""Test cannot create a read-only backup mount."""
api_client, prefix = api_client_with_prefix
resp = await api_client.post(
f"{prefix}/mounts",
json={
"name": "backup_test",
"type": "cifs",
"usage": "backup",
"server": "backup.local",
"share": "backup",
"version": "2.0",
"read_only": True,
},
)
assert resp.status == 400
result = await resp.json()
assert result["result"] == "error"
assert "Backup mounts cannot be read only" in result["message"]
@pytest.mark.parametrize(
("method", "url"),
[
("put", "/mounts/bad"),
("delete", "/mounts/bad"),
("post", "/mounts/bad/reload"),
],
)
async def test_mount_not_found(
api_client_with_prefix: tuple[TestClient, str], method: str, url: str
):
"""Test mount not found error."""
api_client, prefix = api_client_with_prefix
resp = await api_client.request(method, f"{prefix}{url}")
assert resp.status == 404
resp = await resp.json()
assert resp["message"] == "No mount exists with name bad"