293 lines
10 KiB
Python
293 lines
10 KiB
Python
"""Tests for core.logging_utils module."""
|
|
|
|
import pytest
|
|
from io import StringIO
|
|
import sys
|
|
|
|
from core.logging_utils import (
|
|
LOG_LEVEL_DEBUG,
|
|
LOG_LEVEL_MINIMAL,
|
|
LOG_LEVEL_TIMING,
|
|
create_fallback_logger,
|
|
get_log_level,
|
|
handle_exception_safely,
|
|
init_logging_from_config,
|
|
is_log_enabled,
|
|
log_error_safely,
|
|
log_with_exception_info,
|
|
set_global_log_level,
|
|
set_module_log_level,
|
|
get_module_logger,
|
|
)
|
|
|
|
|
|
class TestLogLevels:
|
|
"""Tests for log level constants and configuration."""
|
|
|
|
def test_log_level_constants(self):
|
|
"""Test that log level constants are defined correctly."""
|
|
assert LOG_LEVEL_MINIMAL == "minimal"
|
|
assert LOG_LEVEL_TIMING == "timing"
|
|
assert LOG_LEVEL_DEBUG == "debug"
|
|
|
|
def test_set_global_log_level(self):
|
|
"""Test setting global log level."""
|
|
set_global_log_level(LOG_LEVEL_DEBUG)
|
|
assert get_log_level() == LOG_LEVEL_DEBUG
|
|
|
|
set_global_log_level(LOG_LEVEL_TIMING)
|
|
assert get_log_level() == LOG_LEVEL_TIMING
|
|
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
assert get_log_level() == LOG_LEVEL_MINIMAL
|
|
|
|
def test_set_module_log_level(self):
|
|
"""Test setting module-specific log level."""
|
|
set_global_log_level(LOG_LEVEL_TIMING)
|
|
set_module_log_level("TestModule", LOG_LEVEL_DEBUG)
|
|
|
|
assert get_log_level("TestModule") == LOG_LEVEL_DEBUG
|
|
assert get_log_level("OtherModule") == LOG_LEVEL_TIMING
|
|
|
|
def test_module_level_overrides_global(self):
|
|
"""Test that module-specific level overrides global level."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
set_module_log_level("OverrideModule", LOG_LEVEL_DEBUG)
|
|
|
|
assert get_log_level("OverrideModule") == LOG_LEVEL_DEBUG
|
|
assert get_log_level("OtherModule") == LOG_LEVEL_MINIMAL
|
|
|
|
|
|
class TestIsLogEnabled:
|
|
"""Tests for is_log_enabled function."""
|
|
|
|
def test_debug_enables_all_levels(self):
|
|
"""Test that debug level enables all log levels."""
|
|
set_global_log_level(LOG_LEVEL_DEBUG)
|
|
assert is_log_enabled(LOG_LEVEL_DEBUG)
|
|
assert is_log_enabled(LOG_LEVEL_TIMING)
|
|
assert is_log_enabled(LOG_LEVEL_MINIMAL)
|
|
|
|
def test_timing_enables_timing_and_minimal(self):
|
|
"""Test that timing level enables timing and minimal levels."""
|
|
set_global_log_level(LOG_LEVEL_TIMING)
|
|
assert not is_log_enabled(LOG_LEVEL_DEBUG)
|
|
assert is_log_enabled(LOG_LEVEL_TIMING)
|
|
assert is_log_enabled(LOG_LEVEL_MINIMAL)
|
|
|
|
def test_minimal_only_enables_minimal(self):
|
|
"""Test that minimal level only enables minimal level."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
assert not is_log_enabled(LOG_LEVEL_DEBUG)
|
|
assert not is_log_enabled(LOG_LEVEL_TIMING)
|
|
assert is_log_enabled(LOG_LEVEL_MINIMAL)
|
|
|
|
def test_module_specific_level(self):
|
|
"""Test is_log_enabled with module-specific level."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
set_module_log_level("TestModule", LOG_LEVEL_DEBUG)
|
|
|
|
assert is_log_enabled(LOG_LEVEL_DEBUG, "TestModule")
|
|
assert not is_log_enabled(LOG_LEVEL_DEBUG, "OtherModule")
|
|
|
|
|
|
class TestGetModuleLogger:
|
|
"""Tests for get_module_logger function."""
|
|
|
|
def test_logger_creation(self):
|
|
"""Test that logger is created correctly."""
|
|
log = get_module_logger("TestModule")
|
|
assert callable(log)
|
|
|
|
def test_logger_respects_level(self, capsys):
|
|
"""Test that logger respects log level."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
log = get_module_logger("TestModule")
|
|
|
|
# Should not log timing message at minimal level
|
|
log("Timing message", LOG_LEVEL_TIMING)
|
|
captured = capsys.readouterr()
|
|
assert "Timing message" not in captured.out
|
|
|
|
# Should log minimal message
|
|
log("Minimal message", LOG_LEVEL_MINIMAL)
|
|
captured = capsys.readouterr()
|
|
assert "Minimal message" in captured.out
|
|
assert "[TestModule]" in captured.out
|
|
|
|
def test_logger_includes_timestamp_and_module(self, capsys):
|
|
"""Test that logger includes timestamp and module name."""
|
|
set_global_log_level(LOG_LEVEL_DEBUG)
|
|
log = get_module_logger("MyTestModule")
|
|
|
|
log("Test message", LOG_LEVEL_DEBUG)
|
|
captured = capsys.readouterr()
|
|
|
|
assert "[MyTestModule]" in captured.out
|
|
# Timestamp format: [HH:MM:SS]
|
|
assert "[" in captured.out
|
|
assert "]" in captured.out
|
|
|
|
def test_logger_does_not_raise_on_failure(self, capsys):
|
|
"""Test that logger does not raise exceptions even on failure."""
|
|
set_global_log_level(LOG_LEVEL_DEBUG)
|
|
log = get_module_logger("TestModule")
|
|
|
|
# Should not raise even if print fails internally
|
|
log("Test message", LOG_LEVEL_DEBUG)
|
|
# If we get here without exception, test passes
|
|
|
|
|
|
class TestLogErrorSafely:
|
|
"""Tests for log_error_safely function."""
|
|
|
|
def test_log_error_basic(self, capsys):
|
|
"""Test basic error logging."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
error = ValueError("Test error message")
|
|
|
|
log_error_safely("TestModule", error, "Test context")
|
|
captured = capsys.readouterr()
|
|
|
|
assert "TestModule" in captured.out
|
|
assert "ValueError" in captured.out
|
|
assert "Test error message" in captured.out
|
|
assert "Test context" in captured.out
|
|
|
|
def test_log_error_without_context(self, capsys):
|
|
"""Test error logging without context."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
error = RuntimeError("Error without context")
|
|
|
|
log_error_safely("TestModule", error)
|
|
captured = capsys.readouterr()
|
|
|
|
assert "TestModule" in captured.out
|
|
assert "RuntimeError" in captured.out
|
|
assert "Error without context" in captured.out
|
|
|
|
def test_log_error_respects_level(self, capsys):
|
|
"""Test that error logging respects log level."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
error = Exception("Should not be logged")
|
|
|
|
# Should not log at timing level when global is minimal
|
|
log_error_safely("TestModule", error, "Context", level=LOG_LEVEL_TIMING)
|
|
captured = capsys.readouterr()
|
|
assert "Should not be logged" not in captured.out
|
|
|
|
def test_log_error_does_not_raise(self):
|
|
"""Test that log_error_safely does not raise exceptions."""
|
|
set_global_log_level(LOG_LEVEL_DEBUG)
|
|
error = Exception("Test error")
|
|
|
|
# Should not raise even if internal logging fails
|
|
log_error_safely("TestModule", error, "Context")
|
|
|
|
|
|
class TestLogWithExceptionInfo:
|
|
"""Tests for log_with_exception_info function."""
|
|
|
|
def test_log_with_exc_info_tuple(self, capsys):
|
|
"""Test logging with exception info tuple."""
|
|
set_global_log_level(LOG_LEVEL_DEBUG)
|
|
|
|
try:
|
|
raise ValueError("Test exception")
|
|
except Exception:
|
|
log_with_exception_info("TestModule", "Test context")
|
|
captured = capsys.readouterr()
|
|
|
|
assert "TestModule" in captured.out
|
|
assert "ValueError" in captured.out
|
|
assert "Test exception" in captured.out
|
|
|
|
def test_log_with_exc_info_exception(self, capsys):
|
|
"""Test logging with exception instance."""
|
|
set_global_log_level(LOG_LEVEL_DEBUG)
|
|
error = TypeError("Type error test")
|
|
|
|
log_with_exception_info("TestModule", "Test context", error)
|
|
captured = capsys.readouterr()
|
|
|
|
assert "TypeError" in captured.out
|
|
assert "Type error test" in captured.out
|
|
|
|
def test_log_with_exc_info_respects_level(self, capsys):
|
|
"""Test that exception info logging respects log level."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
|
|
try:
|
|
raise RuntimeError("Should not be logged")
|
|
except Exception:
|
|
log_with_exception_info("TestModule", "Context", level=LOG_LEVEL_DEBUG)
|
|
captured = capsys.readouterr()
|
|
assert "Should not be logged" not in captured.out
|
|
|
|
|
|
class TestCreateFallbackLogger:
|
|
"""Tests for create_fallback_logger function."""
|
|
|
|
def test_fallback_logger_creation(self):
|
|
"""Test fallback logger creation."""
|
|
log_error = create_fallback_logger("TestModule")
|
|
assert callable(log_error)
|
|
|
|
def test_fallback_logger_usage(self, capsys):
|
|
"""Test fallback logger usage."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
log_error = create_fallback_logger("TestModule", LOG_LEVEL_MINIMAL)
|
|
|
|
error = Exception("Fallback test")
|
|
log_error(error, "Fallback context")
|
|
captured = capsys.readouterr()
|
|
|
|
assert "TestModule" in captured.out
|
|
assert "Fallback test" in captured.out
|
|
assert "Fallback context" in captured.out
|
|
|
|
|
|
class TestHandleExceptionSafely:
|
|
"""Tests for handle_exception_safely function."""
|
|
|
|
def test_handle_exception_returns_error(self, capsys):
|
|
"""Test that handle_exception_safely returns the error."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
error = ValueError("Handle test")
|
|
|
|
result = handle_exception_safely("TestModule", error, "test operation")
|
|
captured = capsys.readouterr()
|
|
|
|
assert result is error
|
|
assert "TestModule" in captured.out
|
|
|
|
def test_handle_exception_raises(self, capsys):
|
|
"""Test that handle_exception_safely raises when should_raise=True."""
|
|
set_global_log_level(LOG_LEVEL_MINIMAL)
|
|
error = ValueError("Handle raise test")
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
handle_exception_safely("TestModule", error, "test operation", should_raise=True)
|
|
|
|
assert exc_info.value is error
|
|
captured = capsys.readouterr()
|
|
assert "TestModule" in captured.out
|
|
|
|
|
|
class TestInitLoggingFromConfig:
|
|
"""Tests for init_logging_from_config function."""
|
|
|
|
def test_init_with_log_level(self):
|
|
"""Test initialization with log level."""
|
|
init_logging_from_config(LOG_LEVEL_DEBUG)
|
|
assert get_log_level() == LOG_LEVEL_DEBUG
|
|
|
|
init_logging_from_config(LOG_LEVEL_TIMING)
|
|
assert get_log_level() == LOG_LEVEL_TIMING
|
|
|
|
def test_init_with_none(self):
|
|
"""Test initialization with None does not change level."""
|
|
set_global_log_level(LOG_LEVEL_DEBUG)
|
|
init_logging_from_config(None)
|
|
assert get_log_level() == LOG_LEVEL_DEBUG
|