feat(v0.1.0): project foundation with logging and config

This commit is contained in:
0x_n3m0_
2026-01-05 11:06:46 +02:00
commit 090974259e
65 changed files with 718034 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
"""Tests for configuration loader."""
import os
from pathlib import Path
import pytest
from src.config.config_loader import get_config, load_config
from src.core.exceptions import ConfigurationError
def test_load_config_success(temp_config_dir, monkeypatch):
"""Test successful config loading."""
from src.core import constants
original_path = constants.PATHS["config"]
constants.PATHS["config"] = temp_config_dir
config = load_config()
assert config is not None
assert "app" in config
assert config["app"]["name"] == "Test App"
constants.PATHS["config"] = original_path
def test_load_config_missing_file(temp_dir, monkeypatch):
"""Test config loading with missing file."""
from src.core import constants
original_path = constants.PATHS["config"]
constants.PATHS["config"] = temp_dir / "nonexistent"
with pytest.raises(ConfigurationError):
load_config()
constants.PATHS["config"] = original_path
def test_get_config_with_key(temp_config_dir, monkeypatch):
"""Test getting config value by key."""
from src.core import constants
original_path = constants.PATHS["config"]
constants.PATHS["config"] = temp_config_dir
value = get_config("app.name")
assert value == "Test App"
constants.PATHS["config"] = original_path
def test_get_config_with_default(temp_config_dir, monkeypatch):
"""Test getting config with default value."""
from src.core import constants
original_path = constants.PATHS["config"]
constants.PATHS["config"] = temp_config_dir
value = get_config("nonexistent.key", default="default_value")
assert value == "default_value"
constants.PATHS["config"] = original_path
def test_get_config_none(temp_config_dir, monkeypatch):
"""Test getting entire config."""
from src.core import constants
original_path = constants.PATHS["config"]
constants.PATHS["config"] = temp_config_dir
config = get_config()
assert isinstance(config, dict)
assert "app" in config
constants.PATHS["config"] = original_path
def test_env_var_substitution(temp_config_dir, monkeypatch):
"""Test environment variable substitution in config."""
from src.core import constants
original_path = constants.PATHS["config"]
constants.PATHS["config"] = temp_config_dir
# Set environment variable
os.environ["TEST_VAR"] = "test_value"
# Create config with env var
config_file = temp_config_dir / "config.yaml"
config_file.write_text(
"""
app:
name: "${TEST_VAR}"
version: "0.1.0"
"""
)
# Reset config cache
import src.config.config_loader as config_module
config_module._config = None
config = load_config()
assert config["app"]["name"] == "test_value"
# Cleanup
del os.environ["TEST_VAR"]
constants.PATHS["config"] = original_path

View File

@@ -0,0 +1,90 @@
"""Tests for custom exception classes."""
import pytest
from src.core.exceptions import (
ConfigurationError,
DataError,
DetectorError,
ICTTradingException,
ModelError,
TradingError,
ValidationError,
)
def test_base_exception():
"""Test base exception class."""
exc = ICTTradingException("Test message")
assert str(exc) == "Test message"
assert exc.message == "Test message"
assert exc.error_code is None
assert exc.context == {}
def test_exception_with_error_code():
"""Test exception with error code."""
exc = ICTTradingException("Test message", error_code="TEST_ERROR")
assert str(exc) == "[TEST_ERROR] Test message"
assert exc.error_code == "TEST_ERROR"
def test_exception_with_context():
"""Test exception with context."""
context = {"key": "value", "number": 42}
exc = ICTTradingException("Test message", context=context)
assert exc.context == context
assert exc.to_dict()["context"] == context
def test_exception_to_dict():
"""Test exception to_dict method."""
exc = ICTTradingException("Test message", error_code="TEST", context={"key": "value"})
exc_dict = exc.to_dict()
assert exc_dict["error_type"] == "ICTTradingException"
assert exc_dict["error_code"] == "TEST"
assert exc_dict["message"] == "Test message"
assert exc_dict["context"] == {"key": "value"}
def test_data_error():
"""Test DataError exception."""
exc = DataError("Data loading failed")
assert isinstance(exc, ICTTradingException)
assert exc.error_code == "DATA_ERROR"
def test_detector_error():
"""Test DetectorError exception."""
exc = DetectorError("Detection failed")
assert isinstance(exc, ICTTradingException)
assert exc.error_code == "DETECTOR_ERROR"
def test_model_error():
"""Test ModelError exception."""
exc = ModelError("Model training failed")
assert isinstance(exc, ICTTradingException)
assert exc.error_code == "MODEL_ERROR"
def test_configuration_error():
"""Test ConfigurationError exception."""
exc = ConfigurationError("Invalid config")
assert isinstance(exc, ICTTradingException)
assert exc.error_code == "CONFIG_ERROR"
def test_trading_error():
"""Test TradingError exception."""
exc = TradingError("Trade execution failed")
assert isinstance(exc, ICTTradingException)
assert exc.error_code == "TRADING_ERROR"
def test_validation_error():
"""Test ValidationError exception."""
exc = ValidationError("Validation failed")
assert isinstance(exc, ICTTradingException)
assert exc.error_code == "VALIDATION_ERROR"

View File

@@ -0,0 +1,83 @@
"""Tests for logging system."""
import logging
from pathlib import Path
import pytest
from src.core.exceptions import ConfigurationError
from src.logging import get_logger
def test_get_logger_with_name():
"""Test getting logger with name."""
logger = get_logger("test_module")
assert isinstance(logger, logging.Logger)
assert logger.name == "test_module"
def test_get_logger_root():
"""Test getting root logger."""
logger = get_logger()
assert isinstance(logger, logging.Logger)
def test_logger_logs_message(caplog):
"""Test that logger actually logs messages."""
logger = get_logger("test")
logger.info("Test message")
assert "Test message" in caplog.text
def test_logger_with_missing_config(temp_dir, monkeypatch):
"""Test logger with missing config file."""
# Temporarily change config path to non-existent location
from src.core import constants
original_path = constants.PATHS["config"]
constants.PATHS["config"] = temp_dir / "nonexistent"
with pytest.raises(ConfigurationError):
get_logger("test")
# Restore original path
constants.PATHS["config"] = original_path
def test_logger_creates_directories(temp_dir, monkeypatch):
"""Test that logger creates log directories."""
from src.core import constants
original_path = constants.PATHS["logs"]
constants.PATHS["logs"] = temp_dir / "logs"
# Create minimal config
config_dir = temp_dir / "config"
config_dir.mkdir()
logging_config = config_dir / "logging.yaml"
logging_config.write_text(
"""
version: 1
disable_existing_loggers: false
formatters:
detailed:
format: '%(message)s'
handlers:
console:
class: logging.StreamHandler
level: INFO
formatter: detailed
root:
level: INFO
handlers:
- console
"""
)
logger = get_logger("test")
assert isinstance(logger, logging.Logger)
# Restore original path
constants.PATHS["logs"] = original_path