250 lines
8.9 KiB
Python
250 lines
8.9 KiB
Python
"""Unit tests for the logging module."""
|
|
import unittest
|
|
import tempfile
|
|
import os
|
|
import logging
|
|
from pathlib import Path
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
# Add the src directory to the path
|
|
import sys
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / 'src'))
|
|
|
|
from forensictrails.utils.logging import setup_logging
|
|
|
|
|
|
class TestSetupLogging(unittest.TestCase):
|
|
"""Test cases for setup_logging function."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures."""
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
self.test_log_path = os.path.join(self.temp_dir, 'test.log')
|
|
|
|
# Clear any existing handlers
|
|
logger = logging.getLogger()
|
|
for handler in logger.handlers[:]:
|
|
logger.removeHandler(handler)
|
|
|
|
def tearDown(self):
|
|
"""Clean up test fixtures."""
|
|
# Clear handlers after test
|
|
logger = logging.getLogger()
|
|
for handler in logger.handlers[:]:
|
|
handler.close()
|
|
logger.removeHandler(handler)
|
|
|
|
if os.path.exists(self.test_log_path):
|
|
os.remove(self.test_log_path)
|
|
|
|
# Clean up any nested directories
|
|
if os.path.exists(self.temp_dir):
|
|
for root, dirs, files in os.walk(self.temp_dir, topdown=False):
|
|
for name in files:
|
|
os.remove(os.path.join(root, name))
|
|
for name in dirs:
|
|
os.rmdir(os.path.join(root, name))
|
|
os.rmdir(self.temp_dir)
|
|
|
|
@patch('forensictrails.utils.logging.config')
|
|
def test_setup_logging_creates_log_file(self, mock_config):
|
|
"""Test that setup_logging creates the log file."""
|
|
mock_config.log_path = self.test_log_path
|
|
mock_config.log_level = 'DEBUG'
|
|
|
|
self.assertFalse(os.path.exists(self.test_log_path))
|
|
|
|
setup_logging(self.test_log_path)
|
|
|
|
self.assertTrue(os.path.exists(self.test_log_path))
|
|
|
|
@patch('forensictrails.utils.logging.config')
|
|
def test_setup_logging_creates_nested_directories(self, mock_config):
|
|
"""Test that setup_logging creates nested directories."""
|
|
nested_log_path = os.path.join(self.temp_dir, 'logs', 'nested', 'test.log')
|
|
mock_config.log_path = nested_log_path
|
|
mock_config.log_level = 'INFO'
|
|
|
|
self.assertFalse(os.path.exists(nested_log_path))
|
|
|
|
setup_logging(nested_log_path)
|
|
|
|
self.assertTrue(os.path.exists(nested_log_path))
|
|
self.assertTrue(os.path.isfile(nested_log_path))
|
|
|
|
@patch('forensictrails.utils.logging.config')
|
|
def test_setup_logging_adds_handlers(self, mock_config):
|
|
"""Test that setup_logging adds file and stream handlers."""
|
|
mock_config.log_path = self.test_log_path
|
|
mock_config.log_level = 'DEBUG'
|
|
|
|
logger = logging.getLogger()
|
|
initial_handler_count = len(logger.handlers)
|
|
|
|
setup_logging(self.test_log_path)
|
|
|
|
# Should add 2 handlers: FileHandler and StreamHandler
|
|
self.assertEqual(len(logger.handlers), initial_handler_count + 2)
|
|
|
|
# Check handler types
|
|
handler_types = [type(h).__name__ for h in logger.handlers]
|
|
self.assertIn('FileHandler', handler_types)
|
|
self.assertIn('StreamHandler', handler_types)
|
|
|
|
@patch('forensictrails.utils.logging.config')
|
|
def test_setup_logging_sets_correct_log_level(self, mock_config):
|
|
"""Test that setup_logging sets the correct log level."""
|
|
mock_config.log_path = self.test_log_path
|
|
mock_config.log_level = 'WARNING'
|
|
|
|
setup_logging(self.test_log_path)
|
|
|
|
logger = logging.getLogger()
|
|
self.assertEqual(logger.level, logging.WARNING)
|
|
|
|
@patch('forensictrails.utils.logging.config')
|
|
def test_setup_logging_logs_messages(self, mock_config):
|
|
"""Test that setup_logging enables logging messages."""
|
|
mock_config.log_path = self.test_log_path
|
|
mock_config.log_level = 'DEBUG'
|
|
|
|
setup_logging(self.test_log_path)
|
|
|
|
# Log a test message
|
|
test_message = "Test logging message"
|
|
logging.info(test_message)
|
|
|
|
# Force flush
|
|
for handler in logging.getLogger().handlers:
|
|
handler.flush()
|
|
|
|
# Check that message was written to file
|
|
with open(self.test_log_path, 'r') as f:
|
|
log_content = f.read()
|
|
|
|
self.assertIn(test_message, log_content)
|
|
self.assertIn('INFO', log_content)
|
|
|
|
@patch('forensictrails.utils.logging.config')
|
|
def test_setup_logging_formatter(self, mock_config):
|
|
"""Test that setup_logging uses correct formatter."""
|
|
mock_config.log_path = self.test_log_path
|
|
mock_config.log_level = 'DEBUG'
|
|
|
|
setup_logging(self.test_log_path)
|
|
|
|
# Log a message
|
|
logging.info("Formatter test")
|
|
|
|
# Force flush
|
|
for handler in logging.getLogger().handlers:
|
|
handler.flush()
|
|
|
|
# Check log format
|
|
with open(self.test_log_path, 'r') as f:
|
|
log_content = f.read()
|
|
|
|
# Should contain timestamp, level, and message
|
|
self.assertIn('INFO', log_content)
|
|
self.assertIn('Formatter test', log_content)
|
|
# Check for timestamp pattern (YYYY-MM-DD HH:MM:SS)
|
|
import re
|
|
timestamp_pattern = r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'
|
|
self.assertTrue(re.search(timestamp_pattern, log_content))
|
|
|
|
@patch('forensictrails.utils.logging.config')
|
|
def test_setup_logging_uses_config_when_no_path_provided(self, mock_config):
|
|
"""Test that setup_logging uses config.log_path when no path is provided."""
|
|
mock_config.log_path = self.test_log_path
|
|
mock_config.log_level = 'INFO'
|
|
|
|
setup_logging() # No path provided
|
|
|
|
# Should create file at config.log_path
|
|
self.assertTrue(os.path.exists(self.test_log_path))
|
|
|
|
@patch('forensictrails.utils.logging.config')
|
|
def test_setup_logging_different_log_levels(self, mock_config):
|
|
"""Test setup_logging with different log levels."""
|
|
mock_config.log_path = self.test_log_path
|
|
|
|
for level_name, level_value in [
|
|
('DEBUG', logging.DEBUG),
|
|
('INFO', logging.INFO),
|
|
('WARNING', logging.WARNING),
|
|
('ERROR', logging.ERROR),
|
|
('CRITICAL', logging.CRITICAL)
|
|
]:
|
|
with self.subTest(level=level_name):
|
|
# Clear handlers
|
|
logger = logging.getLogger()
|
|
for handler in logger.handlers[:]:
|
|
handler.close()
|
|
logger.removeHandler(handler)
|
|
|
|
mock_config.log_level = level_name
|
|
setup_logging(self.test_log_path)
|
|
|
|
self.assertEqual(logger.level, level_value)
|
|
|
|
|
|
class TestLoggingIntegration(unittest.TestCase):
|
|
"""Integration tests for logging functionality."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures."""
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
self.test_log_path = os.path.join(self.temp_dir, 'integration.log')
|
|
|
|
# Clear any existing handlers
|
|
logger = logging.getLogger()
|
|
for handler in logger.handlers[:]:
|
|
logger.removeHandler(handler)
|
|
|
|
def tearDown(self):
|
|
"""Clean up test fixtures."""
|
|
# Clear handlers after test
|
|
logger = logging.getLogger()
|
|
for handler in logger.handlers[:]:
|
|
handler.close()
|
|
logger.removeHandler(handler)
|
|
|
|
if os.path.exists(self.test_log_path):
|
|
os.remove(self.test_log_path)
|
|
os.rmdir(self.temp_dir)
|
|
|
|
@patch('forensictrails.utils.logging.config')
|
|
def test_logging_multiple_messages(self, mock_config):
|
|
"""Test logging multiple messages of different levels."""
|
|
mock_config.log_path = self.test_log_path
|
|
mock_config.log_level = 'DEBUG'
|
|
|
|
setup_logging(self.test_log_path)
|
|
|
|
# Log messages at different levels
|
|
logging.debug("Debug message")
|
|
logging.info("Info message")
|
|
logging.warning("Warning message")
|
|
logging.error("Error message")
|
|
|
|
# Force flush
|
|
for handler in logging.getLogger().handlers:
|
|
handler.flush()
|
|
|
|
# Verify all messages are in the log
|
|
with open(self.test_log_path, 'r') as f:
|
|
log_content = f.read()
|
|
|
|
self.assertIn("Debug message", log_content)
|
|
self.assertIn("Info message", log_content)
|
|
self.assertIn("Warning message", log_content)
|
|
self.assertIn("Error message", log_content)
|
|
self.assertIn("DEBUG", log_content)
|
|
self.assertIn("INFO", log_content)
|
|
self.assertIn("WARNING", log_content)
|
|
self.assertIn("ERROR", log_content)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|