|
23 | 23 | #include "google/cloud/internal/background_threads_impl.h" |
24 | 24 | #include "google/cloud/testing_util/async_sequencer.h" |
25 | 25 | #include "google/cloud/testing_util/status_matchers.h" |
| 26 | +#include "google/cloud/testing_util/validate_metadata.h" |
26 | 27 | #include <gmock/gmock.h> |
27 | 28 |
|
28 | 29 | namespace google { |
@@ -238,6 +239,65 @@ TEST(RewriterConnectionImplTest, TooManyTransients) { |
238 | 239 | EXPECT_THAT(r1.get(), StatusIs(TransientError().status().code())); |
239 | 240 | } |
240 | 241 |
|
| 242 | +TEST(RewriterConnectionImplTest, IterateReusesIdempotencyTokenOnRetry) { |
| 243 | + google::cloud::testing_util::ValidateMetadataFixture |
| 244 | + validate_metadata_fixture; |
| 245 | + AsyncSequencer<bool> sequencer; |
| 246 | + auto mock = std::make_shared<MockStorageStub>(); |
| 247 | + std::string first_token; |
| 248 | + |
| 249 | + EXPECT_CALL(*mock, AsyncRewriteObject) |
| 250 | + .WillOnce([&](auto&, auto const& context, auto const&, |
| 251 | + google::storage::v2::RewriteObjectRequest const&) { |
| 252 | + auto metadata = validate_metadata_fixture.GetMetadata(*context); |
| 253 | + auto l = metadata.find("x-goog-gcs-idempotency-token"); |
| 254 | + EXPECT_NE(l, metadata.end()); |
| 255 | + if (l != metadata.end()) { |
| 256 | + first_token = l->second; |
| 257 | + EXPECT_FALSE(first_token.empty()); |
| 258 | + } |
| 259 | + |
| 260 | + return sequencer.PushBack("RewriteObject(1)").then([](auto) { |
| 261 | + return TransientError(); |
| 262 | + }); |
| 263 | + }) |
| 264 | + .WillOnce([&](auto&, auto const& context, auto const&, |
| 265 | + google::storage::v2::RewriteObjectRequest const&) { |
| 266 | + auto metadata = validate_metadata_fixture.GetMetadata(*context); |
| 267 | + auto l = metadata.find("x-goog-gcs-idempotency-token"); |
| 268 | + EXPECT_NE(l, metadata.end()); |
| 269 | + if (l != metadata.end()) { |
| 270 | + EXPECT_EQ(l->second, first_token); |
| 271 | + } |
| 272 | + |
| 273 | + return sequencer.PushBack("RewriteObject(2)").then([](auto) { |
| 274 | + google::storage::v2::RewriteResponse response; |
| 275 | + response.set_total_bytes_rewritten(1000); |
| 276 | + response.set_object_size(3000); |
| 277 | + response.set_rewrite_token("test-rewrite-token"); |
| 278 | + return make_status_or(response); |
| 279 | + }); |
| 280 | + }); |
| 281 | + |
| 282 | + internal::AutomaticallyCreatedBackgroundThreads pool(1); |
| 283 | + auto connection = std::make_shared<RewriterConnectionImpl>( |
| 284 | + pool.cq(), std::move(mock), TestOptions(), MakeRequest()); |
| 285 | + |
| 286 | + auto r1 = connection->Iterate(); |
| 287 | + auto next = sequencer.PopFrontWithName(); |
| 288 | + EXPECT_EQ(next.second, "RewriteObject(1)"); |
| 289 | + next.first.set_value(true); |
| 290 | + next = sequencer.PopFrontWithName(); |
| 291 | + EXPECT_EQ(next.second, "RewriteObject(2)"); |
| 292 | + next.first.set_value(true); |
| 293 | + EXPECT_THAT( |
| 294 | + r1.get(), |
| 295 | + IsOkAndHolds(ResultOf( |
| 296 | + "total bytes", |
| 297 | + [](RewriteResponse const& v) { return v.total_bytes_rewritten(); }, |
| 298 | + 1000))); |
| 299 | +} |
| 300 | + |
241 | 301 | GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END |
242 | 302 | } // namespace storage_internal |
243 | 303 | } // namespace cloud |
|
0 commit comments