From ceb83d5e58caa096843c27fcb1b2605225c209d9 Mon Sep 17 00:00:00 2001 From: Santiago Regusci Date: Fri, 20 Feb 2026 15:33:34 -0300 Subject: [PATCH 1/3] add observability to checkmate test --- tests/unit/viahtml/views/status_test.py | 95 ++++++++++++++++++++++++- viahtml/views/status.py | 46 ++++++++++-- 2 files changed, 134 insertions(+), 7 deletions(-) diff --git a/tests/unit/viahtml/views/status_test.py b/tests/unit/viahtml/views/status_test.py index d3aca549..232b6cc7 100644 --- a/tests/unit/viahtml/views/status_test.py +++ b/tests/unit/viahtml/views/status_test.py @@ -1,4 +1,5 @@ -from unittest.mock import create_autospec +import logging +from unittest.mock import MagicMock, create_autospec import pytest from checkmatelib import CheckmateClient, CheckmateException @@ -82,6 +83,86 @@ def test_it_sends_test_messages_to_sentry(self, context, view, capture_message): "Test message from Via HTML's status view" ) + def test_it_logs_checkmate_failure_details( + self, context, view, checkmate, caplog + ): + exc = CheckmateException("Connection timed out") + checkmate.check_url.side_effect = exc + context.query_params = {"include-checkmate": [""]} + + with caplog.at_level(logging.ERROR, logger="viahtml.views.status"): + view(context) + + assert any( + "Checkmate status check failed" in record.message + and "Connection timed out" in record.message + for record in caplog.records + ), f"Expected checkmate failure log, got: {[r.message for r in caplog.records]}" + + def test_it_logs_checkmate_success(self, context, view, caplog): + context.query_params = {"include-checkmate": [""]} + + with caplog.at_level(logging.INFO, logger="viahtml.views.status"): + view(context) + + assert any( + "Checkmate status check succeeded" in record.message + for record in caplog.records + ), f"Expected checkmate success log, got: {[r.message for r in caplog.records]}" + + def test_it_creates_sentry_span_on_checkmate_failure( + self, context, view, checkmate, sentry_start_span, sentry_capture_exception + ): + exc = CheckmateException("Connection timed out") + checkmate.check_url.side_effect = exc + context.query_params = {"include-checkmate": [""]} + + view(context) + + sentry_start_span.assert_called_once_with( + op="checkmate.status_check", + name="Check Checkmate health via check_url", + ) + span = sentry_start_span.return_value.__enter__.return_value + span.set_status.assert_called_once_with("internal_error") + span.set_data.assert_any_call("error.type", "CheckmateException") + span.set_data.assert_any_call("error.message", "Connection timed out") + sentry_capture_exception.assert_called_once_with(exc) + + def test_it_creates_sentry_span_on_checkmate_success( + self, context, view, sentry_start_span, sentry_capture_exception + ): + context.query_params = {"include-checkmate": [""]} + + view(context) + + sentry_start_span.assert_called_once_with( + op="checkmate.status_check", + name="Check Checkmate health via check_url", + ) + span = sentry_start_span.return_value.__enter__.return_value + span.set_status.assert_called_once_with("ok") + sentry_capture_exception.assert_not_called() + + def test_it_adds_sentry_breadcrumb_on_checkmate_failure( + self, context, view, checkmate, sentry_add_breadcrumb + ): + exc = CheckmateException("Connection timed out") + checkmate.check_url.side_effect = exc + context.query_params = {"include-checkmate": [""]} + + view(context) + + sentry_add_breadcrumb.assert_called_once_with( + category="checkmate", + message="Checkmate status check failed: Connection timed out", + level="error", + data={ + "exception_type": "CheckmateException", + "exception_message": "Connection timed out", + }, + ) + @pytest.fixture def checkmate(self): return create_autospec(CheckmateClient, instance=True, spec_set=True) @@ -95,6 +176,18 @@ def context(self, context): def view(self, checkmate): return StatusView(checkmate) + @pytest.fixture + def sentry_start_span(self, patch): + return patch("viahtml.views.status.sentry_sdk.start_span") + + @pytest.fixture + def sentry_capture_exception(self, patch): + return patch("viahtml.views.status.sentry_sdk.capture_exception") + + @pytest.fixture + def sentry_add_breadcrumb(self, patch): + return patch("viahtml.views.status.sentry_sdk.add_breadcrumb") + @pytest.fixture(autouse=True) def capture_message(patch): diff --git a/viahtml/views/status.py b/viahtml/views/status.py index 4a3552e8..82dcbc22 100644 --- a/viahtml/views/status.py +++ b/viahtml/views/status.py @@ -1,8 +1,13 @@ +import logging +import traceback from http import HTTPStatus +import sentry_sdk from checkmatelib import CheckmateException from sentry_sdk import capture_message +LOG = logging.getLogger(__name__) + class StatusView: def __init__(self, checkmate): @@ -22,12 +27,7 @@ def __call__(self, context): http_status = HTTPStatus.OK if "include-checkmate" in context.query_params: - try: - self._checkmate.check_url("https://example.com/") - except CheckmateException: - body["down"] = ["checkmate"] - else: - body["okay"] = ["checkmate"] + self._check_checkmate(body) # If any of the components checked above were down then report the # status check as a whole as being down. @@ -44,3 +44,37 @@ def __call__(self, context): http_status=http_status, headers={"Cache-Control": "max-age=0, must-revalidate, no-cache, no-store"}, ) + + def _check_checkmate(self, body): + with sentry_sdk.start_span( + op="checkmate.status_check", + name="Check Checkmate health via check_url", + ) as span: + try: + LOG.info("Checking checkmate status via check_url") + self._checkmate.check_url("https://example.com/") + except CheckmateException as exc: + LOG.error( + "Checkmate status check failed: %s: %s\n%s", + type(exc).__name__, + exc, + traceback.format_exc(), + ) + span.set_status("internal_error") + span.set_data("error.type", type(exc).__name__) + span.set_data("error.message", str(exc)) + sentry_sdk.add_breadcrumb( + category="checkmate", + message=f"Checkmate status check failed: {exc}", + level="error", + data={ + "exception_type": type(exc).__name__, + "exception_message": str(exc), + }, + ) + sentry_sdk.capture_exception(exc) + body["down"] = ["checkmate"] + else: + LOG.info("Checkmate status check succeeded") + span.set_status("ok") + body["okay"] = ["checkmate"] From c737977651375bff4a488980ab5438f202db732f Mon Sep 17 00:00:00 2001 From: Santiago Regusci Date: Fri, 20 Feb 2026 15:56:52 -0300 Subject: [PATCH 2/3] add observability to checkmate test --- tests/unit/viahtml/views/status_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/viahtml/views/status_test.py b/tests/unit/viahtml/views/status_test.py index 232b6cc7..282b7245 100644 --- a/tests/unit/viahtml/views/status_test.py +++ b/tests/unit/viahtml/views/status_test.py @@ -176,15 +176,15 @@ def context(self, context): def view(self, checkmate): return StatusView(checkmate) - @pytest.fixture + @pytest.fixture(autouse=True) def sentry_start_span(self, patch): return patch("viahtml.views.status.sentry_sdk.start_span") - @pytest.fixture + @pytest.fixture(autouse=True) def sentry_capture_exception(self, patch): return patch("viahtml.views.status.sentry_sdk.capture_exception") - @pytest.fixture + @pytest.fixture(autouse=True) def sentry_add_breadcrumb(self, patch): return patch("viahtml.views.status.sentry_sdk.add_breadcrumb") From 17e4d2c03162bd54001c9cf6bb6e9718f52915c5 Mon Sep 17 00:00:00 2001 From: Santiago Regusci Date: Fri, 20 Feb 2026 16:08:19 -0300 Subject: [PATCH 3/3] update format and lint --- tests/unit/viahtml/views/status_test.py | 8 +++----- viahtml/views/status.py | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/unit/viahtml/views/status_test.py b/tests/unit/viahtml/views/status_test.py index 282b7245..34339561 100644 --- a/tests/unit/viahtml/views/status_test.py +++ b/tests/unit/viahtml/views/status_test.py @@ -1,5 +1,5 @@ import logging -from unittest.mock import MagicMock, create_autospec +from unittest.mock import create_autospec import pytest from checkmatelib import CheckmateClient, CheckmateException @@ -83,9 +83,7 @@ def test_it_sends_test_messages_to_sentry(self, context, view, capture_message): "Test message from Via HTML's status view" ) - def test_it_logs_checkmate_failure_details( - self, context, view, checkmate, caplog - ): + def test_it_logs_checkmate_failure_details(self, context, view, checkmate, caplog): exc = CheckmateException("Connection timed out") checkmate.check_url.side_effect = exc context.query_params = {"include-checkmate": [""]} @@ -112,7 +110,7 @@ def test_it_logs_checkmate_success(self, context, view, caplog): def test_it_creates_sentry_span_on_checkmate_failure( self, context, view, checkmate, sentry_start_span, sentry_capture_exception - ): + ): # pylint:disable=too-many-arguments exc = CheckmateException("Connection timed out") checkmate.check_url.side_effect = exc context.query_params = {"include-checkmate": [""]} diff --git a/viahtml/views/status.py b/viahtml/views/status.py index 82dcbc22..7722c543 100644 --- a/viahtml/views/status.py +++ b/viahtml/views/status.py @@ -50,8 +50,8 @@ def _check_checkmate(self, body): op="checkmate.status_check", name="Check Checkmate health via check_url", ) as span: + LOG.info("Checking checkmate status via check_url") try: - LOG.info("Checking checkmate status via check_url") self._checkmate.check_url("https://example.com/") except CheckmateException as exc: LOG.error(