mirror of
https://github.com/Xe138/AI-Trader.git
synced 2026-04-16 06:47:24 -04:00
feat: add root-level config merging
Add merge_configs function that performs root-level merging of custom config into default config. Custom config sections completely replace default sections. Implementation does not mutate input dictionaries. Includes comprehensive tests for: - Empty custom config - Section override behavior - Adding new sections - Non-mutating behavior All 7 tests pass.
This commit is contained in:
@@ -2,7 +2,7 @@ import pytest
|
|||||||
import json
|
import json
|
||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tools.config_merger import load_config, ConfigValidationError
|
from tools.config_merger import load_config, ConfigValidationError, merge_configs
|
||||||
|
|
||||||
|
|
||||||
def test_load_config_valid_json():
|
def test_load_config_valid_json():
|
||||||
@@ -35,3 +35,44 @@ def test_load_config_invalid_json():
|
|||||||
load_config(temp_path)
|
load_config(temp_path)
|
||||||
finally:
|
finally:
|
||||||
Path(temp_path).unlink()
|
Path(temp_path).unlink()
|
||||||
|
|
||||||
|
|
||||||
|
def test_merge_configs_empty_custom():
|
||||||
|
"""Test merge with no custom config"""
|
||||||
|
default = {"a": 1, "b": 2}
|
||||||
|
custom = {}
|
||||||
|
result = merge_configs(default, custom)
|
||||||
|
assert result == {"a": 1, "b": 2}
|
||||||
|
|
||||||
|
|
||||||
|
def test_merge_configs_override_section():
|
||||||
|
"""Test custom config overrides entire sections"""
|
||||||
|
default = {
|
||||||
|
"models": [{"name": "default-model", "enabled": True}],
|
||||||
|
"agent_config": {"max_steps": 30}
|
||||||
|
}
|
||||||
|
custom = {
|
||||||
|
"models": [{"name": "custom-model", "enabled": False}]
|
||||||
|
}
|
||||||
|
result = merge_configs(default, custom)
|
||||||
|
|
||||||
|
assert result["models"] == [{"name": "custom-model", "enabled": False}]
|
||||||
|
assert result["agent_config"] == {"max_steps": 30}
|
||||||
|
|
||||||
|
|
||||||
|
def test_merge_configs_add_new_section():
|
||||||
|
"""Test custom config adds new sections"""
|
||||||
|
default = {"a": 1}
|
||||||
|
custom = {"b": 2}
|
||||||
|
result = merge_configs(default, custom)
|
||||||
|
assert result == {"a": 1, "b": 2}
|
||||||
|
|
||||||
|
|
||||||
|
def test_merge_configs_does_not_mutate_inputs():
|
||||||
|
"""Test merge doesn't modify original dicts"""
|
||||||
|
default = {"a": 1}
|
||||||
|
custom = {"a": 2}
|
||||||
|
result = merge_configs(default, custom)
|
||||||
|
|
||||||
|
assert default["a"] == 1 # Original unchanged
|
||||||
|
assert result["a"] == 2
|
||||||
|
|||||||
@@ -34,3 +34,25 @@ def load_config(path: str) -> Dict[str, Any]:
|
|||||||
return json.load(f)
|
return json.load(f)
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
raise ConfigValidationError(f"Invalid JSON in {path}: {e}")
|
raise ConfigValidationError(f"Invalid JSON in {path}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def merge_configs(default: Dict[str, Any], custom: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Merge custom config into default config (root-level override).
|
||||||
|
|
||||||
|
Custom config sections completely replace default sections.
|
||||||
|
Does not mutate input dictionaries.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
default: Default configuration dict
|
||||||
|
custom: Custom configuration dict (overrides)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Merged configuration dict
|
||||||
|
"""
|
||||||
|
merged = dict(default) # Shallow copy
|
||||||
|
|
||||||
|
for key, value in custom.items():
|
||||||
|
merged[key] = value
|
||||||
|
|
||||||
|
return merged
|
||||||
|
|||||||
Reference in New Issue
Block a user