Files
learningcircuit--local-deep…/tests/web/routes/test_settings_validation_edge_cases.py
T
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

156 lines
5.5 KiB
Python

"""Tests for validate_setting() edge cases.
Covers:
- validate_setting() min/max constraints, select edge cases, unknown ui_element (12 tests)
"""
from unittest.mock import patch
from local_deep_research.web.routes.settings_routes import (
validate_setting,
DYNAMIC_SETTINGS,
)
from local_deep_research.web.models.settings import BaseSetting, SettingType
# ── validate_setting() edge case tests ──────────────────────────────
def _make_setting(ui_element, **kwargs):
"""Helper to build a BaseSetting with minimal boilerplate."""
defaults = dict(
key="test.key",
value=0,
type=SettingType.APP,
name="Test",
)
defaults.update(kwargs)
defaults["ui_element"] = ui_element
return BaseSetting(**defaults)
class TestValidateSettingMinMax:
"""Tests for number/slider/range min/max constraint validation."""
def test_number_below_min(self):
"""Number below min_value → (False, error)."""
setting = _make_setting("number", min_value=0)
valid, msg = validate_setting(setting, -1)
assert valid is False
assert "at least" in msg
def test_number_above_max(self):
"""Number above max_value → (False, error)."""
setting = _make_setting("number", max_value=100)
valid, msg = validate_setting(setting, 150)
assert valid is False
assert "at most" in msg
def test_number_no_constraints(self):
"""Number with no min/max → (True, None)."""
setting = _make_setting("number")
valid, msg = validate_setting(setting, 999)
assert valid is True
assert msg is None
def test_number_only_min_passes(self):
"""Number with only min_value, value above → (True, None)."""
setting = _make_setting("number", min_value=0)
valid, msg = validate_setting(setting, 5)
assert valid is True
def test_number_only_max_passes(self):
"""Number with only max_value, value below → (True, None)."""
setting = _make_setting("number", max_value=100)
valid, msg = validate_setting(setting, 50)
assert valid is True
def test_slider_below_min(self):
"""Slider below min_value → (False, error).
'slider' is not in UI_ELEMENT_TO_SETTING_TYPE, so get_typed_setting_value
returns default=None. We patch it to pass through the numeric value so
we can test the min/max constraint logic in validate_setting.
"""
setting = _make_setting("slider", min_value=5)
with patch(
"local_deep_research.web.routes.settings_routes.get_typed_setting_value",
return_value=2,
):
valid, msg = validate_setting(setting, 2)
assert valid is False
assert "at least" in msg
def test_range_above_max(self):
"""Range above max_value → (False, error)."""
setting = _make_setting("range", max_value=10)
valid, msg = validate_setting(setting, 15)
assert valid is False
assert "at most" in msg
class TestValidateSettingSelect:
"""Tests for select ui_element edge cases."""
def test_select_invalid_option(self):
"""Select with value not in options → (False, error)."""
setting = _make_setting(
"select",
options=[{"value": "a"}, {"value": "b"}],
)
valid, msg = validate_setting(setting, "x")
assert valid is False
assert "must be one of" in msg
def test_select_plain_string_options(self):
"""Select with plain string options list → valid option passes.
The Pydantic model enforces List[Dict], but at runtime the
validate_setting code handles both dict and non-dict options
(line 199). We use a mock to bypass Pydantic validation.
"""
from unittest.mock import MagicMock
setting = MagicMock()
setting.key = "test.select"
setting.ui_element = "select"
setting.options = ["a", "b", "c"]
# Patch get_typed_setting_value to pass through the value unchanged
with patch(
"local_deep_research.web.routes.settings_routes.get_typed_setting_value",
return_value="b",
):
valid, msg = validate_setting(setting, "b")
assert valid is True
def test_select_empty_options_skips_validation(self):
"""Select with empty options list → validation skipped → (True, None)."""
setting = _make_setting("select", options=[])
valid, msg = validate_setting(setting, "anything")
assert valid is True
def test_select_dynamic_setting_skips_validation(self):
"""Select with key in DYNAMIC_SETTINGS → validation skipped."""
# Use a real DYNAMIC_SETTINGS key
dynamic_key = DYNAMIC_SETTINGS[0] # "llm.provider"
setting = _make_setting(
"select",
key=dynamic_key,
options=[{"value": "a"}, {"value": "b"}],
)
# Value not in options, but should pass because it's dynamic
valid, msg = validate_setting(setting, "nonexistent_provider")
assert valid is True
class TestValidateSettingUnknownElement:
"""Test that unknown ui_element types pass through."""
def test_unknown_ui_element_passes(self):
"""Unknown ui_element like 'custom_widget' → (True, None)."""
setting = _make_setting("custom_widget")
valid, msg = validate_setting(setting, "any value")
assert valid is True
assert msg is None