mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
202 lines
6.7 KiB
Python
202 lines
6.7 KiB
Python
"""Tests for settings router endpoints and validation behavior."""
|
|
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
|
|
from app.models import AppSettings, BotConfig, Favorite
|
|
from app.routers.settings import (
|
|
AppSettingsUpdate,
|
|
FavoriteRequest,
|
|
MigratePreferencesRequest,
|
|
migrate_preferences,
|
|
toggle_favorite,
|
|
update_settings,
|
|
)
|
|
|
|
|
|
def _settings(
|
|
*,
|
|
favorites: list[Favorite] | None = None,
|
|
migrated: bool = False,
|
|
max_radio_contacts: int = 200,
|
|
experimental_channel_double_send: bool = False,
|
|
) -> AppSettings:
|
|
return AppSettings(
|
|
max_radio_contacts=max_radio_contacts,
|
|
experimental_channel_double_send=experimental_channel_double_send,
|
|
favorites=favorites or [],
|
|
auto_decrypt_dm_on_advert=False,
|
|
sidebar_sort_order="recent",
|
|
last_message_times={},
|
|
preferences_migrated=migrated,
|
|
advert_interval=0,
|
|
last_advert_time=0,
|
|
bots=[],
|
|
)
|
|
|
|
|
|
class TestUpdateSettings:
|
|
@pytest.mark.asyncio
|
|
async def test_forwards_only_provided_fields(self):
|
|
updated = _settings(max_radio_contacts=321)
|
|
with patch(
|
|
"app.routers.settings.AppSettingsRepository.update",
|
|
new_callable=AsyncMock,
|
|
return_value=updated,
|
|
) as mock_update:
|
|
result = await update_settings(
|
|
AppSettingsUpdate(
|
|
max_radio_contacts=321,
|
|
advert_interval=3600,
|
|
experimental_channel_double_send=True,
|
|
)
|
|
)
|
|
|
|
assert result.max_radio_contacts == 321
|
|
assert mock_update.call_count == 1
|
|
assert mock_update.call_args.kwargs == {
|
|
"max_radio_contacts": 321,
|
|
"advert_interval": 3600,
|
|
"experimental_channel_double_send": True,
|
|
}
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_empty_patch_returns_current_settings(self):
|
|
current = _settings()
|
|
with (
|
|
patch(
|
|
"app.routers.settings.AppSettingsRepository.get",
|
|
new_callable=AsyncMock,
|
|
return_value=current,
|
|
) as mock_get,
|
|
patch(
|
|
"app.routers.settings.AppSettingsRepository.update",
|
|
new_callable=AsyncMock,
|
|
) as mock_update,
|
|
):
|
|
result = await update_settings(AppSettingsUpdate())
|
|
|
|
assert result == current
|
|
mock_get.assert_awaited_once()
|
|
mock_update.assert_not_awaited()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_invalid_bot_syntax_returns_400(self):
|
|
bad_bot = BotConfig(
|
|
id="bot-1",
|
|
name="BadBot",
|
|
enabled=True,
|
|
code="def bot(:\n return 'x'\n",
|
|
)
|
|
|
|
with pytest.raises(HTTPException) as exc:
|
|
await update_settings(AppSettingsUpdate(bots=[bad_bot]))
|
|
|
|
assert exc.value.status_code == 400
|
|
assert "syntax error" in exc.value.detail.lower()
|
|
|
|
|
|
class TestToggleFavorite:
|
|
@pytest.mark.asyncio
|
|
async def test_adds_when_not_favorited(self):
|
|
initial = _settings(favorites=[])
|
|
updated = _settings(favorites=[Favorite(type="contact", id="aa" * 32)])
|
|
request = FavoriteRequest(type="contact", id="aa" * 32)
|
|
|
|
with (
|
|
patch(
|
|
"app.routers.settings.AppSettingsRepository.get",
|
|
new_callable=AsyncMock,
|
|
return_value=initial,
|
|
),
|
|
patch(
|
|
"app.routers.settings.AppSettingsRepository.add_favorite",
|
|
new_callable=AsyncMock,
|
|
return_value=updated,
|
|
) as mock_add,
|
|
patch(
|
|
"app.routers.settings.AppSettingsRepository.remove_favorite",
|
|
new_callable=AsyncMock,
|
|
) as mock_remove,
|
|
):
|
|
result = await toggle_favorite(request)
|
|
|
|
assert result.favorites == updated.favorites
|
|
mock_add.assert_awaited_once_with("contact", "aa" * 32)
|
|
mock_remove.assert_not_awaited()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_removes_when_already_favorited(self):
|
|
initial = _settings(favorites=[Favorite(type="channel", id="ABCD")])
|
|
updated = _settings(favorites=[])
|
|
request = FavoriteRequest(type="channel", id="ABCD")
|
|
|
|
with (
|
|
patch(
|
|
"app.routers.settings.AppSettingsRepository.get",
|
|
new_callable=AsyncMock,
|
|
return_value=initial,
|
|
),
|
|
patch(
|
|
"app.routers.settings.AppSettingsRepository.remove_favorite",
|
|
new_callable=AsyncMock,
|
|
return_value=updated,
|
|
) as mock_remove,
|
|
patch(
|
|
"app.routers.settings.AppSettingsRepository.add_favorite",
|
|
new_callable=AsyncMock,
|
|
) as mock_add,
|
|
):
|
|
result = await toggle_favorite(request)
|
|
|
|
assert result.favorites == []
|
|
mock_remove.assert_awaited_once_with("channel", "ABCD")
|
|
mock_add.assert_not_awaited()
|
|
|
|
|
|
class TestMigratePreferences:
|
|
@pytest.mark.asyncio
|
|
async def test_maps_frontend_payload_and_returns_migrated_true(self):
|
|
request = MigratePreferencesRequest(
|
|
favorites=[FavoriteRequest(type="contact", id="aa" * 32)],
|
|
sort_order="alpha",
|
|
last_message_times={"contact-aaaaaaaaaaaa": 123},
|
|
)
|
|
settings = _settings(favorites=[Favorite(type="contact", id="aa" * 32)], migrated=True)
|
|
|
|
with patch(
|
|
"app.routers.settings.AppSettingsRepository.migrate_preferences_from_frontend",
|
|
new_callable=AsyncMock,
|
|
return_value=(settings, True),
|
|
) as mock_migrate:
|
|
response = await migrate_preferences(request)
|
|
|
|
assert response.migrated is True
|
|
assert response.settings == settings
|
|
assert mock_migrate.call_args.kwargs == {
|
|
"favorites": [{"type": "contact", "id": "aa" * 32}],
|
|
"sort_order": "alpha",
|
|
"last_message_times": {"contact-aaaaaaaaaaaa": 123},
|
|
}
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_returns_migrated_false_when_already_done(self):
|
|
request = MigratePreferencesRequest(
|
|
favorites=[],
|
|
sort_order="recent",
|
|
last_message_times={},
|
|
)
|
|
settings = _settings(migrated=True)
|
|
|
|
with patch(
|
|
"app.routers.settings.AppSettingsRepository.migrate_preferences_from_frontend",
|
|
new_callable=AsyncMock,
|
|
return_value=(settings, False),
|
|
):
|
|
response = await migrate_preferences(request)
|
|
|
|
assert response.migrated is False
|
|
assert response.settings.preferences_migrated is True
|