|
| 1 | +from pathlib import Path |
| 2 | +from unittest.mock import patch |
| 3 | + |
| 4 | +import pytest |
| 5 | +import tomllib |
| 6 | + |
| 7 | +from codegen.shared.configs.config import ( |
| 8 | + Config, |
| 9 | + _load_from_env, |
| 10 | + _load_from_toml, |
| 11 | + _merge_configs, |
| 12 | + load, |
| 13 | +) |
| 14 | +from codegen.shared.configs.models import CodebaseFeatureFlags, FeatureFlagsConfig, SecretsConfig |
| 15 | + |
| 16 | + |
| 17 | +# Test _merge_configs |
| 18 | +def test_merge_configs_basic(): |
| 19 | + base = {"a": 1, "b": 2} |
| 20 | + override = {"b": 3, "c": 4} |
| 21 | + result = _merge_configs(base, override) |
| 22 | + assert result == {"a": 1, "b": 3, "c": 4} |
| 23 | + |
| 24 | + |
| 25 | +def test_merge_configs_nested(): |
| 26 | + base = {"feature_flags": {"codebase": {"debug": False, "typescript": {"ts_dependency_manager": False}}}} |
| 27 | + override = {"feature_flags": {"codebase": {"debug": True, "typescript": {"ts_language_engine": True}}}} |
| 28 | + result = _merge_configs(base, override) |
| 29 | + assert result == {"feature_flags": {"codebase": {"debug": True, "typescript": {"ts_dependency_manager": False, "ts_language_engine": True}}}} |
| 30 | + |
| 31 | + |
| 32 | +def test_merge_configs_none_values(): |
| 33 | + base = {"secrets": {"github_token": "token1"}} |
| 34 | + override = {"secrets": {"github_token": None}} |
| 35 | + result = _merge_configs(base, override) |
| 36 | + assert result == {"secrets": {"github_token": "token1"}} |
| 37 | + |
| 38 | + |
| 39 | +def test_merge_configs_empty_string(): |
| 40 | + base = {"repository": {"organization_name": "org1"}} |
| 41 | + override = {"repository": {"organization_name": ""}} |
| 42 | + result = _merge_configs(base, override) |
| 43 | + assert result == {"repository": {"organization_name": "org1"}} |
| 44 | + |
| 45 | + |
| 46 | +# Test _load_from_toml |
| 47 | +def test_load_from_toml_existing_file(temp_config_file): |
| 48 | + config = _load_from_toml(temp_config_file) |
| 49 | + assert isinstance(config, Config) |
| 50 | + assert config.secrets.github_token == "gh_token123" |
| 51 | + assert config.repository.organization_name == "test-org" |
| 52 | + assert config.feature_flags.codebase.debug is True |
| 53 | + assert config.feature_flags.codebase.typescript.ts_dependency_manager is True |
| 54 | + assert config.feature_flags.codebase.import_resolution_overrides == {"@org/pkg": "./local/path"} |
| 55 | + |
| 56 | + |
| 57 | +@patch("codegen.shared.configs.models.SecretsConfig.model_config", {"env_file": "nonexistent.env"}) |
| 58 | +def test_load_from_toml_nonexistent_file(): |
| 59 | + config = _load_from_toml(Path("nonexistent.toml")) |
| 60 | + assert isinstance(config, Config) |
| 61 | + assert config.secrets.github_token is None |
| 62 | + assert config.repository.organization_name is None |
| 63 | + assert config.feature_flags.codebase.debug is None |
| 64 | + |
| 65 | + |
| 66 | +# Test _load_from_env |
| 67 | +@patch.dict("os.environ", {"CODEGEN_SECRETS__GITHUB_TOKEN": "env_token", "CODEGEN_SECRETS__OPENAI_API_KEY": "env_key"}) |
| 68 | +def test_load_from_env(): |
| 69 | + config = _load_from_env() |
| 70 | + assert isinstance(config, Config) |
| 71 | + assert config.secrets.github_token == "env_token" |
| 72 | + assert config.secrets.openai_api_key == "env_key" |
| 73 | + |
| 74 | + |
| 75 | +# Test load function |
| 76 | +@patch.dict("os.environ", {}, clear=True) # Clear all env vars for this test |
| 77 | +@patch("codegen.shared.configs.config._load_from_env") |
| 78 | +@patch("codegen.shared.configs.config._load_from_toml") |
| 79 | +@patch("codegen.shared.configs.models.SecretsConfig.model_config", {"env_file": None, "env_prefix": "CODEGEN_SECRETS__"}) |
| 80 | +def test_load_with_both_configs(mock_toml, mock_env): |
| 81 | + # Setup mock returns |
| 82 | + mock_env.return_value = Config(secrets=SecretsConfig(github_token="env_token"), feature_flags=FeatureFlagsConfig(codebase=CodebaseFeatureFlags(debug=True))) |
| 83 | + mock_toml.return_value = Config(secrets={"openai_api_key": "openai_key"}, repository={"organization_name": "codegen-org"}) |
| 84 | + |
| 85 | + config = load() |
| 86 | + |
| 87 | + assert isinstance(config, Config) |
| 88 | + assert config.secrets.github_token == "env_token" |
| 89 | + assert config.secrets.openai_api_key == "openai_key" |
| 90 | + assert config.repository.organization_name == "codegen-org" |
| 91 | + assert config.feature_flags.codebase.debug is True |
| 92 | + |
| 93 | + |
| 94 | +@patch("codegen.shared.configs.config._load_from_env") |
| 95 | +@patch("codegen.shared.configs.config._load_from_toml") |
| 96 | +def test_load_with_custom_path(mock_toml, mock_env): |
| 97 | + custom_path = Path("custom/config.toml") |
| 98 | + load(config_path=custom_path) |
| 99 | + |
| 100 | + mock_toml.assert_called_once_with(custom_path) |
| 101 | + mock_env.assert_called_once() |
| 102 | + |
| 103 | + |
| 104 | +# Error cases |
| 105 | +def test_load_from_toml_invalid_file(invalid_toml_file): |
| 106 | + with pytest.raises(tomllib.TOMLDecodeError): |
| 107 | + _load_from_toml(invalid_toml_file) |
0 commit comments