1/**
2 * Copyright 2021 Alibaba, Inc. and its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * \author hongqing.hu
17 * \date Dec 2020
18 * \brief
19 */
20
21#include <gtest/gtest.h>
22
23#define private public
24#include "repository/binlog/info_fetcher.h"
25#include "mock_mysql_connector.h"
26#undef private
27
28#include "repository/repository_common/error_code.h"
29
30using namespace ::proxima::be;
31using namespace proxima::be::repository;
32
33class InfoFetcherTest : public testing::Test {
34 protected:
35 void SetUp() {
36 mgr_ = std::make_shared<MysqlConnectorManager>();
37 ASSERT_TRUE(mgr_);
38 connector_ = std::make_shared<MockMysqlConnector>();
39 ASSERT_TRUE(connector_);
40 mgr_->put(connector_);
41 connection_uri_ = "mysql://root:[email protected]:3306/mytest";
42 user_ = "root";
43 password_ = "root";
44 ASSERT_TRUE(ailego::Uri::Parse(connection_uri_.c_str(), &uri_));
45 table_name_ = "table";
46
47 InitFetcher();
48 }
49
50 void TearDown() {}
51
52 MockMysqlResultWrapperPtr BuildQuerySchemaResult() {
53 MockMysqlResultWrapperPtr result =
54 std::make_shared<MockMysqlResultWrapper>();
55 result->append_field_meta("id", MYSQL_TYPE_LONG, 11, 0,
56 AUTO_INCREMENT_FLAG);
57 result->append_field_meta("name", MYSQL_TYPE_VAR_STRING, 100);
58 result->append_field_meta("age", MYSQL_TYPE_LONG, 11);
59 result->append_field_meta("score", MYSQL_TYPE_FLOAT, 12);
60 result->append_field_meta("vector1", MYSQL_TYPE_VAR_STRING, 1024);
61 result->append_field_meta("vector2", MYSQL_TYPE_VAR_STRING, 1024);
62 result->append_field_meta("vector3", MYSQL_TYPE_VAR_STRING, 1024);
63
64 std::vector<std::string> values1 = {
65 "1", "name1", "18", "123.456", "1,2,3,4", "1,2,3,5", "1,2,3,6"};
66 result->append_row_values(values1);
67 std::vector<std::string> values2 = {
68 "2", "name2", "19", "223.456", "2,2,3,4", "2,2,3,5", "2,2,3,6"};
69 result->append_row_values(values2);
70 std::vector<std::string> values3 = {
71 "3", "name3", "29", "323.456", "3,2,3,4", "3,2,3,5", "3,2,3,6"};
72 result->append_row_values(values3);
73
74 return result;
75 }
76
77 MockMysqlResultWrapperPtr BuildQueryCollationResult() {
78 MockMysqlResultWrapperPtr result =
79 std::make_shared<MockMysqlResultWrapper>();
80 result->append_field_meta("Field", MYSQL_TYPE_VAR_STRING, 11);
81 result->append_field_meta("Type", MYSQL_TYPE_VAR_STRING, 100);
82 result->append_field_meta("Collation", MYSQL_TYPE_VAR_STRING, 11);
83
84 std::vector<std::string> values1 = {"id", "", ""};
85 result->append_row_values(values1);
86 std::vector<std::string> values2 = {"name", "", "utf8_general_ci"};
87 result->append_row_values(values2);
88 std::vector<std::string> values3 = {"age", "", ""};
89 result->append_row_values(values3);
90 std::vector<std::string> values4 = {"score", "", "utf8_general_ci"};
91 result->append_row_values(values4);
92 std::vector<std::string> values5 = {"vector1", "", "utf8_general_ci"};
93 result->append_row_values(values5);
94 std::vector<std::string> values6 = {"vector2", "", "utf8_general_ci"};
95 result->append_row_values(values6);
96 std::vector<std::string> values7 = {"vector3", "", "utf8_general_ci"};
97 result->append_row_values(values7);
98
99 return result;
100 }
101
102 MockMysqlResultWrapperPtr BuildInvalidQueryCollationResult() {
103 MockMysqlResultWrapperPtr result =
104 std::make_shared<MockMysqlResultWrapper>();
105 result->append_field_meta("Field", MYSQL_TYPE_VAR_STRING, 11);
106 result->append_field_meta("invalid", MYSQL_TYPE_VAR_STRING, 100);
107 result->append_field_meta("Type", MYSQL_TYPE_VAR_STRING, 100);
108 result->append_field_meta("Collation", MYSQL_TYPE_VAR_STRING, 11);
109
110 std::vector<std::string> values1 = {"id", "", "", ""};
111 result->append_row_values(values1);
112 std::vector<std::string> values2 = {"name", "", "", "utf8_general_ci"};
113 result->append_row_values(values2);
114 std::vector<std::string> values3 = {"age", "", "", ""};
115 result->append_row_values(values3);
116 std::vector<std::string> values4 = {"score", "", "", "utf8_general_ci"};
117 result->append_row_values(values4);
118 std::vector<std::string> values5 = {"vector1", "", "", "utf8_general_ci"};
119 result->append_row_values(values5);
120 std::vector<std::string> values6 = {"vector2", "", "", "utf8_general_ci"};
121 result->append_row_values(values6);
122 std::vector<std::string> values7 = {"vector3", "", "", "utf8_general_ci"};
123 result->append_row_values(values7);
124
125 return result;
126 }
127
128 MockMysqlResultWrapperPtr BuildSnapshotResult() {
129 MockMysqlResultWrapperPtr result =
130 std::make_shared<MockMysqlResultWrapper>();
131 result->append_field_meta("File");
132 result->append_field_meta("Position");
133 result->append_field_meta("Binlog_Do_DB");
134 result->append_field_meta("Binlog_Ignore_DB");
135 result->append_field_meta("Executed_Gtid_Set");
136
137 std::vector<std::string> values1 = {"binlog.000001", "10240", "", "", ""};
138 result->append_row_values(values1);
139
140 return result;
141 }
142
143 MockMysqlResultWrapperPtr BuildInvalidRowsSnapshotResult() {
144 MockMysqlResultWrapperPtr result =
145 std::make_shared<MockMysqlResultWrapper>();
146 result->append_field_meta("File");
147 result->append_field_meta("Position");
148 result->append_field_meta("Binlog_Do_DB");
149 result->append_field_meta("Binlog_Ignore_DB");
150 result->append_field_meta("Executed_Gtid_Set");
151
152 return result;
153 }
154
155 MockMysqlResultWrapperPtr BuildEmptySnapshotResult() {
156 MockMysqlResultWrapperPtr result =
157 std::make_shared<MockMysqlResultWrapper>();
158 result->append_field_meta("File");
159 result->append_field_meta("Position");
160 result->append_field_meta("Binlog_Do_DB");
161 result->append_field_meta("Binlog_Ignore_DB");
162 result->append_field_meta("Executed_Gtid_Set");
163
164 return result;
165 }
166
167 MockMysqlResultWrapperPtr BuildInvalidFieldSnapshotResult() {
168 MockMysqlResultWrapperPtr result =
169 std::make_shared<MockMysqlResultWrapper>();
170 result->append_field_meta("File");
171 result->append_field_meta("Position");
172 result->append_field_meta("Binlog_Do_DB");
173 result->append_field_meta("Binlog_Ignore_DB");
174 // result->append_field_meta("Executed_Gtid_Set");
175 std::vector<std::string> values1 = {"binlog.000001", "10240", "", ""};
176 result->append_row_values(values1);
177 return result;
178 }
179
180 MockMysqlResultWrapperPtr BuildInvalidSnapshotResult() {
181 MockMysqlResultWrapperPtr result =
182 std::make_shared<MockMysqlResultWrapper>();
183 result->append_field_meta("Invalid");
184 result->append_field_meta("Position");
185 result->append_field_meta("Binlog_Do_DB");
186 result->append_field_meta("Binlog_Ignore_DB");
187 result->append_field_meta("Executed_Gtid_Set");
188
189 std::vector<std::string> values1 = {"binlog.000001", "10240", "", "", ""};
190 result->append_row_values(values1);
191
192 return result;
193 }
194
195 void BuildCollectionConfig(CollectionConfig &config) {
196 auto *repo = config.mutable_repository_config();
197 repo->set_repository_type(CollectionConfig::RepositoryConfig::RT_DATABASE);
198 repo->set_repository_name(table_name_);
199 auto *database = repo->mutable_database();
200 database->set_connection_uri(connection_uri_);
201 database->set_table_name(table_name_);
202 database->set_user(user_);
203 database->set_password(password_);
204
205 config.add_forward_column_names("name");
206 config.add_forward_column_names("age");
207 auto *index1 = config.add_index_column_params();
208 index1->set_column_name("vector1");
209 auto *index2 = config.add_index_column_params();
210 index2->set_column_name("vector2");
211 }
212
213 void InitFetcher() {
214 CollectionConfig config;
215 BuildCollectionConfig(config);
216
217 // init
218 ailego::Uri test_uri = uri_;
219 EXPECT_CALL(*connector_, uri())
220 .WillOnce(
221 Invoke([&test_uri]() -> const ailego::Uri & { return test_uri; }))
222 .RetiresOnSaturation();
223 fetcher_ = std::make_shared<InfoFetcher>(config, mgr_);
224 int ret = fetcher_->init();
225 ASSERT_EQ(ret, 0);
226 ASSERT_EQ(fetcher_->database(), "mytest");
227 }
228
229 protected:
230 MysqlConnectorManagerPtr mgr_{};
231 MockMysqlConnectorPtr connector_{};
232 std::string connection_uri_{};
233 std::string user_{};
234 std::string password_{};
235 ailego::Uri uri_{};
236 std::string table_name_{};
237 InfoFetcherPtr fetcher_{};
238};
239
240TEST_F(InfoFetcherTest, TestSimple) {
241 // build schema result
242 MockMysqlResultWrapperPtr result1 = BuildQueryCollationResult();
243 MockMysqlResultWrapperPtr result = BuildQuerySchemaResult();
244 EXPECT_CALL(*connector_, execute_query(_, _, _))
245 .Times(2)
246 .WillOnce(Invoke([&result1](const std::string &,
247 MysqlResultWrapperPtr *out, bool) -> int {
248 *out = result1;
249 return 0;
250 }))
251 .WillOnce(Invoke([&result](const std::string &,
252 MysqlResultWrapperPtr *out, bool) -> int {
253 *out = result;
254 return 0;
255 }))
256 .RetiresOnSaturation();
257 // get table schema
258 TableSchemaPtr schema;
259 int ret = fetcher_->get_table_schema(table_name_, &schema);
260 ASSERT_EQ(ret, 0);
261 auto &forward_ids = schema->selected_forward_ids();
262 ASSERT_EQ(forward_ids.size(), (size_t)2);
263 ASSERT_EQ(forward_ids[0], (size_t)1);
264 ASSERT_EQ(forward_ids[1], (size_t)2);
265 auto &index_ids = schema->selected_index_ids();
266 ASSERT_EQ(index_ids.size(), (size_t)2);
267 ASSERT_EQ(index_ids[0], (size_t)4);
268 ASSERT_EQ(index_ids[1], (size_t)5);
269
270 // get table snapshot
271 MysqlResultWrapperPtr snapshot_result = BuildSnapshotResult();
272 EXPECT_CALL(*connector_, execute_query(_, _, _))
273 .Times(3)
274 .WillOnce(testing::Return(0))
275 .WillOnce(
276 Invoke([&snapshot_result](const std::string &,
277 MysqlResultWrapperPtr *out, bool) -> int {
278 *out = snapshot_result;
279 return 0;
280 }))
281 .WillOnce(testing::Return(0))
282 .RetiresOnSaturation();
283 std::string file_name;
284 uint64_t position;
285 ret = fetcher_->get_table_snapshot(table_name_, &file_name, &position);
286 ASSERT_EQ(ret, 0);
287 ASSERT_EQ(file_name, "binlog.000001");
288 ASSERT_EQ(position, (uint64_t)10240);
289}
290
291TEST_F(InfoFetcherTest, TestGetTableSchemaWithExecuteQueryFailed) {
292 // build schema result
293 MockMysqlResultWrapperPtr result1 = BuildQueryCollationResult();
294 EXPECT_CALL(*connector_, execute_query(_, _, _))
295 .Times(2)
296 .WillOnce(Invoke([&result1](const std::string &,
297 MysqlResultWrapperPtr *out, bool) -> int {
298 *out = result1;
299 return 0;
300 }))
301 .WillOnce(Invoke([](const std::string &, MysqlResultWrapperPtr *out,
302 bool) -> int { return 1; }))
303 .RetiresOnSaturation();
304 // execute query failed
305 TableSchemaPtr schema;
306 int ret = fetcher_->get_table_schema(table_name_, &schema);
307 ASSERT_EQ(ret, 1);
308}
309
310TEST_F(InfoFetcherTest, TestGetTableSchemaWithGetCollationInfoFailed) {
311 // build schema result
312 MockMysqlResultWrapperPtr result1 = BuildQueryCollationResult();
313 EXPECT_CALL(*connector_, execute_query(_, _, _))
314 .Times(1)
315 .WillOnce(Invoke([&result1](const std::string &,
316 MysqlResultWrapperPtr *out, bool) -> int {
317 *out = result1;
318 return 1;
319 }))
320 .RetiresOnSaturation();
321 // execute query failed
322 TableSchemaPtr schema;
323 int ret = fetcher_->get_table_schema(table_name_, &schema);
324 ASSERT_EQ(ret, ErrorCode_ExecuteMysql);
325}
326
327TEST_F(InfoFetcherTest, TestGetTableSchemaWithParseTableSchemaFailed) {
328 MockMysqlResultWrapperPtr result = BuildQuerySchemaResult();
329 MockMysqlResultWrapperPtr result1 = BuildQueryCollationResult();
330 EXPECT_CALL(*connector_, execute_query(_, _, _))
331 .Times(2)
332 .WillOnce(Invoke([&result1](const std::string &,
333 MysqlResultWrapperPtr *out, bool) -> int {
334 *out = result1;
335 return 0;
336 }))
337 .WillOnce(Invoke([&result](const std::string &,
338 MysqlResultWrapperPtr *out, bool) -> int {
339 *out = result;
340 return 0;
341 }))
342 .RetiresOnSaturation();
343 // parse table schema failed
344 TableSchemaPtr schema;
345 fetcher_->selected_fields_->index_fields_.push_back("invalid_column");
346 int ret = fetcher_->get_table_schema(table_name_, &schema);
347 ASSERT_EQ(ret, ErrorCode_InvalidCollectionConfig);
348}
349
350TEST_F(InfoFetcherTest, TestGetTableSchemaSuccess) {
351 MockMysqlResultWrapperPtr result1 = BuildQueryCollationResult();
352 MockMysqlResultWrapperPtr result = BuildQuerySchemaResult();
353 EXPECT_CALL(*connector_, execute_query(_, _, _))
354 .Times(2)
355 .WillOnce(Invoke([&result1](const std::string &,
356 MysqlResultWrapperPtr *out, bool) -> int {
357 *out = result1;
358 return 0;
359 }))
360 .WillOnce(Invoke([&result](const std::string &,
361 MysqlResultWrapperPtr *out, bool) -> int {
362 *out = result;
363 return 0;
364 }))
365 .RetiresOnSaturation();
366 // success
367 TableSchemaPtr schema;
368 int ret = fetcher_->get_table_schema(table_name_, &schema);
369 ASSERT_EQ(ret, 0);
370}
371
372TEST_F(InfoFetcherTest, TestGetCollationInfoSuccess) {
373 MockMysqlResultWrapperPtr result1 = BuildQueryCollationResult();
374 EXPECT_CALL(*connector_, execute_query(_, _, _))
375 .Times(1)
376 .WillOnce(Invoke([&result1](const std::string &,
377 MysqlResultWrapperPtr *out, bool) -> int {
378 *out = result1;
379 return 0;
380 }))
381 .RetiresOnSaturation();
382 std::map<std::string, std::string> kv;
383 int ret = fetcher_->get_collation_info("t1", kv);
384 ASSERT_EQ(ret, 0);
385 EXPECT_EQ(kv.size(), (size_t)7);
386 EXPECT_EQ(kv["id"], "");
387 EXPECT_EQ(kv["name"], "utf8_general_ci");
388 EXPECT_EQ(kv["age"], "");
389 EXPECT_EQ(kv["score"], "utf8_general_ci");
390 EXPECT_EQ(kv["vector1"], "utf8_general_ci");
391 EXPECT_EQ(kv["vector2"], "utf8_general_ci");
392 EXPECT_EQ(kv["vector3"], "utf8_general_ci");
393}
394
395TEST_F(InfoFetcherTest, TestGetCollationInfoWithExecuteFailed) {
396 MockMysqlResultWrapperPtr result1 = BuildQueryCollationResult();
397 EXPECT_CALL(*connector_, execute_query(_, _, _))
398 .Times(1)
399 .WillOnce(Invoke([&result1](const std::string &,
400 MysqlResultWrapperPtr *out, bool) -> int {
401 *out = result1;
402 return 1;
403 }))
404 .RetiresOnSaturation();
405 std::map<std::string, std::string> kv;
406 int ret = fetcher_->get_collation_info("t1", kv);
407 ASSERT_EQ(ret, ErrorCode_ExecuteMysql);
408}
409
410TEST_F(InfoFetcherTest, TestGetCollationInfoWithInvalidResultFailed) {
411 MockMysqlResultWrapperPtr result1 = BuildInvalidQueryCollationResult();
412 EXPECT_CALL(*connector_, execute_query(_, _, _))
413 .Times(1)
414 .WillOnce(Invoke([&result1](const std::string &,
415 MysqlResultWrapperPtr *out, bool) -> int {
416 *out = result1;
417 return 0;
418 }))
419 .RetiresOnSaturation();
420 std::map<std::string, std::string> kv;
421 int ret = fetcher_->get_collation_info("t1", kv);
422 ASSERT_EQ(ret, ErrorCode_InvalidMysqlResult);
423}
424
425TEST_F(InfoFetcherTest, TestGetTableSnapshotSuccess) {
426 // get table snapshot
427 MysqlResultWrapperPtr snapshot_result = BuildSnapshotResult();
428 EXPECT_CALL(*connector_, execute_query(_, _, _))
429 .Times(3)
430 .WillOnce(testing::Return(0))
431 .WillOnce(
432 Invoke([&snapshot_result](const std::string &,
433 MysqlResultWrapperPtr *out, bool) -> int {
434 *out = snapshot_result;
435 return 0;
436 }))
437 .WillOnce(testing::Return(0))
438 .RetiresOnSaturation();
439 std::string file_name;
440 uint64_t position;
441 int ret = fetcher_->get_table_snapshot(table_name_, &file_name, &position);
442 ASSERT_EQ(ret, 0);
443 ASSERT_EQ(file_name, "binlog.000001");
444 ASSERT_EQ(position, (uint64_t)10240);
445}
446
447TEST_F(InfoFetcherTest, TestGetTableSnapshotWithLockTableFailed) {
448 // get table snapshot
449 EXPECT_CALL(*connector_, execute_query(_, _, _))
450 .Times(1)
451 .WillOnce(testing::Return(1))
452 .RetiresOnSaturation();
453 std::string file_name;
454 uint64_t position;
455 int ret = fetcher_->get_table_snapshot(table_name_, &file_name, &position);
456 ASSERT_EQ(ret, ErrorCode_ExecuteMysql);
457}
458
459TEST_F(InfoFetcherTest, TestGetTableSnapshotWithGetInternalFailed) {
460 // get table snapshot
461 MysqlResultWrapperPtr snapshot_result = BuildSnapshotResult();
462 EXPECT_CALL(*connector_, execute_query(_, _, _))
463 .Times(3)
464 .WillOnce(testing::Return(0))
465 .WillOnce(
466 Invoke([&snapshot_result](const std::string &,
467 MysqlResultWrapperPtr *out, bool) -> int {
468 *out = snapshot_result;
469 return 1;
470 }))
471 .WillOnce(testing::Return(0))
472 .RetiresOnSaturation();
473 std::string file_name;
474 uint64_t position;
475 int ret = fetcher_->get_table_snapshot(table_name_, &file_name, &position);
476 ASSERT_EQ(ret, ErrorCode_ExecuteMysql);
477}
478
479TEST_F(InfoFetcherTest, TestGetTableSnapshotInternalSuccess) {
480 // get table snapshot
481 MysqlResultWrapperPtr snapshot_result = BuildSnapshotResult();
482 EXPECT_CALL(*connector_, execute_query(_, _, _))
483 .Times(1)
484 .WillOnce(
485 Invoke([&snapshot_result](const std::string &,
486 MysqlResultWrapperPtr *out, bool) -> int {
487 *out = snapshot_result;
488 return 0;
489 }))
490 .RetiresOnSaturation();
491 std::string file_name;
492 uint64_t position;
493 int ret = fetcher_->get_table_snapshot_internal(&file_name, &position);
494 ASSERT_EQ(ret, 0);
495 ASSERT_EQ(file_name, "binlog.000001");
496 ASSERT_EQ(position, (uint64_t)10240);
497}
498
499TEST_F(InfoFetcherTest, TestGetTableSnapshotInternalWithExecuteQueryFailed) {
500 // get table snapshot
501 MysqlResultWrapperPtr snapshot_result = BuildSnapshotResult();
502 EXPECT_CALL(*connector_, execute_query(_, _, _))
503 .Times(1)
504 .WillOnce(
505 Invoke([&snapshot_result](const std::string &,
506 MysqlResultWrapperPtr *out, bool) -> int {
507 *out = snapshot_result;
508 return 1;
509 }))
510 .RetiresOnSaturation();
511 std::string file_name;
512 uint64_t position;
513 int ret = fetcher_->get_table_snapshot_internal(&file_name, &position);
514 ASSERT_EQ(ret, ErrorCode_ExecuteMysql);
515}
516
517TEST_F(InfoFetcherTest, TestGetTableSnapshotInternalWithInvaildRowsResult) {
518 // get table snapshot
519 MysqlResultWrapperPtr snapshot_result = BuildInvalidRowsSnapshotResult();
520 EXPECT_CALL(*connector_, execute_query(_, _, _))
521 .Times(1)
522 .WillOnce(
523 Invoke([&snapshot_result](const std::string &,
524 MysqlResultWrapperPtr *out, bool) -> int {
525 *out = snapshot_result;
526 return 0;
527 }))
528 .RetiresOnSaturation();
529 std::string file_name;
530 uint64_t position;
531 int ret = fetcher_->get_table_snapshot_internal(&file_name, &position);
532 ASSERT_EQ(ret, ErrorCode_InvalidMysqlResult);
533}
534
535TEST_F(InfoFetcherTest, TestGetTableSnapshotInternalWithEmptyRow) {
536 // get table snapshot
537 MysqlResultWrapperPtr snapshot_result = BuildEmptySnapshotResult();
538 EXPECT_CALL(*connector_, execute_query(_, _, _))
539 .Times(1)
540 .WillOnce(
541 Invoke([&snapshot_result](const std::string &,
542 MysqlResultWrapperPtr *out, bool) -> int {
543 *out = snapshot_result;
544 return 0;
545 }))
546 .RetiresOnSaturation();
547 std::string file_name;
548 uint64_t position;
549 int ret = fetcher_->get_table_snapshot_internal(&file_name, &position);
550 ASSERT_EQ(ret, ErrorCode_InvalidMysqlResult);
551}
552
553TEST_F(InfoFetcherTest, TestGetTableSnapshotInternalWithInvalidFieldsNum) {
554 // get table snapshot
555 MysqlResultWrapperPtr snapshot_result = BuildInvalidFieldSnapshotResult();
556 EXPECT_CALL(*connector_, execute_query(_, _, _))
557 .Times(1)
558 .WillOnce(
559 Invoke([&snapshot_result](const std::string &,
560 MysqlResultWrapperPtr *out, bool) -> int {
561 *out = snapshot_result;
562 return 0;
563 }))
564 .RetiresOnSaturation();
565 std::string file_name;
566 uint64_t position;
567 int ret = fetcher_->get_table_snapshot_internal(&file_name, &position);
568 ASSERT_EQ(ret, ErrorCode_InvalidMysqlResult);
569}
570
571TEST_F(InfoFetcherTest, TestGetTableSnapshotInternalWithInvalidResult) {
572 // get table snapshot
573 MysqlResultWrapperPtr snapshot_result = BuildInvalidSnapshotResult();
574 EXPECT_CALL(*connector_, execute_query(_, _, _))
575 .Times(1)
576 .WillOnce(
577 Invoke([&snapshot_result](const std::string &,
578 MysqlResultWrapperPtr *out, bool) -> int {
579 *out = snapshot_result;
580 return 0;
581 }))
582 .RetiresOnSaturation();
583 std::string file_name;
584 uint64_t position;
585 int ret = fetcher_->get_table_snapshot_internal(&file_name, &position);
586 ASSERT_EQ(ret, ErrorCode_InvalidMysqlResult);
587}
588