55from io import StringIO
66from unittest .mock import patch
77
8+ import pytest
89import structlog
910from structlog .typing import Processor
1011
@@ -26,6 +27,7 @@ def test_setup_logging_returns_qontract_api_logger() -> None:
2627 mock_settings .log_level = "INFO"
2728 mock_settings .log_format_json = True
2829 mock_settings .log_exclude_loggers = ""
30+ mock_settings .sentry_dsn = ""
2931
3032 logger = setup_logging ()
3133
@@ -41,6 +43,7 @@ def test_setup_logging_with_json_format() -> None:
4143 mock_settings .log_level = "INFO"
4244 mock_settings .log_format_json = True
4345 mock_settings .log_exclude_loggers = ""
46+ mock_settings .sentry_dsn = ""
4447
4548 # Clear existing handlers
4649 root_logger = logging .getLogger ()
@@ -61,6 +64,7 @@ def test_setup_logging_with_standard_format() -> None:
6164 mock_settings .log_level = "INFO"
6265 mock_settings .log_format_json = False
6366 mock_settings .log_exclude_loggers = ""
67+ mock_settings .sentry_dsn = ""
6468
6569 # Clear existing handlers
6670 root_logger = logging .getLogger ()
@@ -81,6 +85,7 @@ def test_logger_with_json_output() -> None:
8185 mock_settings .log_level = "INFO"
8286 mock_settings .log_format_json = True
8387 mock_settings .log_exclude_loggers = ""
88+ mock_settings .sentry_dsn = ""
8489
8590 # Setup logging with JSON format
8691 root_logger = logging .getLogger ()
@@ -141,6 +146,7 @@ def test_logger_with_context_vars() -> None:
141146 mock_settings .log_level = "INFO"
142147 mock_settings .log_format_json = True
143148 mock_settings .log_exclude_loggers = ""
149+ mock_settings .sentry_dsn = ""
144150
145151 # Setup logging with JSON format
146152 root_logger = logging .getLogger ()
@@ -200,6 +206,7 @@ def test_logger_with_extra_fields() -> None:
200206 mock_settings .log_level = "INFO"
201207 mock_settings .log_format_json = True
202208 mock_settings .log_exclude_loggers = ""
209+ mock_settings .sentry_dsn = ""
203210
204211 # Setup logging
205212 root_logger = logging .getLogger ()
@@ -267,6 +274,7 @@ def test_logger_with_exception() -> None:
267274 mock_settings .log_level = "INFO"
268275 mock_settings .log_format_json = True
269276 mock_settings .log_exclude_loggers = ""
277+ mock_settings .sentry_dsn = ""
270278
271279 # Setup logging
272280 root_logger = logging .getLogger ()
@@ -324,3 +332,123 @@ def test_logger_with_exception() -> None:
324332 assert exc_info ["exc_value" ] == "Test error"
325333
326334 root_logger .handlers .clear ()
335+
336+
337+ def test_sentry_init_called_when_dsn_provided () -> None :
338+ """Test that sentry_sdk.init is called when SENTRY_DSN is provided."""
339+ with (
340+ patch ("qontract_api.logger.settings" ) as mock_settings ,
341+ patch ("qontract_api.logger.sentry_sdk.init" ) as mock_sentry_init ,
342+ ):
343+ mock_settings .log_level = "INFO"
344+ mock_settings .log_format_json = True
345+ mock_settings .log_exclude_loggers = ""
346+ mock_settings .sentry_dsn = "https://example@sentry.io/123"
347+ mock_settings .sentry_event_level = "ERROR"
348+
349+ root_logger = logging .getLogger ()
350+ root_logger .handlers .clear ()
351+
352+ setup_logging ()
353+
354+ mock_sentry_init .assert_called_once ()
355+ call_args = mock_sentry_init .call_args
356+
357+ assert call_args .kwargs ["dsn" ] == "https://example@sentry.io/123"
358+ assert call_args .kwargs ["send_default_pii" ] is True
359+ assert call_args .kwargs ["enable_logs" ] is True
360+ assert call_args .kwargs ["ignore_errors" ] == [ConnectionError ]
361+
362+ root_logger .handlers .clear ()
363+
364+
365+ def test_sentry_init_not_called_when_dsn_empty () -> None :
366+ """Test that sentry_sdk.init is not called when SENTRY_DSN is empty."""
367+ with (
368+ patch ("qontract_api.logger.settings" ) as mock_settings ,
369+ patch ("qontract_api.logger.sentry_sdk.init" ) as mock_sentry_init ,
370+ ):
371+ mock_settings .log_level = "INFO"
372+ mock_settings .log_format_json = True
373+ mock_settings .log_exclude_loggers = ""
374+ mock_settings .sentry_dsn = ""
375+
376+ root_logger = logging .getLogger ()
377+ root_logger .handlers .clear ()
378+
379+ setup_logging ()
380+
381+ mock_sentry_init .assert_not_called ()
382+
383+ root_logger .handlers .clear ()
384+
385+
386+ def test_sentry_init_with_error_level () -> None :
387+ """Test sentry_sdk.init uses ERROR event level correctly."""
388+ with (
389+ patch ("qontract_api.logger.settings" ) as mock_settings ,
390+ patch ("qontract_api.logger.sentry_sdk.init" ) as mock_sentry_init ,
391+ patch ("qontract_api.logger.LoggingIntegration" ) as mock_logging_integration ,
392+ ):
393+ mock_settings .log_level = "INFO"
394+ mock_settings .log_format_json = True
395+ mock_settings .log_exclude_loggers = ""
396+ mock_settings .sentry_dsn = "https://example@sentry.io/123"
397+ mock_settings .sentry_event_level = "ERROR"
398+
399+ root_logger = logging .getLogger ()
400+ root_logger .handlers .clear ()
401+
402+ setup_logging ()
403+
404+ mock_sentry_init .assert_called_once ()
405+ mock_logging_integration .assert_called_once_with (event_level = logging .ERROR )
406+
407+ root_logger .handlers .clear ()
408+
409+
410+ def test_sentry_init_with_critical_level () -> None :
411+ """Test sentry_sdk.init uses CRITICAL event level correctly."""
412+ with (
413+ patch ("qontract_api.logger.settings" ) as mock_settings ,
414+ patch ("qontract_api.logger.sentry_sdk.init" ) as mock_sentry_init ,
415+ patch ("qontract_api.logger.LoggingIntegration" ) as mock_logging_integration ,
416+ ):
417+ mock_settings .log_level = "INFO"
418+ mock_settings .log_format_json = True
419+ mock_settings .log_exclude_loggers = ""
420+ mock_settings .sentry_dsn = "https://example@sentry.io/123"
421+ mock_settings .sentry_event_level = "CRITICAL"
422+
423+ root_logger = logging .getLogger ()
424+ root_logger .handlers .clear ()
425+
426+ setup_logging ()
427+
428+ mock_sentry_init .assert_called_once ()
429+ mock_logging_integration .assert_called_once_with (event_level = logging .CRITICAL )
430+
431+ root_logger .handlers .clear ()
432+
433+
434+ def test_sentry_init_with_invalid_level_raises_error () -> None :
435+ """Test that invalid SENTRY_EVENT_LEVEL raises NotImplementedError."""
436+ with (
437+ patch ("qontract_api.logger.settings" ) as mock_settings ,
438+ patch ("qontract_api.logger.sentry_sdk.init" ),
439+ ):
440+ mock_settings .log_level = "INFO"
441+ mock_settings .log_format_json = True
442+ mock_settings .log_exclude_loggers = ""
443+ mock_settings .sentry_dsn = "https://example@sentry.io/123"
444+ mock_settings .sentry_event_level = "WARNING"
445+
446+ root_logger = logging .getLogger ()
447+ root_logger .handlers .clear ()
448+
449+ with pytest .raises (
450+ NotImplementedError , match = "Unsupported sentry_event_level: WARNING"
451+ ):
452+ setup_logging ()
453+
454+ root_logger .handlers .clear ()
0 commit comments