1
0
mirror of https://github.com/home-assistant/supervisor.git synced 2026-05-18 05:38:53 +01:00
Files
supervisor/tests/utils/test_sentry.py
T
Stefan Agner 07f02a23be Prevent Sentry event feedback loop from warning handler (#6700)
The custom warning_handler sends warnings to Sentry via
capture_exception. When the Sentry SDK's own HTTP transport triggers a
warning (e.g. urllib3 InsecureRequestWarning due to broken CA certs),
this creates a self-inducing feedback loop: the warning is captured,
sent to Sentry, which triggers another warning, and so on.

Skip capture_exception for warnings originating from Sentry SDK
background threads (identified by the "sentry-sdk." thread name
prefix). Warnings are still logged normally via _LOGGER.warning.

A dedicated test validates that the SDK's BackgroundWorker thread uses
the expected naming convention, so any future SDK change will be caught.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:20:06 +02:00

58 lines
2.1 KiB
Python

"""Unit tests for Sentry."""
import threading
from unittest.mock import patch
import pytest
from sentry_sdk.worker import BackgroundWorker
from supervisor.bootstrap import initialize_coresys, warning_handler
@pytest.mark.usefixtures("supervisor_name", "docker")
async def test_sentry_disabled_by_default():
"""Test diagnostics off by default."""
with (
patch("supervisor.bootstrap.initialize_system"),
patch("sentry_sdk.init") as sentry_init,
):
await initialize_coresys()
sentry_init.assert_not_called()
def test_sentry_sdk_background_worker_thread_name():
"""Test that the Sentry SDK background worker thread name starts with 'sentry-sdk.'.
The warning_handler in bootstrap.py skips capture_exception for warnings
originating from threads named 'sentry-sdk.*' to prevent a feedback loop.
This test ensures the SDK still uses the expected naming convention.
"""
worker = BackgroundWorker()
worker.submit(lambda: None)
try:
assert worker._thread is not None
assert worker._thread.name.startswith("sentry-sdk.")
finally:
worker.kill()
def test_warning_handler_suppresses_on_sentry_thread():
"""Test that warning_handler does not call capture_exception on Sentry threads."""
with patch("supervisor.bootstrap.capture_exception") as mock_capture:
# Simulate being called from a Sentry SDK background thread
original_name = threading.current_thread().name
try:
threading.current_thread().name = "sentry-sdk.BackgroundWorker"
warning_handler(UserWarning("test"), UserWarning, "test.py", 1, None, None)
mock_capture.assert_not_called()
finally:
threading.current_thread().name = original_name
def test_warning_handler_captures_on_main_thread():
"""Test that warning_handler calls capture_exception on non-Sentry threads."""
with patch("supervisor.bootstrap.capture_exception") as mock_capture:
warning = UserWarning("test")
warning_handler(warning, UserWarning, "test.py", 1, None, None)
mock_capture.assert_called_once_with(warning)