7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
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
152 lines
6.0 KiB
Python
152 lines
6.0 KiB
Python
"""
|
|
Tests for InMemorySettingsManager.get_settings_snapshot() and import_settings()
|
|
in api/settings_utils.py.
|
|
|
|
Tests cover:
|
|
- get_settings_snapshot: full format extraction, simplified format passthrough, mixed
|
|
- import_settings: overwrite=True, overwrite=False, delete_extra, type handling, empty
|
|
"""
|
|
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
|
|
def _make_manager_with_settings(settings_dict):
|
|
"""Create an InMemorySettingsManager with pre-loaded settings (no disk I/O)."""
|
|
with patch(
|
|
"local_deep_research.api.settings_utils.InMemorySettingsManager.__init__",
|
|
lambda self: None,
|
|
):
|
|
from local_deep_research.api.settings_utils import (
|
|
InMemorySettingsManager,
|
|
)
|
|
|
|
mgr = InMemorySettingsManager()
|
|
mgr._settings = settings_dict.copy()
|
|
mgr._base_manager = MagicMock()
|
|
return mgr
|
|
|
|
|
|
class TestGetSettingsSnapshot:
|
|
"""Tests for get_settings_snapshot()."""
|
|
|
|
def test_full_format_extracts_value(self):
|
|
"""Dict-with-value entries → snapshot has just the value."""
|
|
mgr = _make_manager_with_settings(
|
|
{"key1": {"value": 42, "ui_element": "number"}}
|
|
)
|
|
snapshot = mgr.get_settings_snapshot()
|
|
assert snapshot["key1"] == 42
|
|
|
|
def test_simplified_format_passthrough(self):
|
|
"""Non-dict or dict-without-value entries pass through as-is."""
|
|
mgr = _make_manager_with_settings({"key1": "simple_value"})
|
|
snapshot = mgr.get_settings_snapshot()
|
|
assert snapshot["key1"] == "simple_value"
|
|
|
|
def test_mixed_formats(self):
|
|
"""Mix of full and simplified formats in one snapshot."""
|
|
mgr = _make_manager_with_settings(
|
|
{
|
|
"full_key": {"value": True, "ui_element": "checkbox"},
|
|
"simple_key": 100,
|
|
"dict_no_value": {"other": "data"},
|
|
}
|
|
)
|
|
snapshot = mgr.get_settings_snapshot()
|
|
assert snapshot["full_key"] is True
|
|
assert snapshot["simple_key"] == 100
|
|
assert snapshot["dict_no_value"] == {"other": "data"}
|
|
|
|
def test_empty_settings(self):
|
|
"""Empty settings → empty snapshot."""
|
|
mgr = _make_manager_with_settings({})
|
|
assert mgr.get_settings_snapshot() == {}
|
|
|
|
def test_none_value_in_full_format(self):
|
|
"""Full format with value=None → snapshot has None."""
|
|
mgr = _make_manager_with_settings(
|
|
{"key": {"value": None, "ui_element": "text"}}
|
|
)
|
|
snapshot = mgr.get_settings_snapshot()
|
|
assert snapshot["key"] is None
|
|
|
|
def test_snapshot_is_independent_copy(self):
|
|
"""Modifying snapshot doesn't affect internal settings."""
|
|
mgr = _make_manager_with_settings(
|
|
{"key": {"value": "original", "ui_element": "text"}}
|
|
)
|
|
snapshot = mgr.get_settings_snapshot()
|
|
snapshot["key"] = "modified"
|
|
# Internal still has original value
|
|
assert mgr._settings["key"]["value"] == "original"
|
|
|
|
def test_list_value(self):
|
|
"""Full format with list value → snapshot has the list."""
|
|
mgr = _make_manager_with_settings(
|
|
{"key": {"value": [1, 2, 3], "ui_element": "text"}}
|
|
)
|
|
snapshot = mgr.get_settings_snapshot()
|
|
assert snapshot["key"] == [1, 2, 3]
|
|
|
|
|
|
class TestImportSettings:
|
|
"""Tests for import_settings()."""
|
|
|
|
def test_import_overwrites_existing(self):
|
|
"""overwrite=True replaces existing settings."""
|
|
mgr = _make_manager_with_settings({"key1": "old_value"})
|
|
mgr.import_settings({"key1": "new_value"}, overwrite=True)
|
|
assert mgr._settings["key1"] == "new_value"
|
|
|
|
def test_import_no_overwrite_skips_existing(self):
|
|
"""overwrite=False skips keys that already exist."""
|
|
mgr = _make_manager_with_settings({"key1": "original"})
|
|
mgr.import_settings({"key1": "ignored", "key2": "new"}, overwrite=False)
|
|
assert mgr._settings["key1"] == "original"
|
|
assert mgr._settings["key2"] == "new"
|
|
|
|
def test_import_adds_new_keys(self):
|
|
"""New keys are always added regardless of overwrite flag."""
|
|
mgr = _make_manager_with_settings({})
|
|
mgr.import_settings({"new_key": "value"}, overwrite=False)
|
|
assert mgr._settings["new_key"] == "value"
|
|
|
|
def test_delete_extra_clears_first(self):
|
|
"""delete_extra=True clears existing settings before importing."""
|
|
mgr = _make_manager_with_settings({"old_key": "old_value"})
|
|
mgr.import_settings({"new_key": "new_value"}, delete_extra=True)
|
|
assert "old_key" not in mgr._settings
|
|
assert mgr._settings["new_key"] == "new_value"
|
|
|
|
def test_delete_extra_false_preserves(self):
|
|
"""delete_extra=False preserves existing settings."""
|
|
mgr = _make_manager_with_settings({"old_key": "old_value"})
|
|
mgr.import_settings({"new_key": "new_value"}, delete_extra=False)
|
|
assert "old_key" in mgr._settings
|
|
assert "new_key" in mgr._settings
|
|
|
|
def test_import_full_format_with_type_conversion(self):
|
|
"""Full format dicts get _get_typed_value applied."""
|
|
mgr = _make_manager_with_settings({})
|
|
mgr._get_typed_value = MagicMock(return_value=42)
|
|
mgr.import_settings(
|
|
{"key": {"value": "42", "ui_element": "number"}}, overwrite=True
|
|
)
|
|
mgr._get_typed_value.assert_called()
|
|
# Value should be the typed result
|
|
assert mgr._settings["key"]["value"] == 42
|
|
|
|
def test_import_simple_value_no_type_conversion(self):
|
|
"""Simple (non-dict) values are stored as-is without type conversion."""
|
|
mgr = _make_manager_with_settings({})
|
|
mgr._get_typed_value = MagicMock()
|
|
mgr.import_settings({"key": "plain_string"}, overwrite=True)
|
|
mgr._get_typed_value.assert_not_called()
|
|
assert mgr._settings["key"] == "plain_string"
|
|
|
|
def test_import_empty_dict(self):
|
|
"""Importing empty dict changes nothing."""
|
|
mgr = _make_manager_with_settings({"existing": "value"})
|
|
mgr.import_settings({})
|
|
assert mgr._settings["existing"] == "value"
|