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
183 lines
6.0 KiB
Python
183 lines
6.0 KiB
Python
"""High-value tests for config/thread_settings.py pure logic."""
|
|
|
|
import unittest
|
|
from unittest.mock import MagicMock
|
|
|
|
from local_deep_research.config.thread_settings import (
|
|
NoSettingsContextError,
|
|
clear_settings_context,
|
|
get_bool_setting_from_snapshot,
|
|
get_setting_from_snapshot,
|
|
get_settings_context,
|
|
set_settings_context,
|
|
settings_context,
|
|
_thread_local,
|
|
)
|
|
|
|
|
|
class TestSettingsContextBasic(unittest.TestCase):
|
|
def tearDown(self):
|
|
clear_settings_context()
|
|
|
|
def test_set_and_get_roundtrip(self):
|
|
ctx = MagicMock()
|
|
set_settings_context(ctx)
|
|
assert get_settings_context() is ctx
|
|
|
|
def test_clear_removes_context(self):
|
|
set_settings_context(MagicMock())
|
|
clear_settings_context()
|
|
assert get_settings_context() is None
|
|
|
|
def test_get_returns_none_when_not_set(self):
|
|
clear_settings_context()
|
|
assert get_settings_context() is None
|
|
|
|
def test_clear_when_never_set(self):
|
|
# Should not raise
|
|
if hasattr(_thread_local, "settings_context"):
|
|
del _thread_local.settings_context
|
|
clear_settings_context()
|
|
|
|
|
|
class TestSettingsContextManager(unittest.TestCase):
|
|
def tearDown(self):
|
|
clear_settings_context()
|
|
|
|
def test_sets_and_clears(self):
|
|
ctx = MagicMock()
|
|
with settings_context(ctx):
|
|
assert get_settings_context() is ctx
|
|
assert get_settings_context() is None
|
|
|
|
def test_clears_on_exception(self):
|
|
ctx = MagicMock()
|
|
try:
|
|
with settings_context(ctx):
|
|
raise ValueError("test")
|
|
except ValueError:
|
|
pass
|
|
assert get_settings_context() is None
|
|
|
|
|
|
class TestGetSettingFromSnapshot(unittest.TestCase):
|
|
def tearDown(self):
|
|
clear_settings_context()
|
|
|
|
def test_key_in_snapshot_simple_value(self):
|
|
snapshot = {"my_key": "my_value"}
|
|
result = get_setting_from_snapshot("my_key", settings_snapshot=snapshot)
|
|
assert result == "my_value"
|
|
|
|
def test_key_in_snapshot_dict_with_value(self):
|
|
snapshot = {"my_key": {"value": 42, "ui_element": "text"}}
|
|
result = get_setting_from_snapshot("my_key", settings_snapshot=snapshot)
|
|
# ui_element="text" maps to str converter, so result is "42"
|
|
assert result == "42"
|
|
|
|
def test_child_key_search(self):
|
|
snapshot = {"parent.child1": "v1", "parent.child2": "v2"}
|
|
result = get_setting_from_snapshot("parent", settings_snapshot=snapshot)
|
|
assert isinstance(result, dict)
|
|
assert result["child1"] == "v1"
|
|
assert result["child2"] == "v2"
|
|
|
|
def test_child_key_with_value_dict(self):
|
|
snapshot = {"parent.child": {"value": "hello", "ui_element": "text"}}
|
|
result = get_setting_from_snapshot("parent", settings_snapshot=snapshot)
|
|
assert isinstance(result, dict)
|
|
assert result["child"] == "hello"
|
|
|
|
def test_fallback_to_thread_context(self):
|
|
ctx = MagicMock()
|
|
ctx.get_setting.return_value = "from_context"
|
|
set_settings_context(ctx)
|
|
result = get_setting_from_snapshot("some_key")
|
|
assert result == "from_context"
|
|
|
|
def test_thread_context_dict_with_value(self):
|
|
ctx = MagicMock()
|
|
ctx.get_setting.return_value = {"value": "extracted"}
|
|
set_settings_context(ctx)
|
|
result = get_setting_from_snapshot("some_key")
|
|
assert result == "extracted"
|
|
|
|
def test_default_returned_when_no_context(self):
|
|
clear_settings_context()
|
|
result = get_setting_from_snapshot("missing", default="fallback")
|
|
assert result == "fallback"
|
|
|
|
def test_raises_when_no_context_no_default(self):
|
|
clear_settings_context()
|
|
with self.assertRaises(NoSettingsContextError):
|
|
get_setting_from_snapshot("missing")
|
|
|
|
def test_snapshot_takes_priority_over_context(self):
|
|
ctx = MagicMock()
|
|
ctx.get_setting.return_value = "from_context"
|
|
set_settings_context(ctx)
|
|
snapshot = {"key": "from_snapshot"}
|
|
result = get_setting_from_snapshot("key", settings_snapshot=snapshot)
|
|
assert result == "from_snapshot"
|
|
|
|
def test_none_value_in_snapshot_is_treated_as_found(self):
|
|
# Regression for #4208: a key present in the snapshot with an
|
|
# explicit None must be returned as None, not collapsed into
|
|
# "not found" and replaced with the default. The previous
|
|
# behavior broke the OpenAI embedding test path because
|
|
# embeddings.openai.dimensions defaults to JSON null, then
|
|
# raised NoSettingsContextError when no thread context was set.
|
|
clear_settings_context()
|
|
result = get_setting_from_snapshot(
|
|
"key", default="default_val", settings_snapshot={"key": None}
|
|
)
|
|
assert result is None
|
|
|
|
|
|
class TestGetBoolSettingFromSnapshot(unittest.TestCase):
|
|
def tearDown(self):
|
|
clear_settings_context()
|
|
|
|
def test_returns_true_from_string(self):
|
|
snapshot = {"flag": "true"}
|
|
result = get_bool_setting_from_snapshot(
|
|
"flag", settings_snapshot=snapshot
|
|
)
|
|
assert result is True
|
|
|
|
def test_returns_false_from_string(self):
|
|
snapshot = {"flag": "false"}
|
|
result = get_bool_setting_from_snapshot(
|
|
"flag", settings_snapshot=snapshot
|
|
)
|
|
assert result is False
|
|
|
|
def test_default_when_missing(self):
|
|
result = get_bool_setting_from_snapshot(
|
|
"missing", default=True, settings_snapshot={}
|
|
)
|
|
assert result is True
|
|
|
|
def test_integer_one_is_true(self):
|
|
snapshot = {"flag": 1}
|
|
result = get_bool_setting_from_snapshot(
|
|
"flag", settings_snapshot=snapshot
|
|
)
|
|
assert result is True
|
|
|
|
|
|
class TestGetSettingFromSnapshotExtra(unittest.TestCase):
|
|
def tearDown(self):
|
|
clear_settings_context()
|
|
|
|
def test_basic_retrieval(self):
|
|
snapshot = {"llm_key": "model_name"}
|
|
result = get_setting_from_snapshot(
|
|
"llm_key", settings_snapshot=snapshot
|
|
)
|
|
assert result == "model_name"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|