Skip to content

Commit 3587adc

Browse files
Add tests
1 parent e290e68 commit 3587adc

2 files changed

Lines changed: 238 additions & 0 deletions

File tree

tests/filterTest.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import logging
2+
import unittest
3+
from importlib.metadata import PackageNotFoundError
4+
from unittest import TestCase
5+
from unittest.mock import patch
6+
7+
from context_logger.filter import ContextSetupFilter
8+
9+
10+
class FilterTest(TestCase):
11+
12+
def test_filter_enriches_string_message_and_context(self):
13+
# Given
14+
filter_ = ContextSetupFilter('example-app', 'message',
15+
global_context={'team': 'platform', 'application': 'ignored'})
16+
record = logging.LogRecord('ExampleClass', logging.INFO, __file__, 10, 'Hello %s', ('World',), None)
17+
18+
with patch('context_logger.filter.socket.gethostname', return_value='test-host'), \
19+
patch.object(filter_, '_get_application_version', return_value='1.2.3'):
20+
# When
21+
result = filter_.filter(record)
22+
23+
# Then
24+
self.assertTrue(result)
25+
self.assertEqual((), record.args)
26+
self.assertEqual('Hello World', record.msg.get('message'))
27+
self.assertEqual('test-host', record.msg.get('hostname'))
28+
self.assertEqual('example-app', record.msg.get('application'))
29+
self.assertEqual('1.2.3', record.msg.get('app_version'))
30+
self.assertEqual('platform', record.msg.get('team'))
31+
32+
def test_filter_updates_process_name_when_key_exists(self):
33+
# Given
34+
filter_ = ContextSetupFilter('example-app', 'message')
35+
record = logging.LogRecord('ExampleClass', logging.INFO, __file__, 10, {'message': 'ok', 'process_name': 'x'},
36+
(), None)
37+
38+
with patch('context_logger.filter.socket.gethostname', return_value='test-host'), \
39+
patch.object(filter_, '_get_application_version', return_value='1.2.3'):
40+
# When
41+
filter_.filter(record)
42+
43+
# Then
44+
self.assertEqual(record.processName, record.msg.get('process_name'))
45+
46+
def test_filter_handles_string_format_error(self):
47+
# Given
48+
filter_ = ContextSetupFilter('example-app', 'message')
49+
record = logging.LogRecord('ExampleClass', logging.INFO, __file__, 10, 'broken %s %s', ('format',), None)
50+
51+
with patch('builtins.print') as print_mock:
52+
# When
53+
result = filter_.filter(record)
54+
55+
# Then
56+
self.assertTrue(result)
57+
self.assertEqual('broken %s %s', record.msg)
58+
print_mock.assert_called_once()
59+
self.assertEqual('Failed to handle log record:', print_mock.call_args.args[0])
60+
61+
def test_filter_handles_non_mapping_message(self):
62+
# Given
63+
filter_ = ContextSetupFilter('example-app', 'message')
64+
record = logging.LogRecord('ExampleClass', logging.INFO, __file__, 10, 123, (), None)
65+
66+
with patch('builtins.print') as print_mock:
67+
# When
68+
result = filter_.filter(record)
69+
70+
# Then
71+
self.assertTrue(result)
72+
self.assertEqual(123, record.msg)
73+
print_mock.assert_called_once()
74+
self.assertEqual('Failed to handle log record:', print_mock.call_args.args[0])
75+
76+
def test_get_application_version_returns_package_version(self):
77+
# Given
78+
filter_ = ContextSetupFilter('example-app', 'message')
79+
80+
with patch('context_logger.filter.version', return_value='9.9.9'):
81+
# When
82+
version = filter_._get_application_version()
83+
84+
# Then
85+
self.assertEqual('9.9.9', version)
86+
87+
def test_get_application_version_returns_none_when_package_is_missing(self):
88+
# Given
89+
filter_ = ContextSetupFilter('missing-package', 'message')
90+
91+
with patch('context_logger.filter.version', side_effect=PackageNotFoundError):
92+
# When
93+
version = filter_._get_application_version()
94+
95+
# Then
96+
self.assertEqual('none', version)
97+
98+
99+
if __name__ == '__main__':
100+
unittest.main()

tests/loggerFallbackTest.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import logging
2+
import unittest
3+
from unittest import TestCase
4+
from unittest.mock import MagicMock, patch
5+
6+
from context_logger.logger import Logger, setup_logging
7+
8+
9+
class _LegacyProcessorFormatter:
10+
def __init__(self, *args, **kwargs):
11+
self.args = args
12+
self.kwargs = kwargs
13+
14+
# Simulate old API: no `processors` support.
15+
if 'processors' in kwargs:
16+
raise AttributeError('processors is not supported')
17+
18+
19+
class LoggerFallbackTest(TestCase):
20+
21+
def test_setup_logging_warns_and_cleans_up_on_overwrite(self):
22+
# Given
23+
previous_logger = MagicMock()
24+
replacement_logger = MagicMock()
25+
26+
with patch('context_logger.logger.LOGGER', previous_logger), \
27+
patch('context_logger.logger.Logger', return_value=replacement_logger) as logger_ctor, \
28+
patch('context_logger.logger.warnings.warn') as warn_mock:
29+
# When
30+
setup_logging('example-app')
31+
32+
# Then
33+
warn_mock.assert_called_once()
34+
previous_logger.cleanup.assert_called_once()
35+
logger_ctor.assert_called_once()
36+
replacement_logger.setup.assert_called_once()
37+
38+
def test_setup_logging_skips_warning_when_warn_on_overwrite_is_false(self):
39+
# Given
40+
previous_logger = MagicMock()
41+
replacement_logger = MagicMock()
42+
43+
with patch('context_logger.logger.LOGGER', previous_logger), \
44+
patch('context_logger.logger.Logger', return_value=replacement_logger), \
45+
patch('context_logger.logger.warnings.warn') as warn_mock:
46+
# When
47+
setup_logging('example-app', warn_on_overwrite=False)
48+
49+
# Then
50+
warn_mock.assert_not_called()
51+
previous_logger.cleanup.assert_called_once()
52+
replacement_logger.setup.assert_called_once()
53+
54+
def test_setup_processors_ignores_missing_event_renamer(self):
55+
# Given
56+
logger = Logger('example-app', 'INFO', None, 1024, 1, False, 'message')
57+
58+
with patch('context_logger.logger.structlog.processors.EventRenamer', side_effect=AttributeError):
59+
# When
60+
logger._setup_processors()
61+
62+
# Then
63+
self.assertIsInstance(logger._shared_processors, list)
64+
self.assertGreater(len(logger._shared_processors), 0)
65+
66+
def test_setup_processors_ignores_missing_callsite_parameter_adder(self):
67+
# Given
68+
logger = Logger('example-app', 'INFO', None, 1024, 1, True, 'message')
69+
70+
with patch('context_logger.logger.structlog.processors.CallsiteParameterAdder', side_effect=AttributeError):
71+
# When
72+
logger._setup_processors()
73+
74+
# Then
75+
self.assertIsInstance(logger._shared_processors, list)
76+
self.assertGreater(len(logger._shared_processors), 0)
77+
78+
def test_create_console_handler_uses_legacy_formatter_fallback(self):
79+
# Given
80+
logger = Logger('example-app', 'INFO', None, 1024, 1, False, 'message')
81+
logger._shared_processors = []
82+
83+
with patch('context_logger.logger.ProcessorFormatter', _LegacyProcessorFormatter):
84+
# When
85+
handler = logger._create_console_handler()
86+
87+
# Then
88+
self.assertIsInstance(handler, logging.Handler)
89+
self.assertIn('processor', handler.formatter.kwargs)
90+
91+
def test_create_file_handler_uses_legacy_formatter_fallback(self):
92+
# Given
93+
logger = Logger('example-app', 'INFO', '/tmp/unused.log', 1024, 1, False, 'message')
94+
logger._shared_processors = []
95+
96+
fake_handler = MagicMock()
97+
fake_handler.setFormatter = MagicMock()
98+
fake_handler.addFilter = MagicMock()
99+
100+
with patch('context_logger.logger.ProcessorFormatter', _LegacyProcessorFormatter), \
101+
patch.object(Logger, '_ensure_directory_exists') as ensure_directory_mock, \
102+
patch('context_logger.logger.RotatingFileHandler', return_value=fake_handler):
103+
# When
104+
handler = logger._create_file_handler('/tmp/test.log')
105+
106+
# Then
107+
self.assertIs(handler, fake_handler)
108+
ensure_directory_mock.assert_called_once_with('/tmp/test.log')
109+
fake_handler.setFormatter.assert_called_once()
110+
fake_handler.addFilter.assert_called_once()
111+
112+
def test_ensure_directory_exists_creates_directory_when_missing(self):
113+
# Given
114+
logger = Logger('example-app', 'INFO', None, 1024, 1, False, 'message')
115+
116+
with patch('context_logger.logger.os.path.exists', return_value=False), \
117+
patch('context_logger.logger.os.makedirs') as makedirs_mock:
118+
# When
119+
logger._ensure_directory_exists('/tmp/new-dir/test.log')
120+
121+
# Then
122+
makedirs_mock.assert_called_once_with('/tmp/new-dir')
123+
124+
def test_ensure_directory_exists_does_not_create_existing_directory(self):
125+
# Given
126+
logger = Logger('example-app', 'INFO', None, 1024, 1, False, 'message')
127+
128+
with patch('context_logger.logger.os.path.exists', return_value=True), \
129+
patch('context_logger.logger.os.makedirs') as makedirs_mock:
130+
# When
131+
logger._ensure_directory_exists('/tmp/existing-dir/test.log')
132+
133+
# Then
134+
makedirs_mock.assert_not_called()
135+
136+
137+
if __name__ == '__main__':
138+
unittest.main()

0 commit comments

Comments
 (0)