Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

773 lines
28 KiB
Python

"""
Behavioral tests for settings/manager module.
Tests pure functions: parse_boolean, _parse_number,
get_typed_setting_value, and check_env_setting.
"""
import os
from unittest.mock import patch
import pytest
class TestParseBoolean:
"""Tests for parse_boolean function (HTML checkbox semantics)."""
def test_true_returns_true(self):
"""Bool True returns True."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean(True) is True
def test_false_returns_false(self):
"""Bool False returns False."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean(False) is False
def test_none_returns_false(self):
"""None returns False (missing checkbox)."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean(None) is False
def test_empty_string_returns_false(self):
"""Empty string returns False."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean("") is False
def test_false_string_returns_false(self):
"""'false' returns False."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean("false") is False
def test_off_string_returns_false(self):
"""'off' returns False."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean("off") is False
def test_zero_string_returns_false(self):
"""'0' returns False."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean("0") is False
def test_no_string_returns_false(self):
"""'no' returns False."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean("no") is False
def test_on_returns_true(self):
"""'on' returns True (HTML checkbox value)."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean("on") is True
def test_true_string_returns_true(self):
"""'true' returns True."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean("true") is True
def test_one_string_returns_true(self):
"""'1' returns True."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean("1") is True
def test_any_nonempty_string_is_true(self):
"""Any non-empty non-falsy string returns True (checkbox semantics)."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean("enabled") is True
assert parse_boolean("custom_value") is True
assert parse_boolean("yes") is True
def test_case_insensitive(self):
"""String comparison is case-insensitive."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean("FALSE") is False
assert parse_boolean("False") is False
assert parse_boolean("OFF") is False
def test_strips_whitespace(self):
"""Strips whitespace from string values."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean(" false ") is False
assert parse_boolean(" true ") is True
def test_integer_truthy(self):
"""Non-zero integers return True."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean(1) is True
assert parse_boolean(42) is True
def test_integer_zero_is_false(self):
"""Zero returns False."""
from local_deep_research.settings.manager import parse_boolean
assert parse_boolean(0) is False
class TestParseJsonValue:
"""Tests for _parse_json_value function."""
def test_parses_json_array_string(self):
"""JSON array string is parsed into a list."""
from local_deep_research.settings.manager import _parse_json_value
assert _parse_json_value('["general", "news"]') == ["general", "news"]
def test_parses_json_array_with_crlf(self):
"""JSON array string with \\r\\n (browser textarea) is parsed correctly."""
from local_deep_research.settings.manager import _parse_json_value
raw = '[\r\n "general"\r\n]'
assert _parse_json_value(raw) == ["general"]
def test_parses_json_object_string(self):
"""JSON object string is parsed into a dict."""
from local_deep_research.settings.manager import _parse_json_value
assert _parse_json_value('{"key": "value"}') == {"key": "value"}
def test_returns_list_unchanged(self):
"""Already-parsed list is returned as-is."""
from local_deep_research.settings.manager import _parse_json_value
original = ["general", "news"]
assert _parse_json_value(original) is original
def test_returns_dict_unchanged(self):
"""Already-parsed dict is returned as-is."""
from local_deep_research.settings.manager import _parse_json_value
original = {"key": "value"}
assert _parse_json_value(original) is original
def test_returns_none_unchanged(self):
"""None is returned as-is."""
from local_deep_research.settings.manager import _parse_json_value
assert _parse_json_value(None) is None
def test_invalid_json_string_returned_as_is(self):
"""Non-JSON string is returned unchanged."""
from local_deep_research.settings.manager import _parse_json_value
assert _parse_json_value("not json") == "not json"
def test_empty_string_returned_as_is(self):
"""Empty string is returned as-is."""
from local_deep_research.settings.manager import _parse_json_value
assert _parse_json_value("") == ""
def test_whitespace_only_string_returned_as_is(self):
"""Whitespace-only string is returned as-is."""
from local_deep_research.settings.manager import _parse_json_value
assert _parse_json_value(" ") == " "
def test_json_ui_element_uses_parse_json_value(self):
"""get_typed_setting_value with 'json' ui_element parses JSON strings."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", '[\r\n "general"\r\n]', "json", check_env=False
)
assert result == ["general"]
class TestParseNumber:
"""Tests for _parse_number function."""
def test_whole_float_returns_int(self):
"""Whole number float string returns int."""
from local_deep_research.settings.manager import _parse_number
assert _parse_number("42.0") == 42
assert isinstance(_parse_number("42.0"), int)
def test_fractional_float_returns_float(self):
"""Fractional float string returns float."""
from local_deep_research.settings.manager import _parse_number
assert _parse_number("3.14") == pytest.approx(3.14)
assert isinstance(_parse_number("3.14"), float)
def test_integer_string_returns_int(self):
"""Integer string returns int."""
from local_deep_research.settings.manager import _parse_number
assert _parse_number("10") == 10
assert isinstance(_parse_number("10"), int)
def test_negative_number(self):
"""Handles negative numbers."""
from local_deep_research.settings.manager import _parse_number
assert _parse_number("-5") == -5
def test_zero_returns_int(self):
"""Zero returns int 0."""
from local_deep_research.settings.manager import _parse_number
result = _parse_number("0")
assert result == 0
assert isinstance(result, int)
def test_raises_for_non_numeric(self):
"""Raises ValueError for non-numeric string."""
from local_deep_research.settings.manager import _parse_number
with pytest.raises(ValueError):
_parse_number("abc")
class TestGetTypedSettingValue:
"""Tests for get_typed_setting_value function."""
def test_text_returns_string(self):
"""Text ui_element returns string."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", "hello", "text", check_env=False
)
assert result == "hello"
assert isinstance(result, str)
def test_number_returns_int_for_whole(self):
"""Number ui_element returns int for whole number."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", "42", "number", check_env=False
)
assert result == 42
assert isinstance(result, int)
def test_number_returns_float_for_fractional(self):
"""Number ui_element returns float for fractional number."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", "3.14", "number", check_env=False
)
assert result == pytest.approx(3.14)
def test_checkbox_true_values(self):
"""Checkbox ui_element converts truthy values."""
from local_deep_research.settings.manager import get_typed_setting_value
assert (
get_typed_setting_value("k", "on", "checkbox", check_env=False)
is True
)
assert (
get_typed_setting_value("k", "true", "checkbox", check_env=False)
is True
)
def test_checkbox_false_values(self):
"""Checkbox ui_element converts falsy values."""
from local_deep_research.settings.manager import get_typed_setting_value
assert (
get_typed_setting_value("k", "off", "checkbox", check_env=False)
is False
)
assert (
get_typed_setting_value("k", "false", "checkbox", check_env=False)
is False
)
def test_password_returns_string(self):
"""Password ui_element returns string."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", "secret123", "password", check_env=False
)
assert result == "secret123"
def test_select_returns_string(self):
"""Select ui_element returns string."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", "option_a", "select", check_env=False
)
assert result == "option_a"
def test_unknown_ui_element_returns_default(self):
"""Unknown ui_element returns default value."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key",
"value",
"unknown_element",
default="fallback",
check_env=False,
)
assert result == "fallback"
def test_none_value_returns_default(self):
"""None value returns default."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", None, "text", default="default_val", check_env=False
)
assert result == "default_val"
def test_range_behaves_like_number(self):
"""Range ui_element behaves like number."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", "50", "range", check_env=False
)
assert result == 50
def test_invalid_number_returns_default(self):
"""Invalid number string returns default."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", "not_a_number", "number", default=0, check_env=False
)
assert result == 0
class TestCheckEnvSetting:
"""Tests for check_env_setting function."""
def test_returns_none_when_not_set(self):
"""Returns None when env var not set."""
from local_deep_research.settings.manager import check_env_setting
result = check_env_setting("nonexistent.setting.xyz_test_12345")
assert result is None
def test_returns_value_when_set(self):
"""Returns value when env var is set."""
from local_deep_research.settings.manager import check_env_setting
with patch.dict(os.environ, {"LDR_MY_TEST_KEY": "test_value"}):
result = check_env_setting("my.test_key")
assert result == "test_value"
def test_env_var_name_format(self):
"""Env var name is LDR_ + key with dots replaced by underscores, uppercased."""
from local_deep_research.settings.manager import check_env_setting
# Key "llm.model" -> env var "LDR_LLM_MODEL"
with patch.dict(os.environ, {"LDR_LLM_MODEL": "gpt-4"}):
result = check_env_setting("llm.model")
assert result == "gpt-4"
def test_nested_key_format(self):
"""Handles deeply nested keys."""
from local_deep_research.settings.manager import check_env_setting
with patch.dict(
os.environ, {"LDR_SEARCH_BRAVE_API_KEY": "brave-key-123"}
):
result = check_env_setting("search.brave.api_key")
assert result == "brave-key-123"
def test_returns_none_for_empty_string(self):
"""Empty string env var is treated as unset, returns None, and logs warning."""
from loguru import logger
from local_deep_research.settings.manager import check_env_setting
warnings = []
logger.enable("local_deep_research")
sink_id = logger.add(
lambda msg: warnings.append(str(msg)),
level="WARNING",
# diagnose=False keeps the captured sink consistent with the
# production policy (#4185 / #4384) — exceptions logged through
# this fixture would otherwise carry frame-local repr() into
# the captured list and into pytest output.
diagnose=False,
)
try:
with patch.dict(os.environ, {"LDR_LLM_PROVIDER": ""}):
result = check_env_setting("llm.provider")
assert result is None
assert any("LDR_LLM_PROVIDER" in w for w in warnings)
finally:
logger.remove(sink_id)
logger.disable("local_deep_research")
def test_empty_string_provider_key(self):
"""Empty LDR_LLM_PROVIDER returns None (Unraid scenario)."""
from local_deep_research.settings.manager import check_env_setting
with patch.dict(os.environ, {"LDR_LLM_PROVIDER": ""}):
result = check_env_setting("llm.provider")
assert result is None
def test_empty_string_model_key(self):
"""Empty LDR_LLM_MODEL returns None."""
from local_deep_research.settings.manager import check_env_setting
with patch.dict(os.environ, {"LDR_LLM_MODEL": ""}):
result = check_env_setting("llm.model")
assert result is None
def test_empty_string_multiple_keys(self):
"""Multiple empty env vars each independently return None."""
from local_deep_research.settings.manager import check_env_setting
with patch.dict(
os.environ,
{"LDR_LLM_PROVIDER": "", "LDR_LLM_MODEL": ""},
):
assert check_env_setting("llm.provider") is None
assert check_env_setting("llm.model") is None
class TestUIElementToSettingType:
"""Tests for UI_ELEMENT_TO_SETTING_TYPE mapping."""
def test_has_text(self):
"""Mapping includes 'text'."""
from local_deep_research.settings.manager import (
UI_ELEMENT_TO_SETTING_TYPE,
)
assert "text" in UI_ELEMENT_TO_SETTING_TYPE
def test_has_checkbox(self):
"""Mapping includes 'checkbox'."""
from local_deep_research.settings.manager import (
UI_ELEMENT_TO_SETTING_TYPE,
)
assert "checkbox" in UI_ELEMENT_TO_SETTING_TYPE
def test_has_number(self):
"""Mapping includes 'number'."""
from local_deep_research.settings.manager import (
UI_ELEMENT_TO_SETTING_TYPE,
)
assert "number" in UI_ELEMENT_TO_SETTING_TYPE
def test_has_password(self):
"""Mapping includes 'password'."""
from local_deep_research.settings.manager import (
UI_ELEMENT_TO_SETTING_TYPE,
)
assert "password" in UI_ELEMENT_TO_SETTING_TYPE
def test_has_select(self):
"""Mapping includes 'select'."""
from local_deep_research.settings.manager import (
UI_ELEMENT_TO_SETTING_TYPE,
)
assert "select" in UI_ELEMENT_TO_SETTING_TYPE
def test_has_json(self):
"""Mapping includes 'json'."""
from local_deep_research.settings.manager import (
UI_ELEMENT_TO_SETTING_TYPE,
)
assert "json" in UI_ELEMENT_TO_SETTING_TYPE
def test_has_range(self):
"""Mapping includes 'range'."""
from local_deep_research.settings.manager import (
UI_ELEMENT_TO_SETTING_TYPE,
)
assert "range" in UI_ELEMENT_TO_SETTING_TYPE
def test_text_type_is_str(self):
"""Text type is str."""
from local_deep_research.settings.manager import (
UI_ELEMENT_TO_SETTING_TYPE,
)
assert UI_ELEMENT_TO_SETTING_TYPE["text"] is str
class TestParseNumberEdgeCases:
"""Edge cases for _parse_number that affect real settings."""
def test_inf_string_returns_float_inf(self):
"""'inf' parses to float — callers must validate range themselves."""
from local_deep_research.settings.manager import _parse_number
result = _parse_number("inf")
assert result == float("inf")
assert isinstance(result, float)
def test_negative_inf_string(self):
"""'-inf' parses to negative infinity."""
from local_deep_research.settings.manager import _parse_number
result = _parse_number("-inf")
assert result == float("-inf")
def test_nan_string_returns_float_nan(self):
"""'nan' parses to float NaN."""
import math
from local_deep_research.settings.manager import _parse_number
result = _parse_number("nan")
assert math.isnan(result)
def test_int_input_passthrough(self):
"""Direct int input is preserved as int (not round-tripped through float)."""
from local_deep_research.settings.manager import _parse_number
result = _parse_number(42)
assert result == 42
assert isinstance(result, int)
def test_float_input_whole_becomes_int(self):
"""Float 5.0 becomes int 5 (is_integer() check)."""
from local_deep_research.settings.manager import _parse_number
result = _parse_number(5.0)
assert result == 5
assert isinstance(result, int)
def test_scientific_notation(self):
"""Scientific notation string parses correctly."""
from local_deep_research.settings.manager import _parse_number
result = _parse_number("1e3")
assert result == 1000
assert isinstance(result, int)
def test_very_large_number(self):
"""Large number beyond typical int range still works."""
from local_deep_research.settings.manager import _parse_number
result = _parse_number("99999999999999")
assert result == 99999999999999
assert isinstance(result, int)
class TestParseMultiselectEdgeCases:
"""Edge cases for _parse_multiselect with non-standard inputs."""
def test_dict_input_returned_as_is(self):
"""Dict passes through unchanged (not list, not string)."""
from local_deep_research.settings.manager import _parse_multiselect
d = {"key": "value"}
assert _parse_multiselect(d) is d
def test_int_input_returned_as_is(self):
"""Integer passes through unchanged."""
from local_deep_research.settings.manager import _parse_multiselect
assert _parse_multiselect(42) == 42
def test_none_input_returned_as_is(self):
"""None passes through unchanged."""
from local_deep_research.settings.manager import _parse_multiselect
assert _parse_multiselect(None) is None
def test_json_array_of_non_list_falls_back_to_csv(self):
"""JSON string that parses to non-list falls through to CSV split."""
from local_deep_research.settings.manager import _parse_multiselect
# Starts with '[' but json.loads returns... actually this is a list
# Use a case where JSON parse succeeds but isn't a list: impossible
# with '[' prefix. Test that invalid JSON starting with '[' falls to CSV.
result = _parse_multiselect("[not valid json")
assert result == ["[not valid json"]
def test_empty_string_returns_empty_list(self):
"""Empty string returns empty list after CSV split filtering."""
from local_deep_research.settings.manager import _parse_multiselect
assert _parse_multiselect("") == []
def test_whitespace_only_items_filtered(self):
"""Comma-separated with blank items filters them out."""
from local_deep_research.settings.manager import _parse_multiselect
result = _parse_multiselect("a, , b, ,c")
assert result == ["a", "b", "c"]
class TestParseJsonValueEdgeCases:
"""Edge cases for _parse_json_value with tricky inputs."""
def test_json_number_string_parses_to_number(self):
"""A plain number string is valid JSON — parses to int."""
from local_deep_research.settings.manager import _parse_json_value
result = _parse_json_value("42")
assert result == 42
def test_json_boolean_string_parses_to_bool(self):
"""'true' is valid JSON — parses to Python True."""
from local_deep_research.settings.manager import _parse_json_value
assert _parse_json_value("true") is True
assert _parse_json_value("false") is False
def test_json_null_string_parses_to_none(self):
"""'null' is valid JSON — parses to None."""
from local_deep_research.settings.manager import _parse_json_value
assert _parse_json_value("null") is None
def test_nested_json_object(self):
"""Nested JSON objects parse correctly."""
from local_deep_research.settings.manager import _parse_json_value
raw = '{"outer": {"inner": [1, 2]}}'
result = _parse_json_value(raw)
assert result == {"outer": {"inner": [1, 2]}}
def test_int_input_passthrough(self):
"""Non-string types pass through unchanged."""
from local_deep_research.settings.manager import _parse_json_value
assert _parse_json_value(42) == 42
def test_bool_input_passthrough(self):
"""Boolean passes through unchanged."""
from local_deep_research.settings.manager import _parse_json_value
assert _parse_json_value(True) is True
def test_json_with_leading_whitespace(self):
"""JSON string with leading whitespace is stripped before parsing."""
from local_deep_research.settings.manager import _parse_json_value
result = _parse_json_value(' {"key": "value"} ')
assert result == {"key": "value"}
class TestGetTypedSettingValueEnvFallback:
"""Tests for env var → DB fallback path in get_typed_setting_value."""
def test_invalid_env_var_falls_back_to_db_value(self):
"""When env var has invalid value for the type, DB value is used."""
from local_deep_research.settings.manager import get_typed_setting_value
with patch.dict(os.environ, {"LDR_TEST_ITERATIONS": "not_a_number"}):
result = get_typed_setting_value(
"test.iterations", 5, "number", default=0, check_env=True
)
# Should fall back to DB value 5, not env "not_a_number"
assert result == 5
def test_valid_env_var_overrides_db_value(self):
"""When env var is valid, it takes priority over DB value."""
from local_deep_research.settings.manager import get_typed_setting_value
with patch.dict(os.environ, {"LDR_TEST_ITERATIONS": "10"}):
result = get_typed_setting_value(
"test.iterations", 5, "number", default=0, check_env=True
)
assert result == 10
def test_env_var_skipped_when_check_env_false(self):
"""check_env=False ignores environment variables entirely."""
from local_deep_research.settings.manager import get_typed_setting_value
with patch.dict(os.environ, {"LDR_TEST_ITERATIONS": "99"}):
result = get_typed_setting_value(
"test.iterations", 5, "number", default=0, check_env=False
)
assert result == 5
def test_env_var_type_error_on_number_does_not_crash(self):
"""TypeError during env var conversion falls through to DB value.
Note: the env path only catches ValueError (line 206), so a TypeError
would propagate. This documents the current behavior — _parse_number
with a string always raises ValueError, never TypeError.
"""
from local_deep_research.settings.manager import get_typed_setting_value
# _parse_number("") raises ValueError, not TypeError
with patch.dict(os.environ, {"LDR_TEST_NUM": ""}):
result = get_typed_setting_value(
"test.num", 42, "number", default=0, check_env=True
)
assert result == 42
def test_db_value_type_error_returns_default(self):
"""TypeError during DB value conversion returns default.
This path (line 220) catches both ValueError and TypeError, unlike
the env path which only catches ValueError.
"""
from local_deep_research.settings.manager import get_typed_setting_value
# Passing a type that causes TypeError in float() — e.g. a list
result = get_typed_setting_value(
"test.key", [1, 2, 3], "number", default=0, check_env=False
)
assert result == 0
def test_textarea_preserves_multiline_string(self):
"""Textarea ui_element preserves newlines (uses str converter)."""
from local_deep_research.settings.manager import get_typed_setting_value
multiline = "line1\nline2\nline3"
result = get_typed_setting_value(
"test.key", multiline, "textarea", check_env=False
)
assert result == multiline
def test_number_with_bool_input_converts_to_int(self):
"""Bool passed to number field: float(True)=1.0, is_integer()→int 1.
This is a real scenario when frontend sends JSON true for a number field.
"""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", True, "number", check_env=False
)
assert result == 1
assert isinstance(result, int)
def test_checkbox_with_int_zero(self):
"""Integer 0 sent for a checkbox field returns False."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", 0, "checkbox", check_env=False
)
assert result is False
def test_checkbox_with_int_one(self):
"""Integer 1 sent for a checkbox field returns True."""
from local_deep_research.settings.manager import get_typed_setting_value
result = get_typed_setting_value(
"test.key", 1, "checkbox", check_env=False
)
assert result is True