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
686 lines
21 KiB
Python
686 lines
21 KiB
Python
"""
|
|
Extended tests for environment settings type classes.
|
|
|
|
Tests cover:
|
|
- IntegerSetting min/max validation and edge cases
|
|
- PathSetting path expansion and validation
|
|
- EnumSetting case sensitivity and matching
|
|
- SecretSetting value hiding
|
|
- Base EnvSetting functionality
|
|
"""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
import pytest
|
|
|
|
from local_deep_research.settings.env_settings import (
|
|
BooleanSetting,
|
|
StringSetting,
|
|
IntegerSetting,
|
|
PathSetting,
|
|
SecretSetting,
|
|
EnumSetting,
|
|
SettingsRegistry,
|
|
)
|
|
|
|
|
|
class TestIntegerSettingValidation:
|
|
"""Tests for IntegerSetting min/max validation."""
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clean_env(self):
|
|
"""Clean environment before each test."""
|
|
original_env = {
|
|
k: v for k, v in os.environ.items() if k.startswith("LDR_TEST_")
|
|
}
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
yield
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
for key, value in original_env.items():
|
|
os.environ[key] = value
|
|
|
|
def test_integer_setting_min_value_enforcement(self):
|
|
"""Test that values below min raise ValueError."""
|
|
setting = IntegerSetting(
|
|
key="test.min_value",
|
|
description="Test setting",
|
|
default=10,
|
|
min_value=5,
|
|
max_value=100,
|
|
)
|
|
|
|
os.environ["LDR_TEST_MIN_VALUE"] = "3" # Below min
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
setting.get_value()
|
|
|
|
assert "below minimum" in str(exc_info.value)
|
|
|
|
def test_integer_setting_max_value_enforcement(self):
|
|
"""Test that values above max raise ValueError."""
|
|
setting = IntegerSetting(
|
|
key="test.max_value",
|
|
description="Test setting",
|
|
default=10,
|
|
min_value=5,
|
|
max_value=100,
|
|
)
|
|
|
|
os.environ["LDR_TEST_MAX_VALUE"] = "200" # Above max
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
setting.get_value()
|
|
|
|
assert "above maximum" in str(exc_info.value)
|
|
|
|
def test_integer_setting_invalid_value_uses_default(self):
|
|
"""Test that non-numeric values fall back to default."""
|
|
setting = IntegerSetting(
|
|
key="test.invalid", description="Test setting", default=42
|
|
)
|
|
|
|
os.environ["LDR_TEST_INVALID"] = "not_a_number"
|
|
|
|
result = setting.get_value()
|
|
|
|
assert result == 42
|
|
|
|
def test_integer_setting_float_string_truncates(self):
|
|
"""Test that float strings are converted to int (truncated)."""
|
|
setting = IntegerSetting(
|
|
key="test.float", description="Test setting", default=0
|
|
)
|
|
|
|
os.environ["LDR_TEST_FLOAT"] = "3.7"
|
|
|
|
# This should fail since int("3.7") raises ValueError
|
|
# The implementation should handle this as invalid
|
|
result = setting.get_value()
|
|
|
|
# Expect default since float string is invalid for int()
|
|
assert result == 0
|
|
|
|
def test_integer_setting_empty_string_uses_default(self):
|
|
"""Test that empty string uses default value."""
|
|
setting = IntegerSetting(
|
|
key="test.empty", description="Test setting", default=99
|
|
)
|
|
|
|
os.environ["LDR_TEST_EMPTY"] = ""
|
|
|
|
result = setting.get_value()
|
|
|
|
# Empty string is invalid, should use default
|
|
assert result == 99
|
|
|
|
def test_integer_setting_valid_value_in_range(self):
|
|
"""Test that valid value within range is returned."""
|
|
setting = IntegerSetting(
|
|
key="test.valid",
|
|
description="Test setting",
|
|
default=10,
|
|
min_value=5,
|
|
max_value=100,
|
|
)
|
|
|
|
os.environ["LDR_TEST_VALID"] = "50"
|
|
|
|
result = setting.get_value()
|
|
|
|
assert result == 50
|
|
|
|
def test_integer_setting_boundary_values(self):
|
|
"""Test that boundary values are accepted."""
|
|
setting = IntegerSetting(
|
|
key="test.boundary",
|
|
description="Test setting",
|
|
default=10,
|
|
min_value=5,
|
|
max_value=100,
|
|
)
|
|
|
|
# Test min boundary
|
|
os.environ["LDR_TEST_BOUNDARY"] = "5"
|
|
assert setting.get_value() == 5
|
|
|
|
# Test max boundary
|
|
os.environ["LDR_TEST_BOUNDARY"] = "100"
|
|
assert setting.get_value() == 100
|
|
|
|
def test_integer_setting_negative_value(self):
|
|
"""Test that negative values work correctly."""
|
|
setting = IntegerSetting(
|
|
key="test.negative",
|
|
description="Test setting",
|
|
default=0,
|
|
min_value=-100,
|
|
max_value=100,
|
|
)
|
|
|
|
os.environ["LDR_TEST_NEGATIVE"] = "-50"
|
|
|
|
result = setting.get_value()
|
|
|
|
assert result == -50
|
|
|
|
|
|
class TestPathSettingValidation:
|
|
"""Tests for PathSetting path expansion and validation."""
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clean_env(self):
|
|
"""Clean environment before each test."""
|
|
original_env = {
|
|
k: v for k, v in os.environ.items() if k.startswith("LDR_TEST_")
|
|
}
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
yield
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
for key, value in original_env.items():
|
|
os.environ[key] = value
|
|
|
|
def test_path_setting_expands_tilde(self):
|
|
"""Test that ~/path is expanded to full home path."""
|
|
setting = PathSetting(
|
|
key="test.tilde_path", description="Test setting", default=None
|
|
)
|
|
|
|
os.environ["LDR_TEST_TILDE_PATH"] = "~/test_dir"
|
|
|
|
result = setting.get_value()
|
|
|
|
assert result.startswith(str(Path.home()))
|
|
assert "test_dir" in result
|
|
|
|
def test_path_setting_expands_env_vars(self):
|
|
"""Test that $HOME/path is expanded to the correct absolute path."""
|
|
setting = PathSetting(
|
|
key="test.env_path", description="Test setting", default=None
|
|
)
|
|
|
|
os.environ["LDR_TEST_ENV_PATH"] = "$HOME/test_env_dir"
|
|
|
|
result = setting.get_value()
|
|
|
|
expected = str(Path(os.environ["HOME"]) / "test_env_dir")
|
|
assert result == expected
|
|
|
|
def test_path_setting_create_if_missing(self, tmp_path):
|
|
"""Test that directory is created when create_if_missing=True."""
|
|
test_dir = tmp_path / "new_directory"
|
|
|
|
setting = PathSetting(
|
|
key="test.create_path",
|
|
description="Test setting",
|
|
default=None,
|
|
create_if_missing=True,
|
|
)
|
|
|
|
os.environ["LDR_TEST_CREATE_PATH"] = str(test_dir)
|
|
|
|
result = setting.get_value()
|
|
|
|
assert test_dir.exists()
|
|
assert result == str(test_dir)
|
|
|
|
def test_path_setting_must_exist_raises(self, tmp_path):
|
|
"""Test that ValueError is raised when path doesn't exist and must_exist=True."""
|
|
nonexistent_path = tmp_path / "nonexistent_dir"
|
|
|
|
setting = PathSetting(
|
|
key="test.must_exist",
|
|
description="Test setting",
|
|
default=None,
|
|
must_exist=True,
|
|
)
|
|
|
|
os.environ["LDR_TEST_MUST_EXIST"] = str(nonexistent_path)
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
setting.get_value()
|
|
|
|
assert "does not exist" in str(exc_info.value)
|
|
|
|
def test_path_setting_none_returns_none(self):
|
|
"""Test that unset path returns None."""
|
|
setting = PathSetting(
|
|
key="test.unset_path", description="Test setting", default=None
|
|
)
|
|
|
|
# Don't set the env var
|
|
result = setting.get_value()
|
|
|
|
assert result is None
|
|
|
|
def test_path_setting_absolute_path_unchanged(self, tmp_path):
|
|
"""Test that absolute path is returned as-is."""
|
|
setting = PathSetting(
|
|
key="test.absolute", description="Test setting", default=None
|
|
)
|
|
|
|
os.environ["LDR_TEST_ABSOLUTE"] = str(tmp_path)
|
|
|
|
result = setting.get_value()
|
|
|
|
assert result == str(tmp_path)
|
|
|
|
def test_path_setting_default_value(self):
|
|
"""Test that default value is used when env var not set."""
|
|
setting = PathSetting(
|
|
key="test.default",
|
|
description="Test setting",
|
|
default="/default/path",
|
|
)
|
|
|
|
result = setting.get_value()
|
|
|
|
assert "/default/path" in result
|
|
|
|
|
|
class TestEnumSettingValidation:
|
|
"""Tests for EnumSetting case sensitivity and matching."""
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clean_env(self):
|
|
"""Clean environment before each test."""
|
|
original_env = {
|
|
k: v for k, v in os.environ.items() if k.startswith("LDR_TEST_")
|
|
}
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
yield
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
for key, value in original_env.items():
|
|
os.environ[key] = value
|
|
|
|
def test_enum_setting_case_insensitive(self):
|
|
"""Test that matching is case-insensitive by default."""
|
|
setting = EnumSetting(
|
|
key="test.enum_ci",
|
|
description="Test setting",
|
|
allowed_values={"DEBUG", "INFO", "WARNING"},
|
|
default="INFO",
|
|
case_sensitive=False,
|
|
)
|
|
|
|
os.environ["LDR_TEST_ENUM_CI"] = "debug"
|
|
|
|
result = setting.get_value()
|
|
|
|
assert result == "DEBUG" # Returns canonical form
|
|
|
|
def test_enum_setting_case_sensitive(self):
|
|
"""Test that case-sensitive matching works."""
|
|
setting = EnumSetting(
|
|
key="test.enum_cs",
|
|
description="Test setting",
|
|
allowed_values={"DEBUG", "INFO", "WARNING"},
|
|
default="INFO",
|
|
case_sensitive=True,
|
|
)
|
|
|
|
os.environ["LDR_TEST_ENUM_CS"] = "debug" # lowercase
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
setting.get_value()
|
|
|
|
assert "not in allowed values" in str(exc_info.value)
|
|
|
|
def test_enum_setting_canonical_form(self):
|
|
"""Test that returned value is in canonical form."""
|
|
setting = EnumSetting(
|
|
key="test.enum_canon",
|
|
description="Test setting",
|
|
allowed_values={"WAL", "TRUNCATE", "DELETE"},
|
|
default="WAL",
|
|
case_sensitive=False,
|
|
)
|
|
|
|
os.environ["LDR_TEST_ENUM_CANON"] = "wal"
|
|
|
|
result = setting.get_value()
|
|
|
|
# Should return uppercase canonical form
|
|
assert result == "WAL"
|
|
|
|
def test_enum_setting_invalid_uses_default(self):
|
|
"""Test that invalid values return default via registry."""
|
|
setting = EnumSetting(
|
|
key="test.enum_invalid",
|
|
description="Test setting",
|
|
allowed_values={"A", "B", "C"},
|
|
default="A",
|
|
)
|
|
|
|
os.environ["LDR_TEST_ENUM_INVALID"] = "X" # Not in allowed
|
|
|
|
# Direct get_value raises, but through registry returns default
|
|
with pytest.raises(ValueError):
|
|
setting.get_value()
|
|
|
|
def test_enum_setting_valid_value(self):
|
|
"""Test that valid value is accepted."""
|
|
setting = EnumSetting(
|
|
key="test.enum_valid",
|
|
description="Test setting",
|
|
allowed_values={"OPTION1", "OPTION2", "OPTION3"},
|
|
default="OPTION1",
|
|
)
|
|
|
|
os.environ["LDR_TEST_ENUM_VALID"] = "OPTION2"
|
|
|
|
result = setting.get_value()
|
|
|
|
assert result == "OPTION2"
|
|
|
|
def test_enum_setting_default_when_not_set(self):
|
|
"""Test that default is used when env var not set."""
|
|
setting = EnumSetting(
|
|
key="test.enum_default",
|
|
description="Test setting",
|
|
allowed_values={"X", "Y", "Z"},
|
|
default="Y",
|
|
)
|
|
|
|
result = setting.get_value()
|
|
|
|
assert result == "Y"
|
|
|
|
|
|
class TestSecretSettingHiding:
|
|
"""Tests for SecretSetting value hiding."""
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clean_env(self):
|
|
"""Clean environment before each test."""
|
|
original_env = {
|
|
k: v for k, v in os.environ.items() if k.startswith("LDR_TEST_")
|
|
}
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
yield
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
for key, value in original_env.items():
|
|
os.environ[key] = value
|
|
|
|
def test_secret_setting_repr_hides_value(self):
|
|
"""Test that repr() hides the actual value."""
|
|
setting = SecretSetting(
|
|
key="test.secret_repr", description="Test setting", default=None
|
|
)
|
|
|
|
os.environ["LDR_TEST_SECRET_REPR"] = "super_secret_value"
|
|
|
|
repr_str = repr(setting)
|
|
|
|
assert "super_secret_value" not in repr_str
|
|
assert "***" in repr_str
|
|
|
|
def test_secret_setting_str_hides_value(self):
|
|
"""Test that str() hides the actual value."""
|
|
setting = SecretSetting(
|
|
key="test.secret_str", description="Test setting", default=None
|
|
)
|
|
|
|
os.environ["LDR_TEST_SECRET_STR"] = "super_secret_value"
|
|
|
|
str_result = str(setting)
|
|
|
|
assert "super_secret_value" not in str_result
|
|
assert "SET" in str_result
|
|
|
|
def test_secret_setting_get_value_returns_actual(self):
|
|
"""Test that get_value() returns the actual secret."""
|
|
setting = SecretSetting(
|
|
key="test.secret_get", description="Test setting", default=None
|
|
)
|
|
|
|
os.environ["LDR_TEST_SECRET_GET"] = "actual_secret_value"
|
|
|
|
result = setting.get_value()
|
|
|
|
assert result == "actual_secret_value"
|
|
|
|
def test_secret_setting_unset_shows_not_set(self):
|
|
"""Test that str() shows NOT SET when unset."""
|
|
setting = SecretSetting(
|
|
key="test.secret_unset", description="Test setting", default=None
|
|
)
|
|
|
|
str_result = str(setting)
|
|
|
|
assert "NOT SET" in str_result
|
|
|
|
|
|
class TestBaseEnvSetting:
|
|
"""Tests for base EnvSetting functionality."""
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clean_env(self):
|
|
"""Clean environment before each test."""
|
|
original_env = {
|
|
k: v for k, v in os.environ.items() if k.startswith("LDR_TEST_")
|
|
}
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
yield
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
for key, value in original_env.items():
|
|
os.environ[key] = value
|
|
|
|
def test_env_setting_env_var_auto_generation(self):
|
|
"""Test that env_var is auto-generated from key."""
|
|
setting = BooleanSetting(
|
|
key="test.nested.setting", description="Test setting", default=False
|
|
)
|
|
|
|
assert setting.env_var == "LDR_TEST_NESTED_SETTING"
|
|
|
|
def test_env_setting_is_set_property_true(self):
|
|
"""Test is_set returns True when env var is set."""
|
|
setting = BooleanSetting(
|
|
key="test.is_set", description="Test setting", default=False
|
|
)
|
|
|
|
os.environ["LDR_TEST_IS_SET"] = "true"
|
|
|
|
assert setting.is_set is True
|
|
|
|
def test_env_setting_is_set_property_false(self):
|
|
"""Test is_set returns False when env var is not set."""
|
|
setting = BooleanSetting(
|
|
key="test.not_set", description="Test setting", default=False
|
|
)
|
|
|
|
assert setting.is_set is False
|
|
|
|
def test_env_setting_required_raises_when_missing(self):
|
|
"""Test that required setting raises when not set."""
|
|
setting = StringSetting(
|
|
key="test.required",
|
|
description="Test setting",
|
|
default=None,
|
|
required=True,
|
|
)
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
setting.get_value()
|
|
|
|
assert "Required environment variable" in str(exc_info.value)
|
|
|
|
def test_env_setting_repr(self):
|
|
"""Test __repr__ method."""
|
|
setting = BooleanSetting(
|
|
key="test.repr", description="Test setting", default=False
|
|
)
|
|
|
|
repr_str = repr(setting)
|
|
|
|
assert "BooleanSetting" in repr_str
|
|
assert "test.repr" in repr_str
|
|
assert "LDR_TEST_REPR" in repr_str
|
|
|
|
|
|
class TestBooleanSettingConversion:
|
|
"""Tests for BooleanSetting value conversion."""
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clean_env(self):
|
|
"""Clean environment before each test."""
|
|
original_env = {
|
|
k: v for k, v in os.environ.items() if k.startswith("LDR_TEST_")
|
|
}
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
yield
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
for key, value in original_env.items():
|
|
os.environ[key] = value
|
|
|
|
@pytest.mark.parametrize(
|
|
"value", ["true", "True", "TRUE", "1", "yes", "on", "enabled"]
|
|
)
|
|
def test_boolean_setting_truthy_values(self, value):
|
|
"""Test that truthy values convert to True."""
|
|
setting = BooleanSetting(
|
|
key="test.bool", description="Test setting", default=False
|
|
)
|
|
|
|
os.environ["LDR_TEST_BOOL"] = value
|
|
|
|
assert setting.get_value() is True
|
|
|
|
@pytest.mark.parametrize(
|
|
"value", ["false", "False", "FALSE", "0", "no", "off", ""]
|
|
)
|
|
def test_boolean_setting_falsy_values(self, value):
|
|
"""Test that falsy values convert to False."""
|
|
setting = BooleanSetting(
|
|
key="test.bool_false", description="Test setting", default=True
|
|
)
|
|
|
|
os.environ["LDR_TEST_BOOL_FALSE"] = value
|
|
|
|
assert setting.get_value() is False
|
|
|
|
|
|
class TestSettingsRegistryExtended:
|
|
"""Extended tests for SettingsRegistry functionality."""
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clean_env(self):
|
|
"""Clean environment before each test."""
|
|
original_env = {
|
|
k: v for k, v in os.environ.items() if k.startswith("LDR_TEST_")
|
|
}
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
yield
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_TEST_"):
|
|
os.environ.pop(key, None)
|
|
for key, value in original_env.items():
|
|
os.environ[key] = value
|
|
|
|
def test_registry_register_category(self):
|
|
"""Test registering a category of settings."""
|
|
registry = SettingsRegistry()
|
|
|
|
settings = [
|
|
BooleanSetting("cat1.setting1", "Test 1", default=False),
|
|
BooleanSetting("cat1.setting2", "Test 2", default=True),
|
|
]
|
|
|
|
registry.register_category("cat1", settings)
|
|
|
|
assert "cat1.setting1" in registry.list_all_settings()
|
|
assert "cat1.setting2" in registry.list_all_settings()
|
|
|
|
def test_registry_get_returns_default_for_unknown(self):
|
|
"""Test that get() returns default for unknown keys."""
|
|
registry = SettingsRegistry()
|
|
|
|
result = registry.get("unknown.key", default="fallback")
|
|
|
|
assert result == "fallback"
|
|
|
|
def test_registry_get_setting_object(self):
|
|
"""Test get_setting_object returns the EnvSetting instance."""
|
|
registry = SettingsRegistry()
|
|
setting = BooleanSetting("test.obj", "Test", default=False)
|
|
registry.register_category("test", [setting])
|
|
|
|
result = registry.get_setting_object("test.obj")
|
|
|
|
assert result is setting
|
|
|
|
def test_registry_is_env_only(self):
|
|
"""Test is_env_only returns correct values."""
|
|
registry = SettingsRegistry()
|
|
setting = BooleanSetting("test.env_only", "Test", default=False)
|
|
registry.register_category("test", [setting])
|
|
|
|
assert registry.is_env_only("test.env_only") is True
|
|
assert registry.is_env_only("unknown.key") is False
|
|
|
|
def test_registry_get_env_var(self):
|
|
"""Test get_env_var returns correct env var name."""
|
|
registry = SettingsRegistry()
|
|
setting = BooleanSetting("test.env_var", "Test", default=False)
|
|
registry.register_category("test", [setting])
|
|
|
|
result = registry.get_env_var("test.env_var")
|
|
|
|
assert result == "LDR_TEST_ENV_VAR"
|
|
|
|
def test_registry_get_all_env_vars(self):
|
|
"""Test get_all_env_vars returns all registered env vars."""
|
|
registry = SettingsRegistry()
|
|
settings = [
|
|
BooleanSetting("test.s1", "Desc 1", default=False),
|
|
StringSetting("test.s2", "Desc 2", default="val"),
|
|
]
|
|
registry.register_category("test", settings)
|
|
|
|
result = registry.get_all_env_vars()
|
|
|
|
assert "LDR_TEST_S1" in result
|
|
assert "LDR_TEST_S2" in result
|
|
assert result["LDR_TEST_S1"] == "Desc 1"
|
|
|
|
def test_registry_list_all_settings(self):
|
|
"""Test list_all_settings returns all registered keys."""
|
|
registry = SettingsRegistry()
|
|
settings = [
|
|
BooleanSetting("test.a", "A", default=False),
|
|
BooleanSetting("test.b", "B", default=False),
|
|
]
|
|
registry.register_category("test", settings)
|
|
|
|
result = registry.list_all_settings()
|
|
|
|
assert "test.a" in result
|
|
assert "test.b" in result
|
|
assert len(result) == 2
|