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

361 lines
14 KiB
Python

"""
Gap tests for settings_utils: _get_typed_value edge cases,
get_bool_setting, get_settings_snapshot, extract_setting_value
corner cases, and extract_bool_setting with non-bool inputs.
These complement the existing test_settings_utils_typed_value.py and
test_settings_utils_behavior.py by covering paths that those files miss.
"""
import pytest
from local_deep_research.api.settings_utils import (
InMemorySettingsManager,
extract_bool_setting,
extract_setting_value,
)
# ---------------------------------------------------------------------------
# Fixture
# ---------------------------------------------------------------------------
@pytest.fixture
def manager():
"""Create an InMemorySettingsManager with defaults loaded."""
return InMemorySettingsManager()
# ---------------------------------------------------------------------------
# _get_typed_value — additional edge cases
# ---------------------------------------------------------------------------
class TestGetTypedValueTextarea:
"""textarea ui_element — should behave like text (str conversion)."""
def test_string_passthrough(self, manager):
result = manager._get_typed_value(
{"ui_element": "textarea"}, "hello\nworld"
)
assert result == "hello\nworld"
def test_int_converted_to_string(self, manager):
result = manager._get_typed_value({"ui_element": "textarea"}, 99)
assert result == "99"
assert isinstance(result, str)
class TestGetTypedValueConversionEdgeCases:
"""Conversion failure and boundary cases not covered elsewhere."""
def test_none_returns_none_regardless_of_ui_element(self, manager):
"""None short-circuits before any type dispatch."""
for ui in ("text", "checkbox", "number", "select", "json", "textarea"):
assert manager._get_typed_value({"ui_element": ui}, None) is None
def test_number_with_empty_string(self, manager):
"""Empty string cannot be parsed as number; returns original value."""
result = manager._get_typed_value({"ui_element": "number"}, "")
assert result == ""
def test_range_with_negative_float(self, manager):
result = manager._get_typed_value({"ui_element": "range"}, "-2.5")
assert result == -2.5
def test_checkbox_with_none_value(self, manager):
"""None should be returned directly (short-circuit), not converted."""
result = manager._get_typed_value({"ui_element": "checkbox"}, None)
assert result is None
def test_json_with_string_json_object(self, manager):
"""A JSON string should be parsed into a dict."""
result = manager._get_typed_value({"ui_element": "json"}, '{"a": 1}')
assert result == {"a": 1}
def test_json_with_empty_string(self, manager):
"""Empty string for json ui_element — should pass through as-is."""
result = manager._get_typed_value({"ui_element": "json"}, "")
# _parse_json_value returns empty strings as-is
assert result == ""
def test_multiselect_with_empty_list(self, manager):
result = manager._get_typed_value({"ui_element": "multiselect"}, [])
assert result == []
def test_multiselect_with_single_item_string(self, manager):
"""A plain string without commas should still be parsed."""
result = manager._get_typed_value({"ui_element": "multiselect"}, "solo")
# _parse_multiselect with a non-bracketed, non-comma string
# returns a single-element list
assert isinstance(result, list)
assert "solo" in result
class TestGetTypedValueMissingUiElement:
"""When setting_data has no ui_element key at all."""
def test_defaults_to_text_converts_to_str(self, manager):
"""Missing ui_element defaults to 'text', so str() is applied."""
result = manager._get_typed_value({}, 42)
assert result == "42"
assert isinstance(result, str)
# ---------------------------------------------------------------------------
# get_bool_setting — InMemorySettingsManager method
# ---------------------------------------------------------------------------
class TestGetBoolSetting:
"""InMemorySettingsManager.get_bool_setting wraps get_setting + to_bool."""
def test_true_bool_value(self, manager):
manager.create_or_update_setting(
{"key": "test.gbool", "value": True, "ui_element": "checkbox"}
)
assert manager.get_bool_setting("test.gbool") is True
def test_false_bool_value(self, manager):
manager.create_or_update_setting(
{"key": "test.gbool", "value": False, "ui_element": "checkbox"}
)
assert manager.get_bool_setting("test.gbool") is False
def test_string_true(self, manager):
manager.create_or_update_setting(
{"key": "test.gbool", "value": "true", "ui_element": "checkbox"}
)
assert manager.get_bool_setting("test.gbool") is True
def test_string_on(self, manager):
manager.create_or_update_setting(
{"key": "test.gbool", "value": "on", "ui_element": "checkbox"}
)
assert manager.get_bool_setting("test.gbool") is True
def test_missing_key_returns_default_true(self, manager):
assert manager.get_bool_setting("nonexistent.key", default=True) is True
def test_missing_key_returns_default_false(self, manager):
assert (
manager.get_bool_setting("nonexistent.key", default=False) is False
)
def test_integer_1_is_true(self, manager):
manager.create_or_update_setting(
{"key": "test.gbool", "value": 1, "ui_element": "checkbox"}
)
# parse_boolean treats any truthy value (including 1) as True
assert manager.get_bool_setting("test.gbool") is True
def test_integer_0_is_false(self, manager):
manager.create_or_update_setting(
{"key": "test.gbool", "value": 0, "ui_element": "checkbox"}
)
assert manager.get_bool_setting("test.gbool") is False
# ---------------------------------------------------------------------------
# get_settings_snapshot — simplified key→value dict
# ---------------------------------------------------------------------------
class TestGetSettingsSnapshot:
"""InMemorySettingsManager.get_settings_snapshot returns flat key→value."""
def test_returns_dict(self, manager):
snapshot = manager.get_settings_snapshot()
assert isinstance(snapshot, dict)
def test_values_are_extracted_from_setting_dicts(self, manager):
"""Each setting's 'value' field should be the snapshot value."""
manager.create_or_update_setting(
{"key": "snap.test", "value": "hello", "ui_element": "text"}
)
snapshot = manager.get_settings_snapshot()
assert snapshot["snap.test"] == "hello"
def test_non_dict_settings_passed_through(self, manager):
"""If a setting is not a dict (unlikely but possible), pass through."""
# Force a raw value into _settings
manager._settings["raw.val"] = "plain_string"
snapshot = manager.get_settings_snapshot()
assert snapshot["raw.val"] == "plain_string"
def test_snapshot_is_independent_copy(self, manager):
"""Modifying snapshot should not affect manager state."""
snapshot = manager.get_settings_snapshot()
snapshot["injected"] = "bad"
snapshot2 = manager.get_settings_snapshot()
assert "injected" not in snapshot2
# ---------------------------------------------------------------------------
# create_or_update_setting — edge cases
# ---------------------------------------------------------------------------
class TestCreateOrUpdateSettingEdgeCases:
def test_non_dict_returns_none(self, manager):
"""Passing a non-dict returns None."""
assert manager.create_or_update_setting("not_a_dict") is None
def test_dict_without_key_returns_none(self, manager):
assert manager.create_or_update_setting({"value": 42}) is None
def test_does_not_mutate_input_dict(self, manager):
"""The original dict should not be modified when value is typed."""
original = {
"key": "test.nomut",
"value": "42",
"ui_element": "number",
}
original_value = original["value"]
manager.create_or_update_setting(original)
# Original dict's value should remain unchanged
assert original["value"] == original_value
def test_setting_without_value_key(self, manager):
"""A dict with 'key' but no 'value' should still be stored."""
result = manager.create_or_update_setting(
{"key": "test.noval", "ui_element": "text"}
)
assert result is not None
assert "test.noval" in manager.get_all_settings()
# ---------------------------------------------------------------------------
# import_settings — type conversion path
# ---------------------------------------------------------------------------
class TestImportSettingsTypedValues:
"""import_settings applies _get_typed_value when value dicts are imported."""
def test_number_value_converted_on_import(self, manager):
manager.import_settings(
{"imp.num": {"value": "7", "ui_element": "number"}},
delete_extra=True,
)
# The stored value should be int 7 (whole-number float → int)
all_settings = manager.get_all_settings()
assert all_settings["imp.num"]["value"] == 7
def test_checkbox_value_converted_on_import(self, manager):
manager.import_settings(
{"imp.bool": {"value": "true", "ui_element": "checkbox"}},
delete_extra=True,
)
all_settings = manager.get_all_settings()
assert all_settings["imp.bool"]["value"] is True
def test_plain_value_stored_as_is(self, manager):
"""Non-dict values are stored without type conversion."""
manager.import_settings(
{"imp.raw": "just_a_string"},
delete_extra=True,
)
all_settings = manager.get_all_settings()
assert all_settings["imp.raw"] == "just_a_string"
# ---------------------------------------------------------------------------
# extract_setting_value — additional corner cases
# ---------------------------------------------------------------------------
class TestExtractSettingValueEdgeCases:
def test_dict_without_value_key_returns_whole_dict(self):
"""A dict that lacks 'value' is returned as-is (it's not a setting dict)."""
settings = {"key": {"other_field": "data"}}
result = extract_setting_value(settings, "key")
assert result == {"other_field": "data"}
def test_value_is_none_explicitly(self):
"""When value is explicitly None, it should be returned (not default)."""
settings = {"key": {"value": None}}
result = extract_setting_value(settings, "key", default="fallback")
assert result is None
def test_value_is_zero(self):
"""Falsy value 0 should be returned, not the default."""
settings = {"key": {"value": 0}}
result = extract_setting_value(settings, "key", default=99)
assert result == 0
def test_value_is_empty_string(self):
"""Falsy value '' should be returned, not the default."""
settings = {"key": {"value": ""}}
result = extract_setting_value(settings, "key", default="fallback")
assert result == ""
def test_value_is_false(self):
"""Falsy value False should be returned, not the default."""
settings = {"key": {"value": False}}
result = extract_setting_value(settings, "key", default=True)
assert result is False
def test_empty_snapshot(self):
result = extract_setting_value({}, "any", default="d")
assert result == "d"
# ---------------------------------------------------------------------------
# extract_bool_setting — additional edge cases
# ---------------------------------------------------------------------------
class TestExtractBoolSettingEdgeCases:
def test_integer_1_is_true(self):
settings = {"flag": {"value": 1}}
assert extract_bool_setting(settings, "flag") is True
def test_integer_0_is_false(self):
settings = {"flag": {"value": 0}}
assert extract_bool_setting(settings, "flag") is False
def test_none_value_uses_default_true(self):
settings = {"flag": {"value": None}}
assert extract_bool_setting(settings, "flag", default=True) is True
def test_none_value_uses_default_false(self):
settings = {"flag": {"value": None}}
assert extract_bool_setting(settings, "flag", default=False) is False
def test_empty_string_is_false(self):
"""Empty string is not in the truthy set, so it's False."""
settings = {"flag": {"value": ""}}
assert extract_bool_setting(settings, "flag") is False
def test_whitespace_string_true(self):
"""to_bool strips whitespace before comparison."""
settings = {"flag": {"value": " true "}}
assert extract_bool_setting(settings, "flag") is True
def test_case_insensitive_TRUE(self):
settings = {"flag": {"value": "TRUE"}}
assert extract_bool_setting(settings, "flag") is True
def test_plain_value_not_dict(self):
"""When value is stored directly (not in a dict with 'value' key)."""
settings = {"flag": "yes"}
assert extract_bool_setting(settings, "flag") is True
def test_none_snapshot_returns_default(self):
assert extract_bool_setting(None, "flag", default=True) is True
def test_string_enabled_is_true(self):
settings = {"flag": {"value": "enabled"}}
assert extract_bool_setting(settings, "flag") is True
def test_string_on_is_true(self):
settings = {"flag": {"value": "on"}}
assert extract_bool_setting(settings, "flag") is True
def test_arbitrary_string_is_false(self):
"""An unrecognized string should be False."""
settings = {"flag": {"value": "maybe"}}
assert extract_bool_setting(settings, "flag") is False