Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

1103 lines
37 KiB
Python

"""
Extended tests for settings/manager.py
Tests cover additional functionality not in the main test file:
- Type conversion edge cases (get_bool_setting, various ui_elements)
- Environment variable edge cases
- Import/export settings
- Version checking
- Default settings loading
- create_setting helper
- WebSocket event emission
- Nested settings paths
- Password/sensitive field handling
"""
import os
import json
from unittest.mock import Mock, MagicMock, patch
class MockSetting:
"""Mock Setting model for testing."""
def __init__(
self,
key,
value,
ui_element="text",
editable=True,
type=None,
name=None,
description=None,
category=None,
options=None,
min_value=None,
max_value=None,
step=None,
visible=True,
):
self.key = key
self.value = value
self.ui_element = ui_element
self.editable = editable
self.type = type or Mock(name="APP")
self.name = name or key
self.description = description or ""
self.category = category
self.options = options
self.min_value = min_value
self.max_value = max_value
self.step = step
self.visible = visible
self.updated_at = None
class TestGetBoolSetting:
"""Tests for get_bool_setting method."""
def test_get_bool_setting_true_string(self):
"""get_bool_setting converts 'true' string to True."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="app.debug", value="true", ui_element="text"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_bool_setting("app.debug", check_env=False)
assert result is True
def test_get_bool_setting_false_string(self):
"""get_bool_setting converts 'false' string to False."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="app.debug", value="false", ui_element="text"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_bool_setting("app.debug", check_env=False)
assert result is False
def test_get_bool_setting_1_string(self):
"""get_bool_setting converts '1' string to True."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="app.debug", value="1", ui_element="text"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_bool_setting("app.debug", check_env=False)
assert result is True
def test_get_bool_setting_0_string(self):
"""get_bool_setting converts '0' string to False."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="app.debug", value="0", ui_element="text"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_bool_setting("app.debug", check_env=False)
assert result is False
def test_get_bool_setting_boolean_true(self):
"""get_bool_setting handles actual boolean True."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="app.debug", value=True, ui_element="checkbox"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_bool_setting("app.debug", check_env=False)
assert result is True
def test_get_bool_setting_returns_default(self):
"""get_bool_setting returns default when setting not found."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.all.return_value = []
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_bool_setting("app.nonexistent", default=True)
assert result is True
def test_get_bool_setting_yes_string(self):
"""get_bool_setting converts 'yes' string to True."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="app.enabled", value="yes", ui_element="text"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_bool_setting("app.enabled", check_env=False)
assert result is True
class TestUIElementTypeConversion:
"""Tests for UI element type conversion."""
def test_checkbox_returns_bool(self):
"""Checkbox ui_element returns boolean."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="app.debug", value=True, ui_element="checkbox"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("app.debug", check_env=False)
assert isinstance(result, bool)
def test_number_returns_float(self):
"""Number ui_element returns float."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="llm.temperature", value=0.7, ui_element="number"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("llm.temperature", check_env=False)
assert isinstance(result, float)
def test_range_returns_float(self):
"""Range ui_element returns float."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="llm.top_p", value=0.9, ui_element="range"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("llm.top_p", check_env=False)
assert isinstance(result, float)
def test_text_returns_string(self):
"""Text ui_element returns string."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="llm.model", value="gpt-4", ui_element="text"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("llm.model", check_env=False)
assert isinstance(result, str)
def test_password_returns_string(self):
"""Password ui_element returns string."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="llm.api_key", value="secret123", ui_element="password"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("llm.api_key", check_env=False)
assert isinstance(result, str)
def test_select_returns_string(self):
"""Select ui_element returns string."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="app.theme", value="dark", ui_element="select"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("app.theme", check_env=False)
assert isinstance(result, str)
def test_json_returns_as_is(self):
"""JSON ui_element returns value as-is."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
json_value = {"key": "value", "nested": {"data": 123}}
mock_setting = MockSetting(
key="app.config", value=json_value, ui_element="json"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("app.config", check_env=False)
assert result == json_value
def test_unknown_ui_element_returns_default(self):
"""Unknown ui_element returns default value."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="app.unknown", value="test", ui_element="unknown_type"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting(
"app.unknown", default="default_val", check_env=False
)
assert result == "default_val"
class TestEnvironmentVariableEdgeCases:
"""Tests for environment variable handling edge cases."""
def test_env_override_invalid_number(self):
"""Invalid number in env var falls back to DB value."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="llm.temperature", value=0.7, ui_element="number"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
os.environ["LDR_LLM_TEMPERATURE"] = "not_a_number"
try:
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("llm.temperature", check_env=True)
# Should fall back to DB value
assert result == 0.7
finally:
del os.environ["LDR_LLM_TEMPERATURE"]
def test_env_override_empty_string(self):
"""Empty string env var is treated as unset (Docker/Unraid templates create blank entries)."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="llm.model", value="gpt-4", ui_element="text"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
os.environ["LDR_LLM_MODEL"] = ""
try:
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("llm.model", check_env=True)
# Empty env var should NOT override the DB value
assert result == "gpt-4"
finally:
del os.environ["LDR_LLM_MODEL"]
def test_env_override_empty_string_no_db_value(self):
"""Empty env var with no DB value falls through to default.
This is the first-launch Unraid scenario: env vars are empty strings,
no DB row exists yet, so the default should be returned.
"""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
# No DB rows match this key
mock_query.filter.return_value.all.return_value = []
mock_session.query.return_value = mock_query
os.environ["LDR_LLM_PROVIDER"] = ""
try:
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting(
"llm.provider", default="openai", check_env=True
)
assert result == "openai"
finally:
del os.environ["LDR_LLM_PROVIDER"]
def test_env_override_empty_string_checkbox(self):
"""Empty env var for checkbox setting falls through to DB value."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="app.debug", value=True, ui_element="checkbox"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
os.environ["LDR_APP_DEBUG"] = ""
try:
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("app.debug", check_env=True)
assert result is True
finally:
del os.environ["LDR_APP_DEBUG"]
def test_env_override_empty_string_number(self):
"""Empty env var for number setting falls through to DB value."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="llm.temperature", value=0.7, ui_element="number"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
os.environ["LDR_LLM_TEMPERATURE"] = ""
try:
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("llm.temperature", check_env=True)
assert result == 0.7
finally:
del os.environ["LDR_LLM_TEMPERATURE"]
def test_env_override_with_nested_key(self):
"""Nested key with multiple dots converts correctly."""
from local_deep_research.settings.manager import (
check_env_setting,
)
os.environ["LDR_SEARCH_TOOL_MAX_RESULTS"] = "50"
try:
result = check_env_setting("search.tool.max_results")
assert result == "50"
finally:
del os.environ["LDR_SEARCH_TOOL_MAX_RESULTS"]
class TestNestedSettingsPaths:
"""Tests for nested settings path handling."""
def test_get_setting_returns_nested_dict(self):
"""Getting higher-level key returns nested dict."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_settings = [
MockSetting(key="llm.model", value="gpt-4", ui_element="text"),
MockSetting(key="llm.temperature", value=0.7, ui_element="number"),
]
mock_query.filter.return_value.all.return_value = mock_settings
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("llm", check_env=False)
assert isinstance(result, dict)
assert "model" in result
assert "temperature" in result
def test_get_setting_exact_match_single(self):
"""Getting exact key returns single value."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(
key="llm.model", value="gpt-4", ui_element="text"
)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.get_setting("llm.model", check_env=False)
assert result == "gpt-4"
class TestImportExportSettings:
"""Tests for import/export functionality."""
def test_import_settings_basic(self):
"""import_settings loads settings from dict."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.delete.return_value = 1
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
settings_data = {
"app.debug": {
"value": True,
"type": "APP",
"name": "Debug",
"ui_element": "checkbox",
},
}
with patch.object(manager, "_emit_settings_changed"):
manager.import_settings(settings_data)
mock_session.add.assert_called()
mock_session.commit.assert_called()
def test_import_settings_no_overwrite(self):
"""import_settings preserves existing values when overwrite=False."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.delete.return_value = 0
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
manager.get_setting = Mock(return_value="existing_value")
settings_data = {
"app.key": {
"value": "new_value",
"type": "APP",
"name": "Key",
"ui_element": "text",
},
}
with patch.object(manager, "_emit_settings_changed"):
manager.import_settings(settings_data, overwrite=False)
# Check that existing value was preserved
added_setting = mock_session.add.call_args[0][0]
assert added_setting.value == "existing_value"
def test_import_settings_delete_extra(self):
"""import_settings deletes extra settings when delete_extra=True."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.delete.return_value = 1
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
# Mock get_all_settings to return extra setting
manager.get_all_settings = Mock(
return_value={
"app.keep": {"value": True},
"app.extra": {"value": False},
}
)
manager.delete_setting = Mock(return_value=True)
settings_data = {
"app.keep": {
"value": True,
"type": "APP",
"name": "Keep",
"ui_element": "checkbox",
},
}
with patch.object(manager, "_emit_settings_changed"):
manager.import_settings(settings_data, delete_extra=True)
# Should have deleted the extra setting
manager.delete_setting.assert_any_call("app.extra", commit=False)
class TestLoadFromDefaultsFile:
"""Tests for load_from_defaults_file method."""
def test_load_from_defaults_file_calls_import(self):
"""load_from_defaults_file calls import_settings."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
manager = SettingsManager(db_session=mock_session)
# Mock import_settings and default_settings
manager.import_settings = Mock()
manager.__dict__.pop("default_settings", None)
with patch.object(
type(manager),
"default_settings",
new_callable=lambda: property(
lambda self: {"app.test": {"value": True}}
),
):
manager.load_from_defaults_file()
manager.import_settings.assert_called_once()
class TestVersionChecking:
"""Tests for version checking functionality."""
def test_update_db_version(self):
"""update_db_version updates the version in DB."""
from local_deep_research.settings.manager import (
SettingsManager,
)
from local_deep_research.__version__ import __version__
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.delete.return_value = 1
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
manager.update_db_version()
mock_session.add.assert_called_once()
added_setting = mock_session.add.call_args[0][0]
assert added_setting.value == __version__
mock_session.commit.assert_called_once()
class TestWebSocketEmission:
"""Tests for WebSocket event emission."""
@staticmethod
def _request_ctx(username="alice"):
"""A Flask request context with ``username`` in the session, so
_emit_settings_changed can resolve the owning user to scope the emit."""
from flask import Flask, session
app = Flask(__name__)
app.secret_key = "test-secret"
ctx = app.test_request_context()
ctx.push()
if username is not None:
session["username"] = username
return ctx
def _patched_socket(self, mock_socket_instance):
# Give user_room a realistic return so room scoping can be asserted.
mock_socket_instance.user_room.side_effect = lambda u: f"user:{u}"
return patch.dict(
"sys.modules",
{
"local_deep_research.web.services.socket_service": MagicMock(
SocketIOService=Mock(return_value=mock_socket_instance)
)
},
)
@staticmethod
def _working_manager():
"""A SettingsManager whose get_setting("app.debug") resolves to a real
value, so the only thing that can suppress the emit is the scoping
guard under test — not an incidental failure reading the setting."""
from local_deep_research.settings.manager import SettingsManager
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.all.return_value = [
MockSetting(key="app.debug", value=True)
]
mock_session.query.return_value = mock_query
return SettingsManager(db_session=mock_session)
def test_emit_settings_changed_scoped_to_owning_user_room(self):
"""The event is emitted only to the changing user's per-user room —
never broadcast — so one user's setting values (incl. plaintext API
keys) never reach other connected clients."""
from local_deep_research.settings.manager import SettingsManager
mock_session = Mock()
mock_query = Mock()
mock_setting = MockSetting(key="app.debug", value=True)
mock_query.filter.return_value.all.return_value = [mock_setting]
mock_session.query.return_value = mock_query
mock_socket_instance = Mock()
manager = SettingsManager(db_session=mock_session)
ctx = self._request_ctx(username="alice")
try:
with self._patched_socket(mock_socket_instance):
manager._emit_settings_changed(["app.debug"])
finally:
ctx.pop()
mock_socket_instance.emit_socket_event.assert_called_once()
args, kwargs = mock_socket_instance.emit_socket_event.call_args
assert args[0] == "settings_changed"
assert "app.debug" in args[1]["changed_keys"]
# The targeting room must be the owning user's room.
assert kwargs.get("room") == "user:alice"
def test_emit_settings_changed_skipped_without_request_context(self):
"""A settings change outside any request (start-up defaults, background
workers, migrations) has no user tab to notify, so nothing is emitted
— crucially, it must not fall back to a cross-user broadcast."""
mock_socket_instance = Mock()
manager = self._working_manager()
# No request context pushed.
with self._patched_socket(mock_socket_instance):
manager._emit_settings_changed(["app.debug"])
mock_socket_instance.emit_socket_event.assert_not_called()
def test_emit_settings_changed_skipped_when_no_username(self):
"""A request without an authenticated username emits nothing."""
mock_socket_instance = Mock()
manager = self._working_manager()
ctx = self._request_ctx(username=None)
try:
with self._patched_socket(mock_socket_instance):
manager._emit_settings_changed(["app.debug"])
finally:
ctx.pop()
mock_socket_instance.emit_socket_event.assert_not_called()
def test_emit_settings_changed_handles_exception(self):
"""_emit_settings_changed handles exceptions gracefully."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
manager = SettingsManager(db_session=mock_session)
# Mock SocketIOService to raise ValueError
mock_socket_module = MagicMock()
mock_socket_module.SocketIOService.side_effect = ValueError(
"Not initialized"
)
with patch.dict(
"sys.modules",
{
"local_deep_research.web.services.socket_service": mock_socket_module
},
):
# Should not raise
manager._emit_settings_changed(["app.debug"])
class TestDefaultSettingsProperty:
"""Tests for default_settings property."""
@patch("local_deep_research.settings.manager.defaults")
def test_default_settings_loads_json_files(self, mock_defaults):
"""default_settings loads JSON files from defaults directory."""
from local_deep_research.settings.manager import (
SettingsManager,
)
import tempfile
with tempfile.TemporaryDirectory() as tmpdir:
# Create mock defaults path
from pathlib import Path
mock_defaults.__file__ = str(Path(tmpdir) / "__init__.py")
# Create a test JSON file
json_file = Path(tmpdir) / "test_settings.json"
json_file.write_text(
json.dumps(
{
"test.setting": {
"value": "test_value",
"type": "APP",
"name": "Test",
"ui_element": "text",
}
}
)
)
manager = SettingsManager()
settings = manager.default_settings
assert "test.setting" in settings
def test_default_settings_handles_invalid_json(self):
"""default_settings handles invalid JSON gracefully."""
from local_deep_research.settings.manager import (
SettingsManager,
)
manager = SettingsManager()
# Should not raise even if there are issues
settings = manager.default_settings
assert isinstance(settings, dict)
class TestSettingTypeDetermination:
"""Tests for automatic setting type determination."""
def test_set_setting_determines_llm_type(self):
"""set_setting creates LLM type for llm. keys."""
from local_deep_research.settings.manager import (
SettingsManager,
)
from local_deep_research.database.models import SettingType
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.first.return_value = None
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
with patch.object(manager, "_emit_settings_changed"):
manager.set_setting("llm.new_setting", "value")
added_setting = mock_session.add.call_args[0][0]
assert added_setting.type == SettingType.LLM
def test_set_setting_determines_search_type(self):
"""set_setting creates SEARCH type for search. keys."""
from local_deep_research.settings.manager import (
SettingsManager,
)
from local_deep_research.database.models import SettingType
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.first.return_value = None
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
with patch.object(manager, "_emit_settings_changed"):
manager.set_setting("search.new_setting", "value")
added_setting = mock_session.add.call_args[0][0]
assert added_setting.type == SettingType.SEARCH
def test_set_setting_determines_report_type(self):
"""set_setting creates REPORT type for report. keys."""
from local_deep_research.settings.manager import (
SettingsManager,
)
from local_deep_research.database.models import SettingType
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.first.return_value = None
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
with patch.object(manager, "_emit_settings_changed"):
manager.set_setting("report.new_setting", "value")
added_setting = mock_session.add.call_args[0][0]
assert added_setting.type == SettingType.REPORT
def test_set_setting_defaults_to_app_type(self):
"""set_setting creates APP type for other keys."""
from local_deep_research.settings.manager import (
SettingsManager,
)
from local_deep_research.database.models import SettingType
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.first.return_value = None
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
with patch.object(manager, "_emit_settings_changed"):
manager.set_setting("other.new_setting", "value")
added_setting = mock_session.add.call_args[0][0]
assert added_setting.type == SettingType.APP
class TestCreateOrUpdateSettingTypes:
"""Tests for create_or_update_setting type handling."""
def test_create_or_update_with_llm_setting_class(self):
"""create_or_update_setting handles LLMSetting class."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.first.return_value = None
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
with patch.object(manager, "_emit_settings_changed"):
result = manager.create_or_update_setting(
{
"key": "llm.model",
"value": "gpt-4",
"name": "Model",
"description": "LLM model",
}
)
assert result is not None
def test_create_or_update_with_search_setting_class(self):
"""create_or_update_setting handles SearchSetting class."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_query.filter.return_value.first.return_value = None
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
with patch.object(manager, "_emit_settings_changed"):
result = manager.create_or_update_setting(
{
"key": "search.max_results",
"value": 10,
"name": "Max Results",
"description": "Maximum results",
}
)
assert result is not None
def test_create_or_update_with_non_editable_returns_none(self):
"""create_or_update_setting returns None for non-editable setting."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
existing_setting = MockSetting(
key="app.version", value="1.0", editable=False
)
mock_query.filter.return_value.first.return_value = existing_setting
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.create_or_update_setting(
{"key": "app.version", "value": "2.0", "name": "Version"}
)
assert result is None
class TestEdgeCases:
"""Tests for edge cases and boundary conditions."""
def test_get_setting_without_db_uses_defaults(self):
"""get_setting uses defaults when no DB session."""
from local_deep_research.settings.manager import (
SettingsManager,
)
manager = SettingsManager() # No db_session
# Should use default_settings
result = manager.get_setting("app.nonexistent", default="fallback")
# Returns default since setting likely doesn't exist
assert result == "fallback"
def test_get_all_settings_marks_locked_non_editable(self):
"""get_all_settings marks settings non-editable when locked."""
from local_deep_research.settings.manager import (
SettingsManager,
)
mock_session = Mock()
mock_query = Mock()
mock_settings = [
MockSetting(key="app.debug", value=True, editable=True),
]
mock_query.all.return_value = mock_settings
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
manager._SettingsManager__settings_locked = True
result = manager.get_all_settings()
assert result["app.debug"]["editable"] is False
def test_db_error_returns_default(self):
"""Database error returns default value."""
from local_deep_research.settings.manager import (
SettingsManager,
)
from sqlalchemy.exc import SQLAlchemyError
mock_session = Mock()
# Let __init__'s _ensure_settings_initialized() succeed
mock_session.query.return_value.count.return_value = 1
manager = SettingsManager(db_session=mock_session)
# Now make query raise for get_setting
mock_session.query.side_effect = SQLAlchemyError("DB Error")
result = manager.get_setting("app.test", default="default")
assert result == "default"
def test_thread_safety_no_db_passes(self):
"""Thread safety check passes without DB session."""
from local_deep_research.settings.manager import (
SettingsManager,
)
manager = SettingsManager() # No db_session
# Should not raise even from different thread concept
manager._check_thread_safety()
def test_set_setting_rollback_on_error(self):
"""set_setting calls rollback on SQLAlchemy error."""
from local_deep_research.settings.manager import (
SettingsManager,
)
from sqlalchemy.exc import SQLAlchemyError
mock_session = Mock()
mock_session.commit.side_effect = SQLAlchemyError("Commit failed")
mock_query = Mock()
mock_query.filter.return_value.first.return_value = MockSetting(
key="app.test", value="old"
)
mock_session.query.return_value = mock_query
manager = SettingsManager(db_session=mock_session)
result = manager.set_setting("app.test", "new")
assert result is False
mock_session.rollback.assert_called_once()