1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "google/cloud/bigtable/admin_client.h"
16 #include "google/cloud/bigtable/instance_admin.h"
17 #include "google/cloud/bigtable/testing/mock_instance_admin_client.h"
18 #include "google/cloud/bigtable/testing/mock_response_reader.h"
19 #include "google/cloud/bigtable/testing/table_test_fixture.h"
20 #include "google/cloud/internal/api_client_header.h"
21 #include "google/cloud/testing_util/assert_ok.h"
22 #include "google/cloud/testing_util/chrono_literals.h"
23 #include "google/cloud/testing_util/fake_completion_queue_impl.h"
24 #include "google/cloud/testing_util/validate_metadata.h"
25 #include <gmock/gmock.h>
26 #include <thread>
27 
28 namespace google {
29 namespace cloud {
30 namespace bigtable {
31 inline namespace BIGTABLE_CLIENT_NS {
32 namespace {
33 
34 namespace btproto = google::bigtable::admin::v2;
35 using ::google::cloud::testing_util::IsContextMDValid;
36 using ::google::cloud::testing_util::chrono_literals::operator"" _ms;
37 using ::testing::_;
38 using ::testing::ReturnRef;
39 using MockAsyncListInstancesReader =
40     google::cloud::bigtable::testing::MockAsyncResponseReader<
41         btproto::ListInstancesResponse>;
42 using google::cloud::testing_util::FakeCompletionQueueImpl;
43 
44 using Functor =
45     std::function<void(CompletionQueue&, InstanceList&, grpc::Status&)>;
46 
47 std::string const kProjectId = "the-project";
48 
49 class AsyncListInstancesTest : public ::testing::Test {
50  public:
AsyncListInstancesTest()51   AsyncListInstancesTest()
52       : cq_impl_(new FakeCompletionQueueImpl),
53         cq_(cq_impl_),
54         client_(new testing::MockInstanceAdminClient),
55         instances_reader_1_(new MockAsyncListInstancesReader),
56         instances_reader_2_(new MockAsyncListInstancesReader),
57         instances_reader_3_(new MockAsyncListInstancesReader) {
58     EXPECT_CALL(*client_, project()).WillRepeatedly(ReturnRef(kProjectId));
59   }
60 
61  protected:
Start()62   void Start() {
63     InstanceAdmin instance_admin(client_);
64     user_future_ = instance_admin.AsyncListInstances(cq_);
65   }
66 
67   std::shared_ptr<FakeCompletionQueueImpl> cq_impl_;
68   CompletionQueue cq_;
69   std::shared_ptr<testing::MockInstanceAdminClient> client_;
70   future<StatusOr<InstanceList>> user_future_;
71   std::unique_ptr<MockAsyncListInstancesReader> instances_reader_1_;
72   std::unique_ptr<MockAsyncListInstancesReader> instances_reader_2_;
73   std::unique_ptr<MockAsyncListInstancesReader> instances_reader_3_;
74 };
75 
76 // Dynamically create the lambda for `Finish()`. Note that the return type is
77 // unknown, so a function or function template would not work. Alternatively,
78 // writing this inline is very repetitive.
79 auto create_list_instances_lambda =
80     [](std::string const& returned_token,
81        std::vector<std::string> const& instance_names,
__anon5a75af640202(std::string const& returned_token, std::vector<std::string> const& instance_names, std::vector<std::string> const& failed_locations) 82        std::vector<std::string> const& failed_locations) {
83       return [returned_token, instance_names, failed_locations](
84                  btproto::ListInstancesResponse* response, grpc::Status* status,
85                  void*) {
86         for (auto const& instance_name : instance_names) {
87           auto& instance = *response->add_instances();
88           instance.set_name(instance_name);
89         }
90         // Return the right token.
91         response->set_next_page_token(returned_token);
92         for (auto const& failed_location : failed_locations) {
93           response->add_failed_locations(failed_location);
94         }
95         *status = grpc::Status::OK;
96       };
97     };
98 
InstanceNames(InstanceList const & response)99 std::vector<std::string> InstanceNames(InstanceList const& response) {
100   std::vector<std::string> res;
101   std::transform(
102       response.instances.begin(), response.instances.end(),
103       std::back_inserter(res),
104       [](btproto::Instance const& instance) { return instance.name(); });
105   return res;
106 }
107 
108 /// @test One successful page with 1 one instance.
TEST_F(AsyncListInstancesTest,Simple)109 TEST_F(AsyncListInstancesTest, Simple) {
110   EXPECT_CALL(*client_, AsyncListInstances(_, _, _))
111       .WillOnce([this](grpc::ClientContext* context,
112                        btproto::ListInstancesRequest const& request,
113                        grpc::CompletionQueue*) {
114         EXPECT_STATUS_OK(IsContextMDValid(
115             *context,
116             "google.bigtable.admin.v2.BigtableInstanceAdmin.ListInstances",
117             google::cloud::internal::ApiClientHeader()));
118         EXPECT_TRUE(request.page_token().empty());
119         // This is safe, see comments in MockAsyncResponseReader.
120         return std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<
121             google::bigtable::admin::v2::ListInstancesResponse>>(
122             instances_reader_1_.get());
123       });
124   EXPECT_CALL(*instances_reader_1_, Finish(_, _, _))
125       .WillOnce(
126           create_list_instances_lambda("", {"instance_1"}, {"failed_loc_1"}));
127 
128   Start();
129 
130   EXPECT_EQ(user_future_.wait_for(1_ms), std::future_status::timeout);
131   EXPECT_EQ(1, cq_impl_->size());
132   cq_impl_->SimulateCompletion(true);
133 
134   auto res = user_future_.get();
135   EXPECT_STATUS_OK(res);
136   EXPECT_EQ(std::vector<std::string>{"instance_1"}, InstanceNames(*res));
137   EXPECT_EQ(std::vector<std::string>{"failed_loc_1"}, res->failed_locations);
138   EXPECT_TRUE(cq_impl_->empty());
139 }
140 
141 /// @test Test 3 pages, no failures, multiple clusters and failed locations.
TEST_F(AsyncListInstancesTest,MultipleInstancesAndLocations)142 TEST_F(AsyncListInstancesTest, MultipleInstancesAndLocations) {
143   EXPECT_CALL(*client_, AsyncListInstances(_, _, _))
144       .WillOnce([this](grpc::ClientContext* context,
145                        btproto::ListInstancesRequest const& request,
146                        grpc::CompletionQueue*) {
147         EXPECT_STATUS_OK(IsContextMDValid(
148             *context,
149             "google.bigtable.admin.v2.BigtableInstanceAdmin.ListInstances",
150             google::cloud::internal::ApiClientHeader()));
151         EXPECT_TRUE(request.page_token().empty());
152         // This is safe, see comments in MockAsyncResponseReader.
153         return std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<
154             google::bigtable::admin::v2::ListInstancesResponse>>(
155             instances_reader_1_.get());
156       })
157       .WillOnce([this](grpc::ClientContext* context,
158                        btproto::ListInstancesRequest const& request,
159                        grpc::CompletionQueue*) {
160         EXPECT_STATUS_OK(IsContextMDValid(
161             *context,
162             "google.bigtable.admin.v2.BigtableInstanceAdmin.ListInstances",
163             google::cloud::internal::ApiClientHeader()));
164         EXPECT_EQ("token_1", request.page_token());
165         // This is safe, see comments in MockAsyncResponseReader.
166         return std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<
167             google::bigtable::admin::v2::ListInstancesResponse>>(
168             instances_reader_2_.get());
169       })
170       .WillOnce([this](grpc::ClientContext* context,
171                        btproto::ListInstancesRequest const& request,
172                        grpc::CompletionQueue*) {
173         EXPECT_STATUS_OK(IsContextMDValid(
174             *context,
175             "google.bigtable.admin.v2.BigtableInstanceAdmin.ListInstances",
176             google::cloud::internal::ApiClientHeader()));
177         EXPECT_EQ("token_2", request.page_token());
178         // This is safe, see comments in MockAsyncResponseReader.
179         return std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<
180             google::bigtable::admin::v2::ListInstancesResponse>>(
181             instances_reader_3_.get());
182       });
183   EXPECT_CALL(*instances_reader_1_, Finish(_, _, _))
184       .WillOnce(create_list_instances_lambda("token_1", {"instance_1"},
185                                              {"failed_loc_1"}));
186   EXPECT_CALL(*instances_reader_2_, Finish(_, _, _))
187       .WillOnce(create_list_instances_lambda("token_2",
188                                              {"instance_2", "instance_3"},
189                                              {"failed_loc_1", "failed_loc_2"}));
190   EXPECT_CALL(*instances_reader_3_, Finish(_, _, _))
191       .WillOnce(
192           create_list_instances_lambda("", {"instance_4"}, {"failed_loc_1"}));
193 
194   Start();
195 
196   EXPECT_EQ(user_future_.wait_for(1_ms), std::future_status::timeout);
197   EXPECT_EQ(1, cq_impl_->size());
198   cq_impl_->SimulateCompletion(true);
199 
200   EXPECT_EQ(user_future_.wait_for(1_ms), std::future_status::timeout);
201   EXPECT_EQ(1, cq_impl_->size());
202   cq_impl_->SimulateCompletion(true);
203 
204   EXPECT_EQ(user_future_.wait_for(1_ms), std::future_status::timeout);
205   EXPECT_EQ(1, cq_impl_->size());
206   cq_impl_->SimulateCompletion(true);
207 
208   auto res = user_future_.get();
209   EXPECT_STATUS_OK(res);
210   std::vector<std::string> const expected_instances{
211       "instance_1",
212       "instance_2",
213       "instance_3",
214       "instance_4",
215   };
216   EXPECT_EQ(expected_instances, InstanceNames(*res));
217   std::vector<std::string> expected_failed_locations{"failed_loc_1",
218                                                      "failed_loc_2"};
219   std::sort(res->failed_locations.begin(), res->failed_locations.end());
220   EXPECT_EQ(expected_failed_locations, res->failed_locations);
221   EXPECT_TRUE(cq_impl_->empty());
222 }
223 
224 /// @test Test 2 pages, with a failure between them.
TEST_F(AsyncListInstancesTest,FailuresAreRetried)225 TEST_F(AsyncListInstancesTest, FailuresAreRetried) {
226   EXPECT_CALL(*client_, AsyncListInstances(_, _, _))
227       .WillOnce([this](grpc::ClientContext* context,
228                        btproto::ListInstancesRequest const& request,
229                        grpc::CompletionQueue*) {
230         EXPECT_STATUS_OK(IsContextMDValid(
231             *context,
232             "google.bigtable.admin.v2.BigtableInstanceAdmin.ListInstances",
233             google::cloud::internal::ApiClientHeader()));
234         EXPECT_TRUE(request.page_token().empty());
235         // This is safe, see comments in MockAsyncResponseReader.
236         return std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<
237             google::bigtable::admin::v2::ListInstancesResponse>>(
238             instances_reader_1_.get());
239       })
240       .WillOnce([this](grpc::ClientContext* context,
241                        btproto::ListInstancesRequest const& request,
242                        grpc::CompletionQueue*) {
243         EXPECT_STATUS_OK(IsContextMDValid(
244             *context,
245             "google.bigtable.admin.v2.BigtableInstanceAdmin.ListInstances",
246             google::cloud::internal::ApiClientHeader()));
247         EXPECT_EQ("token_1", request.page_token());
248         // This is safe, see comments in MockAsyncResponseReader.
249         return std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<
250             google::bigtable::admin::v2::ListInstancesResponse>>(
251             instances_reader_2_.get());
252       })
253       .WillOnce([this](grpc::ClientContext* context,
254                        btproto::ListInstancesRequest const& request,
255                        grpc::CompletionQueue*) {
256         EXPECT_STATUS_OK(IsContextMDValid(
257             *context,
258             "google.bigtable.admin.v2.BigtableInstanceAdmin.ListInstances",
259             google::cloud::internal::ApiClientHeader()));
260         EXPECT_EQ("token_1", request.page_token());
261         // This is safe, see comments in MockAsyncResponseReader.
262         return std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<
263             google::bigtable::admin::v2::ListInstancesResponse>>(
264             instances_reader_3_.get());
265       });
266   EXPECT_CALL(*instances_reader_1_, Finish(_, _, _))
267       .WillOnce(create_list_instances_lambda("token_1", {"instance_1"},
268                                              {"failed_loc_1"}));
269   EXPECT_CALL(*instances_reader_2_, Finish(_, _, _))
270       .WillOnce(
271           [](btproto::ListInstancesResponse*, grpc::Status* status, void*) {
272             *status = grpc::Status(grpc::StatusCode::UNAVAILABLE, "");
273           });
274   EXPECT_CALL(*instances_reader_3_, Finish(_, _, _))
275       .WillOnce(
276           create_list_instances_lambda("", {"instance_2"}, {"failed_loc_2"}));
277 
278   Start();
279 
280   EXPECT_EQ(user_future_.wait_for(1_ms), std::future_status::timeout);
281   EXPECT_EQ(1, cq_impl_->size());
282   cq_impl_->SimulateCompletion(true);
283 
284   EXPECT_EQ(user_future_.wait_for(1_ms), std::future_status::timeout);
285   EXPECT_EQ(1, cq_impl_->size());
286   cq_impl_->SimulateCompletion(true);
287 
288   EXPECT_EQ(user_future_.wait_for(1_ms), std::future_status::timeout);
289   EXPECT_EQ(1, cq_impl_->size());
290   cq_impl_->SimulateCompletion(true);  // the timer
291 
292   EXPECT_EQ(user_future_.wait_for(1_ms), std::future_status::timeout);
293   EXPECT_EQ(1, cq_impl_->size());
294   cq_impl_->SimulateCompletion(true);
295 
296   auto res = user_future_.get();
297   EXPECT_STATUS_OK(res);
298   std::vector<std::string> const expected_instances{
299       "instance_1",
300       "instance_2",
301   };
302   EXPECT_EQ(expected_instances, InstanceNames(*res));
303   std::vector<std::string> expected_failed_locations{"failed_loc_1",
304                                                      "failed_loc_2"};
305   std::sort(res->failed_locations.begin(), res->failed_locations.end());
306   EXPECT_EQ(expected_failed_locations, res->failed_locations);
307   EXPECT_TRUE(cq_impl_->empty());
308 }
309 
310 }  // namespace
311 }  // namespace BIGTABLE_CLIENT_NS
312 }  // namespace bigtable
313 }  // namespace cloud
314 }  // namespace google
315