@@ -446,3 +446,215 @@ def test_wrap_method_otel_tracing_enabled_error(monkeypatch):
446446
447447 mock_span .record_exception .assert_called_once_with (err )
448448 mock_span .set_status .assert_called_once_with ("ERROR" , str (err ))
449+
450+
451+ def test_wrap_method_otel_tracing_bytes_method (monkeypatch ):
452+ """Proves that when raw _method is bytes, it is decoded properly to utf-8."""
453+ monkeypatch .setenv ("GOOGLE_SDK_EXPERIMENTAL_PYTHON_TRACING_ENABLED" , "true" )
454+ mock_target = mock .Mock (return_value = "success" )
455+ mock_target ._method = (
456+ b"/google.cloud.secretmanager.v1.SecretManagerService/ListSecrets"
457+ )
458+
459+ mock_span = mock .MagicMock ()
460+ mock_tracer = mock .MagicMock ()
461+ mock_tracer .start_as_current_span .return_value .__enter__ .return_value = mock_span
462+
463+ mock_trace = mock .Mock ()
464+ mock_trace .get_tracer .return_value = mock_tracer
465+ mock_trace .SpanKind .CLIENT = "CLIENT"
466+
467+ wrapped = google .api_core .gapic_v1 .method .wrap_method (
468+ mock_target , default_timeout = 60
469+ )
470+
471+ with (
472+ mock .patch (
473+ "google.api_core._observability.is_otel_capabilities_enabled" ,
474+ return_value = True ,
475+ ),
476+ mock .patch .dict (
477+ sys .modules ,
478+ {
479+ "opentelemetry" : mock .Mock (trace = mock_trace ),
480+ "opentelemetry.trace" : mock_trace ,
481+ },
482+ ),
483+ ):
484+ result = wrapped ()
485+
486+ assert result == "success"
487+ mock_tracer .start_as_current_span .assert_called_once_with (
488+ "google.cloud.secretmanager.v1.SecretManagerService/ListSecrets" ,
489+ kind = "CLIENT" ,
490+ attributes = {
491+ "rpc.system" : "grpc" ,
492+ "rpc.service" : "google.cloud.secretmanager.v1.SecretManagerService" ,
493+ "rpc.method" : "ListSecrets" ,
494+ },
495+ )
496+
497+
498+ def test_wrap_method_otel_tracing_fallback_with_name (monkeypatch ):
499+ """Proves that when raw _method is absent, fallback uses target.__name__."""
500+ monkeypatch .setenv ("GOOGLE_SDK_EXPERIMENTAL_PYTHON_TRACING_ENABLED" , "true" )
501+
502+ def custom_rpc (* args , ** kwargs ):
503+ return "success"
504+
505+ mock_span = mock .MagicMock ()
506+ mock_tracer = mock .MagicMock ()
507+ mock_tracer .start_as_current_span .return_value .__enter__ .return_value = mock_span
508+
509+ mock_trace = mock .Mock ()
510+ mock_trace .get_tracer .return_value = mock_tracer
511+ mock_trace .SpanKind .CLIENT = "CLIENT"
512+
513+ wrapped = google .api_core .gapic_v1 .method .wrap_method (custom_rpc )
514+
515+ with (
516+ mock .patch (
517+ "google.api_core._observability.is_otel_capabilities_enabled" ,
518+ return_value = True ,
519+ ),
520+ mock .patch .dict (
521+ sys .modules ,
522+ {
523+ "opentelemetry" : mock .Mock (trace = mock_trace ),
524+ "opentelemetry.trace" : mock_trace ,
525+ },
526+ ),
527+ ):
528+ result = wrapped ()
529+
530+ assert result == "success"
531+ mock_tracer .start_as_current_span .assert_called_once_with (
532+ "google.api_core/custom_rpc" ,
533+ kind = "CLIENT" ,
534+ attributes = {
535+ "rpc.system" : "grpc" ,
536+ "rpc.service" : "google.api_core" ,
537+ "rpc.method" : "custom_rpc" ,
538+ },
539+ )
540+
541+
542+ def test_wrap_method_otel_tracing_fallback_without_name (monkeypatch ):
543+ """Proves that when raw _method is absent and target has no explicit __name__,
544+ fallback uses target class name assigned by error wrapper.
545+ """
546+ monkeypatch .setenv ("GOOGLE_SDK_EXPERIMENTAL_PYTHON_TRACING_ENABLED" , "true" )
547+
548+ class TargetWithoutName :
549+ def __call__ (self , * args , ** kwargs ):
550+ return "success"
551+
552+ mock_span = mock .MagicMock ()
553+ mock_tracer = mock .MagicMock ()
554+ mock_tracer .start_as_current_span .return_value .__enter__ .return_value = mock_span
555+
556+ mock_trace = mock .Mock ()
557+ mock_trace .get_tracer .return_value = mock_tracer
558+ mock_trace .SpanKind .CLIENT = "CLIENT"
559+
560+ wrapped = google .api_core .gapic_v1 .method .wrap_method (TargetWithoutName ())
561+
562+ with (
563+ mock .patch (
564+ "google.api_core._observability.is_otel_capabilities_enabled" ,
565+ return_value = True ,
566+ ),
567+ mock .patch .dict (
568+ sys .modules ,
569+ {
570+ "opentelemetry" : mock .Mock (trace = mock_trace ),
571+ "opentelemetry.trace" : mock_trace ,
572+ },
573+ ),
574+ ):
575+ result = wrapped ()
576+
577+ assert result == "success"
578+ mock_tracer .start_as_current_span .assert_called_once_with (
579+ "google.api_core/TargetWithoutName" ,
580+ kind = "CLIENT" ,
581+ attributes = {
582+ "rpc.system" : "grpc" ,
583+ "rpc.service" : "google.api_core" ,
584+ "rpc.method" : "TargetWithoutName" ,
585+ },
586+ )
587+
588+
589+ def test_gapic_callable_otel_tracing_fallback_call_default (monkeypatch ):
590+ """Proves that _GapicCallable defaults method to 'call' if target has no __name__."""
591+ monkeypatch .setenv ("GOOGLE_SDK_EXPERIMENTAL_PYTHON_TRACING_ENABLED" , "true" )
592+
593+ class NoNameTarget :
594+ def __call__ (self , * args , ** kwargs ):
595+ return "success"
596+
597+ target = NoNameTarget ()
598+ callable_obj = google .api_core .gapic_v1 .method ._GapicCallable (
599+ target , None , None , None
600+ )
601+
602+ mock_span = mock .MagicMock ()
603+ mock_tracer = mock .MagicMock ()
604+ mock_tracer .start_as_current_span .return_value .__enter__ .return_value = mock_span
605+
606+ mock_trace = mock .Mock ()
607+ mock_trace .get_tracer .return_value = mock_tracer
608+ mock_trace .SpanKind .CLIENT = "CLIENT"
609+
610+ with (
611+ mock .patch (
612+ "google.api_core._observability.is_otel_capabilities_enabled" ,
613+ return_value = True ,
614+ ),
615+ mock .patch .dict (
616+ sys .modules ,
617+ {
618+ "opentelemetry" : mock .Mock (trace = mock_trace ),
619+ "opentelemetry.trace" : mock_trace ,
620+ },
621+ ),
622+ ):
623+ result = callable_obj ()
624+
625+ assert result == "success"
626+ mock_tracer .start_as_current_span .assert_called_once_with (
627+ "google.api_core/call" ,
628+ kind = "CLIENT" ,
629+ attributes = {
630+ "rpc.system" : "grpc" ,
631+ "rpc.service" : "google.api_core" ,
632+ "rpc.method" : "call" ,
633+ },
634+ )
635+
636+
637+ def test_wrap_method_otel_tracing_import_error (monkeypatch ):
638+ """Proves that if opentelemetry raises ImportError, execution proceeds gracefully."""
639+ monkeypatch .setenv ("GOOGLE_SDK_EXPERIMENTAL_PYTHON_TRACING_ENABLED" , "true" )
640+ mock_target = mock .Mock (return_value = "success" )
641+
642+ wrapped = google .api_core .gapic_v1 .method .wrap_method (mock_target )
643+
644+ with (
645+ mock .patch (
646+ "google.api_core._observability.is_otel_capabilities_enabled" ,
647+ return_value = True ,
648+ ),
649+ mock .patch .dict (
650+ sys .modules ,
651+ {
652+ "opentelemetry" : None ,
653+ "opentelemetry.trace" : None ,
654+ },
655+ ),
656+ ):
657+ result = wrapped ()
658+
659+ assert result == "success"
660+ mock_target .assert_called_once ()
0 commit comments