""" Behavioral tests for settings/env_settings module and env_definitions. Tests the EnvSetting class hierarchy, type conversion, validation, and the SettingsRegistry. """ import os from unittest.mock import patch import pytest class TestBooleanSetting: """Tests for BooleanSetting class.""" def test_key_generates_env_var(self): """Key auto-generates LDR_ prefixed env var.""" from local_deep_research.settings.env_settings import BooleanSetting setting = BooleanSetting(key="testing.test_mode", description="desc") assert setting.env_var == "LDR_TESTING_TEST_MODE" def test_default_is_false(self): """Default value is False.""" from local_deep_research.settings.env_settings import BooleanSetting setting = BooleanSetting(key="test.flag", description="desc") assert setting.default is False def test_custom_default(self): """Can set custom default.""" from local_deep_research.settings.env_settings import BooleanSetting setting = BooleanSetting( key="test.flag", description="desc", default=True ) assert setting.default is True def test_converts_true_strings(self): """Converts truthy strings to True.""" from local_deep_research.settings.env_settings import BooleanSetting setting = BooleanSetting(key="test.flag", description="desc") for val in ("true", "1", "yes", "on", "enabled"): assert setting._convert_value(val) is True def test_converts_false_strings(self): """Converts non-truthy strings to False.""" from local_deep_research.settings.env_settings import BooleanSetting setting = BooleanSetting(key="test.flag", description="desc") for val in ("false", "0", "no", "off", "disabled"): assert setting._convert_value(val) is False def test_case_insensitive_conversion(self): """Conversion is case-insensitive.""" from local_deep_research.settings.env_settings import BooleanSetting setting = BooleanSetting(key="test.flag", description="desc") assert setting._convert_value("TRUE") is True assert setting._convert_value("Enabled") is True def test_get_value_returns_default_when_not_set(self): """get_value returns default when env var not set.""" from local_deep_research.settings.env_settings import BooleanSetting setting = BooleanSetting(key="test.unset_xyz", description="desc") with patch.dict(os.environ, {}, clear=False): if setting.env_var in os.environ: del os.environ[setting.env_var] result = setting.get_value() assert result is False def test_get_value_from_env(self): """get_value reads from environment.""" from local_deep_research.settings.env_settings import BooleanSetting setting = BooleanSetting(key="test.env_bool_xyz", description="desc") with patch.dict(os.environ, {setting.env_var: "true"}): result = setting.get_value() assert result is True class TestStringSetting: """Tests for StringSetting class.""" def test_converts_value_as_is(self): """Returns string value unchanged.""" from local_deep_research.settings.env_settings import StringSetting setting = StringSetting(key="test.str", description="desc") assert setting._convert_value("hello world") == "hello world" def test_default_is_none(self): """Default value is None.""" from local_deep_research.settings.env_settings import StringSetting setting = StringSetting(key="test.str", description="desc") assert setting.default is None def test_required_raises_when_not_set(self): """Required setting raises ValueError when not set.""" from local_deep_research.settings.env_settings import StringSetting setting = StringSetting( key="test.required_str_xyz", description="desc", required=True ) with patch.dict(os.environ, {}, clear=False): if setting.env_var in os.environ: del os.environ[setting.env_var] with pytest.raises( ValueError, match="Required environment variable" ): setting.get_value() class TestIntegerSetting: """Tests for IntegerSetting class.""" def test_converts_valid_int(self): """Converts valid integer string.""" from local_deep_research.settings.env_settings import IntegerSetting setting = IntegerSetting(key="test.int", description="desc") assert setting._convert_value("42") == 42 def test_min_value_validation(self): """Raises ValueError when below minimum.""" from local_deep_research.settings.env_settings import IntegerSetting setting = IntegerSetting( key="test.int", description="desc", min_value=10 ) with pytest.raises(ValueError, match="below minimum"): setting._convert_value("5") def test_max_value_validation(self): """Raises ValueError when above maximum.""" from local_deep_research.settings.env_settings import IntegerSetting setting = IntegerSetting( key="test.int", description="desc", max_value=100 ) with pytest.raises(ValueError, match="above maximum"): setting._convert_value("200") def test_value_at_min_accepted(self): """Value exactly at minimum is accepted.""" from local_deep_research.settings.env_settings import IntegerSetting setting = IntegerSetting( key="test.int", description="desc", min_value=10 ) assert setting._convert_value("10") == 10 def test_value_at_max_accepted(self): """Value exactly at maximum is accepted.""" from local_deep_research.settings.env_settings import IntegerSetting setting = IntegerSetting( key="test.int", description="desc", max_value=100 ) assert setting._convert_value("100") == 100 def test_invalid_int_returns_default(self): """Invalid integer string returns default.""" from local_deep_research.settings.env_settings import IntegerSetting setting = IntegerSetting(key="test.int", description="desc", default=42) result = setting._convert_value("abc") assert result == 42 class TestSecretSetting: """Tests for SecretSetting class.""" def test_repr_hides_value(self): """Repr hides the actual value.""" from local_deep_research.settings.env_settings import SecretSetting setting = SecretSetting(key="test.secret", description="desc") assert "***" in repr(setting) assert "test.secret" in repr(setting) def test_str_shows_set_status(self): """Str shows whether env var is set.""" from local_deep_research.settings.env_settings import SecretSetting setting = SecretSetting(key="test.secret_xyz", description="desc") with patch.dict(os.environ, {}, clear=False): if setting.env_var in os.environ: del os.environ[setting.env_var] assert "NOT SET" in str(setting) def test_str_shows_set_when_env_present(self): """Str shows SET when env var is present.""" from local_deep_research.settings.env_settings import SecretSetting setting = SecretSetting(key="test.secret_set_xyz", description="desc") with patch.dict(os.environ, {setting.env_var: "secret_value"}): result = str(setting) assert "" in result assert "secret_value" not in result class TestEnumSetting: """Tests for EnumSetting class.""" def test_accepts_valid_value(self): """Accepts value in allowed set.""" from local_deep_research.settings.env_settings import EnumSetting setting = EnumSetting( key="test.enum", description="desc", allowed_values={"WAL", "DELETE", "OFF"}, ) assert setting._convert_value("wal") == "WAL" def test_rejects_invalid_value(self): """Raises ValueError for value not in allowed set.""" from local_deep_research.settings.env_settings import EnumSetting setting = EnumSetting( key="test.enum", description="desc", allowed_values={"WAL", "DELETE"}, ) with pytest.raises(ValueError, match="not in allowed values"): setting._convert_value("INVALID") def test_case_insensitive_by_default(self): """Case-insensitive matching by default.""" from local_deep_research.settings.env_settings import EnumSetting setting = EnumSetting( key="test.enum", description="desc", allowed_values={"WAL", "DELETE"}, ) assert setting._convert_value("wal") == "WAL" assert setting._convert_value("WAL") == "WAL" assert setting._convert_value("Wal") == "WAL" def test_case_sensitive_mode(self): """Case-sensitive mode rejects wrong case.""" from local_deep_research.settings.env_settings import EnumSetting setting = EnumSetting( key="test.enum", description="desc", allowed_values={"WAL", "DELETE"}, case_sensitive=True, ) assert setting._convert_value("WAL") == "WAL" with pytest.raises(ValueError): setting._convert_value("wal") def test_returns_canonical_form(self): """Returns canonical (original) form regardless of input case.""" from local_deep_research.settings.env_settings import EnumSetting setting = EnumSetting( key="test.enum", description="desc", allowed_values={"NORMAL", "FULL"}, ) assert setting._convert_value("normal") == "NORMAL" assert setting._convert_value("NORMAL") == "NORMAL" class TestEnvSettingBase: """Tests for EnvSetting base class behavior.""" def test_env_var_generation(self): """Env var is generated from key with LDR_ prefix.""" from local_deep_research.settings.env_settings import BooleanSetting setting = BooleanSetting( key="bootstrap.encryption_key", description="desc" ) assert setting.env_var == "LDR_BOOTSTRAP_ENCRYPTION_KEY" def test_is_set_returns_false_when_not_set(self): """is_set returns False when env var not present.""" from local_deep_research.settings.env_settings import StringSetting setting = StringSetting(key="test.is_set_xyz", description="desc") with patch.dict(os.environ, {}, clear=False): if setting.env_var in os.environ: del os.environ[setting.env_var] assert setting.is_set is False def test_is_set_returns_true_when_set(self): """is_set returns True when env var is present.""" from local_deep_research.settings.env_settings import StringSetting setting = StringSetting(key="test.is_set_yes_xyz", description="desc") with patch.dict(os.environ, {setting.env_var: "value"}): assert setting.is_set is True def test_repr_includes_key_and_env_var(self): """Repr includes key and env_var.""" from local_deep_research.settings.env_settings import BooleanSetting setting = BooleanSetting(key="test.repr", description="desc") r = repr(setting) assert "test.repr" in r assert "LDR_TEST_REPR" in r class TestSettingsRegistry: """Tests for SettingsRegistry class.""" def test_empty_registry(self): """New registry has no settings.""" from local_deep_research.settings.env_settings import SettingsRegistry registry = SettingsRegistry() assert registry.list_all_settings() == [] def test_register_category(self): """Can register a category of settings.""" from local_deep_research.settings.env_settings import ( SettingsRegistry, BooleanSetting, ) registry = SettingsRegistry() settings = [ BooleanSetting(key="cat.flag1", description="flag 1"), BooleanSetting(key="cat.flag2", description="flag 2"), ] registry.register_category("test_cat", settings) assert "cat.flag1" in registry.list_all_settings() assert "cat.flag2" in registry.list_all_settings() def test_get_returns_default_for_unknown_key(self): """get() returns default for unknown key.""" from local_deep_research.settings.env_settings import SettingsRegistry registry = SettingsRegistry() assert registry.get("unknown.key", "fallback") == "fallback" def test_is_env_only(self): """is_env_only returns True for registered keys.""" from local_deep_research.settings.env_settings import ( SettingsRegistry, BooleanSetting, ) registry = SettingsRegistry() registry.register_category( "test", [BooleanSetting(key="test.flag", description="desc")], ) assert registry.is_env_only("test.flag") is True assert registry.is_env_only("other.key") is False def test_get_env_var(self): """get_env_var returns env var name for registered key.""" from local_deep_research.settings.env_settings import ( SettingsRegistry, StringSetting, ) registry = SettingsRegistry() registry.register_category( "test", [StringSetting(key="test.name", description="desc")], ) assert registry.get_env_var("test.name") == "LDR_TEST_NAME" assert registry.get_env_var("unknown") is None def test_get_all_env_vars(self): """get_all_env_vars returns dict of env var to description.""" from local_deep_research.settings.env_settings import ( SettingsRegistry, BooleanSetting, ) registry = SettingsRegistry() registry.register_category( "test", [BooleanSetting(key="test.a", description="A desc")], ) env_vars = registry.get_all_env_vars() assert "LDR_TEST_A" in env_vars assert env_vars["LDR_TEST_A"] == "A desc" def test_get_category_settings(self): """get_category_settings returns settings in a category.""" from local_deep_research.settings.env_settings import ( SettingsRegistry, BooleanSetting, ) registry = SettingsRegistry() settings = [BooleanSetting(key="cat.x", description="x")] registry.register_category("mycat", settings) assert registry.get_category_settings("mycat") == settings assert registry.get_category_settings("unknown") == [] def test_get_setting_object(self): """get_setting_object returns the setting object.""" from local_deep_research.settings.env_settings import ( SettingsRegistry, StringSetting, ) registry = SettingsRegistry() setting = StringSetting(key="test.obj", description="desc") registry.register_category("test", [setting]) assert registry.get_setting_object("test.obj") is setting assert registry.get_setting_object("unknown") is None class TestBootstrapSettings: """Tests for bootstrap env_definitions.""" def test_bootstrap_settings_is_list(self): """BOOTSTRAP_SETTINGS is a list.""" from local_deep_research.settings.env_definitions.bootstrap import ( BOOTSTRAP_SETTINGS, ) assert isinstance(BOOTSTRAP_SETTINGS, list) def test_bootstrap_settings_non_empty(self): """BOOTSTRAP_SETTINGS is non-empty.""" from local_deep_research.settings.env_definitions.bootstrap import ( BOOTSTRAP_SETTINGS, ) assert len(BOOTSTRAP_SETTINGS) > 0 def test_all_have_key_attribute(self): """All settings have a key attribute.""" from local_deep_research.settings.env_definitions.bootstrap import ( BOOTSTRAP_SETTINGS, ) for setting in BOOTSTRAP_SETTINGS: assert hasattr(setting, "key") assert setting.key.startswith("bootstrap.") def test_all_have_env_var_attribute(self): """All settings have auto-generated env_var.""" from local_deep_research.settings.env_definitions.bootstrap import ( BOOTSTRAP_SETTINGS, ) for setting in BOOTSTRAP_SETTINGS: assert hasattr(setting, "env_var") assert setting.env_var.startswith("LDR_BOOTSTRAP_") class TestDbConfigSettings: """Tests for db_config env_definitions.""" def test_db_config_settings_is_list(self): """DB_CONFIG_SETTINGS is a list.""" from local_deep_research.settings.env_definitions.db_config import ( DB_CONFIG_SETTINGS, ) assert isinstance(DB_CONFIG_SETTINGS, list) def test_db_config_settings_non_empty(self): """DB_CONFIG_SETTINGS is non-empty.""" from local_deep_research.settings.env_definitions.db_config import ( DB_CONFIG_SETTINGS, ) assert len(DB_CONFIG_SETTINGS) > 0 def test_all_keys_start_with_db_config(self): """All settings keys start with db_config.""" from local_deep_research.settings.env_definitions.db_config import ( DB_CONFIG_SETTINGS, ) for setting in DB_CONFIG_SETTINGS: assert setting.key.startswith("db_config.") def test_journal_mode_enum_has_wal(self): """Journal mode setting includes WAL.""" from local_deep_research.settings.env_definitions.db_config import ( DB_CONFIG_SETTINGS, ) journal_setting = None for s in DB_CONFIG_SETTINGS: if s.key == "db_config.journal_mode": journal_setting = s break assert journal_setting is not None assert "WAL" in journal_setting.allowed_values def test_cache_size_has_bounds(self): """Cache size setting has min/max bounds.""" from local_deep_research.settings.env_definitions.db_config import ( DB_CONFIG_SETTINGS, ) cache_setting = None for s in DB_CONFIG_SETTINGS: if s.key == "db_config.cache_size_mb": cache_setting = s break assert cache_setting is not None assert cache_setting.min_value == 1 assert cache_setting.max_value == 10000 class TestTestingSettings: """Tests for testing env_definitions.""" def test_testing_settings_is_list(self): """TESTING_SETTINGS is a list.""" from local_deep_research.settings.env_definitions.testing import ( TESTING_SETTINGS, ) assert isinstance(TESTING_SETTINGS, list) def test_has_test_mode_setting(self): """Has test_mode setting.""" from local_deep_research.settings.env_definitions.testing import ( TESTING_SETTINGS, ) keys = [s.key for s in TESTING_SETTINGS] assert "testing.test_mode" in keys def test_ci_constant_is_bool(self): """CI constant is a boolean.""" from local_deep_research.settings.env_definitions.testing import CI assert isinstance(CI, bool) def test_github_actions_constant_is_bool(self): """GITHUB_ACTIONS constant is a boolean.""" from local_deep_research.settings.env_definitions.testing import ( GITHUB_ACTIONS, ) assert isinstance(GITHUB_ACTIONS, bool)