1111
1212import pytest
1313
14+ import sentry_sdk
1415from sentry_sdk import capture_message , get_baggage , get_traceparent
1516from sentry_sdk .integrations .asgi import SentryAsgiMiddleware
1617from sentry_sdk .integrations .starlette import (
@@ -648,24 +649,30 @@ def test_user_information_transaction_no_pii(sentry_init, capture_events):
648649 assert "user" not in transaction_event
649650
650651
651- def test_middleware_spans (sentry_init , capture_events ):
652+ @pytest .mark .parametrize ("span_streaming" , [True , False ])
653+ def test_middleware_spans (sentry_init , capture_events , capture_items , span_streaming ):
652654 sentry_init (
653655 traces_sample_rate = 1.0 ,
654656 integrations = [StarletteIntegration (middleware_spans = True )],
657+ _experiments = {
658+ "trace_lifecycle" : "stream" if span_streaming else "static" ,
659+ },
655660 )
656661 starlette_app = starlette_app_factory (
657662 middleware = [Middleware (AuthenticationMiddleware , backend = BasicAuthBackend ())]
658663 )
659- events = capture_events ()
664+
665+ if span_streaming :
666+ items = capture_items ("span" )
667+ else :
668+ events = capture_events ()
660669
661670 client = TestClient (starlette_app , raise_server_exceptions = False )
662671 try :
663672 client .get ("/message" , auth = ("Gabriela" , "hello123" ))
664673 except Exception :
665674 pass
666675
667- (_ , transaction_event ) = events
668-
669676 expected_middleware_spans = [
670677 "ServerErrorMiddleware" ,
671678 "AuthenticationMiddleware" ,
@@ -676,55 +683,108 @@ def test_middleware_spans(sentry_init, capture_events):
676683 "ServerErrorMiddleware" , # 'op': 'middleware.starlette.send'
677684 ]
678685
679- assert len (transaction_event ["spans" ]) == len (expected_middleware_spans )
686+ if span_streaming :
687+ sentry_sdk .flush ()
688+
689+ middleware_spans = sorted (
690+ [
691+ item .payload
692+ for item in items
693+ if item .payload .get ("attributes" , {})
694+ .get ("sentry.op" , "" )
695+ .startswith ("middleware.starlette" )
696+ ],
697+ key = lambda s : s ["start_timestamp" ],
698+ )
680699
681- idx = 0
682- for span in transaction_event [ "spans" ]:
683- if span [ "op" ]. startswith ( "middleware.starlette" ):
700+ assert len ( middleware_spans ) == len ( expected_middleware_spans )
701+
702+ for idx , span in enumerate ( middleware_spans ):
684703 assert (
685- span ["tags " ]["starlette.middleware_name" ]
704+ span ["attributes " ]["starlette.middleware_name" ]
686705 == expected_middleware_spans [idx ]
687706 )
688- idx += 1
707+ else :
708+ (_ , transaction_event ) = events
709+
710+ assert len (transaction_event ["spans" ]) == len (expected_middleware_spans )
689711
712+ idx = 0
713+ for span in transaction_event ["spans" ]:
714+ if span ["op" ].startswith ("middleware.starlette" ):
715+ assert (
716+ span ["tags" ]["starlette.middleware_name" ]
717+ == expected_middleware_spans [idx ]
718+ )
719+ idx += 1
690720
691- def test_middleware_spans_disabled (sentry_init , capture_events ):
721+
722+ @pytest .mark .parametrize ("span_streaming" , [True , False ])
723+ def test_middleware_spans_disabled (
724+ sentry_init , capture_events , capture_items , span_streaming
725+ ):
692726 sentry_init (
693727 traces_sample_rate = 1.0 ,
694728 integrations = [StarletteIntegration (middleware_spans = False )],
729+ _experiments = {
730+ "trace_lifecycle" : "stream" if span_streaming else "static" ,
731+ },
695732 )
696733 starlette_app = starlette_app_factory (
697734 middleware = [Middleware (AuthenticationMiddleware , backend = BasicAuthBackend ())]
698735 )
699- events = capture_events ()
736+
737+ if span_streaming :
738+ items = capture_items ("span" )
739+ else :
740+ events = capture_events ()
700741
701742 client = TestClient (starlette_app , raise_server_exceptions = False )
702743 try :
703744 client .get ("/message" , auth = ("Gabriela" , "hello123" ))
704745 except Exception :
705746 pass
706747
707- (_ , transaction_event ) = events
708-
709- assert len (transaction_event ["spans" ]) == 0
748+ if span_streaming :
749+ sentry_sdk .flush ()
750+
751+ middleware_spans = [
752+ item .payload
753+ for item in items
754+ if item .payload .get ("attributes" , {})
755+ .get ("sentry.op" , "" )
756+ .startswith ("middleware.starlette" )
757+ ]
758+ assert len (middleware_spans ) == 0
759+ else :
760+ (_ , transaction_event ) = events
761+ assert len (transaction_event ["spans" ]) == 0
710762
711763
712- def test_middleware_callback_spans (sentry_init , capture_events ):
764+ @pytest .mark .parametrize ("span_streaming" , [True , False ])
765+ def test_middleware_callback_spans (
766+ sentry_init , capture_events , capture_items , span_streaming
767+ ):
713768 sentry_init (
714769 traces_sample_rate = 1.0 ,
715- integrations = [StarletteIntegration ()],
770+ integrations = [StarletteIntegration (middleware_spans = True )],
771+ _experiments = {
772+ "trace_lifecycle" : "stream" if span_streaming else "static" ,
773+ },
716774 )
717775 starlette_app = starlette_app_factory (middleware = [Middleware (SampleMiddleware )])
718- events = capture_events ()
776+
777+ if span_streaming :
778+ items = capture_items ("span" )
779+ else :
780+ events = capture_events ()
719781
720782 client = TestClient (starlette_app , raise_server_exceptions = False )
721783 try :
722784 client .get ("/message" , auth = ("Gabriela" , "hello123" ))
723785 except Exception :
724786 pass
725787
726- (_ , transaction_event ) = events
727-
728788 expected = [
729789 {
730790 "op" : "middleware.starlette" ,
@@ -773,12 +833,37 @@ def test_middleware_callback_spans(sentry_init, capture_events):
773833 },
774834 ]
775835
776- idx = 0
777- for span in transaction_event ["spans" ]:
778- assert span ["op" ] == expected [idx ]["op" ]
779- assert span ["description" ] == expected [idx ]["description" ]
780- assert span ["tags" ] == expected [idx ]["tags" ]
781- idx += 1
836+ if span_streaming :
837+ sentry_sdk .flush ()
838+
839+ middleware_spans = sorted (
840+ [
841+ item .payload
842+ for item in items
843+ if item .payload .get ("attributes" , {})
844+ .get ("sentry.op" , "" )
845+ .startswith ("middleware.starlette" )
846+ ],
847+ key = lambda s : s ["start_timestamp" ],
848+ )
849+
850+ assert len (middleware_spans ) == len (expected )
851+ for span , exp in zip (middleware_spans , expected ):
852+ assert span ["attributes" ]["sentry.op" ] == exp ["op" ]
853+ assert span ["name" ] == exp ["description" ]
854+ assert (
855+ span ["attributes" ]["starlette.middleware_name" ]
856+ == exp ["tags" ]["starlette.middleware_name" ]
857+ )
858+ else :
859+ (_ , transaction_event ) = events
860+
861+ idx = 0
862+ for span in transaction_event ["spans" ]:
863+ assert span ["op" ] == expected [idx ]["op" ]
864+ assert span ["description" ] == expected [idx ]["description" ]
865+ assert span ["tags" ] == expected [idx ]["tags" ]
866+ idx += 1
782867
783868
784869def test_middleware_receive_send (sentry_init , capture_events ):
@@ -946,6 +1031,31 @@ def test_active_thread_id(sentry_init, capture_envelopes, teardown_profiling, en
9461031 assert str (data ["active" ]) == trace_context ["data" ]["thread.id" ]
9471032
9481033
1034+ @pytest .mark .parametrize ("endpoint" , ["/sync/thread_ids" , "/async/thread_ids" ])
1035+ def test_active_thread_id_span_streaming (sentry_init , capture_items , endpoint ):
1036+ sentry_init (
1037+ auto_enabling_integrations = False , # avoid legacy spans from auto-enabled integrations leaking into streaming mode
1038+ integrations = [StarletteIntegration ()],
1039+ traces_sample_rate = 1.0 ,
1040+ _experiments = {"trace_lifecycle" : "stream" },
1041+ )
1042+ app = starlette_app_factory ()
1043+
1044+ items = capture_items ("span" )
1045+
1046+ client = TestClient (app )
1047+ response = client .get (endpoint )
1048+ assert response .status_code == 200
1049+
1050+ data = json .loads (response .content )
1051+
1052+ sentry_sdk .flush ()
1053+
1054+ segments = [item .payload for item in items if item .payload .get ("is_segment" )]
1055+ assert len (segments ) == 1
1056+ assert str (data ["active" ]) == segments [0 ]["attributes" ]["thread.id" ]
1057+
1058+
9491059def test_original_request_not_scrubbed (sentry_init , capture_events ):
9501060 sentry_init (integrations = [StarletteIntegration ()])
9511061
@@ -1167,27 +1277,43 @@ def test_transaction_name_in_middleware(
11671277 )
11681278
11691279
1170- def test_span_origin (sentry_init , capture_events ):
1280+ @pytest .mark .parametrize ("span_streaming" , [True , False ])
1281+ def test_span_origin (sentry_init , capture_events , capture_items , span_streaming ):
11711282 sentry_init (
1172- integrations = [StarletteIntegration ()],
1283+ auto_enabling_integrations = False , # avoid httpx auto-instrumentation leaking spans
1284+ integrations = [StarletteIntegration (middleware_spans = True )],
11731285 traces_sample_rate = 1.0 ,
1286+ _experiments = {
1287+ "trace_lifecycle" : "stream" if span_streaming else "static" ,
1288+ },
11741289 )
11751290 starlette_app = starlette_app_factory (
11761291 middleware = [Middleware (AuthenticationMiddleware , backend = BasicAuthBackend ())]
11771292 )
1178- events = capture_events ()
1293+
1294+ if span_streaming :
1295+ items = capture_items ("span" )
1296+ else :
1297+ events = capture_events ()
11791298
11801299 client = TestClient (starlette_app , raise_server_exceptions = False )
11811300 try :
11821301 client .get ("/message" , auth = ("Gabriela" , "hello123" ))
11831302 except Exception :
11841303 pass
11851304
1186- (_ , event ) = events
1305+ if span_streaming :
1306+ sentry_sdk .flush ()
1307+
1308+ assert len (items ) > 0
1309+ for item in items :
1310+ assert item .payload ["attributes" ]["sentry.origin" ] == "auto.http.starlette"
1311+ else :
1312+ (_ , event ) = events
11871313
1188- assert event ["contexts" ]["trace" ]["origin" ] == "auto.http.starlette"
1189- for span in event ["spans" ]:
1190- assert span ["origin" ] == "auto.http.starlette"
1314+ assert event ["contexts" ]["trace" ]["origin" ] == "auto.http.starlette"
1315+ for span in event ["spans" ]:
1316+ assert span ["origin" ] == "auto.http.starlette"
11911317
11921318
11931319class NonIterableContainer :
0 commit comments