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
280 lines
10 KiB
Python
280 lines
10 KiB
Python
"""Test suite for the environment settings system."""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
import pytest
|
|
|
|
from local_deep_research.settings.env_registry import (
|
|
registry,
|
|
get_env_setting,
|
|
is_test_mode,
|
|
is_ci_environment,
|
|
)
|
|
from local_deep_research.settings.manager import SettingsManager
|
|
|
|
|
|
class TestEnvRegistry:
|
|
"""Test the environment registry functionality."""
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clean_env(self):
|
|
"""Clean environment before each test."""
|
|
# Store original env vars
|
|
original_env = {
|
|
k: v
|
|
for k, v in os.environ.items()
|
|
if k.startswith("LDR_") or k in ["CI", "TESTING"]
|
|
}
|
|
|
|
# Clean env vars
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_") or key in ["CI", "TESTING"]:
|
|
os.environ.pop(key, None)
|
|
|
|
yield
|
|
|
|
# Restore original env vars
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_") or key in ["CI", "TESTING"]:
|
|
os.environ.pop(key, None)
|
|
for key, value in original_env.items():
|
|
os.environ[key] = value
|
|
|
|
def test_registry_has_settings(self):
|
|
"""Test that registry has settings registered."""
|
|
all_settings = registry.list_all_settings()
|
|
assert len(all_settings) > 0, "Registry should have settings"
|
|
assert "testing.test_mode" in all_settings
|
|
assert "bootstrap.encryption_key" in all_settings
|
|
assert "db_config.cache_size_mb" in all_settings
|
|
|
|
def test_categories(self):
|
|
"""Test that all categories have settings."""
|
|
categories = ["testing", "bootstrap", "db_config"]
|
|
for cat in categories:
|
|
settings = registry.get_category_settings(cat)
|
|
assert len(settings) > 0, f"Category '{cat}' should have settings"
|
|
|
|
def test_boolean_setting(self):
|
|
"""Test boolean setting conversion."""
|
|
# Test various true values
|
|
for value in [
|
|
"true",
|
|
"True",
|
|
"TRUE",
|
|
"1",
|
|
"yes",
|
|
"YES",
|
|
"on",
|
|
"enabled",
|
|
]:
|
|
os.environ["LDR_TESTING_TEST_MODE"] = value
|
|
assert is_test_mode(), f"'{value}' should be True"
|
|
|
|
# Test various false values
|
|
for value in ["false", "False", "FALSE", "0", "no", "NO", "off", ""]:
|
|
os.environ["LDR_TESTING_TEST_MODE"] = value
|
|
assert not is_test_mode(), f"'{value}' should be False"
|
|
|
|
def test_ci_environment(self):
|
|
"""Test CI environment detection."""
|
|
assert not is_ci_environment()
|
|
|
|
os.environ["CI"] = "true"
|
|
assert is_ci_environment()
|
|
|
|
os.environ["CI"] = "false"
|
|
assert not is_ci_environment()
|
|
|
|
def test_integer_setting(self):
|
|
"""Test integer setting conversion and validation."""
|
|
os.environ["LDR_DB_CONFIG_CACHE_SIZE_MB"] = "512"
|
|
cache_size = get_env_setting("db_config.cache_size_mb")
|
|
assert cache_size == 512
|
|
|
|
# Test invalid integer returns setting's registered default
|
|
os.environ["LDR_DB_CONFIG_CACHE_SIZE_MB"] = "invalid"
|
|
cache_size = get_env_setting("db_config.cache_size_mb", 100)
|
|
assert cache_size == 64
|
|
|
|
# Test when not set uses default from setting definition
|
|
os.environ.pop("LDR_DB_CONFIG_CACHE_SIZE_MB", None)
|
|
cache_size = get_env_setting("db_config.cache_size_mb")
|
|
assert cache_size == 64 # Default from db_config.py
|
|
|
|
def test_enum_setting(self):
|
|
"""Test enum setting with case-insensitive matching."""
|
|
# Test lowercase input returns canonical form
|
|
os.environ["LDR_DB_CONFIG_JOURNAL_MODE"] = "wal"
|
|
journal_mode = get_env_setting("db_config.journal_mode")
|
|
assert journal_mode == "WAL"
|
|
|
|
os.environ["LDR_DB_CONFIG_JOURNAL_MODE"] = "TRUNCATE"
|
|
journal_mode = get_env_setting("db_config.journal_mode")
|
|
assert journal_mode == "TRUNCATE"
|
|
|
|
# Test default when not set
|
|
os.environ.pop("LDR_DB_CONFIG_JOURNAL_MODE", None)
|
|
journal_mode = get_env_setting("db_config.journal_mode")
|
|
assert journal_mode == "WAL" # Default value
|
|
|
|
def test_path_setting(self):
|
|
"""Test path setting expansion."""
|
|
test_path = "/tmp/test_ldr_path"
|
|
os.environ["LDR_BOOTSTRAP_DATA_DIR"] = test_path
|
|
data_dir = get_env_setting("bootstrap.data_dir")
|
|
assert data_dir == test_path
|
|
|
|
# Test home expansion
|
|
os.environ["LDR_BOOTSTRAP_DATA_DIR"] = "~/test_path"
|
|
data_dir = get_env_setting("bootstrap.data_dir")
|
|
assert data_dir.startswith(str(Path.home()))
|
|
assert "test_path" in data_dir
|
|
|
|
def test_secret_setting(self):
|
|
"""Test secret setting hides values."""
|
|
os.environ["LDR_BOOTSTRAP_ENCRYPTION_KEY"] = "secret_key_123"
|
|
setting_obj = registry.get_setting_object("bootstrap.encryption_key")
|
|
|
|
# Value should be hidden in string representation
|
|
assert "secret_key_123" not in str(setting_obj)
|
|
assert "SET" in str(setting_obj)
|
|
|
|
# But value should still be retrievable
|
|
value = get_env_setting("bootstrap.encryption_key")
|
|
assert value == "secret_key_123"
|
|
|
|
|
|
class TestSettingsManagerIntegration:
|
|
"""Test SettingsManager integration with env settings."""
|
|
|
|
@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_")
|
|
}
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_"):
|
|
os.environ.pop(key, None)
|
|
yield
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("LDR_"):
|
|
os.environ.pop(key, None)
|
|
for key, value in original_env.items():
|
|
os.environ[key] = value
|
|
|
|
def test_get_env_only_setting(self):
|
|
"""Test getting env-only settings through SettingsManager."""
|
|
sm = SettingsManager(db_session=None)
|
|
|
|
os.environ["LDR_TESTING_TEST_MODE"] = "true"
|
|
test_mode = sm.get_setting("testing.test_mode")
|
|
assert test_mode
|
|
|
|
def test_is_env_only_setting(self):
|
|
"""Test checking if a setting is env-only."""
|
|
sm = SettingsManager(db_session=None)
|
|
|
|
# These should be env-only
|
|
assert sm.is_env_only_setting("testing.test_mode")
|
|
assert sm.is_env_only_setting("bootstrap.encryption_key")
|
|
assert sm.is_env_only_setting("db_config.cache_size_mb")
|
|
|
|
# These should NOT be env-only (regular settings)
|
|
assert not sm.is_env_only_setting("app.debug")
|
|
assert not sm.is_env_only_setting("llm.provider")
|
|
assert not sm.is_env_only_setting("search.max_concurrent")
|
|
|
|
def test_get_bootstrap_vars(self):
|
|
"""Test getting bootstrap environment variables."""
|
|
sm = SettingsManager(db_session=None)
|
|
|
|
bootstrap_vars = sm.get_bootstrap_env_vars()
|
|
|
|
# Should include bootstrap settings
|
|
assert "LDR_BOOTSTRAP_ENCRYPTION_KEY" in bootstrap_vars
|
|
assert "LDR_BOOTSTRAP_DATA_DIR" in bootstrap_vars
|
|
assert "LDR_BOOTSTRAP_CONFIG_DIR" in bootstrap_vars
|
|
|
|
# Should include db_config settings
|
|
assert "LDR_DB_CONFIG_CACHE_SIZE_MB" in bootstrap_vars
|
|
assert "LDR_DB_CONFIG_JOURNAL_MODE" in bootstrap_vars
|
|
|
|
# Should have a reasonable number of vars
|
|
assert len(bootstrap_vars) >= 10
|
|
|
|
def test_is_bootstrap_env_var(self):
|
|
"""Test checking if an env var is a bootstrap variable."""
|
|
sm = SettingsManager(db_session=None)
|
|
|
|
# Bootstrap vars
|
|
assert sm.is_bootstrap_env_var("LDR_BOOTSTRAP_ENCRYPTION_KEY")
|
|
assert sm.is_bootstrap_env_var("LDR_DB_CONFIG_PAGE_SIZE")
|
|
assert sm.is_bootstrap_env_var("LDR_BOOTSTRAP_DATA_DIR")
|
|
|
|
# Testing vars are in ALL_ENV_ONLY_VARS but not specifically bootstrap
|
|
# They are allowed by the pre-commit hook but not considered bootstrap vars
|
|
assert not sm.is_bootstrap_env_var("LDR_TESTING_TEST_MODE")
|
|
|
|
# Non-existent var
|
|
assert not sm.is_bootstrap_env_var("RANDOM_VAR")
|
|
assert not sm.is_bootstrap_env_var("SOME_OTHER_VAR")
|
|
|
|
|
|
class TestEnvVarMapping:
|
|
"""Test environment variable name mapping."""
|
|
|
|
def test_get_env_var_for_setting(self):
|
|
"""Test getting env var name for a setting."""
|
|
assert (
|
|
registry.get_env_var("testing.test_mode") == "LDR_TESTING_TEST_MODE"
|
|
)
|
|
assert (
|
|
registry.get_env_var("bootstrap.encryption_key")
|
|
== "LDR_BOOTSTRAP_ENCRYPTION_KEY"
|
|
)
|
|
assert (
|
|
registry.get_env_var("db_config.cache_size_mb")
|
|
== "LDR_DB_CONFIG_CACHE_SIZE_MB"
|
|
)
|
|
assert registry.get_env_var("nonexistent.setting") is None
|
|
|
|
def test_get_all_env_vars(self):
|
|
"""Test getting all environment variables."""
|
|
all_env_vars = registry.get_all_env_vars()
|
|
|
|
# Should have a reasonable number
|
|
assert len(all_env_vars) >= 15
|
|
|
|
# Should include key variables
|
|
assert "LDR_TESTING_TEST_MODE" in all_env_vars
|
|
assert "LDR_BOOTSTRAP_ENCRYPTION_KEY" in all_env_vars
|
|
# CI and TESTING are external vars, not in registry anymore
|
|
|
|
def test_category_specific_vars(self):
|
|
"""Test getting category-specific environment variables."""
|
|
# Testing vars
|
|
testing_vars = registry.get_testing_vars()
|
|
assert len(testing_vars) >= 1
|
|
assert "LDR_TESTING_TEST_MODE" in testing_vars
|
|
|
|
# Bootstrap vars (includes db_config)
|
|
bootstrap_vars = registry.get_bootstrap_vars()
|
|
assert len(bootstrap_vars) >= 10
|
|
assert "LDR_BOOTSTRAP_ENCRYPTION_KEY" in bootstrap_vars
|
|
assert "LDR_BOOTSTRAP_DATA_DIR" in bootstrap_vars
|
|
assert "LDR_DB_CONFIG_CACHE_SIZE_MB" in bootstrap_vars
|
|
assert "LDR_DB_CONFIG_JOURNAL_MODE" in bootstrap_vars
|
|
|
|
def test_is_env_only(self):
|
|
"""Test checking if a setting is env-only."""
|
|
# These should be env-only
|
|
assert registry.is_env_only("testing.test_mode")
|
|
assert registry.is_env_only("bootstrap.encryption_key")
|
|
assert registry.is_env_only("db_config.cache_size_mb")
|
|
|
|
# These should not exist in env registry
|
|
assert not registry.is_env_only("app.debug")
|
|
assert not registry.is_env_only("nonexistent.setting")
|