Skip to content

Commit f18bad4

Browse files
committed
Add tests
1 parent b9bd172 commit f18bad4

3 files changed

Lines changed: 223 additions & 1 deletion

File tree

google/cloud/storage/internal/async/connection_tracing_test.cc

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ using ::google::cloud::testing_util::EventNamed;
4545
using ::google::cloud::testing_util::InstallSpanCatcher;
4646
using ::google::cloud::testing_util::IsOk;
4747
using ::google::cloud::testing_util::IsOkAndHolds;
48+
using ::google::cloud::testing_util::OTelAttribute;
4849
using ::google::cloud::testing_util::OTelContextCaptured;
4950
using ::google::cloud::testing_util::PromiseWithOTelContext;
51+
using ::google::cloud::testing_util::SpanHasAttributes;
5052
using ::google::cloud::testing_util::SpanHasEvents;
5153
using ::google::cloud::testing_util::SpanHasInstrumentationScope;
5254
using ::google::cloud::testing_util::SpanKindIsClient;
@@ -763,6 +765,209 @@ TEST(ConnectionTracing, GetBucketError) {
763765
SpanHasInstrumentationScope(), SpanKindIsClient())));
764766
}
765767

768+
TEST(ConnectionTracing, GetBucketSpanEnrichment) {
769+
auto span_catcher = InstallSpanCatcher();
770+
PromiseWithOTelContext<StatusOr<google::storage::v2::Bucket>> p;
771+
772+
auto options =
773+
TracingEnabled()
774+
.set<google::cloud::storage_experimental::OTelSpanEnrichmentOption>(
775+
true);
776+
auto mock = std::make_unique<MockAsyncConnection>();
777+
EXPECT_CALL(*mock, options).WillRepeatedly(Return(options));
778+
EXPECT_CALL(*mock, GetBucket).WillOnce(expect_context(p));
779+
auto actual = MakeTracingAsyncConnection(std::move(mock));
780+
781+
google::storage::v2::GetBucketRequest req;
782+
req.set_name("projects/_/buckets/test-bucket");
783+
auto result =
784+
actual->GetBucket({std::move(req), options}).then(expect_no_context);
785+
786+
google::storage::v2::Bucket bucket_meta;
787+
bucket_meta.set_project("projects/123456");
788+
bucket_meta.set_location("us-east1");
789+
bucket_meta.set_location_type("regional");
790+
p.set_value(make_status_or(std::move(bucket_meta)));
791+
ASSERT_STATUS_OK(result.get());
792+
793+
auto spans = span_catcher->GetSpans();
794+
EXPECT_THAT(
795+
spans,
796+
ElementsAre(AllOf(
797+
SpanNamed("storage::AsyncConnection::GetBucket"),
798+
SpanWithStatus(opentelemetry::trace::StatusCode::kOk),
799+
SpanHasInstrumentationScope(), SpanKindIsClient(),
800+
SpanHasAttributes(
801+
OTelAttribute<std::string>("gcp.resource.destination.id",
802+
"projects/123456/buckets/test-bucket"),
803+
OTelAttribute<std::string>("gcp.resource.destination.location",
804+
"us-east1")))));
805+
}
806+
807+
TEST(ConnectionTracing, BucketMetadataCacheSuccess) {
808+
auto span_catcher = InstallSpanCatcher();
809+
PromiseWithOTelContext<Status> delete_promise_1;
810+
PromiseWithOTelContext<StatusOr<google::storage::v2::Bucket>> bucket_promise;
811+
PromiseWithOTelContext<Status> delete_promise_2;
812+
813+
auto options =
814+
TracingEnabled()
815+
.set<google::cloud::storage_experimental::OTelSpanEnrichmentOption>(
816+
true);
817+
818+
auto mock = std::make_unique<MockAsyncConnection>();
819+
EXPECT_CALL(*mock, options).WillRepeatedly(Return(options));
820+
821+
// 1st call: DeleteObject on a cache miss triggers background GetBucket
822+
EXPECT_CALL(*mock, DeleteObject)
823+
.WillOnce(expect_context(delete_promise_1))
824+
.WillOnce(expect_context(delete_promise_2));
825+
826+
EXPECT_CALL(*mock, GetBucket).WillOnce([&](auto const&) {
827+
return bucket_promise.get_future();
828+
});
829+
830+
auto actual = MakeTracingAsyncConnection(std::move(mock));
831+
832+
google::storage::v2::DeleteObjectRequest req;
833+
req.set_bucket("projects/_/buckets/test-bucket");
834+
req.set_object("test-object-1");
835+
836+
auto res1 = actual->DeleteObject({req, options}).then(expect_no_context);
837+
delete_promise_1.set_value(Status{});
838+
ASSERT_STATUS_OK(res1.get());
839+
840+
// Complete the background GetBucket fetch to populate the cache
841+
google::storage::v2::Bucket bucket_meta;
842+
bucket_meta.set_project("projects/123456");
843+
bucket_meta.set_location("us-east1");
844+
bucket_meta.set_location_type("regional");
845+
bucket_promise.set_value(make_status_or(std::move(bucket_meta)));
846+
847+
// Clear spans from the first operation and background GetBucket
848+
(void)span_catcher->GetSpans();
849+
850+
// 2nd call: DeleteObject hits the cache and span is enriched
851+
req.set_object("test-object-2");
852+
auto res2 = actual->DeleteObject({req, options}).then(expect_no_context);
853+
delete_promise_2.set_value(Status{});
854+
ASSERT_STATUS_OK(res2.get());
855+
856+
auto spans = span_catcher->GetSpans();
857+
EXPECT_THAT(
858+
spans,
859+
ElementsAre(AllOf(
860+
SpanNamed("storage::AsyncConnection::DeleteObject"),
861+
SpanWithStatus(opentelemetry::trace::StatusCode::kOk),
862+
SpanHasInstrumentationScope(), SpanKindIsClient(),
863+
SpanHasAttributes(
864+
OTelAttribute<std::string>("gcp.resource.destination.id",
865+
"projects/123456/buckets/test-bucket"),
866+
OTelAttribute<std::string>("gcp.resource.destination.location",
867+
"us-east1")))));
868+
}
869+
870+
TEST(ConnectionTracing, GetBucketMaybeInvalidateEvicts) {
871+
auto span_catcher = InstallSpanCatcher();
872+
PromiseWithOTelContext<StatusOr<google::storage::v2::Bucket>> p1;
873+
PromiseWithOTelContext<StatusOr<google::storage::v2::Bucket>> p2;
874+
875+
auto options =
876+
TracingEnabled()
877+
.set<google::cloud::storage_experimental::OTelSpanEnrichmentOption>(
878+
true);
879+
auto mock = std::make_unique<MockAsyncConnection>();
880+
EXPECT_CALL(*mock, options).WillRepeatedly(Return(options));
881+
882+
EXPECT_CALL(*mock, GetBucket)
883+
.WillOnce(expect_context(p1))
884+
.WillOnce(expect_context(p2));
885+
886+
auto actual = MakeTracingAsyncConnection(std::move(mock));
887+
888+
// 1st call populates the cache
889+
google::storage::v2::GetBucketRequest req;
890+
req.set_name("projects/_/buckets/test-bucket");
891+
auto res1 = actual->GetBucket({req, options}).then(expect_no_context);
892+
google::storage::v2::Bucket bucket_meta;
893+
bucket_meta.set_project("projects/123456");
894+
bucket_meta.set_location("us-east1");
895+
bucket_meta.set_location_type("regional");
896+
p1.set_value(make_status_or(std::move(bucket_meta)));
897+
ASSERT_STATUS_OK(res1.get());
898+
899+
// 2nd call returns kNotFound and evicts the cached metadata
900+
auto res2 = actual->GetBucket({req, options}).then(expect_no_context);
901+
p2.set_value(StatusOr<google::storage::v2::Bucket>(
902+
Status(StatusCode::kNotFound, "not found")));
903+
EXPECT_THAT(res2.get(), StatusIs(StatusCode::kNotFound));
904+
}
905+
906+
TEST(ConnectionTracing, DeleteObjectNoEvictOnError) {
907+
auto span_catcher = InstallSpanCatcher();
908+
PromiseWithOTelContext<StatusOr<google::storage::v2::Bucket>> bucket_p;
909+
PromiseWithOTelContext<Status> delete_p1;
910+
PromiseWithOTelContext<Status> delete_p2;
911+
912+
auto options =
913+
TracingEnabled()
914+
.set<google::cloud::storage_experimental::OTelSpanEnrichmentOption>(
915+
true);
916+
auto mock = std::make_unique<MockAsyncConnection>();
917+
EXPECT_CALL(*mock, options).WillRepeatedly(Return(options));
918+
919+
EXPECT_CALL(*mock, GetBucket).WillOnce(expect_context(bucket_p));
920+
EXPECT_CALL(*mock, DeleteObject)
921+
.WillOnce(expect_context(delete_p1))
922+
.WillOnce(expect_context(delete_p2));
923+
924+
auto actual = MakeTracingAsyncConnection(std::move(mock));
925+
926+
// 1st call: GetBucket populates the cache
927+
google::storage::v2::GetBucketRequest bucket_req;
928+
bucket_req.set_name("projects/_/buckets/test-bucket");
929+
auto res_bucket =
930+
actual->GetBucket({bucket_req, options}).then(expect_no_context);
931+
google::storage::v2::Bucket bucket_meta;
932+
bucket_meta.set_project("projects/123456");
933+
bucket_meta.set_location("us-east1");
934+
bucket_meta.set_location_type("regional");
935+
bucket_p.set_value(make_status_or(std::move(bucket_meta)));
936+
ASSERT_STATUS_OK(res_bucket.get());
937+
938+
// 2nd call: DeleteObject fails with kNotFound (object missing)
939+
google::storage::v2::DeleteObjectRequest del_req;
940+
del_req.set_bucket("projects/_/buckets/test-bucket");
941+
del_req.set_object("missing-object");
942+
auto res_del1 =
943+
actual->DeleteObject({del_req, options}).then(expect_no_context);
944+
delete_p1.set_value(Status(StatusCode::kNotFound, "not found"));
945+
EXPECT_THAT(res_del1.get(), StatusIs(StatusCode::kNotFound));
946+
947+
// Clear spans to verify next span enrichment
948+
(void)span_catcher->GetSpans();
949+
950+
// 3rd call: DeleteObject proves bucket metadata was NOT evicted by object
951+
// error
952+
del_req.set_object("another-object");
953+
auto res_del2 =
954+
actual->DeleteObject({del_req, options}).then(expect_no_context);
955+
delete_p2.set_value(Status{});
956+
ASSERT_STATUS_OK(res_del2.get());
957+
958+
auto spans = span_catcher->GetSpans();
959+
EXPECT_THAT(
960+
spans,
961+
ElementsAre(AllOf(
962+
SpanNamed("storage::AsyncConnection::DeleteObject"),
963+
SpanWithStatus(opentelemetry::trace::StatusCode::kOk),
964+
SpanHasAttributes(
965+
OTelAttribute<std::string>("gcp.resource.destination.id",
966+
"projects/123456/buckets/test-bucket"),
967+
OTelAttribute<std::string>("gcp.resource.destination.location",
968+
"us-east1")))));
969+
}
970+
766971
} // namespace
767972
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
768973
} // namespace storage_internal

google/cloud/storage/internal/async/default_options.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "google/cloud/storage/async/retry_policy.h"
2020
#include "google/cloud/storage/async/writer_connection.h"
2121
#include "google/cloud/storage/internal/grpc/default_options.h"
22+
#include "google/cloud/storage/options.h"
2223
#include <limits>
2324

2425
namespace google {
@@ -76,7 +77,8 @@ Options DefaultOptionsAsync(Options opts) {
7677
storage::MakeStrictAsyncIdempotencyPolicy)
7778
.set<storage::EnableCrc32cValidationOption>(true)
7879
.set<storage::MaximumRangeSizeOption>(128 * 1024 * 1024L)
79-
.set<storage::EnableMultiStreamOptimizationOption>(true));
80+
.set<storage::EnableMultiStreamOptimizationOption>(true)
81+
.set<storage_experimental::OTelSpanEnrichmentOption>(true));
8082
return Adjust(DefaultOptionsGrpc(std::move(opts)));
8183
}
8284

google/cloud/storage/internal/async/default_options_test.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "google/cloud/storage/async/options.h"
2222
#include "google/cloud/storage/async/resume_policy.h"
2323
#include "google/cloud/storage/async/writer_connection.h"
24+
#include "google/cloud/storage/options.h"
2425
#include "google/cloud/common_options.h"
2526
#include <gmock/gmock.h>
2627

@@ -109,6 +110,20 @@ TEST(DefaultOptionsAsync, EnableMultiStreamOptimizationOption) {
109110
updated_options.get<storage::EnableMultiStreamOptimizationOption>());
110111
}
111112

113+
TEST(DefaultOptionsAsync, OTelSpanEnrichmentOption) {
114+
auto const options = DefaultOptionsAsync({});
115+
EXPECT_TRUE(options.get<
116+
google::cloud::storage_experimental::OTelSpanEnrichmentOption>());
117+
118+
auto const updated_options = DefaultOptionsAsync(
119+
Options{}
120+
.set<google::cloud::storage_experimental::OTelSpanEnrichmentOption>(
121+
false));
122+
EXPECT_FALSE(
123+
updated_options.get<
124+
google::cloud::storage_experimental::OTelSpanEnrichmentOption>());
125+
}
126+
112127
} // namespace
113128
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
114129
} // namespace storage_internal

0 commit comments

Comments
 (0)