From 03f81b3b5cc3c7d546073026b40084cc129114d5 Mon Sep 17 00:00:00 2001 From: Bill Date: Sat, 1 Nov 2025 16:55:40 -0400 Subject: [PATCH] feat: add config file loading with error handling Implement load_config() function with comprehensive error handling - Loads and parses JSON config files - Raises ConfigValidationError for missing files - Raises ConfigValidationError for malformed JSON - Includes 3 passing tests for all error cases Test coverage: - test_load_config_valid_json: Verifies successful JSON parsing - test_load_config_file_not_found: Validates error on missing file - test_load_config_invalid_json: Validates error on malformed JSON --- tests/unit/test_config_merger.py | 37 ++++++++++++++++++++++++++++++++ tools/config_merger.py | 36 +++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/unit/test_config_merger.py create mode 100644 tools/config_merger.py diff --git a/tests/unit/test_config_merger.py b/tests/unit/test_config_merger.py new file mode 100644 index 0000000..9ea7ace --- /dev/null +++ b/tests/unit/test_config_merger.py @@ -0,0 +1,37 @@ +import pytest +import json +import tempfile +from pathlib import Path +from tools.config_merger import load_config, ConfigValidationError + + +def test_load_config_valid_json(): + """Test loading a valid JSON config file""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f: + json.dump({"key": "value"}, f) + temp_path = f.name + + try: + result = load_config(temp_path) + assert result == {"key": "value"} + finally: + Path(temp_path).unlink() + + +def test_load_config_file_not_found(): + """Test loading non-existent config file""" + with pytest.raises(ConfigValidationError, match="not found"): + load_config("/nonexistent/path.json") + + +def test_load_config_invalid_json(): + """Test loading malformed JSON""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f: + f.write("{invalid json") + temp_path = f.name + + try: + with pytest.raises(ConfigValidationError, match="Invalid JSON"): + load_config(temp_path) + finally: + Path(temp_path).unlink() diff --git a/tools/config_merger.py b/tools/config_merger.py new file mode 100644 index 0000000..b79569c --- /dev/null +++ b/tools/config_merger.py @@ -0,0 +1,36 @@ +"""Configuration merging and validation for AI-Trader.""" + +import json +import sys +from pathlib import Path +from typing import Dict, Any, Optional + + +class ConfigValidationError(Exception): + """Raised when config validation fails.""" + pass + + +def load_config(path: str) -> Dict[str, Any]: + """ + Load and parse JSON config file. + + Args: + path: Path to JSON config file + + Returns: + Parsed config dictionary + + Raises: + ConfigValidationError: If file not found or invalid JSON + """ + config_path = Path(path) + + if not config_path.exists(): + raise ConfigValidationError(f"Config file not found: {path}") + + try: + with open(config_path, 'r') as f: + return json.load(f) + except json.JSONDecodeError as e: + raise ConfigValidationError(f"Invalid JSON in {path}: {e}")