3232
3333grpc = pytest .importorskip ("grpc" ) # isort:skip
3434
35+ import asyncio
36+ from concurrent import futures
37+
38+ import elasticapm
3539from elasticapm .conf import constants
3640from elasticapm .conf .constants import TRANSACTION
3741from elasticapm .traces import capture_span
42+ from elasticapm import Client
43+ from elasticapm .contrib .grpc .client_interceptor import _ClientInterceptor
44+ from elasticapm .contrib .grpc .server_interceptor import _ServerInterceptor
45+ from elasticapm .contrib .grpc .async_server_interceptor import _AsyncServerInterceptor
46+ from tests .fixtures import TempStoreClient , instrument
47+ from tests .instrumentation .test_pb2 import UnaryUnaryRequest , UnaryUnaryResponse
48+ from tests .instrumentation .test_pb2_grpc import TestServiceServicer , TestServiceStub , add_TestServiceServicer_to_server
3849
3950pytestmark = pytest .mark .grpc
4051
4152
53+ class TestService (TestServiceServicer ):
54+ def UnaryUnary (self , request , context ):
55+ return UnaryUnaryResponse (message = request .message )
56+
57+
58+ @pytest .fixture
59+ def elasticapm_client ():
60+ return TempStoreClient ()
61+
62+
4263def test_grpc_client_instrumentation (instrument , elasticapm_client ):
43- """Test that gRPC client instrumentation creates transactions and adds interceptors"""
44- # Create a test channel
45- channel = grpc .insecure_channel ("localhost:50051" )
46-
47- # Verify that the channel was created with our interceptor
64+ """Test that gRPC client instrumentation adds interceptors"""
65+ elasticapm_client .begin_transaction ("test" )
66+ with capture_span ("test_grpc_client" , "test" ):
67+ elasticapm .instrument () # Ensure instrumentation is done before channel creation
68+ channel = grpc .insecure_channel ("localhost:50051" )
69+ elasticapm_client .end_transaction ("MyView" )
70+
71+ # Verify that the channel has the interceptor
4872 assert hasattr (channel , "_interceptor" )
49- assert channel ._interceptor .__class__ .__name__ == "_ClientInterceptor"
50-
51- # Verify transaction was created
52- transaction = elasticapm_client .events [TRANSACTION ][0 ]
53- assert transaction ["type" ] == "script"
54- assert transaction ["name" ] == "grpc_client_instrumentation"
73+ assert isinstance (channel ._interceptor , _ClientInterceptor )
5574
5675
5776def test_grpc_secure_channel_instrumentation (instrument , elasticapm_client ):
58- """Test that secure channel instrumentation works correctly"""
59- # Create a secure channel
60- channel = grpc .secure_channel ("localhost:50051" , grpc .local_channel_credentials ())
61-
62- # Verify that the channel was created with our interceptor
77+ """Test that secure channel instrumentation adds interceptors"""
78+ elasticapm_client .begin_transaction ("test" )
79+ with capture_span ("test_grpc_secure_channel" , "test" ):
80+ elasticapm .instrument () # Ensure instrumentation is done before channel creation
81+ channel = grpc .secure_channel ("localhost:50051" , grpc .local_channel_credentials ())
82+ elasticapm_client .end_transaction ("MyView" )
83+
84+ # Verify that the channel has the interceptor
6385 assert hasattr (channel , "_interceptor" )
64- assert channel ._interceptor .__class__ .__name__ == "_ClientInterceptor"
65-
66- # Verify transaction was created
67- transaction = elasticapm_client .events [TRANSACTION ][0 ]
68- assert transaction ["type" ] == "script"
69- assert transaction ["name" ] == "grpc_client_instrumentation"
86+ assert isinstance (channel ._interceptor , _ClientInterceptor )
7087
7188
7289def test_grpc_server_instrumentation (instrument , elasticapm_client ):
7390 """Test that gRPC server instrumentation adds interceptors"""
7491 # Create a test server
75- server = grpc .server (None )
76-
77- # Verify that the server was created with our interceptor
78- assert len (server ._interceptors ) > 0
79- assert server ._interceptors [0 ].__class__ .__name__ == "_ServerInterceptor"
80-
81- # Verify transaction was created
82- transaction = elasticapm_client .events [TRANSACTION ][0 ]
83- assert transaction ["type" ] == "script"
84- assert transaction ["name" ] == "grpc_server_instrumentation"
85-
86-
87- def test_grpc_async_server_instrumentation (instrument , elasticapm_client ):
92+ elasticapm_client .begin_transaction ("test" )
93+ with capture_span ("test_grpc_server" , "test" ):
94+ elasticapm .instrument () # Ensure instrumentation is done before server creation
95+ server = grpc .server (futures .ThreadPoolExecutor (max_workers = 1 ))
96+ port = server .add_insecure_port ("[::]:0" ) # Let the OS choose a port
97+ servicer = TestService ()
98+ add_TestServiceServicer_to_server (servicer , server )
99+ server .start ()
100+ elasticapm_client .end_transaction ("MyView" )
101+
102+ try :
103+ # Make a test call to verify the interceptor is working
104+ channel = grpc .insecure_channel (f"localhost:{ port } " )
105+ stub = TestServiceStub (channel )
106+ response = stub .UnaryUnary (UnaryUnaryRequest (message = "test" ))
107+ assert response .message == "test"
108+
109+ # Verify that a transaction was created for the server call
110+ assert len (elasticapm_client .events ["transaction" ]) == 2 # One for our test, one for the server call
111+ transaction = elasticapm_client .events ["transaction" ][1 ] # Second is from the server interceptor
112+ assert transaction ["name" ] == "/test.TestService/UnaryUnary"
113+ assert transaction ["type" ] == "request"
114+ finally :
115+ server .stop (0 )
116+
117+
118+ @pytest .mark .asyncio
119+ async def test_grpc_async_server_instrumentation (instrument , elasticapm_client ):
88120 """Test that async server instrumentation adds interceptors"""
89121 # Create a test async server
90- server = grpc .aio .server ()
91-
92- # Verify that the server was created with our interceptor
93- assert len (server ._interceptors ) > 0
94- assert server ._interceptors [0 ].__class__ .__name__ == "_AsyncServerInterceptor"
95-
96- # Verify transaction was created
97- transaction = elasticapm_client .events [TRANSACTION ][0 ]
98- assert transaction ["type" ] == "script"
99- assert transaction ["name" ] == "grpc_async_server_instrumentation"
122+ elasticapm_client .begin_transaction ("test" )
123+ with capture_span ("test_grpc_async_server" , "test" ):
124+ elasticapm .instrument () # Ensure instrumentation is done before server creation
125+ server = grpc .aio .server ()
126+ port = server .add_insecure_port ("[::]:0" ) # Let the OS choose a port
127+ servicer = TestService ()
128+ add_TestServiceServicer_to_server (servicer , server )
129+ elasticapm_client .end_transaction ("MyView" )
130+
131+ await server .start ()
132+ try :
133+ # Make a test call to verify the interceptor is working
134+ channel = grpc .aio .insecure_channel (f"localhost:{ port } " )
135+ stub = TestServiceStub (channel )
136+ response = await stub .UnaryUnary (UnaryUnaryRequest (message = "test" ))
137+ assert response .message == "test"
138+
139+ # Verify that a transaction was created for the server call
140+ assert len (elasticapm_client .events ["transaction" ]) == 2 # One for our test, one for the server call
141+ transaction = elasticapm_client .events ["transaction" ][1 ] # Second is from the server interceptor
142+ assert transaction ["name" ] == "/test.TestService/UnaryUnary"
143+ assert transaction ["type" ] == "request"
144+ finally :
145+ await server .stop (0 )
100146
101147
102148def test_grpc_client_target_parsing (instrument , elasticapm_client ):
103- """Test that target parsing works correctly for different formats"""
104- # Test with host:port format
105- channel = grpc .insecure_channel ("localhost:50051" )
106- assert channel ._interceptor .host == "localhost"
107- assert channel ._interceptor .port == 50051
108-
109- # Test with just host format
110- channel = grpc .insecure_channel ("localhost" )
111- assert channel ._interceptor .host == "localhost"
112- assert channel ._interceptor .port is None
113-
114- # Test with invalid port format
115- channel = grpc .insecure_channel ("localhost:invalid" )
116- assert channel ._interceptor .host == "localhost"
117- assert channel ._interceptor .port is None
149+ """Test that gRPC client target parsing works correctly"""
150+ elasticapm_client .begin_transaction ("test" )
151+ with capture_span ("test_grpc_client_target" , "test" ):
152+ elasticapm .instrument () # Ensure instrumentation is done before channel creation
153+ channel = grpc .insecure_channel ("localhost:50051" )
154+ elasticapm_client .end_transaction ("MyView" )
155+
156+ # Verify that the channel has the interceptor
157+ assert hasattr (channel , "_interceptor" )
158+ assert isinstance (channel ._interceptor , _ClientInterceptor )
0 commit comments