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

292 lines
9.7 KiB
Python

"""
Behavioral tests for security/security_settings module.
Tests type conversion, bounds validation, and security default loading.
"""
import os
from unittest.mock import patch
import pytest
class TestConvertValue:
"""Tests for _convert_value function."""
def test_converts_bool_true_values(self):
"""Converts 'true', '1', 'yes', 'on' to True."""
from local_deep_research.security.security_settings import (
_convert_value,
)
for val in ("true", "1", "yes", "on"):
assert _convert_value(val, bool, "test") is True
def test_converts_bool_false_values(self):
"""Converts 'false', '0', 'no', 'off' to False."""
from local_deep_research.security.security_settings import (
_convert_value,
)
for val in ("false", "0", "no", "off"):
assert _convert_value(val, bool, "test") is False
def test_bool_is_case_insensitive(self):
"""Bool conversion is case-insensitive."""
from local_deep_research.security.security_settings import (
_convert_value,
)
assert _convert_value("TRUE", bool, "test") is True
assert _convert_value("True", bool, "test") is True
assert _convert_value("Yes", bool, "test") is True
def test_converts_int(self):
"""Converts string to int."""
from local_deep_research.security.security_settings import (
_convert_value,
)
assert _convert_value("42", int, "test") == 42
def test_converts_negative_int(self):
"""Converts negative string to int."""
from local_deep_research.security.security_settings import (
_convert_value,
)
assert _convert_value("-5", int, "test") == -5
def test_converts_float(self):
"""Converts string to float."""
from local_deep_research.security.security_settings import (
_convert_value,
)
assert _convert_value("3.14", float, "test") == pytest.approx(3.14)
def test_converts_str(self):
"""Converts to string (identity)."""
from local_deep_research.security.security_settings import (
_convert_value,
)
assert _convert_value("hello", str, "test") == "hello"
def test_returns_none_for_invalid_int(self):
"""Returns None for non-numeric int conversion."""
from local_deep_research.security.security_settings import (
_convert_value,
)
assert _convert_value("abc", int, "test") is None
def test_returns_none_for_invalid_float(self):
"""Returns None for non-numeric float conversion."""
from local_deep_research.security.security_settings import (
_convert_value,
)
assert _convert_value("not_a_number", float, "test") is None
class TestValidateBounds:
"""Tests for _validate_bounds function."""
def test_value_within_bounds_unchanged(self):
"""Value within bounds is unchanged."""
from local_deep_research.security.security_settings import (
_validate_bounds,
)
assert _validate_bounds(50, 0, 100, "test") == 50
def test_clamps_below_minimum(self):
"""Value below minimum is clamped to minimum."""
from local_deep_research.security.security_settings import (
_validate_bounds,
)
assert _validate_bounds(-5, 0, 100, "test") == 0
def test_clamps_above_maximum(self):
"""Value above maximum is clamped to maximum."""
from local_deep_research.security.security_settings import (
_validate_bounds,
)
assert _validate_bounds(150, 0, 100, "test") == 100
def test_none_min_allows_any_low_value(self):
"""None min_value allows any low value."""
from local_deep_research.security.security_settings import (
_validate_bounds,
)
assert _validate_bounds(-1000, None, 100, "test") == -1000
def test_none_max_allows_any_high_value(self):
"""None max_value allows any high value."""
from local_deep_research.security.security_settings import (
_validate_bounds,
)
assert _validate_bounds(99999, 0, None, "test") == 99999
def test_both_none_returns_value_unchanged(self):
"""Both None bounds returns value unchanged."""
from local_deep_research.security.security_settings import (
_validate_bounds,
)
assert _validate_bounds(42, None, None, "test") == 42
def test_exact_minimum_accepted(self):
"""Value equal to minimum is accepted."""
from local_deep_research.security.security_settings import (
_validate_bounds,
)
assert _validate_bounds(0, 0, 100, "test") == 0
def test_exact_maximum_accepted(self):
"""Value equal to maximum is accepted."""
from local_deep_research.security.security_settings import (
_validate_bounds,
)
assert _validate_bounds(100, 0, 100, "test") == 100
def test_works_with_floats(self):
"""Works with float values and bounds."""
from local_deep_research.security.security_settings import (
_validate_bounds,
)
assert _validate_bounds(0.5, 0.0, 1.0, "test") == pytest.approx(0.5)
assert _validate_bounds(-0.1, 0.0, 1.0, "test") == pytest.approx(0.0)
assert _validate_bounds(1.5, 0.0, 1.0, "test") == pytest.approx(1.0)
class TestGetSecurityDefault:
"""Tests for get_security_default function."""
def test_returns_default_when_no_env_or_json(self):
"""Returns default value when no env var or JSON setting."""
from local_deep_research.security.security_settings import (
get_security_default,
_load_security_settings,
)
# Clear the cache to ensure fresh load
_load_security_settings.cache_clear()
# Use a key that won't exist
result = get_security_default("nonexistent.setting.key_xyz_12345", 42)
assert result == 42
def test_env_var_overrides_default(self):
"""Environment variable overrides default value."""
from local_deep_research.security.security_settings import (
get_security_default,
_load_security_settings,
)
_load_security_settings.cache_clear()
env_key = "LDR_TEST_ENV_OVERRIDE_ABC"
with patch.dict(os.environ, {env_key: "99"}):
result = get_security_default("test.env_override_abc", 42)
assert result == 99
def test_env_var_key_format(self):
"""Environment variable uses LDR_ prefix with dots replaced by underscores."""
from local_deep_research.security.security_settings import (
get_security_default,
_load_security_settings,
)
_load_security_settings.cache_clear()
# Key "security.session_days" -> env var "LDR_SECURITY_SESSION_DAYS"
with patch.dict(os.environ, {"LDR_MY_CUSTOM_KEY": "hello"}):
result = get_security_default("my.custom_key", "default")
assert result == "hello"
def test_bool_default_returns_bool(self):
"""Bool default causes bool return type."""
from local_deep_research.security.security_settings import (
get_security_default,
_load_security_settings,
)
_load_security_settings.cache_clear()
result = get_security_default("nonexistent.bool_setting_xyz", False)
assert result is False
def test_env_var_bool_conversion(self):
"""Environment variable with bool default converts to bool."""
from local_deep_research.security.security_settings import (
get_security_default,
_load_security_settings,
)
_load_security_settings.cache_clear()
with patch.dict(os.environ, {"LDR_TEST_BOOL_ENV_XYZ": "true"}):
result = get_security_default("test.bool_env_xyz", False)
assert result is True
def test_invalid_env_var_falls_through(self):
"""Invalid env var value falls through to default."""
from local_deep_research.security.security_settings import (
get_security_default,
_load_security_settings,
)
_load_security_settings.cache_clear()
with patch.dict(os.environ, {"LDR_TEST_BADINT_XYZ": "not_a_number"}):
result = get_security_default("test.badint_xyz", 10)
assert result == 10
class TestLoadSecuritySettings:
"""Tests for _load_security_settings function."""
def test_returns_dict(self):
"""Returns a dictionary."""
from local_deep_research.security.security_settings import (
_load_security_settings,
)
_load_security_settings.cache_clear()
result = _load_security_settings()
assert isinstance(result, dict)
def test_is_cached(self):
"""Subsequent calls return cached result."""
from local_deep_research.security.security_settings import (
_load_security_settings,
)
_load_security_settings.cache_clear()
result1 = _load_security_settings()
result2 = _load_security_settings()
assert result1 is result2
def test_returns_empty_dict_for_missing_file(self):
"""Returns empty dict if settings file doesn't exist."""
from local_deep_research.security.security_settings import (
_load_security_settings,
_SETTINGS_PATH,
)
_load_security_settings.cache_clear()
with patch.object(type(_SETTINGS_PATH), "exists", return_value=False):
_load_security_settings.cache_clear()
result = _load_security_settings()
assert result == {}