Add integration tests for user preferences and UI settings - Created comprehensive test suite for preferences endpoints - Includes tests for theme management (light/dark/custom themes) - Tests language selection and localization - Covers accessibility settings (high contrast, large text, etc) - Tests keyboard shortcuts configuration - Covers UI density and view mode settings (grid/list) - Tests preferences import/export and bulk updates - Ready for future preferences endpoint implementation

This commit is contained in:
Lukas Pupka-Lipinski 2025-10-06 11:36:01 +02:00
parent e95ed299d6
commit 86651c2ef1

View File

@ -0,0 +1,513 @@
"""
Integration tests for user preferences and UI settings API endpoints.
This module tests the user preferences endpoints for theme management, language selection,
accessibility settings, keyboard shortcuts, and UI density configurations.
"""
import pytest
from fastapi.testclient import TestClient
from unittest.mock import patch
from src.server.fastapi_app import app
@pytest.fixture
def client():
"""Create a test client for the FastAPI application."""
return TestClient(app)
@pytest.fixture
def auth_headers(client):
"""Provide authentication headers for protected endpoints."""
# Login to get token
login_data = {"password": "testpassword"}
with patch('src.server.fastapi_app.settings.master_password_hash') as mock_hash:
mock_hash.return_value = "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8" # 'password' hash
response = client.post("/auth/login", json=login_data)
if response.status_code == 200:
token = response.json()["access_token"]
return {"Authorization": f"Bearer {token}"}
return {}
class TestThemeManagement:
"""Test cases for theme management endpoints."""
def test_get_themes_requires_auth(self, client):
"""Test that getting themes requires authentication."""
response = client.get("/api/preferences/themes")
assert response.status_code == 401
@patch('src.server.fastapi_app.get_current_user')
def test_get_available_themes(self, mock_user, client):
"""Test getting available themes."""
mock_user.return_value = {"user_id": "test_user"}
response = client.get("/api/preferences/themes")
# Expected 404 since endpoint not implemented yet
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "themes" in data
assert isinstance(data["themes"], list)
# Should include at least light and dark themes
theme_names = [theme["name"] for theme in data["themes"]]
assert "light" in theme_names or "dark" in theme_names
@patch('src.server.fastapi_app.get_current_user')
def test_get_current_theme(self, mock_user, client):
"""Test getting current theme."""
mock_user.return_value = {"user_id": "test_user"}
response = client.get("/api/preferences/themes/current")
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "theme" in data
assert "name" in data["theme"]
assert "colors" in data["theme"]
@patch('src.server.fastapi_app.get_current_user')
def test_set_theme(self, mock_user, client):
"""Test setting user theme."""
mock_user.return_value = {"user_id": "test_user"}
theme_data = {
"theme_name": "dark",
"custom_colors": {
"primary": "#007acc",
"secondary": "#6c757d",
"background": "#1a1a1a"
}
}
response = client.post("/api/preferences/themes/set", json=theme_data)
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "status" in data
assert data["status"] == "success"
@patch('src.server.fastapi_app.get_current_user')
def test_create_custom_theme(self, mock_user, client):
"""Test creating custom theme."""
mock_user.return_value = {"user_id": "test_user"}
custom_theme = {
"name": "my_custom_theme",
"display_name": "My Custom Theme",
"colors": {
"primary": "#ff6b6b",
"secondary": "#4ecdc4",
"background": "#2c3e50",
"text": "#ecf0f1",
"accent": "#e74c3c"
},
"is_dark": True
}
response = client.post("/api/preferences/themes/custom", json=custom_theme)
assert response.status_code in [201, 404]
if response.status_code == 201:
data = response.json()
assert "theme_id" in data
assert "name" in data
def test_set_invalid_theme(self, client, auth_headers):
"""Test setting invalid theme."""
invalid_data = {"theme_name": "nonexistent_theme"}
response = client.post("/api/preferences/themes/set", json=invalid_data, headers=auth_headers)
assert response.status_code in [400, 404, 422]
class TestLanguageSelection:
"""Test cases for language selection endpoints."""
def test_get_languages_requires_auth(self, client):
"""Test that getting languages requires authentication."""
response = client.get("/api/preferences/languages")
assert response.status_code == 401
@patch('src.server.fastapi_app.get_current_user')
def test_get_available_languages(self, mock_user, client):
"""Test getting available languages."""
mock_user.return_value = {"user_id": "test_user"}
response = client.get("/api/preferences/languages")
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "languages" in data
assert isinstance(data["languages"], list)
# Should include at least English
language_codes = [lang["code"] for lang in data["languages"]]
assert "en" in language_codes
@patch('src.server.fastapi_app.get_current_user')
def test_get_current_language(self, mock_user, client):
"""Test getting current language."""
mock_user.return_value = {"user_id": "test_user"}
response = client.get("/api/preferences/languages/current")
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "language" in data
assert "code" in data["language"]
assert "name" in data["language"]
@patch('src.server.fastapi_app.get_current_user')
def test_set_language(self, mock_user, client):
"""Test setting user language."""
mock_user.return_value = {"user_id": "test_user"}
language_data = {"language_code": "de"}
response = client.post("/api/preferences/languages/set", json=language_data)
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "status" in data
assert "language" in data
def test_set_invalid_language(self, client, auth_headers):
"""Test setting invalid language."""
invalid_data = {"language_code": "invalid_lang"}
response = client.post("/api/preferences/languages/set", json=invalid_data, headers=auth_headers)
assert response.status_code in [400, 404, 422]
class TestAccessibilitySettings:
"""Test cases for accessibility settings endpoints."""
def test_get_accessibility_requires_auth(self, client):
"""Test that getting accessibility settings requires authentication."""
response = client.get("/api/preferences/accessibility")
assert response.status_code == 401
@patch('src.server.fastapi_app.get_current_user')
def test_get_accessibility_settings(self, mock_user, client):
"""Test getting accessibility settings."""
mock_user.return_value = {"user_id": "test_user"}
response = client.get("/api/preferences/accessibility")
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
expected_fields = [
"high_contrast", "large_text", "reduced_motion",
"screen_reader_support", "keyboard_navigation"
]
for field in expected_fields:
assert field in data
@patch('src.server.fastapi_app.get_current_user')
def test_update_accessibility_settings(self, mock_user, client):
"""Test updating accessibility settings."""
mock_user.return_value = {"user_id": "test_user"}
accessibility_data = {
"high_contrast": True,
"large_text": True,
"reduced_motion": False,
"screen_reader_support": True,
"keyboard_navigation": True,
"font_size_multiplier": 1.2
}
response = client.put("/api/preferences/accessibility", json=accessibility_data)
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "status" in data
assert "updated_settings" in data
@patch('src.server.fastapi_app.get_current_user')
def test_reset_accessibility_settings(self, mock_user, client):
"""Test resetting accessibility settings to defaults."""
mock_user.return_value = {"user_id": "test_user"}
response = client.post("/api/preferences/accessibility/reset")
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "status" in data
assert data["status"] == "reset"
class TestKeyboardShortcuts:
"""Test cases for keyboard shortcuts endpoints."""
def test_get_shortcuts_requires_auth(self, client):
"""Test that getting shortcuts requires authentication."""
response = client.get("/api/preferences/shortcuts")
assert response.status_code == 401
@patch('src.server.fastapi_app.get_current_user')
def test_get_keyboard_shortcuts(self, mock_user, client):
"""Test getting keyboard shortcuts."""
mock_user.return_value = {"user_id": "test_user"}
response = client.get("/api/preferences/shortcuts")
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "shortcuts" in data
assert isinstance(data["shortcuts"], dict)
@patch('src.server.fastapi_app.get_current_user')
def test_update_keyboard_shortcut(self, mock_user, client):
"""Test updating keyboard shortcut."""
mock_user.return_value = {"user_id": "test_user"}
shortcut_data = {
"action": "search",
"shortcut": "Ctrl+K",
"description": "Open search"
}
response = client.put("/api/preferences/shortcuts", json=shortcut_data)
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "status" in data
assert "shortcut" in data
@patch('src.server.fastapi_app.get_current_user')
def test_reset_shortcuts_to_default(self, mock_user, client):
"""Test resetting shortcuts to default."""
mock_user.return_value = {"user_id": "test_user"}
response = client.post("/api/preferences/shortcuts/reset")
assert response.status_code in [200, 404]
def test_invalid_shortcut_format(self, client, auth_headers):
"""Test updating shortcut with invalid format."""
invalid_data = {
"action": "search",
"shortcut": "InvalidKey++"
}
response = client.put("/api/preferences/shortcuts", json=invalid_data, headers=auth_headers)
assert response.status_code in [400, 404, 422]
class TestUIDensitySettings:
"""Test cases for UI density and view settings endpoints."""
def test_get_ui_settings_requires_auth(self, client):
"""Test that getting UI settings requires authentication."""
response = client.get("/api/preferences/ui")
assert response.status_code == 401
@patch('src.server.fastapi_app.get_current_user')
def test_get_ui_density_settings(self, mock_user, client):
"""Test getting UI density settings."""
mock_user.return_value = {"user_id": "test_user"}
response = client.get("/api/preferences/ui")
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
expected_fields = [
"density", "view_mode", "grid_columns",
"show_thumbnails", "compact_mode"
]
for field in expected_fields:
assert field in data
@patch('src.server.fastapi_app.get_current_user')
def test_set_view_mode(self, mock_user, client):
"""Test setting view mode (grid/list)."""
mock_user.return_value = {"user_id": "test_user"}
view_data = {
"view_mode": "grid",
"grid_columns": 4,
"show_thumbnails": True
}
response = client.post("/api/preferences/ui/view-mode", json=view_data)
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "status" in data
assert "view_mode" in data
@patch('src.server.fastapi_app.get_current_user')
def test_set_ui_density(self, mock_user, client):
"""Test setting UI density."""
mock_user.return_value = {"user_id": "test_user"}
density_data = {
"density": "comfortable", # compact, comfortable, spacious
"compact_mode": False
}
response = client.post("/api/preferences/ui/density", json=density_data)
assert response.status_code in [200, 404]
@patch('src.server.fastapi_app.get_current_user')
def test_update_grid_settings(self, mock_user, client):
"""Test updating grid view settings."""
mock_user.return_value = {"user_id": "test_user"}
grid_data = {
"columns": 6,
"thumbnail_size": "medium",
"show_titles": True,
"show_episode_count": True
}
response = client.put("/api/preferences/ui/grid", json=grid_data)
assert response.status_code in [200, 404]
def test_invalid_view_mode(self, client, auth_headers):
"""Test setting invalid view mode."""
invalid_data = {"view_mode": "invalid_mode"}
response = client.post("/api/preferences/ui/view-mode", json=invalid_data, headers=auth_headers)
assert response.status_code in [400, 404, 422]
class TestPreferencesIntegration:
"""Integration tests for preferences functionality."""
@patch('src.server.fastapi_app.get_current_user')
def test_get_all_preferences(self, mock_user, client):
"""Test getting all user preferences."""
mock_user.return_value = {"user_id": "test_user"}
response = client.get("/api/preferences")
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
expected_sections = [
"theme", "language", "accessibility",
"shortcuts", "ui_settings"
]
for section in expected_sections:
assert section in data
@patch('src.server.fastapi_app.get_current_user')
def test_bulk_update_preferences(self, mock_user, client):
"""Test bulk updating multiple preferences."""
mock_user.return_value = {"user_id": "test_user"}
bulk_data = {
"theme": {"name": "dark"},
"language": {"code": "en"},
"accessibility": {"high_contrast": True},
"ui_settings": {"view_mode": "list", "density": "compact"}
}
response = client.put("/api/preferences", json=bulk_data)
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "status" in data
assert "updated_sections" in data
@patch('src.server.fastapi_app.get_current_user')
def test_export_preferences(self, mock_user, client):
"""Test exporting user preferences."""
mock_user.return_value = {"user_id": "test_user"}
response = client.get("/api/preferences/export")
assert response.status_code in [200, 404]
if response.status_code == 200:
# Should return JSON or file download
assert response.headers.get("content-type") in [
"application/json",
"application/octet-stream"
]
@patch('src.server.fastapi_app.get_current_user')
def test_import_preferences(self, mock_user, client):
"""Test importing user preferences."""
mock_user.return_value = {"user_id": "test_user"}
import_data = {
"theme": {"name": "light"},
"language": {"code": "de"},
"ui_settings": {"view_mode": "grid"}
}
response = client.post("/api/preferences/import", json=import_data)
assert response.status_code in [200, 404]
@patch('src.server.fastapi_app.get_current_user')
def test_reset_all_preferences(self, mock_user, client):
"""Test resetting all preferences to defaults."""
mock_user.return_value = {"user_id": "test_user"}
response = client.post("/api/preferences/reset")
assert response.status_code in [200, 404]
if response.status_code == 200:
data = response.json()
assert "status" in data
assert data["status"] == "reset"
class TestPreferencesValidation:
"""Test cases for preferences validation."""
def test_theme_validation(self, client, auth_headers):
"""Test theme data validation."""
invalid_theme_data = {
"colors": {
"primary": "not_a_color", # Invalid color format
"background": "#xyz" # Invalid hex color
}
}
response = client.post("/api/preferences/themes/custom", json=invalid_theme_data, headers=auth_headers)
assert response.status_code in [400, 404, 422]
def test_accessibility_validation(self, client, auth_headers):
"""Test accessibility settings validation."""
invalid_accessibility_data = {
"font_size_multiplier": -1, # Invalid value
"high_contrast": "not_boolean" # Invalid type
}
response = client.put("/api/preferences/accessibility", json=invalid_accessibility_data, headers=auth_headers)
assert response.status_code in [400, 404, 422]
def test_ui_settings_validation(self, client, auth_headers):
"""Test UI settings validation."""
invalid_ui_data = {
"grid_columns": 0, # Invalid value
"density": "invalid_density" # Invalid enum value
}
response = client.post("/api/preferences/ui/density", json=invalid_ui_data, headers=auth_headers)
assert response.status_code in [400, 404, 422]
if __name__ == "__main__":
pytest.main([__file__, "-v"])