blob: 045e459d56f95f94f65f7da9208b2ed66994e792 [file] [log] [blame]
Rolf Badorekef2bf512019-08-20 11:17:15 +03001/*
2 Copyright (c) 2018-2019 Nokia.
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
Timo Tietavainena0745d22019-11-28 09:55:22 +020017/*
18 * This source code is part of the near-RT RIC (RAN Intelligent Controller)
19 * platform project (RICP).
20*/
21
Rolf Badorekef2bf512019-08-20 11:17:15 +030022#include <type_traits>
23#include <memory>
24#include <cstring>
25#include <sys/epoll.h>
26#include <sys/timerfd.h>
27#include <sys/eventfd.h>
28#include <arpa/inet.h>
29#include <gtest/gtest.h>
30#include <async.h>
31#include "private/createlogger.hpp"
32#include "private/error.hpp"
33#include "private/logger.hpp"
34#include "private/redis/asynchirediscommanddispatcher.hpp"
35#include "private/redis/reply.hpp"
36#include "private/redis/contents.hpp"
37#include "private/timer.hpp"
38#include "private/tst/contentsbuildermock.hpp"
39#include "private/tst/enginemock.hpp"
40#include "private/tst/hiredissystemmock.hpp"
41#include "private/tst/hiredisepolladaptermock.hpp"
42#include "private/tst/redisreplybuilder.hpp"
43
44using namespace shareddatalayer;
45using namespace shareddatalayer::redis;
46using namespace shareddatalayer::tst;
47using namespace testing;
48
49namespace
50{
51 class AsyncHiredisCommandDispatcherBaseTest: public testing::Test
52 {
53 public:
54 std::shared_ptr<ContentsBuilderMock> contentsBuilderMock;
55 StrictMock<EngineMock> engineMock;
56 HiredisSystemMock hiredisSystemMock;
57 std::shared_ptr<HiredisEpollAdapterMock> adapterMock;
58 redisAsyncContext ac;
59 int hiredisFd;
60 std::unique_ptr<AsyncHiredisCommandDispatcher> dispatcher;
61 void (*connected)(const redisAsyncContext*, int);
62 void (*disconnected)(const redisAsyncContext*, int);
63 Timer::Callback savedConnectionRetryTimerCallback;
64 Timer::Duration expectedConnectionRetryTimerDuration;
65 Timer::Duration expectedConnectionVerificationRetryTimerDuration;
66 RedisReplyBuilder redisReplyBuilder;
67 const AsyncConnection::Namespace defaultNamespace;
68 std::shared_ptr<Logger> logger;
69
70 AsyncHiredisCommandDispatcherBaseTest():
71 contentsBuilderMock(std::make_shared<ContentsBuilderMock>(AsyncConnection::SEPARATOR)),
72 adapterMock(std::make_shared<HiredisEpollAdapterMock>(engineMock, hiredisSystemMock)),
73 ac { },
74 hiredisFd(3),
75 connected(nullptr),
76 disconnected(nullptr),
77 expectedConnectionRetryTimerDuration(std::chrono::seconds(1)),
78 expectedConnectionVerificationRetryTimerDuration(std::chrono::seconds(10)),
79 redisReplyBuilder { },
80 defaultNamespace("namespace"),
81 logger(createLogger(SDL_LOG_PREFIX))
82 {
83 }
84
85 virtual ~AsyncHiredisCommandDispatcherBaseTest()
86 {
87 }
88
89 MOCK_METHOD0(connectAck, void());
90
91 MOCK_METHOD0(disconnectCallback, void());
92
93 MOCK_METHOD2(ack, void(const std::error_code&, const Reply&));
94
95 void expectationsUntilConnect()
96 {
97 expectationsUntilConnect(ac);
98 }
99
100 void expectationsUntilConnect(redisAsyncContext& ac)
101 {
102 expectRedisAsyncConnect(ac);
103 }
104
105 void expectRedisAsyncConnect()
106 {
107 expectRedisAsyncConnect(ac);
108 }
109
110 void expectRedisAsyncConnect(redisAsyncContext& ac)
111 {
112 EXPECT_CALL(hiredisSystemMock, redisAsyncConnect(StrEq("host"), 6379U))
113 .Times(1)
114 .WillOnce(InvokeWithoutArgs([this, &ac]()
115 {
116 ac.c.fd = hiredisFd;
117 return &ac;
118 }));
119 }
120
121 void expectRedisAsyncConnectReturnNullptr()
122 {
123 EXPECT_CALL(hiredisSystemMock, redisAsyncConnect(StrEq("host"), 6379U))
124 .Times(1)
125 .WillOnce(InvokeWithoutArgs([this]()
126 {
127 ac.c.fd = hiredisFd;
128 return nullptr;
129 }));
130 }
131
132 void expectRedisAsyncSetConnectCallback()
133 {
134 expectRedisAsyncSetConnectCallback(ac);
135 }
136
137 void expectRedisAsyncSetConnectCallback(redisAsyncContext& ac)
138 {
139 EXPECT_CALL(hiredisSystemMock, redisAsyncSetConnectCallback(&ac, _))
140 .Times(1)
141 .WillOnce(Invoke([this](const redisAsyncContext*, redisConnectCallback* cb)
142 {
143 connected = cb;
144 return REDIS_OK;
145 }));
146 }
147
148 void expectRedisAsyncSetDisconnectCallback()
149 {
150 expectRedisAsyncSetDisconnectCallback(ac);
151 }
152
153 void expectRedisAsyncSetDisconnectCallback(redisAsyncContext& ac)
154 {
155 EXPECT_CALL(hiredisSystemMock, redisAsyncSetDisconnectCallback(&ac, _))
156 .Times(1)
157 .WillOnce(Invoke([this](const redisAsyncContext*, redisDisconnectCallback* cb)
158 {
159 disconnected = cb;
160 return REDIS_OK;
161 }));
162 }
163
164 void expectAdapterAttach()
165 {
166 expectAdapterAttach(ac);
167 }
168
169 void expectAdapterAttach(redisAsyncContext& ac)
170 {
171 EXPECT_CALL(*adapterMock, attach(&ac))
172 .Times(1);
173 }
174
175 void expectConnectAck()
176 {
177 EXPECT_CALL(*this, connectAck())
178 .Times(1);
179 }
180
181 void expectDisconnectCallback()
182 {
183 EXPECT_CALL(*this, disconnectCallback())
184 .Times(1);
185 }
186
187 void expectRedisAsyncFree()
188 {
189 EXPECT_CALL(hiredisSystemMock, redisAsyncFree(&ac))
190 .Times(1);
191 }
192
193 void expectRedisAsyncDisconnect()
194 {
195 EXPECT_CALL(hiredisSystemMock, redisAsyncDisconnect(&ac))
196 .Times(1);
197 }
198
199 void expectRedisAsyncCommandArgv(redisReply& rr)
200 {
201 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
202 .Times(1)
203 .WillOnce(Invoke([&rr](redisAsyncContext* ac, redisCallbackFn* cb, void* pd,
204 int, const char**, const size_t*)
205 {
206 cb(ac, &rr, pd);
207 return REDIS_OK;
208 }));
209 }
210
211 void expectCommandListQuery()
212 {
213 expectRedisAsyncCommandArgv(redisReplyBuilder.buildCommandListQueryReply());
214 }
215
216 void expectCommandListQueryReturnError()
217 {
218 expectRedisAsyncCommandArgv(redisReplyBuilder.buildErrorReply("SomeErrorForCommandListQuery"));
219 }
220
221 void verifyAckErrorReply(const Reply& reply)
222 {
223 EXPECT_EQ(Reply::Type::NIL, reply.getType());
224 EXPECT_EQ(0, reply.getInteger());
225 EXPECT_TRUE(reply.getString()->str.empty());
226 EXPECT_EQ(static_cast<ReplyStringLength>(0), reply.getString()->len);
227 EXPECT_TRUE(reply.getArray()->empty());
228 }
229
230 void expectAckError()
231 {
232 EXPECT_CALL(*this, ack(Ne(std::error_code()), _))
233 .Times(1)
234 .WillOnce(Invoke([this](const std::error_code&, const Reply& reply)
235 {
236 verifyAckErrorReply(reply);
237 }));
238 }
239
240 void expectAckError(const std::error_code& ec)
241 {
242 EXPECT_CALL(*this, ack(ec, _))
243 .Times(1)
244 .WillOnce(Invoke([this](const std::error_code&, const Reply& reply)
245 {
246 verifyAckErrorReply(reply);
247 }));
248 }
249
250 void expectArmConnectionRetryTimer()
251 {
252 EXPECT_CALL(engineMock, armTimer(_, expectedConnectionRetryTimerDuration, _))
253 .Times(1)
254 .WillOnce(SaveArg<2>(&savedConnectionRetryTimerCallback));
255 }
256
257 void expectArmConnectionVerificationRetryTimer()
258 {
259 EXPECT_CALL(engineMock, armTimer(_, expectedConnectionVerificationRetryTimerDuration, _))
260 .Times(1)
261 .WillOnce(SaveArg<2>(&savedConnectionRetryTimerCallback));
262 }
263
264 void expectDisarmConnectionRetryTimer()
265 {
266 EXPECT_CALL(engineMock, disarmTimer(_))
267 .Times(1);
268 }
269 };
270
271 class AsyncHiredisCommandDispatcherDisconnectedTest: public AsyncHiredisCommandDispatcherBaseTest
272 {
273 public:
274 AsyncHiredisCommandDispatcherDisconnectedTest()
275 {
276 InSequence dummy;
277 expectationsUntilConnect();
278 expectAdapterAttach();
279 expectRedisAsyncSetConnectCallback();
280 expectRedisAsyncSetDisconnectCallback();
281 dispatcher.reset(new AsyncHiredisCommandDispatcher(engineMock,
282 "host",
283 htons(6379U),
284 contentsBuilderMock,
285 false,
286 hiredisSystemMock,
287 adapterMock,
Rolf Badorek8324d022019-09-17 16:47:20 +0300288 logger,
289 false));
Rolf Badorekef2bf512019-08-20 11:17:15 +0300290 }
291
292 ~AsyncHiredisCommandDispatcherDisconnectedTest()
293 {
294 }
295 };
296
297 class AsyncHiredisCommandDispatcherWithPermanentCommandCallbacksTest: public AsyncHiredisCommandDispatcherBaseTest
298 {
299 public:
300 AsyncHiredisCommandDispatcherWithPermanentCommandCallbacksTest()
301 {
302 InSequence dummy;
303 expectationsUntilConnect();
304 expectAdapterAttach();
305 expectRedisAsyncSetConnectCallback();
306 expectRedisAsyncSetDisconnectCallback();
307 dispatcher.reset(new AsyncHiredisCommandDispatcher(engineMock,
308 "host",
309 htons(6379U),
310 contentsBuilderMock,
311 true,
312 hiredisSystemMock,
313 adapterMock,
Rolf Badorek8324d022019-09-17 16:47:20 +0300314 logger,
315 false));
Rolf Badorekef2bf512019-08-20 11:17:15 +0300316 }
317
318 ~AsyncHiredisCommandDispatcherWithPermanentCommandCallbacksTest()
319 {
320 expectRedisAsyncFree();
321 }
322 };
323
324 class AsyncHiredisCommandDispatcherConnectedTest: public AsyncHiredisCommandDispatcherDisconnectedTest
325 {
326 public:
327 Contents contents;
328 redisCallbackFn* savedCb;
329 void* savedPd;
330
331 AsyncHiredisCommandDispatcherConnectedTest():
332 contents { { "CMD", "key1", "value1", "key2", "value2" },
333 { 3, 4, 6, 4, 6 } },
334 savedCb(nullptr),
335 savedPd(nullptr)
336 {
337 expectCommandListQuery();
338 connected(&ac, 0);
339 }
340
341 ~AsyncHiredisCommandDispatcherConnectedTest()
342 {
343 expectRedisAsyncFree();
344 }
345
346 void expectAck()
347 {
348 EXPECT_CALL(*this, ack(std::error_code(), _))
349 .Times(1);
350 }
351
352 void expectReplyError(const std::string& msg)
353 {
354 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
355 .Times(1)
356 .WillOnce(Invoke([this, msg](redisAsyncContext* ac, redisCallbackFn* cb, void* pd,
357 int, const char**, const size_t*)
358 {
359 cb(ac, &redisReplyBuilder.buildErrorReply(msg), pd);
360 return REDIS_OK;
361 }));
362 }
363
364 void expectContextError(int code)
365 {
366 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
367 .Times(1)
368 .WillOnce(Invoke([code](redisAsyncContext* ac, redisCallbackFn* cb, void* pd, int, const char**, const size_t*)
369 {
370 ac->err = code;
371 cb(ac, nullptr, pd);
372 return REDIS_OK;
373 }));
374 }
375
376 void expectRedisAsyncFreeCallPendingCallback(redisCallbackFn* cb, void* pd)
377 {
378 EXPECT_CALL(hiredisSystemMock, redisAsyncFree(&ac))
379 .Times(1)
380 .WillOnce(Invoke([cb, pd](redisAsyncContext* ac)
381 {
382 cb(ac, nullptr, pd);
383 }));
384 }
385
386 void expectAckNotCalled()
387 {
388 EXPECT_CALL(*this, ack(_,_))
389 .Times(0);
390 }
391
392 void expectRedisAsyncCommandArgv_SaveCb()
393 {
394 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
395 .Times(1)
396 .WillRepeatedly(Invoke([this](redisAsyncContext*, redisCallbackFn* cb, void* pd,
397 int, const char**, const size_t*)
398 {
399 savedCb = cb;
400 savedPd = pd;
401 return REDIS_OK;
402 }));
403 }
404 };
405
406 using AsyncHiredisCommandDispatcherDeathTest = AsyncHiredisCommandDispatcherConnectedTest;
Rolf Badorek8324d022019-09-17 16:47:20 +0300407
408 class AsyncHiredisCommandDispatcherForSentinelTest: public AsyncHiredisCommandDispatcherBaseTest
409 {
410 public:
411 AsyncHiredisCommandDispatcherForSentinelTest()
412 {
413 InSequence dummy;
414 expectationsUntilConnect();
415 expectAdapterAttach();
416 expectRedisAsyncSetConnectCallback();
417 expectRedisAsyncSetDisconnectCallback();
418 dispatcher.reset(new AsyncHiredisCommandDispatcher(engineMock,
419 "host",
420 htons(6379U),
421 contentsBuilderMock,
422 true,
423 hiredisSystemMock,
424 adapterMock,
425 logger,
426 true));
427 }
428
429 ~AsyncHiredisCommandDispatcherForSentinelTest()
430 {
431 expectRedisAsyncFree();
432 }
433 };
Rolf Badorekef2bf512019-08-20 11:17:15 +0300434}
435
436TEST_F(AsyncHiredisCommandDispatcherDisconnectedTest, IsNotCopyable)
437{
438 EXPECT_FALSE(std::is_copy_constructible<AsyncHiredisCommandDispatcher>::value);
439 EXPECT_FALSE(std::is_copy_assignable<AsyncHiredisCommandDispatcher>::value);
440}
441
442TEST_F(AsyncHiredisCommandDispatcherDisconnectedTest, ImplementsAsyncRedisCommandDispatcher)
443{
444 EXPECT_TRUE((std::is_base_of<AsyncCommandDispatcher, AsyncHiredisCommandDispatcher>::value));
445}
446
447TEST_F(AsyncHiredisCommandDispatcherDisconnectedTest, CannotDispatchCommandsIfDisconnected)
448{
449 Engine::Callback storedCallback;
450 EXPECT_CALL(engineMock, postCallback(_))
451 .Times(1)
452 .WillOnce(SaveArg<0>(&storedCallback));
453 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherDisconnectedTest::ack,
454 this,
455 std::placeholders::_1,
456 std::placeholders::_2),
457 defaultNamespace,
458 { });
459 expectAckError(std::error_code(AsyncRedisCommandDispatcherErrorCode::NOT_CONNECTED));
460 storedCallback();
461}
462
463TEST_F(AsyncHiredisCommandDispatcherDisconnectedTest, ContextErrorInConnectArmsRetryTimer)
464{
465 InSequence dummy;
466 expectationsUntilConnect();
467 expectArmConnectionRetryTimer();
468 ac.err = 123;
469 dispatcher.reset(new AsyncHiredisCommandDispatcher(engineMock,
470 "host",
471 htons(6379U),
472 contentsBuilderMock,
473 false,
474 hiredisSystemMock,
475 adapterMock,
Rolf Badorek8324d022019-09-17 16:47:20 +0300476 logger,
477 false));
Rolf Badorekef2bf512019-08-20 11:17:15 +0300478 expectDisarmConnectionRetryTimer();
479}
480
481TEST_F(AsyncHiredisCommandDispatcherBaseTest, NullRedisContextInConnectArmsRetryTimer)
482{
483 InSequence dummy;
484 expectRedisAsyncConnectReturnNullptr();
485 expectArmConnectionRetryTimer();
486 expectDisarmConnectionRetryTimer();
487
488 dispatcher.reset(new AsyncHiredisCommandDispatcher(engineMock,
489 "host",
490 htons(6379U),
491 contentsBuilderMock,
492 false,
493 hiredisSystemMock,
494 adapterMock,
Rolf Badorek8324d022019-09-17 16:47:20 +0300495 logger,
496 false));
Rolf Badorekef2bf512019-08-20 11:17:15 +0300497}
498
499TEST_F(AsyncHiredisCommandDispatcherDisconnectedTest, FailedCommandListQueryArmsRetryTimer)
500{
501 InSequence dummy;
502 expectCommandListQueryReturnError();
503 expectArmConnectionVerificationRetryTimer();
504 expectRedisAsyncFree();
505 connected(&ac, 0);
506 expectDisarmConnectionRetryTimer();
507}
508
509TEST_F(AsyncHiredisCommandDispatcherDisconnectedTest, ErrorInConnectedCallbackArmsRetryTimer)
510{
511 InSequence dummy;
512 expectArmConnectionRetryTimer();
513 connected(&ac, -1);
514 expectDisarmConnectionRetryTimer();
515}
516
517TEST_F(AsyncHiredisCommandDispatcherDisconnectedTest, ConnectionSucceedsWithRetryTimer)
518{
519 InSequence dummy;
520 expectArmConnectionRetryTimer();
521
522 connected(&ac, -1);
523
524 expectationsUntilConnect();
525 expectAdapterAttach();
526 expectRedisAsyncSetConnectCallback();
527 expectRedisAsyncSetDisconnectCallback();
528
529 savedConnectionRetryTimerCallback();
530
531 expectCommandListQuery();
532 expectConnectAck();
533
534 dispatcher->waitConnectedAsync(std::bind(&AsyncHiredisCommandDispatcherDisconnectedTest::connectAck, this));
535 connected(&ac, 0);
536
537 expectRedisAsyncFree();
538}
539
540TEST_F(AsyncHiredisCommandDispatcherDisconnectedTest, ConnectAckCalledOnceConnected)
541{
542 InSequence dummy;
543 expectCommandListQuery();
544 expectConnectAck();
545 dispatcher->waitConnectedAsync(std::bind(&AsyncHiredisCommandDispatcherDisconnectedTest::connectAck, this));
546 connected(&ac, 0);
547 expectRedisAsyncFree();
548}
549
550TEST_F(AsyncHiredisCommandDispatcherDisconnectedTest, ConnectAckCalledIfConnected)
551{
552 Engine::Callback storedCallback;
553 EXPECT_CALL(engineMock, postCallback(_))
554 .Times(1)
555 .WillOnce(SaveArg<0>(&storedCallback));
556 expectCommandListQuery();
557 connected(&ac, 0);
558 dispatcher->waitConnectedAsync(std::bind(&AsyncHiredisCommandDispatcherDisconnectedTest::connectAck, this));
559 expectConnectAck();
560 storedCallback();
561 expectRedisAsyncFree();
562}
563
564TEST_F(AsyncHiredisCommandDispatcherConnectedTest, CanDispatchCommands)
565{
566 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
567 .Times(1)
568 .WillOnce(Invoke([this](redisAsyncContext* ac, redisCallbackFn* cb, void* pd,
569 int argc, const char** argv, const size_t* argvlen)
570 {
571 EXPECT_EQ((int)contents.stack.size(), argc);
572 EXPECT_EQ(contents.sizes[0], argvlen[0]);
573 EXPECT_EQ(contents.sizes[1], argvlen[1]);
574 EXPECT_EQ(contents.sizes[2], argvlen[2]);
575 EXPECT_EQ(contents.sizes[3], argvlen[3]);
576 EXPECT_EQ(contents.sizes[4], argvlen[4]);
577 EXPECT_FALSE(std::memcmp(argv[0], contents.stack[0].c_str(), contents.sizes[0]));
578 EXPECT_FALSE(std::memcmp(argv[1], contents.stack[1].c_str(), contents.sizes[1]));
579 EXPECT_FALSE(std::memcmp(argv[2], contents.stack[2].c_str(), contents.sizes[2]));
580 EXPECT_FALSE(std::memcmp(argv[3], contents.stack[3].c_str(), contents.sizes[3]));
581 EXPECT_FALSE(std::memcmp(argv[4], contents.stack[4].c_str(), contents.sizes[4]));
582 cb(ac, &redisReplyBuilder.buildNilReply(), pd);
583 return REDIS_OK;
584 }));
585 expectAck();
586 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
587 this,
588 std::placeholders::_1,
589 std::placeholders::_2),
590 defaultNamespace,
591 contents);
592}
593
594TEST_F(AsyncHiredisCommandDispatcherConnectedTest, CanParseNilReply)
595{
596 expectRedisAsyncCommandArgv(redisReplyBuilder.buildNilReply());
597 EXPECT_CALL(*this, ack(std::error_code(), _))
598 .Times(1)
599 .WillOnce(Invoke([](const std::error_code&, const Reply& reply)
600 {
601 EXPECT_EQ(Reply::Type::NIL, reply.getType());
602 EXPECT_EQ(0, reply.getInteger());
603 EXPECT_TRUE(reply.getString()->str.empty());
604 EXPECT_EQ(static_cast<ReplyStringLength>(0), reply.getString()->len);
605 EXPECT_TRUE(reply.getArray()->empty());
606 }));
607 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
608 this,
609 std::placeholders::_1,
610 std::placeholders::_2),
611 defaultNamespace,
612 contents);
613}
614
615TEST_F(AsyncHiredisCommandDispatcherConnectedTest, CanParseIntegerReply)
616{
617 expectRedisAsyncCommandArgv(redisReplyBuilder.buildIntegerReply());
618 EXPECT_CALL(*this, ack(std::error_code(), _))
619 .Times(1)
620 .WillOnce(Invoke([this](const std::error_code&, const Reply& reply)
621 {
622 auto expected(redisReplyBuilder.buildIntegerReply());
623 EXPECT_EQ(Reply::Type::INTEGER, reply.getType());
624 EXPECT_EQ(expected.integer, reply.getInteger());
625 }));
626 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
627 this,
628 std::placeholders::_1,
629 std::placeholders::_2),
630 defaultNamespace,
631 contents);
632}
633
634TEST_F(AsyncHiredisCommandDispatcherConnectedTest, CanParseStatusReply)
635{
636 expectRedisAsyncCommandArgv(redisReplyBuilder.buildStatusReply());
637 EXPECT_CALL(*this, ack(std::error_code(), _))
638 .Times(1)
639 .WillOnce(Invoke([this](const std::error_code&, const Reply& reply)
640 {
641 auto expected(redisReplyBuilder.buildStatusReply());
642 EXPECT_EQ(Reply::Type::STATUS, reply.getType());
643 EXPECT_EQ(expected.len, reply.getString()->len);
644 EXPECT_FALSE(std::memcmp(reply.getString()->str.c_str(), expected.str, expected.len));
645 }));
646 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
647 this,
648 std::placeholders::_1,
649 std::placeholders::_2),
650 defaultNamespace,
651 contents);
652}
653
654TEST_F(AsyncHiredisCommandDispatcherConnectedTest, CanParseStringReply)
655{
656 expectRedisAsyncCommandArgv(redisReplyBuilder.buildStringReply());
657 EXPECT_CALL(*this, ack(std::error_code(), _))
658 .Times(1)
659 .WillOnce(Invoke([this](const std::error_code&, const Reply& reply)
660 {
661 auto expected(redisReplyBuilder.buildStringReply());
662 EXPECT_EQ(Reply::Type::STRING, reply.getType());
663 EXPECT_EQ(expected.len, reply.getString()->len);
664 EXPECT_FALSE(std::memcmp(reply.getString()->str.c_str(), expected.str, expected.len));
665 }));
666 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
667 this,
668 std::placeholders::_1,
669 std::placeholders::_2),
670 defaultNamespace,
671 contents);
672}
673
674TEST_F(AsyncHiredisCommandDispatcherConnectedTest, CanParseArrayReply)
675{
676 expectRedisAsyncCommandArgv(redisReplyBuilder.buildArrayReply());
677 EXPECT_CALL(*this, ack(std::error_code(), _))
678 .Times(1)
679 .WillOnce(Invoke([](const std::error_code&, const Reply& reply)
680 {
681 auto array(reply.getArray());
682 EXPECT_EQ(Reply::Type::ARRAY, reply.getType());
683 EXPECT_EQ(Reply::Type::STRING, (*array)[0]->getType());
684 EXPECT_EQ(Reply::Type::NIL, (*array)[1]->getType());
685 }));
686 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
687 this,
688 std::placeholders::_1,
689 std::placeholders::_2),
690 defaultNamespace,
691 contents);
692}
693
694TEST_F(AsyncHiredisCommandDispatcherConnectedTest, CanHandleDispatchHiredisBufferErrors)
695{
696 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
697 .Times(1)
698 .WillOnce(Invoke([](redisAsyncContext* ac, redisCallbackFn*, void*, int, const char**, const size_t*)
699 {
700 ac->err = REDIS_ERR;
701 return REDIS_ERR;
702 }));
703 Engine::Callback storedCallback;
704 EXPECT_CALL(engineMock, postCallback(_))
705 .Times(1)
706 .WillOnce(SaveArg<0>(&storedCallback));
707 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
708 this,
709 std::placeholders::_1,
710 std::placeholders::_2),
711 defaultNamespace,
712 contents);
713 expectAckError();
714 storedCallback();
715}
716
717TEST_F(AsyncHiredisCommandDispatcherConnectedTest, CanHandleDispatchHiredisCbErrors)
718{
719 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
720 .Times(1)
721 .WillOnce(Invoke([](redisAsyncContext* ac, redisCallbackFn* cb, void* pd, int, const char**, const size_t*)
722 {
723 cb(ac, nullptr, pd);
724 return REDIS_OK;
725 }));
726 expectAckError();
727 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
728 this,
729 std::placeholders::_1,
730 std::placeholders::_2),
731 defaultNamespace,
732 contents);
733}
734
735TEST_F(AsyncHiredisCommandDispatcherConnectedTest, DatasetStillBeingLoadedInMemoryIsRecognizedFromReply)
736{
737 expectReplyError("LOADING Redis is loading the dataset in memory");
738 EXPECT_CALL(*this, ack(std::error_code(AsyncRedisCommandDispatcherErrorCode::DATASET_LOADING), _))
739 .Times(1);
740 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
741 this,
742 std::placeholders::_1,
743 std::placeholders::_2),
744 defaultNamespace,
745 contents);
746}
747
748TEST_F(AsyncHiredisCommandDispatcherConnectedTest, ProtocolErrorIsRecognizedFromReply)
749{
750 expectReplyError("ERR Protocol error: invalid bulk length");
751 EXPECT_CALL(*this, ack(std::error_code(AsyncRedisCommandDispatcherErrorCode::PROTOCOL_ERROR), _))
752 .Times(1);
753 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
754 this,
755 std::placeholders::_1,
756 std::placeholders::_2),
757 defaultNamespace,
758 contents);
759}
760
761TEST_F(AsyncHiredisCommandDispatcherConnectedTest, UnrecognizedReplyErrorIsConvertedToUnknownError)
762{
763 expectReplyError("something sinister");
764 EXPECT_CALL(*this, ack(std::error_code(AsyncRedisCommandDispatcherErrorCode::UNKNOWN_ERROR), _))
765 .Times(1);
766 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
767 this,
768 std::placeholders::_1,
769 std::placeholders::_2),
770 defaultNamespace,
771 contents);
772}
773
774TEST_F(AsyncHiredisCommandDispatcherConnectedTest, IOErrorInContext)
775{
776 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
777 .Times(1)
778 .WillOnce(Invoke([](redisAsyncContext* ac, redisCallbackFn* cb, void* pd, int, const char**, const size_t*)
779 {
780 ac->err = REDIS_ERR_IO;
781 errno = EINVAL;
782 cb(ac, nullptr, pd);
783 return REDIS_OK;
784 }));
785 EXPECT_CALL(*this, ack(std::error_code(AsyncRedisCommandDispatcherErrorCode::IO_ERROR), _))
786 .Times(1);
787 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
788 this,
789 std::placeholders::_1,
790 std::placeholders::_2),
791 defaultNamespace,
792 contents);
793}
794
795TEST_F(AsyncHiredisCommandDispatcherConnectedTest, IOErrorInContextWithECONNRESETerrnoValue)
796{
797 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
798 .Times(1)
799 .WillOnce(Invoke([](redisAsyncContext* ac, redisCallbackFn* cb, void* pd, int, const char**, const size_t*)
800 {
801 ac->err = REDIS_ERR_IO;
802 errno = ECONNRESET;
803 cb(ac, nullptr, pd);
804 return REDIS_OK;
805 }));
806 EXPECT_CALL(*this, ack(std::error_code(AsyncRedisCommandDispatcherErrorCode::CONNECTION_LOST), _))
807 .Times(1);
808 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
809 this,
810 std::placeholders::_1,
811 std::placeholders::_2),
812 defaultNamespace,
813 contents);
814}
815
816TEST_F(AsyncHiredisCommandDispatcherConnectedTest, EofErrorInContext)
817{
818 expectContextError(REDIS_ERR_EOF);
819 EXPECT_CALL(*this, ack(std::error_code(AsyncRedisCommandDispatcherErrorCode::CONNECTION_LOST), _))
820 .Times(1);
821 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
822 this,
823 std::placeholders::_1,
824 std::placeholders::_2),
825 defaultNamespace,
826 contents);
827}
828
829TEST_F(AsyncHiredisCommandDispatcherConnectedTest, ProtocolErrorInContext)
830{
831 expectContextError(REDIS_ERR_PROTOCOL);
832 EXPECT_CALL(*this, ack(std::error_code(AsyncRedisCommandDispatcherErrorCode::PROTOCOL_ERROR), _))
833 .Times(1);
834 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
835 this,
836 std::placeholders::_1,
837 std::placeholders::_2),
838 defaultNamespace,
839 contents);
840}
841
842TEST_F(AsyncHiredisCommandDispatcherConnectedTest, OomErrorInContext)
843{
844 expectContextError(REDIS_ERR_OOM);
845 EXPECT_CALL(*this, ack(std::error_code(AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY), _))
846 .Times(1);
847 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
848 this,
849 std::placeholders::_1,
850 std::placeholders::_2),
851 defaultNamespace,
852 contents);
853}
854
855TEST_F(AsyncHiredisCommandDispatcherConnectedTest, UnrecognizedContextErrorIsConvertedToUnknownError)
856{
857 expectContextError(REDIS_ERR_OTHER);
858 EXPECT_CALL(*this, ack(std::error_code(AsyncRedisCommandDispatcherErrorCode::UNKNOWN_ERROR), _))
859 .Times(1);
860 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
861 this,
862 std::placeholders::_1,
863 std::placeholders::_2),
864 defaultNamespace,
865 contents);
866}
867
868TEST_F(AsyncHiredisCommandDispatcherConnectedTest, PendingClientCallbacksAreNotCalledAfterDisabled)
869{
870 InSequence dummy;
871 expectRedisAsyncCommandArgv_SaveCb();
872 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
873 this,
874 std::placeholders::_1,
875 std::placeholders::_2),
876 defaultNamespace,
877 contents);
878 expectAck();
879 savedCb(&ac, &redisReplyBuilder.buildStringReply(), savedPd);
880 dispatcher->disableCommandCallbacks();
881 expectRedisAsyncCommandArgv_SaveCb();
882 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
883 this,
884 std::placeholders::_1,
885 std::placeholders::_2),
886 defaultNamespace,
887 contents);
888 expectAckNotCalled();
889 savedCb(&ac, &redisReplyBuilder.buildStringReply(), savedPd);
890}
891
892TEST_F(AsyncHiredisCommandDispatcherConnectedTest, RegisteredClientDisconnectCallbackIsCalled)
893{
894 InSequence dummy;
895 dispatcher->registerDisconnectCb(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::disconnectCallback,
896 this));
897 expectDisconnectCallback();
898 expectArmConnectionRetryTimer();
899 disconnected(&ac, 0);
900 expectDisarmConnectionRetryTimer();
901 expectCommandListQuery();
902 connected(&ac, 0); // restore connection to meet destructor expectations
903}
904
905TEST_F(AsyncHiredisCommandDispatcherWithPermanentCommandCallbacksTest, CanHandleMultipleRepliesForSameRedisCommand)
906{
907 InSequence dummy;
908 redisCallbackFn* savedCb;
909 void* savedPd;
910 Contents contents({ { "cmd", "key", "value" }, { 3, 3, 5 } });
911 expectCommandListQuery();
912 connected(&ac, 0);
913 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
914 .Times(1)
915 .WillOnce(Invoke([&savedCb, &savedPd](redisAsyncContext*, redisCallbackFn* cb, void* pd,
916 int, const char**, const size_t*)
917 {
918 savedCb = cb;
919 savedPd = pd;
920 return REDIS_OK;
921 }));
922 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
923 this,
924 std::placeholders::_1,
925 std::placeholders::_2),
926 defaultNamespace,
927 contents);
928 EXPECT_CALL(*this, ack(std::error_code(), _))
929 .Times(3);
930 redisReply rr;
931 rr.type = REDIS_REPLY_NIL;
932 savedCb(&ac, &rr, savedPd);
933 savedCb(&ac, &rr, savedPd);
934 savedCb(&ac, &rr, savedPd);
935}
936
937TEST_F(AsyncHiredisCommandDispatcherDeathTest, CbRemovedAfterHiredisCb)
938{
939 redisCallbackFn* savedCb;
940 void* savedPd;
941 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
942 .Times(1)
943 .WillOnce(Invoke([this, &savedCb, &savedPd](redisAsyncContext* ac, redisCallbackFn* cb, void* pd,
944 int, const char**, const size_t*)
945 {
946 savedCb = cb;
947 savedPd = pd;
948 cb(ac, &redisReplyBuilder.buildNilReply(), pd);
949 return REDIS_OK;
950 }));
951 expectAck();
952 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherDeathTest::ack,
953 this,
954 std::placeholders::_1,
955 std::placeholders::_2),
956 defaultNamespace,
957 contents);
958 EXPECT_EXIT(savedCb(&ac, &redisReplyBuilder.buildNilReply(), savedPd), KilledBySignal(SIGABRT), "");
959}
960
961TEST_F(AsyncHiredisCommandDispatcherDeathTest, TooManyRepliesAborts)
962{
963 InSequence dummy;
964 redisCallbackFn* savedCb;
965 void* savedPd;
966 Contents contents({ { "cmd", "key", "value" }, { 3, 3, 5 } });
967 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(&ac, _, _, _, _, _))
968 .Times(1)
969 .WillOnce(Invoke([&savedCb, &savedPd](redisAsyncContext*, redisCallbackFn* cb, void* pd,
970 int, const char**, const size_t*)
971 {
972 savedCb = cb;
973 savedPd = pd;
974 return REDIS_OK;
975 }));
976 expectAck();
977 dispatcher->dispatchAsync(std::bind(&AsyncHiredisCommandDispatcherConnectedTest::ack,
978 this,
979 std::placeholders::_1,
980 std::placeholders::_2),
981 defaultNamespace,
982 contents);
983 savedCb(&ac, &redisReplyBuilder.buildNilReply(), savedPd);
984 EXPECT_EXIT(savedCb(&ac, &redisReplyBuilder.buildNilReply(), savedPd), KilledBySignal(SIGABRT), "");
985}
Rolf Badorek8324d022019-09-17 16:47:20 +0300986
987TEST_F(AsyncHiredisCommandDispatcherForSentinelTest, CommandListInquiryIsNotSent)
988{
989 EXPECT_CALL(hiredisSystemMock, redisAsyncCommandArgv(_, _, _, _, _, _))
990 .Times(0);
991 connected(&ac, 0);
992}