// // // Copyright 2023 gRPC authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // #include "src/cpp/ext/otel/otel_plugin.h" #include "absl/functional/any_invocable.h" #include "api/include/opentelemetry/metrics/provider.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "opentelemetry/sdk/metrics/meter_provider.h" #include "opentelemetry/sdk/metrics/metric_reader.h" #include #include "src/core/lib/channel/call_tracer.h" #include "src/core/lib/config/core_configuration.h" #include "test/core/util/test_config.h" #include "test/cpp/end2end/test_service_impl.h" #include "test/cpp/ext/otel/otel_test_library.h" namespace grpc { namespace testing { namespace { TEST(OTelPluginBuildTest, ApiDependency) { opentelemetry::metrics::Provider::GetMeterProvider(); } TEST(OTelPluginBuildTest, SdkDependency) { opentelemetry::sdk::metrics::MeterProvider(); } TEST_F(OTelPluginEnd2EndTest, ClientAttemptStarted) { Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}); SendRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } TEST_F(OTelPluginEnd2EndTest, ClientAttemptDuration) { Init({grpc::internal::OTelClientAttemptDurationInstrumentName()}); SendRPC(); const char* kMetricName = "grpc.client.attempt.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 3); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } TEST_F(OTelPluginEnd2EndTest, ClientAttemptSentTotalCompressedMessageSize) { Init({grpc::internal:: OTelClientAttemptSentTotalCompressedMessageSizeInstrumentName()}); SendRPC(); const char* kMetricName = "grpc.client.attempt.sent_total_compressed_message_size"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); ASSERT_EQ(absl::get(point_data->max_), 5); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 3); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } TEST_F(OTelPluginEnd2EndTest, ClientAttemptRcvdTotalCompressedMessageSize) { Init({grpc::internal:: OTelClientAttemptRcvdTotalCompressedMessageSizeInstrumentName()}); SendRPC(); const char* kMetricName = "grpc.client.attempt.rcvd_total_compressed_message_size"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); ASSERT_EQ(absl::get(point_data->max_), 5); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 3); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } TEST_F(OTelPluginEnd2EndTest, ServerCallStarted) { Init({grpc::internal::OTelServerCallStartedInstrumentName()}); SendRPC(); const char* kMetricName = "grpc.server.call.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto server_started_value = absl::get_if(&point_data->value_); ASSERT_NE(server_started_value, nullptr); ASSERT_EQ(*server_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 1); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); } TEST_F(OTelPluginEnd2EndTest, ServerCallDuration) { Init({grpc::internal::OTelServerCallDurationInstrumentName()}); SendRPC(); const char* kMetricName = "grpc.server.call.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } TEST_F(OTelPluginEnd2EndTest, ServerCallSentTotalCompressedMessageSize) { Init({grpc::internal:: OTelServerCallSentTotalCompressedMessageSizeInstrumentName()}); SendRPC(); const char* kMetricName = "grpc.server.call.sent_total_compressed_message_size"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); EXPECT_EQ(point_data->count_, 1); ASSERT_EQ(absl::get(point_data->max_), 5); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } TEST_F(OTelPluginEnd2EndTest, ServerCallRcvdTotalCompressedMessageSize) { Init({grpc::internal:: OTelServerCallRcvdTotalCompressedMessageSizeInstrumentName()}); SendRPC(); const char* kMetricName = "grpc.server.call.rcvd_total_compressed_message_size"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); ASSERT_EQ(absl::get(point_data->max_), 5); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } // Make sure that no meter provider results in normal operations. TEST_F(OTelPluginEnd2EndTest, NoMeterProviderRegistered) { Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/true); SendRPC(); } // Test that a channel selector returning true records metrics on the channel. TEST_F(OTelPluginEnd2EndTest, TargetSelectorReturnsTrue) { Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/ opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*target_selector=*/ [](absl::string_view /*target*/) { return true; }); SendRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } // Test that a target selector returning false does not record metrics on the // channel. TEST_F(OTelPluginEnd2EndTest, TargetSelectorReturnsFalse) { Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/ opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*target_selector=*/ [](absl::string_view /*target*/) { return false; }); SendRPC(); auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_TRUE(data.empty()); } // Test that a target attribute filter returning true records metrics with the // target as is on the channel. TEST_F(OTelPluginEnd2EndTest, TargetAttributeFilterReturnsTrue) { Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/ opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*target_selector=*/absl::AnyInvocable(), /*target_attribute_filter=*/[](absl::string_view /*target*/) { return true; }); SendRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } // Test that a target attribute filter returning false records metrics with the // target as "other". TEST_F(OTelPluginEnd2EndTest, TargetAttributeFilterReturnsFalse) { Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/ opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*target_selector=*/absl::AnyInvocable(), /*target_attribute_filter=*/ [server_address = canonical_server_address_]( absl::string_view /*target*/) { return false; }); SendRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, "other"); } // Test that generic method names are scrubbed properly on the client side. TEST_F(OTelPluginEnd2EndTest, GenericClientRpc) { Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}); SendGenericRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, "other"); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } // Test that generic method names are scrubbed properly on the client side if // the method attribute filter is set and it returns false. TEST_F(OTelPluginEnd2EndTest, GenericClientRpcWithMethodAttributeFilterReturningFalse) { Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*target_selector=*/absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ [](absl::string_view /*generic_method*/) { return false; }); SendGenericRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, "other"); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } // Test that generic method names is not scrubbed on the client side if // the method attribute filter is set and it returns true. TEST_F(OTelPluginEnd2EndTest, GenericClientRpcWithMethodAttributeFilterReturningTrue) { Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*target_selector=*/absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ [](absl::string_view /*generic_method*/) { return true; }); SendGenericRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kGenericMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } // Test that generic method names are scrubbed properly on the server side. TEST_F(OTelPluginEnd2EndTest, GenericServerRpc) { Init({grpc::internal::OTelServerCallDurationInstrumentName()}); SendGenericRPC(); const char* kMetricName = "grpc.server.call.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, "other"); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "UNIMPLEMENTED"); } // Test that generic method names are scrubbed properly on the server side if // the method attribute filter is set and it returns false. TEST_F(OTelPluginEnd2EndTest, GenericServerRpcWithMethodAttributeFilterReturningFalse) { Init({grpc::internal::OTelServerCallDurationInstrumentName()}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*target_selector=*/absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ [](absl::string_view /*generic_method*/) { return false; }); SendGenericRPC(); const char* kMetricName = "grpc.server.call.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, "other"); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "UNIMPLEMENTED"); } // Test that generic method names are not scrubbed on the server side if // the method attribute filter is set and it returns true. TEST_F(OTelPluginEnd2EndTest, GenericServerRpcWithMethodAttributeFilterReturningTrue) { Init({grpc::internal::OTelServerCallDurationInstrumentName()}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*target_selector=*/absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ [](absl::string_view /*generic_method*/) { return true; }); SendGenericRPC(); const char* kMetricName = "grpc.server.call.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kGenericMethodName); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "UNIMPLEMENTED"); } } // namespace } // namespace testing } // namespace grpc int main(int argc, char** argv) { grpc::testing::TestEnvironment env(&argc, argv); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }