From 56d2cc6d5d08a1408b53b2c19c8bb54f44b619ed Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 18 Jun 2024 16:24:11 +0200 Subject: [PATCH] Cleaning up ASGI tests for Django (#3180) Cleaning up the ASGI tests for Django. Making sure it is always `wait()`ed for the application to finish and also made the tests a bit more readable and removed some useless asserts. Fixes #3142 --- tests/integrations/django/asgi/test_asgi.py | 122 ++++++++++++-------- tests/integrations/django/myapp/urls.py | 5 + tests/integrations/django/myapp/views.py | 4 + 3 files changed, 85 insertions(+), 46 deletions(-) diff --git a/tests/integrations/django/asgi/test_asgi.py b/tests/integrations/django/asgi/test_asgi.py index 9d36a5e3db..abc27ccff4 100644 --- a/tests/integrations/django/asgi/test_asgi.py +++ b/tests/integrations/django/asgi/test_asgi.py @@ -31,12 +31,17 @@ @pytest.mark.asyncio @pytest.mark.forked async def test_basic(sentry_init, capture_events, application): - sentry_init(integrations=[DjangoIntegration()], send_default_pii=True) + sentry_init( + integrations=[DjangoIntegration()], + send_default_pii=True, + ) events = capture_events() comm = HttpCommunicator(application, "GET", "/view-exc?test=query") response = await comm.get_response() + await comm.wait() + assert response["status"] == 500 (event,) = events @@ -67,12 +72,17 @@ async def test_basic(sentry_init, capture_events, application): django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1" ) async def test_async_views(sentry_init, capture_events, application): - sentry_init(integrations=[DjangoIntegration()], send_default_pii=True) + sentry_init( + integrations=[DjangoIntegration()], + send_default_pii=True, + ) events = capture_events() comm = HttpCommunicator(application, "GET", "/async_message") response = await comm.get_response() + await comm.wait() + assert response["status"] == 200 (event,) = events @@ -108,17 +118,16 @@ async def test_active_thread_id(sentry_init, capture_envelopes, endpoint, applic comm = HttpCommunicator(application, "GET", endpoint) response = await comm.get_response() - assert response["status"] == 200, response["body"] - await comm.wait() - data = json.loads(response["body"]) - envelopes = [envelope for envelope in envelopes] + assert response["status"] == 200, response["body"] assert len(envelopes) == 1 profiles = [item for item in envelopes[0].items if item.type == "profile"] assert len(profiles) == 1 + data = json.loads(response["body"]) + for profile in profiles: transactions = profile.payload.json["transactions"] assert len(transactions) == 1 @@ -137,7 +146,10 @@ async def test_async_views_concurrent_execution(sentry_init, settings): settings.MIDDLEWARE = [] asgi_application.load_middleware(is_async=True) - sentry_init(integrations=[DjangoIntegration()], send_default_pii=True) + sentry_init( + integrations=[DjangoIntegration()], + send_default_pii=True, + ) comm = HttpCommunicator( asgi_application, "GET", "/my_async_view" @@ -181,7 +193,10 @@ async def test_async_middleware_that_is_function_concurrent_execution( ] asgi_application.load_middleware(is_async=True) - sentry_init(integrations=[DjangoIntegration()], send_default_pii=True) + sentry_init( + integrations=[DjangoIntegration()], + send_default_pii=True, + ) comm = HttpCommunicator( asgi_application, "GET", "/my_async_view" @@ -233,13 +248,13 @@ async def test_async_middleware_spans( events = capture_events() - comm = HttpCommunicator(asgi_application, "GET", "/async_message") + comm = HttpCommunicator(asgi_application, "GET", "/simple_async_view") response = await comm.get_response() - assert response["status"] == 200 - await comm.wait() - message, transaction = events + assert response["status"] == 200 + + (transaction,) = events assert ( render_span_tree(transaction) @@ -252,7 +267,7 @@ async def test_async_middleware_spans( - op="middleware.django": description="django.middleware.csrf.CsrfViewMiddleware.__acall__" - op="middleware.django": description="tests.integrations.django.myapp.settings.TestMiddleware.__acall__" - op="middleware.django": description="django.middleware.csrf.CsrfViewMiddleware.process_view" - - op="view.render": description="async_message" + - op="view.render": description="simple_async_view" - op="event.django": description="django.db.close_old_connections" - op="event.django": description="django.core.cache.close_caches" - op="event.django": description="django.core.handlers.base.reset_urlconf\"""" @@ -265,27 +280,25 @@ async def test_async_middleware_spans( django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1" ) async def test_has_trace_if_performance_enabled(sentry_init, capture_events): - sentry_init(integrations=[DjangoIntegration()], traces_sample_rate=1.0) + sentry_init( + integrations=[DjangoIntegration()], + traces_sample_rate=1.0, + ) events = capture_events() comm = HttpCommunicator(asgi_application, "GET", "/view-exc-with-msg") response = await comm.get_response() - assert response["status"] == 500 - - # ASGI Django does not create transactions per default, - # so we do not have a transaction_event here. - (msg_event, error_event) = events + await comm.wait() - assert msg_event["contexts"]["trace"] - assert "trace_id" in msg_event["contexts"]["trace"] + assert response["status"] == 500 - assert error_event["contexts"]["trace"] - assert "trace_id" in error_event["contexts"]["trace"] + (msg_event, error_event, transaction_event) = events assert ( msg_event["contexts"]["trace"]["trace_id"] == error_event["contexts"]["trace"]["trace_id"] + == transaction_event["contexts"]["trace"]["trace_id"] ) @@ -295,12 +308,16 @@ async def test_has_trace_if_performance_enabled(sentry_init, capture_events): django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1" ) async def test_has_trace_if_performance_disabled(sentry_init, capture_events): - sentry_init(integrations=[DjangoIntegration()]) + sentry_init( + integrations=[DjangoIntegration()], + ) events = capture_events() comm = HttpCommunicator(asgi_application, "GET", "/view-exc-with-msg") response = await comm.get_response() + await comm.wait() + assert response["status"] == 500 (msg_event, error_event) = events @@ -322,7 +339,10 @@ async def test_has_trace_if_performance_disabled(sentry_init, capture_events): django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1" ) async def test_trace_from_headers_if_performance_enabled(sentry_init, capture_events): - sentry_init(integrations=[DjangoIntegration()], traces_sample_rate=1.0) + sentry_init( + integrations=[DjangoIntegration()], + traces_sample_rate=1.0, + ) events = capture_events() @@ -336,20 +356,15 @@ async def test_trace_from_headers_if_performance_enabled(sentry_init, capture_ev headers=[(b"sentry-trace", sentry_trace_header.encode())], ) response = await comm.get_response() - assert response["status"] == 500 + await comm.wait() - # ASGI Django does not create transactions per default, - # so we do not have a transaction_event here. - (msg_event, error_event) = events + assert response["status"] == 500 - assert msg_event["contexts"]["trace"] - assert "trace_id" in msg_event["contexts"]["trace"] - - assert error_event["contexts"]["trace"] - assert "trace_id" in error_event["contexts"]["trace"] + (msg_event, error_event, transaction_event) = events assert msg_event["contexts"]["trace"]["trace_id"] == trace_id assert error_event["contexts"]["trace"]["trace_id"] == trace_id + assert transaction_event["contexts"]["trace"]["trace_id"] == trace_id @pytest.mark.asyncio @@ -358,7 +373,9 @@ async def test_trace_from_headers_if_performance_enabled(sentry_init, capture_ev django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1" ) async def test_trace_from_headers_if_performance_disabled(sentry_init, capture_events): - sentry_init(integrations=[DjangoIntegration()]) + sentry_init( + integrations=[DjangoIntegration()], + ) events = capture_events() @@ -372,16 +389,12 @@ async def test_trace_from_headers_if_performance_disabled(sentry_init, capture_e headers=[(b"sentry-trace", sentry_trace_header.encode())], ) response = await comm.get_response() + await comm.wait() + assert response["status"] == 500 (msg_event, error_event) = events - assert msg_event["contexts"]["trace"] - assert "trace_id" in msg_event["contexts"]["trace"] - - assert error_event["contexts"]["trace"] - assert "trace_id" in error_event["contexts"]["trace"] - assert msg_event["contexts"]["trace"]["trace_id"] == trace_id assert error_event["contexts"]["trace"]["trace_id"] == trace_id @@ -504,10 +517,8 @@ async def test_asgi_request_body( expected_data, ): sentry_init( + integrations=[DjangoIntegration()], send_default_pii=send_default_pii, - integrations=[ - DjangoIntegration(), - ], ) envelopes = capture_envelopes() @@ -520,9 +531,9 @@ async def test_asgi_request_body( body=body, ) response = await comm.get_response() - assert response["status"] == 200 - await comm.wait() + + assert response["status"] == 200 assert response["body"] == body (envelope,) = envelopes @@ -594,3 +605,22 @@ def get_response(): ... instance = sentry_asgi_mixin(get_response) assert not inspect.iscoroutinefunction(instance) + + +@pytest.mark.parametrize("application", APPS) +@pytest.mark.asyncio +async def test_async_view(sentry_init, capture_events, application): + sentry_init( + integrations=[DjangoIntegration()], + traces_sample_rate=1.0, + ) + + events = capture_events() + + comm = HttpCommunicator(application, "GET", "/simple_async_view") + await comm.get_response() + await comm.wait() + + (event,) = events + assert event["type"] == "transaction" + assert event["transaction"] == "/simple_async_view" diff --git a/tests/integrations/django/myapp/urls.py b/tests/integrations/django/myapp/urls.py index b6565c3cdd..1a1fa163a3 100644 --- a/tests/integrations/django/myapp/urls.py +++ b/tests/integrations/django/myapp/urls.py @@ -88,6 +88,11 @@ def path(path, *args, **kwargs): if views.my_async_view is not None: urlpatterns.append(path("my_async_view", views.my_async_view, name="my_async_view")) +if views.my_async_view is not None: + urlpatterns.append( + path("simple_async_view", views.simple_async_view, name="simple_async_view") + ) + if views.thread_ids_async is not None: urlpatterns.append( path("async/thread_ids", views.thread_ids_async, name="thread_ids_async") diff --git a/tests/integrations/django/myapp/views.py b/tests/integrations/django/myapp/views.py index 4e6b4ee27f..971baf0785 100644 --- a/tests/integrations/django/myapp/views.py +++ b/tests/integrations/django/myapp/views.py @@ -240,6 +240,10 @@ async def my_async_view(request): return HttpResponse("Hello World") +async def simple_async_view(request): + return HttpResponse("Simple Hello World") + + async def thread_ids_async(request): response = json.dumps( {