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
347 lines
12 KiB
Python
347 lines
12 KiB
Python
"""High-value tests for settings/env_definitions/db_config.py.
|
|
|
|
Tests the DB_CONFIG_SETTINGS list: IntegerSetting and EnumSetting instances,
|
|
their defaults, boundaries, env-var resolution, deprecated aliases, and
|
|
validation of invalid values.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from local_deep_research.settings.env_definitions.db_config import (
|
|
DB_CONFIG_SETTINGS,
|
|
)
|
|
from local_deep_research.settings.env_settings import (
|
|
IntegerSetting,
|
|
EnumSetting,
|
|
SettingsRegistry,
|
|
)
|
|
|
|
|
|
def _find(key: str):
|
|
"""Return the setting object with the given key."""
|
|
for s in DB_CONFIG_SETTINGS:
|
|
if s.key == key:
|
|
return s
|
|
raise KeyError(f"Setting {key!r} not found in DB_CONFIG_SETTINGS")
|
|
|
|
|
|
class TestDbConfigRegistry:
|
|
"""Tests for registering DB_CONFIG_SETTINGS in a SettingsRegistry."""
|
|
|
|
def test_register_all_settings(self):
|
|
"""All settings can be registered under the db_config category."""
|
|
registry = SettingsRegistry()
|
|
registry.register_category("db_config", DB_CONFIG_SETTINGS)
|
|
for s in DB_CONFIG_SETTINGS:
|
|
assert registry.is_env_only(s.key)
|
|
|
|
def test_setting_count(self):
|
|
"""DB_CONFIG_SETTINGS contains the expected number of settings."""
|
|
assert len(DB_CONFIG_SETTINGS) == 9
|
|
|
|
def test_all_keys_start_with_db_config(self):
|
|
"""Every key should use the 'db_config.' prefix."""
|
|
for s in DB_CONFIG_SETTINGS:
|
|
assert s.key.startswith("db_config."), f"{s.key} missing prefix"
|
|
|
|
|
|
class TestEnvVarNaming:
|
|
"""Verify that the auto-generated env_var matches the canonical form."""
|
|
|
|
@pytest.mark.parametrize(
|
|
"key, expected_env_var",
|
|
[
|
|
("db_config.cache_size_mb", "LDR_DB_CONFIG_CACHE_SIZE_MB"),
|
|
("db_config.journal_mode", "LDR_DB_CONFIG_JOURNAL_MODE"),
|
|
("db_config.synchronous", "LDR_DB_CONFIG_SYNCHRONOUS"),
|
|
(
|
|
"db_config.wal_autocheckpoint",
|
|
"LDR_DB_CONFIG_WAL_AUTOCHECKPOINT",
|
|
),
|
|
("db_config.page_size", "LDR_DB_CONFIG_PAGE_SIZE"),
|
|
("db_config.kdf_iterations", "LDR_DB_CONFIG_KDF_ITERATIONS"),
|
|
("db_config.kdf_algorithm", "LDR_DB_CONFIG_KDF_ALGORITHM"),
|
|
("db_config.hmac_algorithm", "LDR_DB_CONFIG_HMAC_ALGORITHM"),
|
|
(
|
|
"db_config.cipher_memory_security",
|
|
"LDR_DB_CONFIG_CIPHER_MEMORY_SECURITY",
|
|
),
|
|
],
|
|
)
|
|
def test_canonical_env_var(self, key, expected_env_var):
|
|
s = _find(key)
|
|
assert s.env_var == expected_env_var
|
|
|
|
|
|
class TestDeprecatedEnvVars:
|
|
"""Settings that have a deprecated alias should resolve via it."""
|
|
|
|
@pytest.mark.parametrize(
|
|
"key, deprecated_var",
|
|
[
|
|
("db_config.cache_size_mb", "LDR_DB_CACHE_SIZE_MB"),
|
|
("db_config.journal_mode", "LDR_DB_JOURNAL_MODE"),
|
|
("db_config.synchronous", "LDR_DB_SYNCHRONOUS"),
|
|
("db_config.page_size", "LDR_DB_PAGE_SIZE"),
|
|
("db_config.kdf_iterations", "LDR_DB_KDF_ITERATIONS"),
|
|
("db_config.kdf_algorithm", "LDR_DB_KDF_ALGORITHM"),
|
|
("db_config.hmac_algorithm", "LDR_DB_HMAC_ALGORITHM"),
|
|
],
|
|
)
|
|
def test_deprecated_alias_stored(self, key, deprecated_var):
|
|
"""The deprecated_env_var attribute matches expectations."""
|
|
s = _find(key)
|
|
assert s.deprecated_env_var == deprecated_var
|
|
|
|
def test_cipher_memory_security_has_no_deprecated_alias(self):
|
|
s = _find("db_config.cipher_memory_security")
|
|
assert s.deprecated_env_var is None
|
|
|
|
def test_deprecated_alias_fallback_returns_value(self, monkeypatch):
|
|
"""When only the deprecated alias is set, the value should be resolved."""
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.delenv(s.env_var, raising=False)
|
|
monkeypatch.setenv(s.deprecated_env_var, "128")
|
|
assert s.get_value() == 128
|
|
|
|
def test_canonical_takes_precedence_over_deprecated(self, monkeypatch):
|
|
"""When both canonical and deprecated vars are set, canonical wins."""
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.setenv(s.env_var, "32")
|
|
monkeypatch.setenv(s.deprecated_env_var, "999")
|
|
assert s.get_value() == 32
|
|
|
|
|
|
class TestCacheSizeMb:
|
|
"""Tests for db_config.cache_size_mb."""
|
|
|
|
def test_type(self):
|
|
assert isinstance(_find("db_config.cache_size_mb"), IntegerSetting)
|
|
|
|
def test_default_value(self, monkeypatch):
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.delenv(s.env_var, raising=False)
|
|
monkeypatch.delenv(s.deprecated_env_var, raising=False)
|
|
assert s.get_value() == 64
|
|
|
|
def test_valid_value(self, monkeypatch):
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.setenv(s.env_var, "256")
|
|
assert s.get_value() == 256
|
|
|
|
def test_min_boundary(self, monkeypatch):
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.setenv(s.env_var, "1")
|
|
assert s.get_value() == 1
|
|
|
|
def test_below_min_raises(self, monkeypatch):
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.setenv(s.env_var, "0")
|
|
with pytest.raises(ValueError, match="below minimum"):
|
|
s.get_value()
|
|
|
|
def test_max_boundary(self, monkeypatch):
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.setenv(s.env_var, "10000")
|
|
assert s.get_value() == 10000
|
|
|
|
def test_above_max_raises(self, monkeypatch):
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.setenv(s.env_var, "10001")
|
|
with pytest.raises(ValueError, match="above maximum"):
|
|
s.get_value()
|
|
|
|
def test_non_integer_returns_default(self, monkeypatch):
|
|
"""Non-integer strings fall back to the default value."""
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.setenv(s.env_var, "not_a_number")
|
|
assert s.get_value() == s.default
|
|
|
|
def test_negative_value_raises(self, monkeypatch):
|
|
"""Negative values are below the minimum."""
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.setenv(s.env_var, "-10")
|
|
with pytest.raises(ValueError, match="below minimum"):
|
|
s.get_value()
|
|
|
|
|
|
class TestPageSize:
|
|
"""Tests for db_config.page_size."""
|
|
|
|
def test_default(self, monkeypatch):
|
|
s = _find("db_config.page_size")
|
|
monkeypatch.delenv(s.env_var, raising=False)
|
|
monkeypatch.delenv(s.deprecated_env_var, raising=False)
|
|
assert s.get_value() == 16384
|
|
|
|
def test_min_boundary(self, monkeypatch):
|
|
s = _find("db_config.page_size")
|
|
monkeypatch.setenv(s.env_var, "512")
|
|
assert s.get_value() == 512
|
|
|
|
def test_below_min_raises(self, monkeypatch):
|
|
s = _find("db_config.page_size")
|
|
monkeypatch.setenv(s.env_var, "256")
|
|
with pytest.raises(ValueError, match="below minimum"):
|
|
s.get_value()
|
|
|
|
def test_max_boundary(self, monkeypatch):
|
|
s = _find("db_config.page_size")
|
|
monkeypatch.setenv(s.env_var, "65536")
|
|
assert s.get_value() == 65536
|
|
|
|
def test_above_max_raises(self, monkeypatch):
|
|
s = _find("db_config.page_size")
|
|
monkeypatch.setenv(s.env_var, "65537")
|
|
with pytest.raises(ValueError, match="above maximum"):
|
|
s.get_value()
|
|
|
|
|
|
class TestJournalMode:
|
|
"""Tests for db_config.journal_mode."""
|
|
|
|
def test_type(self):
|
|
assert isinstance(_find("db_config.journal_mode"), EnumSetting)
|
|
|
|
def test_default(self, monkeypatch):
|
|
s = _find("db_config.journal_mode")
|
|
monkeypatch.delenv(s.env_var, raising=False)
|
|
monkeypatch.delenv(s.deprecated_env_var, raising=False)
|
|
assert s.get_value() == "WAL"
|
|
|
|
@pytest.mark.parametrize(
|
|
"raw_input, expected",
|
|
[
|
|
("WAL", "WAL"),
|
|
("wal", "WAL"),
|
|
("DELETE", "DELETE"),
|
|
("TRUNCATE", "TRUNCATE"),
|
|
("PERSIST", "PERSIST"),
|
|
("MEMORY", "MEMORY"),
|
|
("OFF", "OFF"),
|
|
],
|
|
)
|
|
def test_valid_values_case_insensitive(
|
|
self, monkeypatch, raw_input, expected
|
|
):
|
|
s = _find("db_config.journal_mode")
|
|
monkeypatch.setenv(s.env_var, raw_input)
|
|
assert s.get_value() == expected
|
|
|
|
def test_invalid_value_raises(self, monkeypatch):
|
|
s = _find("db_config.journal_mode")
|
|
monkeypatch.setenv(s.env_var, "INVALID_MODE")
|
|
with pytest.raises(ValueError, match="not in allowed values"):
|
|
s.get_value()
|
|
|
|
|
|
class TestSynchronous:
|
|
"""Tests for db_config.synchronous."""
|
|
|
|
def test_default(self, monkeypatch):
|
|
s = _find("db_config.synchronous")
|
|
monkeypatch.delenv(s.env_var, raising=False)
|
|
monkeypatch.delenv(s.deprecated_env_var, raising=False)
|
|
assert s.get_value() == "NORMAL"
|
|
|
|
@pytest.mark.parametrize("value", ["OFF", "NORMAL", "FULL", "EXTRA"])
|
|
def test_all_valid_values(self, monkeypatch, value):
|
|
s = _find("db_config.synchronous")
|
|
monkeypatch.setenv(s.env_var, value)
|
|
assert s.get_value() == value
|
|
|
|
def test_invalid_raises(self, monkeypatch):
|
|
s = _find("db_config.synchronous")
|
|
monkeypatch.setenv(s.env_var, "LAZY")
|
|
with pytest.raises(ValueError, match="not in allowed values"):
|
|
s.get_value()
|
|
|
|
|
|
class TestKdfAlgorithm:
|
|
"""Tests for db_config.kdf_algorithm."""
|
|
|
|
def test_default(self, monkeypatch):
|
|
s = _find("db_config.kdf_algorithm")
|
|
monkeypatch.delenv(s.env_var, raising=False)
|
|
monkeypatch.delenv(s.deprecated_env_var, raising=False)
|
|
assert s.get_value() == "PBKDF2_HMAC_SHA512"
|
|
|
|
@pytest.mark.parametrize(
|
|
"value",
|
|
["PBKDF2_HMAC_SHA512", "PBKDF2_HMAC_SHA256", "PBKDF2_HMAC_SHA1"],
|
|
)
|
|
def test_all_valid_values(self, monkeypatch, value):
|
|
s = _find("db_config.kdf_algorithm")
|
|
monkeypatch.setenv(s.env_var, value)
|
|
assert s.get_value() == value
|
|
|
|
def test_invalid_raises(self, monkeypatch):
|
|
s = _find("db_config.kdf_algorithm")
|
|
monkeypatch.setenv(s.env_var, "SCRYPT")
|
|
with pytest.raises(ValueError, match="not in allowed values"):
|
|
s.get_value()
|
|
|
|
|
|
class TestCipherMemorySecurity:
|
|
"""Tests for db_config.cipher_memory_security."""
|
|
|
|
def test_default(self, monkeypatch):
|
|
s = _find("db_config.cipher_memory_security")
|
|
monkeypatch.delenv(s.env_var, raising=False)
|
|
assert s.get_value() == "OFF"
|
|
|
|
@pytest.mark.parametrize(
|
|
"raw_input, expected",
|
|
[("ON", "ON"), ("on", "ON"), ("OFF", "OFF"), ("off", "OFF")],
|
|
)
|
|
def test_valid_values(self, monkeypatch, raw_input, expected):
|
|
s = _find("db_config.cipher_memory_security")
|
|
monkeypatch.setenv(s.env_var, raw_input)
|
|
assert s.get_value() == expected
|
|
|
|
def test_invalid_raises(self, monkeypatch):
|
|
s = _find("db_config.cipher_memory_security")
|
|
monkeypatch.setenv(s.env_var, "YES")
|
|
with pytest.raises(ValueError, match="not in allowed values"):
|
|
s.get_value()
|
|
|
|
|
|
class TestRegistryGetIntegration:
|
|
"""Test SettingsRegistry.get() for db_config settings."""
|
|
|
|
def _make_registry(self):
|
|
r = SettingsRegistry()
|
|
r.register_category("db_config", DB_CONFIG_SETTINGS)
|
|
return r
|
|
|
|
def test_get_returns_default_when_env_not_set(self, monkeypatch):
|
|
"""When no env var is set, registry.get() returns the setting default."""
|
|
r = self._make_registry()
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.delenv(s.env_var, raising=False)
|
|
monkeypatch.delenv(s.deprecated_env_var, raising=False)
|
|
assert r.get("db_config.cache_size_mb") == 64
|
|
|
|
def test_get_returns_override_default_for_unknown_key(self):
|
|
"""Unknown key returns the caller-supplied default."""
|
|
r = self._make_registry()
|
|
assert r.get("db_config.nonexistent", default=42) == 42
|
|
|
|
def test_get_returns_caller_default_on_validation_error(self, monkeypatch):
|
|
"""When the env var fails validation, get() returns the caller default."""
|
|
r = self._make_registry()
|
|
s = _find("db_config.cache_size_mb")
|
|
monkeypatch.setenv(s.env_var, "0") # below minimum
|
|
assert r.get("db_config.cache_size_mb", default=99) == 99
|
|
|
|
def test_get_env_var_for_known_key(self):
|
|
r = self._make_registry()
|
|
assert (
|
|
r.get_env_var("db_config.cache_size_mb")
|
|
== "LDR_DB_CONFIG_CACHE_SIZE_MB"
|
|
)
|
|
|
|
def test_get_env_var_for_unknown_key(self):
|
|
r = self._make_registry()
|
|
assert r.get_env_var("no.such.key") is None
|