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