|
26 | 26 | except ImportError: |
27 | 27 | pytest.skip("No GRPC", allow_module_level=True) |
28 | 28 |
|
| 29 | +from google.api_core import ( |
| 30 | + client_options as client_options_lib, |
| 31 | +) |
29 | 32 | from google.api_core import ( |
30 | 33 | exceptions, |
31 | 34 | gapic_v1, |
32 | 35 | grpc_helpers_async, |
33 | 36 | retry_async, |
34 | 37 | timeout, |
35 | 38 | ) |
| 39 | +from google.api_core.gapic_v1 import client_info |
| 40 | +from tests.helpers import assert_uninstrumented_gapic_callable |
36 | 41 |
|
37 | 42 |
|
38 | 43 | def _utcnow_monotonic(): |
@@ -276,50 +281,104 @@ async def test_wrap_method_without_wrap_errors(): |
276 | 281 | method.assert_not_called() |
277 | 282 |
|
278 | 283 |
|
| 284 | +_ASYNC_SERVICE_DEFAULT_SPAN_ATTRIBUTES = { |
| 285 | + "rpc.system": "grpc", |
| 286 | + "rpc.service": "google.test.AsyncService", |
| 287 | + "rpc.method": "AsyncMethod", |
| 288 | + "gcp.client.service": "AsyncService", |
| 289 | + "gcp.client.repo": "googleapis/google-cloud-python", |
| 290 | +} |
| 291 | + |
| 292 | + |
279 | 293 | @pytest.mark.asyncio |
280 | | -async def test_wrap_method_async_with_otel_tracing(monkeypatch): |
281 | | - import sys |
| 294 | +async def test_wrap_method_async_with_otel_tracing(mock_otel): |
| 295 | + """Proves that method_async.wrap_method creates a T3 span upon invocation.""" |
| 296 | + fake_call = grpc_helpers_async.FakeUnaryUnaryCall(42) |
| 297 | + method = mock.Mock(spec=aio.UnaryUnaryMultiCallable, return_value=fake_call) |
| 298 | + |
| 299 | + wrapped_method = gapic_v1.method_async.wrap_method( |
| 300 | + method, |
| 301 | + method_name="google.test.AsyncService/AsyncMethod", |
| 302 | + ) |
| 303 | + result = await wrapped_method(1, 2, meep="moop") |
282 | 304 |
|
283 | | - monkeypatch.setenv("GOOGLE_SDK_EXPERIMENTAL_PYTHON_TRACING_ENABLED", "true") |
| 305 | + assert result == 42 |
| 306 | + mock_otel.tracer.start_as_current_span.assert_called_once_with( |
| 307 | + "google.test.AsyncService/AsyncMethod", |
| 308 | + kind="CLIENT", |
| 309 | + attributes=_ASYNC_SERVICE_DEFAULT_SPAN_ATTRIBUTES, |
| 310 | + ) |
| 311 | + |
| 312 | + |
| 313 | +@pytest.mark.asyncio |
| 314 | +async def test_wrap_method_async_otel_tracing_streaming_skips_span(mock_otel): |
| 315 | + """Proves that method_async.wrap_method with is_streaming=True skips span creation.""" |
284 | 316 | fake_call = grpc_helpers_async.FakeUnaryUnaryCall(42) |
285 | 317 | method = mock.Mock(spec=aio.UnaryUnaryMultiCallable, return_value=fake_call) |
286 | 318 |
|
287 | | - mock_span = mock.MagicMock() |
288 | | - mock_tracer = mock.MagicMock() |
289 | | - mock_tracer.start_as_current_span.return_value.__enter__.return_value = mock_span |
| 319 | + wrapped = gapic_v1.method_async.wrap_method( |
| 320 | + method, |
| 321 | + method_name="google.test.AsyncService/AsyncMethod", |
| 322 | + is_streaming=True, |
| 323 | + ) |
| 324 | + result = await wrapped(1, 2) |
| 325 | + |
| 326 | + assert_uninstrumented_gapic_callable( |
| 327 | + wrapped, |
| 328 | + result, |
| 329 | + method, |
| 330 | + mock_trace=mock_otel.trace, |
| 331 | + expected_result=42, |
| 332 | + ) |
290 | 333 |
|
291 | | - mock_trace = mock.Mock() |
292 | | - mock_trace.get_tracer.return_value = mock_tracer |
293 | | - mock_trace.SpanKind.CLIENT = "CLIENT" |
294 | 334 |
|
295 | | - with ( |
296 | | - mock.patch( |
297 | | - "google.api_core._observability.is_otel_capabilities_enabled", |
298 | | - return_value=True, |
299 | | - ), |
300 | | - mock.patch.dict( |
301 | | - sys.modules, |
302 | | - { |
303 | | - "opentelemetry": mock.Mock(trace=mock_trace), |
304 | | - "opentelemetry.trace": mock_trace, |
305 | | - }, |
306 | | - ), |
307 | | - ): |
308 | | - wrapped_method = gapic_v1.method_async.wrap_method( |
309 | | - method, |
310 | | - method_name="google.test.AsyncService/AsyncMethod", |
311 | | - ) |
312 | | - result = await wrapped_method(1, 2, meep="moop") |
| 335 | +@pytest.mark.asyncio |
| 336 | +async def test_wrap_method_async_otel_tracing_custom_client_options(mock_otel): |
| 337 | + """Proves that method_async.wrap_method forwards custom client_options tracer_provider.""" |
| 338 | + fake_call = grpc_helpers_async.FakeUnaryUnaryCall(42) |
| 339 | + method = mock.Mock(spec=aio.UnaryUnaryMultiCallable, return_value=fake_call) |
| 340 | + |
| 341 | + mock_provider = mock.Mock() |
| 342 | + mock_provider.get_tracer.return_value = mock_otel.tracer |
| 343 | + client_options = client_options_lib.ClientOptions(tracer_provider=mock_provider) |
| 344 | + |
| 345 | + wrapped = gapic_v1.method_async.wrap_method( |
| 346 | + method, |
| 347 | + client_options=client_options, |
| 348 | + method_name="google.test.AsyncService/AsyncMethod", |
| 349 | + ) |
| 350 | + result = await wrapped(1, 2) |
| 351 | + |
| 352 | + assert result == 42 |
| 353 | + mock_provider.get_tracer.assert_called_once_with("google.api_core") |
| 354 | + mock_otel.tracer.start_as_current_span.assert_called_once_with( |
| 355 | + "google.test.AsyncService/AsyncMethod", |
| 356 | + kind="CLIENT", |
| 357 | + attributes=_ASYNC_SERVICE_DEFAULT_SPAN_ATTRIBUTES, |
| 358 | + ) |
| 359 | + |
| 360 | + |
| 361 | +@pytest.mark.asyncio |
| 362 | +async def test_wrap_method_async_otel_tracing_with_client_info(mock_otel): |
| 363 | + """Proves that method_async.wrap_method passes client_info to _GapicCallable.""" |
| 364 | + fake_call = grpc_helpers_async.FakeUnaryUnaryCall(42) |
| 365 | + method = mock.Mock(spec=aio.UnaryUnaryMultiCallable, return_value=fake_call) |
| 366 | + |
| 367 | + info = client_info.ClientInfo(client_library_version="3.0.0") |
| 368 | + |
| 369 | + wrapped = gapic_v1.method_async.wrap_method( |
| 370 | + method, |
| 371 | + client_info=info, |
| 372 | + method_name="google.test.AsyncService/AsyncMethod", |
| 373 | + ) |
| 374 | + result = await wrapped(1, 2) |
313 | 375 |
|
314 | 376 | assert result == 42 |
315 | | - mock_tracer.start_as_current_span.assert_called_once_with( |
| 377 | + mock_otel.tracer.start_as_current_span.assert_called_once_with( |
316 | 378 | "google.test.AsyncService/AsyncMethod", |
317 | 379 | kind="CLIENT", |
318 | 380 | attributes={ |
319 | | - "rpc.system": "grpc", |
320 | | - "rpc.service": "google.test.AsyncService", |
321 | | - "rpc.method": "AsyncMethod", |
322 | | - "gcp.client.service": "AsyncService", |
323 | | - "gcp.client.repo": "googleapis/google-cloud-python", |
| 381 | + **_ASYNC_SERVICE_DEFAULT_SPAN_ATTRIBUTES, |
| 382 | + "gcp.client.version": "3.0.0", |
324 | 383 | }, |
325 | 384 | ) |
0 commit comments