From 551ac5576c3fcc226a7f5c916d158e681fc4655c Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 6 Aug 2026 14:29:55 -0400 Subject: [PATCH 1/7] test(django): Remove stale xfail marker from test_read_request The marker was added in 2019 (6326cb68b) with no reason or issue reference, and the test now XPASSes across the supported Django range (verified 5.2.16 and 6.x; the already-read-body handling it exercises is stable Django public-contract behavior). A non-strict xfail that always passes can never fail the build, so the test was dead weight that would have silently xpassed a real regression. Removing the marker restores it as a genuine regression guard. --- tests/integrations/django/test_basic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index 93b5477010..1c34b1680f 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -1369,7 +1369,6 @@ def test_request_body( assert "" not in event -@pytest.mark.xfail @pytest.mark.parametrize("span_streaming", [True, False]) def test_read_request( sentry_init, From c48f5baa5af1682d76ea0854b9c30d7683625bb7 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 6 Aug 2026 14:34:50 -0400 Subject: [PATCH 2/7] test(django): Remove dead rest_hello view and URL from test app No test references rest_hello (its test was removed in a 2019-era revert cycle); every other registered URL is exercised by the suite. --- tests/integrations/django/myapp/urls.py | 1 - tests/integrations/django/myapp/views.py | 4 ---- 2 files changed, 5 deletions(-) diff --git a/tests/integrations/django/myapp/urls.py b/tests/integrations/django/myapp/urls.py index 87d11b791b..e1d0c4c90c 100644 --- a/tests/integrations/django/myapp/urls.py +++ b/tests/integrations/django/myapp/urls.py @@ -149,7 +149,6 @@ def path(path, *args, **kwargs): name="rest_framework_read_body_and_exc", ) ) - urlpatterns.append(path("rest-hello", views.rest_hello, name="rest_hello")) urlpatterns.append( path( "rest-authenticated-hello", diff --git a/tests/integrations/django/myapp/views.py b/tests/integrations/django/myapp/views.py index 21f27455a5..f926ddd1a6 100644 --- a/tests/integrations/django/myapp/views.py +++ b/tests/integrations/django/myapp/views.py @@ -49,10 +49,6 @@ def rest_framework_read_body_and_exc(request): request.data 1 / 0 - @api_view(["GET"]) - def rest_hello(request): - return HttpResponse("ok") - @api_view(["GET"]) def rest_permission_denied_exc(request): raise PermissionDenied("bye") From e5a5204b04d5b817e49734d17414802c4b1f32b9 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Fri, 7 Aug 2026 09:57:39 -0400 Subject: [PATCH 3/7] test(django): Remove spurious django_db marker from test_response_trace Empirical audit of all 61 django_db-marked tests in the django suite: 60 are load-bearing (DB queries, login, or session-cookie loads via SessionMiddleware). The cache tests' markers also stay: stripping them broke a pytest-forked x pytest-django teardown invariant. This test is the only one that is genuinely DB-free (DRF view with no ORM work, no session cookie sent) and safe to unmark. --- tests/integrations/django/test_basic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index 1c34b1680f..bb5014932c 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -815,7 +815,6 @@ def test_sql_dict_query_params( @pytest.mark.forked -@pytest_mark_django_db_decorator() @pytest.mark.parametrize("span_streaming", [True, False]) def test_response_trace( sentry_init, From 7b3b9110f54be59981af6ec5921754147ed6884a Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Fri, 7 Aug 2026 10:03:25 -0400 Subject: [PATCH 4/7] test(django): Remove unused fixture args from 7 test signatures AST-verified unused: capture_events in test_materialized_user_captured (never called), client in 6 raw-cursor executemany tests (no HTTP). Unrequested fixtures are never built, so removal is behavior-neutral; signatures now truthfully describe each test's dependencies. --- tests/integrations/django/test_basic.py | 1 - tests/integrations/django/test_db_query_data.py | 1 - tests/integrations/django/test_db_transactions.py | 5 ----- 3 files changed, 7 deletions(-) diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index bb5014932c..491e185610 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -535,7 +535,6 @@ def test_user_captured( def test_materialized_user_captured( sentry_init, client, - capture_events, capture_items, ): sentry_init( diff --git a/tests/integrations/django/test_db_query_data.py b/tests/integrations/django/test_db_query_data.py index 92162cf1c3..0739b0992b 100644 --- a/tests/integrations/django/test_db_query_data.py +++ b/tests/integrations/django/test_db_query_data.py @@ -900,7 +900,6 @@ def test_db_span_origin_execute( @pytest.mark.parametrize("span_streaming", [True, False]) def test_db_span_origin_executemany( sentry_init, - client, capture_events, capture_items, span_streaming, diff --git a/tests/integrations/django/test_db_transactions.py b/tests/integrations/django/test_db_transactions.py index 179b9920c6..3b5dadfea7 100644 --- a/tests/integrations/django/test_db_transactions.py +++ b/tests/integrations/django/test_db_transactions.py @@ -617,7 +617,6 @@ def test_db_no_autocommit_execute( @pytest.mark.parametrize("span_streaming", [True, False]) def test_db_no_autocommit_executemany( sentry_init, - client, capture_events, capture_items, span_streaming, @@ -900,7 +899,6 @@ def test_db_no_autocommit_rollback_execute( @pytest.mark.parametrize("span_streaming", [True, False]) def test_db_no_autocommit_rollback_executemany( sentry_init, - client, capture_events, capture_items, span_streaming, @@ -1181,7 +1179,6 @@ def test_db_atomic_execute( @pytest.mark.parametrize("span_streaming", [True, False]) def test_db_atomic_executemany( sentry_init, - client, capture_events, capture_items, span_streaming, @@ -1457,7 +1454,6 @@ def test_db_atomic_rollback_execute( @pytest.mark.parametrize("span_streaming", [True, False]) def test_db_atomic_rollback_executemany( sentry_init, - client, capture_events, capture_items, span_streaming, @@ -1737,7 +1733,6 @@ def test_db_atomic_execute_exception( @pytest.mark.parametrize("span_streaming", [True, False]) def test_db_atomic_executemany_exception( sentry_init, - client, capture_events, capture_items, span_streaming, From f294b9ea205236140a954816d43eacc420ecca59 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Mon, 10 Aug 2026 14:58:44 -0400 Subject: [PATCH 5/7] test(django): Drop span_streaming parametrize where the arm is inert These tests are event-only: they capture events, never spans, and the span_streaming argument was only ever fed to trace_lifecycle. Both arms therefore exercised identical code paths and asserted identical payloads, so the parametrize doubled the case count without adding coverage. Affected: 18 event-only tests in test_basic.py, the 3 cookie-scrubbing tests in test_data_scrubbing.py, test_set_db_data_custom_backend, and test_cache_spans_get_span_name (a pure unit test of _get_span_description that never referenced the argument). Tests that inspect spans or transactions keep their parametrize. --- tests/integrations/django/test_basic.py | 597 +++++------------- .../integrations/django/test_cache_module.py | 5 +- .../django/test_data_scrubbing.py | 9 - 3 files changed, 144 insertions(+), 467 deletions(-) diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index 491e185610..a14c6aa113 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -47,50 +47,34 @@ def client(): return Client(application) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_view_exceptions( sentry_init, client, capture_exceptions, capture_events, - capture_items, - span_streaming, ): sentry_init( integrations=[DjangoIntegration()], send_default_pii=True, - trace_lifecycle="stream" if span_streaming else "static", ) exceptions = capture_exceptions() - if span_streaming: - items = capture_items("event") - client.get(reverse("view_exc")) - - (error,) = exceptions - assert isinstance(error, ZeroDivisionError) - - (event,) = (item.payload for item in items) - else: - events = capture_events() - client.get(reverse("view_exc")) + events = capture_events() + client.get(reverse("view_exc")) - (error,) = exceptions - assert isinstance(error, ZeroDivisionError) + (error,) = exceptions + assert isinstance(error, ZeroDivisionError) - (event,) = events + (event,) = events assert event["exception"]["values"][0]["mechanism"]["type"] == "django" -@pytest.mark.parametrize("span_streaming", [True, False]) def test_ensures_x_forwarded_header_is_honored_in_sdk_when_enabled_in_django( sentry_init, client, capture_exceptions, capture_events, - capture_items, settings, - span_streaming, ): """ Test that ensures if django settings.USE_X_FORWARDED_HOST is set to True @@ -101,37 +85,24 @@ def test_ensures_x_forwarded_header_is_honored_in_sdk_when_enabled_in_django( sentry_init( integrations=[DjangoIntegration()], send_default_pii=True, - trace_lifecycle="stream" if span_streaming else "static", ) exceptions = capture_exceptions() - if span_streaming: - items = capture_items("event") - client.get(reverse("view_exc"), headers={"X_FORWARDED_HOST": "example.com"}) - - (error,) = exceptions - assert isinstance(error, ZeroDivisionError) - - (event,) = (item.payload for item in items) - else: - events = capture_events() - client.get(reverse("view_exc"), headers={"X_FORWARDED_HOST": "example.com"}) + events = capture_events() + client.get(reverse("view_exc"), headers={"X_FORWARDED_HOST": "example.com"}) - (error,) = exceptions - assert isinstance(error, ZeroDivisionError) + (error,) = exceptions + assert isinstance(error, ZeroDivisionError) - (event,) = events + (event,) = events assert event["request"]["url"] == "http://example.com/view-exc" -@pytest.mark.parametrize("span_streaming", [True, False]) def test_ensures_x_forwarded_header_is_not_honored_when_unenabled_in_django( sentry_init, client, capture_exceptions, capture_events, - capture_items, - span_streaming, ): """ Test that ensures if django settings.USE_X_FORWARDED_HOST is set to False @@ -140,23 +111,14 @@ def test_ensures_x_forwarded_header_is_not_honored_when_unenabled_in_django( sentry_init( integrations=[DjangoIntegration()], send_default_pii=True, - trace_lifecycle="stream" if span_streaming else "static", ) exceptions = capture_exceptions() - if span_streaming: - items = capture_items("event") - client.get(reverse("view_exc"), headers={"X_FORWARDED_HOST": "example.com"}) - - (error,) = exceptions - assert isinstance(error, ZeroDivisionError) - (event,) = (item.payload for item in items) - else: - events = capture_events() - client.get(reverse("view_exc"), headers={"X_FORWARDED_HOST": "example.com"}) + events = capture_events() + client.get(reverse("view_exc"), headers={"X_FORWARDED_HOST": "example.com"}) - (error,) = exceptions - assert isinstance(error, ZeroDivisionError) - (event,) = events + (error,) = exceptions + assert isinstance(error, ZeroDivisionError) + (event,) = events assert event["request"]["url"] == "http://localhost/view-exc" @@ -170,56 +132,30 @@ def test_middleware_exceptions(sentry_init, client, capture_exceptions): assert isinstance(error, ZeroDivisionError) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_request_captured( sentry_init, client, capture_events, - capture_items, - span_streaming, ): sentry_init( integrations=[DjangoIntegration()], send_default_pii=True, - trace_lifecycle="stream" if span_streaming else "static", ) - if span_streaming: - items = capture_items("event") - content, status, headers = unpack_werkzeug_response( - client.get(reverse("message")) - ) - - assert content == b"ok" - - (event,) = (item.payload for item in items) - - assert event["transaction"] == "/message" - assert event["request"] == { - "cookies": {}, - "env": {"SERVER_NAME": "localhost", "SERVER_PORT": "80"}, - "headers": {"Host": "localhost"}, - "method": "GET", - "query_string": "", - "url": "http://localhost/message", - } - else: - events = capture_events() - content, status, headers = unpack_werkzeug_response( - client.get(reverse("message")) - ) + events = capture_events() + content, status, headers = unpack_werkzeug_response(client.get(reverse("message"))) - assert content == b"ok" + assert content == b"ok" - (event,) = events - assert event["transaction"] == "/message" - assert event["request"] == { - "cookies": {}, - "env": {"SERVER_NAME": "localhost", "SERVER_PORT": "80"}, - "headers": {"Host": "localhost"}, - "method": "GET", - "query_string": "", - "url": "http://localhost/message", - } + (event,) = events + assert event["transaction"] == "/message" + assert event["request"] == { + "cookies": {}, + "env": {"SERVER_NAME": "localhost", "SERVER_PORT": "80"}, + "headers": {"Host": "localhost"}, + "method": "GET", + "query_string": "", + "url": "http://localhost/message", + } @pytest.mark.parametrize("span_streaming", [True, False]) @@ -564,39 +500,24 @@ def test_materialized_user_captured( @pytest.mark.forked @pytest_mark_django_db_decorator() -@pytest.mark.parametrize("span_streaming", [True, False]) def test_queryset_repr( sentry_init, capture_events, - capture_items, - span_streaming, ): sentry_init( integrations=[DjangoIntegration()], - trace_lifecycle="stream" if span_streaming else "static", ) User.objects.create_user("john", "lennon@thebeatles.com", "johnpassword") - if span_streaming: - items = capture_items("event") - - try: - my_queryset = User.objects.all() # noqa - 1 / 0 - except Exception: - capture_exception() - - (event,) = (item.payload for item in items) - else: - events = capture_events() + events = capture_events() - try: - my_queryset = User.objects.all() # noqa - 1 / 0 - except Exception: - capture_exception() + try: + my_queryset = User.objects.all() # noqa + 1 / 0 + except Exception: + capture_exception() - (event,) = events + (event,) = events (exception,) = event["exception"]["values"] assert exception["type"] == "ZeroDivisionError" @@ -608,38 +529,23 @@ def test_queryset_repr( @pytest.mark.forked @pytest_mark_django_db_decorator() -@pytest.mark.parametrize("span_streaming", [True, False]) def test_context_nested_queryset_repr( sentry_init, capture_events, - capture_items, - span_streaming, ): sentry_init( integrations=[DjangoIntegration()], - trace_lifecycle="stream" if span_streaming else "static", ) User.objects.create_user("john", "lennon@thebeatles.com", "johnpassword") - if span_streaming: - items = capture_items("event") - - try: - context = make_context({"entries": User.objects.all()}) # noqa - 1 / 0 - except Exception: - capture_exception() + events = capture_events() - (event,) = (item.payload for item in items) - else: - events = capture_events() + try: + context = make_context({"entries": User.objects.all()}) # noqa + 1 / 0 + except Exception: + capture_exception() - try: - context = make_context({"entries": User.objects.all()}) # noqa - 1 / 0 - except Exception: - capture_exception() - - (event,) = events + (event,) = events (exception,) = event["exception"]["values"] assert exception["type"] == "ZeroDivisionError" @@ -647,30 +553,19 @@ def test_context_nested_queryset_repr( assert " Date: Tue, 11 Aug 2026 10:08:50 -0400 Subject: [PATCH 6/7] test(django): Replace capture_events fixture with capture_items Update all Django tests to use the `capture_items("event")` fixture API instead of the `capture_events()` fixture. The new API returns items with a `.payload` attribute rather than event objects directly. --- tests/integrations/django/test_basic.py | 112 ++++++++++++------------ 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index a14c6aa113..83ded9fbdf 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -51,20 +51,20 @@ def test_view_exceptions( sentry_init, client, capture_exceptions, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], send_default_pii=True, ) exceptions = capture_exceptions() - events = capture_events() + items = capture_items("event") client.get(reverse("view_exc")) (error,) = exceptions assert isinstance(error, ZeroDivisionError) - (event,) = events + (event,) = (item.payload for item in items) assert event["exception"]["values"][0]["mechanism"]["type"] == "django" @@ -73,7 +73,7 @@ def test_ensures_x_forwarded_header_is_honored_in_sdk_when_enabled_in_django( sentry_init, client, capture_exceptions, - capture_events, + capture_items, settings, ): """ @@ -87,13 +87,13 @@ def test_ensures_x_forwarded_header_is_honored_in_sdk_when_enabled_in_django( send_default_pii=True, ) exceptions = capture_exceptions() - events = capture_events() + items = capture_items("event") client.get(reverse("view_exc"), headers={"X_FORWARDED_HOST": "example.com"}) (error,) = exceptions assert isinstance(error, ZeroDivisionError) - (event,) = events + (event,) = (item.payload for item in items) assert event["request"]["url"] == "http://example.com/view-exc" @@ -102,7 +102,7 @@ def test_ensures_x_forwarded_header_is_not_honored_when_unenabled_in_django( sentry_init, client, capture_exceptions, - capture_events, + capture_items, ): """ Test that ensures if django settings.USE_X_FORWARDED_HOST is set to False @@ -113,12 +113,12 @@ def test_ensures_x_forwarded_header_is_not_honored_when_unenabled_in_django( send_default_pii=True, ) exceptions = capture_exceptions() - events = capture_events() + items = capture_items("event") client.get(reverse("view_exc"), headers={"X_FORWARDED_HOST": "example.com"}) (error,) = exceptions assert isinstance(error, ZeroDivisionError) - (event,) = events + (event,) = (item.payload for item in items) assert event["request"]["url"] == "http://localhost/view-exc" @@ -135,18 +135,18 @@ def test_middleware_exceptions(sentry_init, client, capture_exceptions): def test_request_captured( sentry_init, client, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], send_default_pii=True, ) - events = capture_events() + items = capture_items("event") content, status, headers = unpack_werkzeug_response(client.get(reverse("message"))) assert content == b"ok" - (event,) = events + (event,) = (item.payload for item in items) assert event["transaction"] == "/message" assert event["request"] == { "cookies": {}, @@ -502,14 +502,14 @@ def test_materialized_user_captured( @pytest_mark_django_db_decorator() def test_queryset_repr( sentry_init, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], ) User.objects.create_user("john", "lennon@thebeatles.com", "johnpassword") - events = capture_events() + items = capture_items("event") try: my_queryset = User.objects.all() # noqa @@ -517,7 +517,7 @@ def test_queryset_repr( except Exception: capture_exception() - (event,) = events + (event,) = (item.payload for item in items) (exception,) = event["exception"]["values"] assert exception["type"] == "ZeroDivisionError" @@ -531,13 +531,13 @@ def test_queryset_repr( @pytest_mark_django_db_decorator() def test_context_nested_queryset_repr( sentry_init, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], ) User.objects.create_user("john", "lennon@thebeatles.com", "johnpassword") - events = capture_events() + items = capture_items("event") try: context = make_context({"entries": User.objects.all()}) # noqa @@ -545,7 +545,7 @@ def test_context_nested_queryset_repr( except Exception: capture_exception() - (event,) = events + (event,) = (item.payload for item in items) (exception,) = event["exception"]["values"] assert exception["type"] == "ZeroDivisionError" @@ -556,16 +556,16 @@ def test_context_nested_queryset_repr( def test_custom_error_handler_request_context( sentry_init, client, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], ) - events = capture_events() + items = capture_items("event") content, status, headers = unpack_werkzeug_response(client.post("/404")) assert status.lower() == "404 not found" - (event,) = events + (event,) = (item.payload for item in items) assert event["message"] == "not found" assert event["level"] == "error" @@ -602,7 +602,7 @@ def test_management_command_raises(): @pytest.mark.parametrize("with_integration", [True, False]) def test_sql_queries( sentry_init, - capture_events, + capture_items, with_integration, ): sentry_init( @@ -613,7 +613,7 @@ def test_sql_queries( from django.db import connection - events = capture_events() + items = capture_items("event") sql = connection.cursor() @@ -625,7 +625,7 @@ def test_sql_queries( capture_message("HI") - (event,) = events + (event,) = (item.payload for item in items) if with_integration: crumb = event["breadcrumbs"]["values"][-1] @@ -638,7 +638,7 @@ def test_sql_queries( @pytest_mark_django_db_decorator() def test_sql_dict_query_params( sentry_init, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], @@ -653,7 +653,7 @@ def test_sql_dict_query_params( sql = connections["postgres"].cursor() - events = capture_events() + items = capture_items("event") sentry_sdk.get_isolation_scope().clear_breadcrumbs() with pytest.raises(ProgrammingError): @@ -664,7 +664,7 @@ def test_sql_dict_query_params( capture_message("HI") - (event,) = events + (event,) = (item.payload for item in items) crumb = event["breadcrumbs"]["values"][-1] assert crumb["message"] == ( @@ -730,7 +730,7 @@ def test_response_trace( @pytest_mark_django_db_decorator() def test_sql_psycopg2_string_composition( sentry_init, - capture_events, + capture_items, query, ): sentry_init( @@ -751,14 +751,14 @@ def test_sql_psycopg2_string_composition( sentry_sdk.get_isolation_scope().clear_breadcrumbs() - events = capture_events() + items = capture_items("event") with pytest.raises(ProgrammingError): sql.execute(query(psycopg2.sql), {"my_param": 10}) capture_message("HI") - (event,) = events + (event,) = (item.payload for item in items) crumb = event["breadcrumbs"]["values"][-1] assert crumb["message"] == ('SELECT %(my_param)s FROM "foobar"') @@ -769,7 +769,7 @@ def test_sql_psycopg2_string_composition( @pytest_mark_django_db_decorator() def test_sql_psycopg2_placeholders( sentry_init, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], @@ -787,7 +787,7 @@ def test_sql_psycopg2_placeholders( sql = connections["postgres"].cursor() - events = capture_events() + items = capture_items("event") sentry_sdk.get_isolation_scope().clear_breadcrumbs() with pytest.raises(DataError): @@ -806,7 +806,7 @@ def test_sql_psycopg2_placeholders( capture_message("HI") - (event,) = events + (event,) = (item.payload for item in items) for crumb in event["breadcrumbs"]["values"]: del crumb["timestamp"] @@ -901,7 +901,7 @@ def test_django_connect_trace( @pytest_mark_django_db_decorator(transaction=True) def test_django_connect_breadcrumbs( sentry_init, - capture_events, + capture_items, ): """ Verify we record a breadcrumb when opening a new database. @@ -919,14 +919,14 @@ def test_django_connect_breadcrumbs( # trigger Django to open a new connection by marking the existing one as None. connections["postgres"].connection = None - events = capture_events() + items = capture_items("event") cursor = connections["postgres"].cursor() cursor.execute("select 1") # trigger recording of event. capture_message("HI") - (event,) = events + (event,) = (item.payload for item in items) for crumb in event["breadcrumbs"]["values"]: del crumb["timestamp"] @@ -1095,19 +1095,19 @@ def test_transaction_style( def test_request_body( sentry_init, client, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], ) - events = capture_events() + items = capture_items("event") content, status, headers = unpack_werkzeug_response( client.post(reverse("post_echo"), data=b"heyooo", content_type="text/plain") ) assert status.lower() == "200 ok" assert content == b"heyooo" - (event,) = events + (event,) = (item.payload for item in items) assert event["message"] == "hi" assert event["request"]["data"] == "" @@ -1115,7 +1115,7 @@ def test_request_body( "rem": [["!raw", "x"]], } - del events[:] + del items[:] content, status, headers = unpack_werkzeug_response( client.post( @@ -1127,7 +1127,7 @@ def test_request_body( assert status.lower() == "200 ok" assert content == b'{"hey": 42}' - (event,) = events + (event,) = (item.payload for item in items) assert event["message"] == "hi" assert event["request"]["data"] == {"hey": 42} @@ -1137,12 +1137,12 @@ def test_request_body( def test_read_request( sentry_init, client, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], ) - events = capture_events() + items = capture_items("event") content, status, headers = unpack_werkzeug_response( client.post( @@ -1154,7 +1154,7 @@ def test_read_request( assert status.lower() == "500 internal server error" - (event,) = events + (event,) = (item.payload for item in items) assert "data" not in event["request"] @@ -1162,7 +1162,7 @@ def test_read_request( def test_request_body_already_read( sentry_init, client, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], @@ -1172,7 +1172,7 @@ class MockExtractor(DjangoRequestExtractor): def raw_data(self): raise RawPostDataException - events = capture_events() + items = capture_items("event") with patch("sentry_sdk.integrations.django.DjangoRequestExtractor", MockExtractor): client.post( @@ -1181,7 +1181,7 @@ def raw_data(self): content_type="application/json", ) - (event,) = events + (event,) = (item.payload for item in items) assert event["message"] == "hi" assert "data" not in event["request"] @@ -1190,17 +1190,17 @@ def raw_data(self): def test_template_tracing_meta( sentry_init, client, - capture_events, + capture_items, ): sentry_init( integrations=[DjangoIntegration()], ) - events = capture_events() + items = capture_items("event") content, _, _ = unpack_werkzeug_response(client.get(reverse("template_test3"))) rendered_meta = content.decode("utf-8") - traceparent, baggage = events[0]["message"].split("\n") + traceparent, baggage = items[0].payload["message"].split("\n") assert traceparent != "" assert baggage != "" @@ -1219,20 +1219,20 @@ def test_template_tracing_meta( def test_template_exception( sentry_init, client, - capture_events, + capture_items, with_executing_integration, ): sentry_init( integrations=[DjangoIntegration()] + with_executing_integration, ) - events = capture_events() + items = capture_items("event") content, status, headers = unpack_werkzeug_response( client.get(reverse("template_exc")) ) assert status.lower() == "500 internal server error" - (event,) = events + (event,) = (item.payload for item in items) exception = event["exception"]["values"][-1] assert exception["type"] == "TemplateSyntaxError" @@ -1430,7 +1430,7 @@ def test_rest_framework_authentication_span_without_authenticators( def test_does_not_capture_403( sentry_init, client, - capture_events, + capture_items, endpoint, ): if endpoint == "rest_permission_denied_exc": @@ -1439,12 +1439,12 @@ def test_does_not_capture_403( sentry_init( integrations=[DjangoIntegration()], ) - events = capture_events() + items = capture_items("event") _, status, _ = unpack_werkzeug_response(client.get(reverse(endpoint))) assert status.lower() == "403 forbidden" - assert not events + assert not items @pytest.mark.parametrize("span_streaming", [True, False]) From a932d5413486f61ae318bc77b76557ae00a5d14c Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Wed, 12 Aug 2026 10:01:09 -0400 Subject: [PATCH 7/7] address CR comments --- tests/integrations/django/test_basic.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index 83ded9fbdf..e5afa91dad 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -132,14 +132,18 @@ def test_middleware_exceptions(sentry_init, client, capture_exceptions): assert isinstance(error, ZeroDivisionError) +@pytest.mark.parametrize("span_streaming", [True, False]) def test_request_captured( sentry_init, client, + capture_events, capture_items, + span_streaming, ): sentry_init( integrations=[DjangoIntegration()], send_default_pii=True, + trace_lifecycle="stream" if span_streaming else "static", ) items = capture_items("event") content, status, headers = unpack_werkzeug_response(client.get(reverse("message"))) @@ -147,6 +151,7 @@ def test_request_captured( assert content == b"ok" (event,) = (item.payload for item in items) + assert event["transaction"] == "/message" assert event["request"] == { "cookies": {}, @@ -1187,20 +1192,27 @@ def raw_data(self): assert "data" not in event["request"] +@pytest.mark.parametrize("span_streaming", [True, False]) def test_template_tracing_meta( sentry_init, client, + capture_events, capture_items, + span_streaming, ): sentry_init( integrations=[DjangoIntegration()], + trace_lifecycle="stream" if span_streaming else "static", ) + items = capture_items("event") content, _, _ = unpack_werkzeug_response(client.get(reverse("template_test3"))) rendered_meta = content.decode("utf-8") - traceparent, baggage = items[0].payload["message"].split("\n") + events = [item.payload for item in items] + + traceparent, baggage = events[0]["message"].split("\n") assert traceparent != "" assert baggage != "" @@ -1427,19 +1439,24 @@ def test_rest_framework_authentication_span_without_authenticators( @pytest.mark.parametrize( "endpoint", ["rest_permission_denied_exc", "permission_denied_exc"] ) +@pytest.mark.parametrize("span_streaming", [True, False]) def test_does_not_capture_403( sentry_init, client, + capture_events, capture_items, endpoint, + span_streaming, ): if endpoint == "rest_permission_denied_exc": pytest.importorskip("rest_framework") sentry_init( integrations=[DjangoIntegration()], + trace_lifecycle="stream" if span_streaming else "static", ) - items = capture_items("event") + + items = capture_items("event", "transaction", "span") _, status, _ = unpack_werkzeug_response(client.get(reverse(endpoint))) assert status.lower() == "403 forbidden"