Add Sentinel configuration reading
[ric-plt/sdl.git] / tst / asyncsentineldatabasediscovery_test.cpp
1 /*
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
17 #include <gtest/gtest.h>
18 #include <arpa/inet.h>
19 #include <string>
20 #include <sdl/asyncstorage.hpp>
21 #include "private/createlogger.hpp"
22 #include "private/hostandport.hpp"
23 #include "private/timer.hpp"
24 #include "private/redis/asyncsentineldatabasediscovery.hpp"
25 #include "private/tst/asynccommanddispatchermock.hpp"
26 #include "private/tst/contentsbuildermock.hpp"
27 #include "private/tst/enginemock.hpp"
28 #include "private/tst/replymock.hpp"
29 #include "private/tst/wellknownerrorcode.hpp"
30
31 using namespace shareddatalayer;
32 using namespace shareddatalayer::redis;
33 using namespace shareddatalayer::tst;
34 using namespace testing;
35
36 namespace
37 {
38     class AsyncSentinelDatabaseDiscoveryBaseTest: public testing::Test
39     {
40     public:
41         std::unique_ptr<AsyncSentinelDatabaseDiscovery> asyncSentinelDatabaseDiscovery;
42         std::shared_ptr<StrictMock<EngineMock>> engineMock;
43         std::shared_ptr<StrictMock<AsyncCommandDispatcherMock>> subscriberMock;
44         std::shared_ptr<StrictMock<AsyncCommandDispatcherMock>> dispatcherMock;
45         std::shared_ptr<StrictMock<ContentsBuilderMock>> contentsBuilderMock;
46         std::shared_ptr<Logger> logger;
47         Contents contents;
48         AsyncCommandDispatcher::ConnectAck subscriberConnectAck;
49         AsyncCommandDispatcher::DisconnectCb subscriberDisconnectCb;
50         AsyncCommandDispatcher::ConnectAck dispatcherConnectAck;
51         AsyncCommandDispatcher::CommandCb savedSubscriberCommandCb;
52         AsyncCommandDispatcher::CommandCb savedDispatcherCommandCb;
53         ReplyMock masterInquiryReplyMock;
54         std::string someHost;
55         uint16_t somePort;
56         std::string someOtherHost;
57         uint16_t someOtherPort;
58         Reply::DataItem hostDataItem;
59         Reply::DataItem portDataItem;
60         std::shared_ptr<ReplyMock> masterInquiryReplyHost;
61         std::shared_ptr<ReplyMock> masterInquiryReplyPort;
62         Reply::ReplyVector masterInquiryReply;
63         Timer::Duration expectedMasterInquiryRetryTimerDuration;
64         Timer::Callback savedMasterInquiryRetryTimerCallback;
65         // Mocks for SUBSCRIBE command replies are a bit complicated, because reply might have several
66         // meanings/structures: https://redis.io/topics/pubsub#format-of-pushed-messages
67         ReplyMock subscribeReplyMock;
68         std::shared_ptr<ReplyMock> subscribeReplyArrayElement0;
69         std::shared_ptr<ReplyMock> subscribeReplyArrayElement1;
70         std::shared_ptr<ReplyMock> subscribeReplyArrayElement2;
71         Reply::ReplyVector subscribeReplyVector;
72         Reply::DataItem subscribeDataItem;
73         ReplyMock notificationReplyMock;
74         std::shared_ptr<ReplyMock> notificationReplyArrayElement0;
75         std::shared_ptr<ReplyMock> notificationReplyArrayElement1;
76         std::shared_ptr<ReplyMock> notificationReplyArrayElement2;
77         Reply::ReplyVector notificationReplyVector;
78         Reply::DataItem notificationDataItem;
79         std::string notificationMessage;
80         Reply::DataItem notificationMessageDataItem;
81         Timer::Duration expectedSubscribeRetryTimerDuration;
82         Timer::Callback savedSubscribeRetryTimerCallback;
83
84         AsyncSentinelDatabaseDiscoveryBaseTest():
85             engineMock(std::make_shared<StrictMock<EngineMock>>()),
86             contentsBuilderMock(std::make_shared<StrictMock<ContentsBuilderMock>>(AsyncStorage::SEPARATOR)),
87             logger(createLogger(SDL_LOG_PREFIX)),
88             contents({{"aaa","bbb"},{3,3}}),
89             someHost("somehost"),
90             somePort(1234),
91             someOtherHost("someotherhost"),
92             someOtherPort(5678),
93             hostDataItem({someHost,ReplyStringLength(someHost.length())}),
94             portDataItem({std::to_string(somePort),ReplyStringLength(std::to_string(somePort).length())}),
95             masterInquiryReplyHost(std::make_shared<ReplyMock>()),
96             masterInquiryReplyPort(std::make_shared<ReplyMock>()),
97             expectedMasterInquiryRetryTimerDuration(std::chrono::seconds(1)),
98             subscribeReplyArrayElement0(std::make_shared<ReplyMock>()),
99             subscribeReplyArrayElement1(std::make_shared<ReplyMock>()),
100             subscribeReplyArrayElement2(std::make_shared<ReplyMock>()),
101             subscribeDataItem({"subscribe",9}),
102             notificationReplyArrayElement0(std::make_shared<ReplyMock>()),
103             notificationReplyArrayElement1(std::make_shared<ReplyMock>()),
104             notificationReplyArrayElement2(std::make_shared<ReplyMock>()),
105             notificationDataItem({"message",7}),
106             notificationMessage("mymaster " + someHost + " " + std::to_string(somePort) + " " + someOtherHost + " " + std::to_string(someOtherPort)),
107             notificationMessageDataItem({notificationMessage, ReplyStringLength(notificationMessage.length())}),
108             expectedSubscribeRetryTimerDuration(std::chrono::seconds(1))
109         {
110             masterInquiryReply.push_back(masterInquiryReplyHost);
111             masterInquiryReply.push_back(masterInquiryReplyPort);
112             subscribeReplyVector.push_back(subscribeReplyArrayElement0);
113             subscribeReplyVector.push_back(subscribeReplyArrayElement1);
114             subscribeReplyVector.push_back(subscribeReplyArrayElement2);
115             notificationReplyVector.push_back(notificationReplyArrayElement0);
116             notificationReplyVector.push_back(notificationReplyArrayElement1);
117             notificationReplyVector.push_back(notificationReplyArrayElement2);
118         }
119
120         virtual ~AsyncSentinelDatabaseDiscoveryBaseTest()
121         {
122         }
123
124         std::shared_ptr<AsyncCommandDispatcher> asyncCommandDispatcherCreator()
125         {
126             // @TODO Add database info checking when configuration support for sentinel is added.
127             if (!subscriberMock)
128             {
129                 subscriberMock = std::make_shared<StrictMock<AsyncCommandDispatcherMock>>();
130                 newDispatcherCreated();
131                 return subscriberMock;
132             }
133             if (!dispatcherMock)
134             {
135                 dispatcherMock = std::make_shared<StrictMock<AsyncCommandDispatcherMock>>();
136                 newDispatcherCreated();
137                 return dispatcherMock;
138             }
139             return nullptr;
140         }
141
142         MOCK_METHOD0(newDispatcherCreated, void());
143
144         void expectDispatchersCreated()
145         {
146             EXPECT_CALL(*this, newDispatcherCreated())
147                 .Times(2);
148         }
149
150         void expectSubscriberWaitConnectedAsync()
151         {
152             EXPECT_CALL(*subscriberMock, waitConnectedAsync(_))
153                 .Times(1)
154                 .WillOnce(Invoke([this](const AsyncCommandDispatcher::ConnectAck& connectAck)
155                         {
156                             subscriberConnectAck = connectAck;
157                         }));
158         }
159
160         void expectSubscriberRegisterDisconnectCb()
161         {
162             EXPECT_CALL(*subscriberMock, registerDisconnectCb(_))
163                 .Times(1)
164                 .WillOnce(Invoke([this](const AsyncCommandDispatcher::DisconnectCb& disconnectCb)
165                         {
166                             subscriberDisconnectCb = disconnectCb;
167                         }));
168         }
169
170         void expectDispatcherWaitConnectedAsync()
171         {
172             EXPECT_CALL(*dispatcherMock, waitConnectedAsync(_))
173                 .Times(1)
174                 .WillOnce(Invoke([this](const AsyncCommandDispatcher::ConnectAck& connectAck)
175                         {
176                             dispatcherConnectAck = connectAck;
177                         }));
178         }
179
180         void expectContentsBuild(const std::string& string,
181                                  const std::string& string2)
182         {
183             EXPECT_CALL(*contentsBuilderMock, build(string, string2))
184                 .Times(1)
185                 .WillOnce(Return(contents));
186         }
187
188         void expectContentsBuild(const std::string& string,
189                                  const std::string& string2,
190                                  const std::string& string3)
191         {
192             EXPECT_CALL(*contentsBuilderMock, build(string, string2, string3))
193                 .Times(1)
194                 .WillOnce(Return(contents));
195         }
196
197         void expectSubscriberDispatchAsync()
198         {
199             EXPECT_CALL(*subscriberMock, dispatchAsync(_, _, contents))
200                 .Times(1)
201                 .WillOnce(SaveArg<0>(&savedSubscriberCommandCb));
202         }
203
204         void expectDispatcherDispatchAsync()
205         {
206             EXPECT_CALL(*dispatcherMock, dispatchAsync(_, _, contents))
207                 .Times(1)
208                 .WillOnce(SaveArg<0>(&savedDispatcherCommandCb));
209         }
210
211         void expectSubscribeNotifications()
212         {
213             expectContentsBuild("SUBSCRIBE", "+switch-master");
214             expectSubscriberDispatchAsync();
215         }
216
217         void expectMasterInquiry()
218         {
219             expectContentsBuild("SENTINEL", "get-master-addr-by-name", "mymaster");
220             expectDispatcherDispatchAsync();
221         }
222
223         MOCK_METHOD1(stateChangedCb, void(const DatabaseInfo&));
224
225         void expectStateChangedCb(const std::string& host, uint16_t port)
226         {
227             EXPECT_CALL(*this, stateChangedCb(_))
228                 .Times(1)
229                 .WillOnce(Invoke([this, host, port](const DatabaseInfo& databaseInfo)
230                                  {
231                                      EXPECT_THAT(DatabaseConfiguration::Addresses({ HostAndPort(host, htons(port)) }),
232                                                  ContainerEq(databaseInfo.hosts));
233                                      EXPECT_EQ(DatabaseInfo::Type::SINGLE, databaseInfo.type);
234                                      EXPECT_EQ(boost::none, databaseInfo.ns);
235                                      EXPECT_EQ(DatabaseInfo::Discovery::SENTINEL, databaseInfo.discovery);
236                                  }));
237         }
238
239         void expectMasterIquiryReply()
240         {
241             expectGetType(masterInquiryReplyMock, Reply::Type::ARRAY);
242             expectGetArray(masterInquiryReplyMock, masterInquiryReply);
243             expectGetType(*masterInquiryReplyHost, Reply::Type::STRING);
244             expectGetString(*masterInquiryReplyHost, hostDataItem);
245             expectGetType(*masterInquiryReplyPort, Reply::Type::STRING);
246             expectGetString(*masterInquiryReplyPort, portDataItem);
247         }
248
249         void expectMasterInquiryRetryTimer()
250         {
251             EXPECT_CALL(*engineMock, armTimer(_, expectedMasterInquiryRetryTimerDuration, _))
252                 .Times(1)
253                 .WillOnce(SaveArg<2>(&savedMasterInquiryRetryTimerCallback));
254         }
255
256         void expectSubscribeRetryTimer()
257         {
258             EXPECT_CALL(*engineMock, armTimer(_, expectedSubscribeRetryTimerDuration, _))
259                 .Times(1)
260                 .WillOnce(SaveArg<2>(&savedSubscribeRetryTimerCallback));
261         }
262
263         void setStateChangedCbExpectsBeforeMasterInquiry()
264         {
265             expectSubscriberRegisterDisconnectCb();
266             expectSubscriberWaitConnectedAsync();
267             asyncSentinelDatabaseDiscovery->setStateChangedCb(std::bind(&AsyncSentinelDatabaseDiscoveryBaseTest::stateChangedCb,
268                     this,
269                     std::placeholders::_1));
270             expectSubscribeNotifications();
271             subscriberConnectAck();
272             expectSubscribeReply();
273             expectDispatcherWaitConnectedAsync();
274             savedSubscriberCommandCb(std::error_code(), subscribeReplyMock);
275             expectMasterInquiry();
276         }
277
278         void setDefaultResponsesForMasterInquiryReplyParsing()
279         {
280             ON_CALL(masterInquiryReplyMock, getType())
281                 .WillByDefault(Return(Reply::Type::ARRAY));
282             ON_CALL(masterInquiryReplyMock, getArray())
283                 .WillByDefault(Return(&masterInquiryReply));
284             ON_CALL(*masterInquiryReplyHost, getType())
285                 .WillByDefault(Return(Reply::Type::STRING));
286             ON_CALL(*masterInquiryReplyHost, getString())
287                 .WillByDefault(Return(&hostDataItem));
288             ON_CALL(*masterInquiryReplyPort, getType())
289                 .WillByDefault(Return(Reply::Type::STRING));
290             ON_CALL(*masterInquiryReplyHost, getString())
291                 .WillByDefault(Return(&portDataItem));
292         }
293
294         void expectGetType(ReplyMock& mock, const Reply::Type& type)
295         {
296             EXPECT_CALL(mock, getType())
297                 .Times(1)
298                 .WillOnce(Return(type));
299         }
300
301         void expectGetString(ReplyMock& mock, const Reply::DataItem& item)
302         {
303             EXPECT_CALL(mock, getString())
304                 .Times(1)
305                 .WillOnce(Return(&item));
306         }
307
308         void expectGetInteger(ReplyMock& mock, int value)
309         {
310             EXPECT_CALL(mock, getInteger())
311                 .Times(1)
312                 .WillOnce(Return(value));
313         }
314
315         void expectGetArray(ReplyMock& mock, Reply::ReplyVector& replyVector)
316         {
317             EXPECT_CALL(mock, getArray())
318                 .Times(1)
319                 .WillOnce(Return(&replyVector));
320         }
321
322         void expectSubscribeReply()
323         {
324             expectGetType(subscribeReplyMock, Reply::Type::ARRAY);
325             expectGetArray(subscribeReplyMock, subscribeReplyVector);
326             expectGetType(*subscribeReplyArrayElement0, Reply::Type::STRING);
327             expectGetString(*subscribeReplyArrayElement0, subscribeDataItem);
328         }
329
330         void expectNotificationReply()
331         {
332             expectGetType(notificationReplyMock, Reply::Type::ARRAY);
333             expectGetArray(notificationReplyMock, notificationReplyVector);
334             expectGetType(*notificationReplyArrayElement0, Reply::Type::STRING);
335             expectGetString(*notificationReplyArrayElement0, notificationDataItem);
336             expectGetType(*notificationReplyArrayElement2, Reply::Type::STRING);
337             expectGetString(*notificationReplyArrayElement2, notificationMessageDataItem);
338         }
339
340         void setDefaultResponsesForNotificationReplyParsing()
341         {
342             ON_CALL(notificationReplyMock, getType())
343                 .WillByDefault(Return(Reply::Type::ARRAY));
344             ON_CALL(notificationReplyMock, getArray())
345                 .WillByDefault(Return(&notificationReplyVector));
346             ON_CALL(*notificationReplyArrayElement0, getType())
347                 .WillByDefault(Return(Reply::Type::STRING));
348             ON_CALL(*notificationReplyArrayElement0, getString())
349                 .WillByDefault(Return(&notificationDataItem));
350             ON_CALL(*notificationReplyArrayElement2, getType())
351                 .WillByDefault(Return(Reply::Type::STRING));
352             ON_CALL(*notificationReplyArrayElement2, getString())
353                 .WillByDefault(Return(&notificationMessageDataItem));
354         }
355     };
356
357     class AsyncSentinelDatabaseDiscoveryTest: public AsyncSentinelDatabaseDiscoveryBaseTest
358     {
359     public:
360         AsyncSentinelDatabaseDiscoveryTest()
361         {
362             expectDispatchersCreated();
363             asyncSentinelDatabaseDiscovery.reset(
364                     new AsyncSentinelDatabaseDiscovery(
365                             engineMock,
366                             logger,
367                             HostAndPort(someHost, somePort),
368                             "mymaster",
369                             std::bind(&AsyncSentinelDatabaseDiscoveryBaseTest::asyncCommandDispatcherCreator,
370                                       this),
371                             contentsBuilderMock));
372         }
373
374         ~AsyncSentinelDatabaseDiscoveryTest()
375         {
376             EXPECT_CALL(*subscriberMock, disableCommandCallbacks())
377                 .Times(1);
378             EXPECT_CALL(*dispatcherMock, disableCommandCallbacks())
379                 .Times(1);
380         }
381     };
382
383     class AsyncSentinelDatabaseDiscoveryInListeningModeTest: public AsyncSentinelDatabaseDiscoveryTest
384     {
385     public:
386         AsyncSentinelDatabaseDiscoveryInListeningModeTest()
387         {
388             InSequence dummy;
389             setStateChangedCbExpectsBeforeMasterInquiry();
390             dispatcherConnectAck();
391             expectMasterIquiryReply();
392             expectStateChangedCb(someHost, somePort);
393             savedDispatcherCommandCb(std::error_code(), masterInquiryReplyMock);
394         }
395     };
396
397     using AsyncSentinelDatabaseDiscoveryDeathTest = AsyncSentinelDatabaseDiscoveryTest;
398
399     using AsyncSentinelDatabaseDiscoveryInListeningModeDeathTest = AsyncSentinelDatabaseDiscoveryInListeningModeTest;
400 }
401
402 TEST_F(AsyncSentinelDatabaseDiscoveryBaseTest, IsNotCopyable)
403 {
404     InSequence dummy;
405     EXPECT_FALSE(std::is_copy_constructible<AsyncSentinelDatabaseDiscovery>::value);
406     EXPECT_FALSE(std::is_copy_assignable<AsyncSentinelDatabaseDiscovery>::value);
407 }
408
409 TEST_F(AsyncSentinelDatabaseDiscoveryBaseTest, ImplementsAsyncDatabaseDiscovery)
410 {
411     InSequence dummy;
412     EXPECT_TRUE((std::is_base_of<AsyncDatabaseDiscovery, AsyncSentinelDatabaseDiscovery>::value));
413 }
414
415 TEST_F(AsyncSentinelDatabaseDiscoveryTest, SettingChangedCallbackTriggersSentinelNotificationsSubscriptionAndMasterInquiry)
416 {
417     InSequence dummy;
418     setStateChangedCbExpectsBeforeMasterInquiry();
419     dispatcherConnectAck();
420     expectMasterIquiryReply();
421     expectStateChangedCb(someHost, somePort);
422     savedDispatcherCommandCb(std::error_code(), masterInquiryReplyMock);
423 }
424
425 TEST_F(AsyncSentinelDatabaseDiscoveryTest, MasterInquiryErrorTriggersRetry)
426 {
427     InSequence dummy;
428     setStateChangedCbExpectsBeforeMasterInquiry();
429     dispatcherConnectAck();
430     expectMasterInquiryRetryTimer();
431     savedDispatcherCommandCb(getWellKnownErrorCode(), masterInquiryReplyMock);
432     expectMasterInquiry();
433     savedMasterInquiryRetryTimerCallback();
434     expectMasterIquiryReply();
435     expectStateChangedCb(someHost, somePort);
436     savedDispatcherCommandCb(std::error_code(), masterInquiryReplyMock);
437 }
438
439 TEST_F(AsyncSentinelDatabaseDiscoveryDeathTest, MasterInquiryParsingErrorAborts_InvalidReplyType)
440 {
441     InSequence dummy;
442     setStateChangedCbExpectsBeforeMasterInquiry();
443     dispatcherConnectAck();
444     ON_CALL(masterInquiryReplyMock, getType())
445         .WillByDefault(Return(Reply::Type::NIL));
446     EXPECT_EXIT(savedDispatcherCommandCb(std::error_code(), masterInquiryReplyMock), KilledBySignal(SIGABRT), ".*Master inquiry reply parsing error");
447 }
448
449 TEST_F(AsyncSentinelDatabaseDiscoveryDeathTest, MasterInquiryParsingErrorAborts_InvalidHostElementType)
450 {
451     InSequence dummy;
452     setStateChangedCbExpectsBeforeMasterInquiry();
453     dispatcherConnectAck();
454     setDefaultResponsesForMasterInquiryReplyParsing();
455     ON_CALL(*masterInquiryReplyHost, getType())
456         .WillByDefault(Return(Reply::Type::NIL));
457     EXPECT_EXIT(savedDispatcherCommandCb(std::error_code(), masterInquiryReplyMock), KilledBySignal(SIGABRT), ".*Master inquiry reply parsing error");
458 }
459
460 TEST_F(AsyncSentinelDatabaseDiscoveryDeathTest, MasterInquiryParsingErrorAborts_InvalidPortElementType)
461 {
462     InSequence dummy;
463     setStateChangedCbExpectsBeforeMasterInquiry();
464     dispatcherConnectAck();
465     setDefaultResponsesForMasterInquiryReplyParsing();
466     ON_CALL(*masterInquiryReplyPort, getType())
467         .WillByDefault(Return(Reply::Type::NIL));
468     EXPECT_EXIT(savedDispatcherCommandCb(std::error_code(), masterInquiryReplyMock), KilledBySignal(SIGABRT), ".*Master inquiry reply parsing error");
469 }
470
471 TEST_F(AsyncSentinelDatabaseDiscoveryDeathTest, MasterInquiryParsingErrorAborts_PortCantBeCastedToInt)
472 {
473     InSequence dummy;
474     setStateChangedCbExpectsBeforeMasterInquiry();
475     dispatcherConnectAck();
476     setDefaultResponsesForMasterInquiryReplyParsing();
477     std::string invalidPort("invalidPort");
478     Reply::DataItem invalidPortDataItem({invalidPort,ReplyStringLength(invalidPort.length())});
479     ON_CALL(*masterInquiryReplyPort, getString())
480         .WillByDefault(Return(&invalidPortDataItem));
481     EXPECT_EXIT(savedDispatcherCommandCb(std::error_code(), masterInquiryReplyMock), KilledBySignal(SIGABRT), ".*Master inquiry reply parsing error");
482 }
483
484 TEST_F(AsyncSentinelDatabaseDiscoveryTest, CallbackIsNotCalledAfterCleared)
485 {
486     InSequence dummy;
487     setStateChangedCbExpectsBeforeMasterInquiry();
488     dispatcherConnectAck();
489     expectMasterInquiryRetryTimer();
490     savedDispatcherCommandCb(getWellKnownErrorCode(), masterInquiryReplyMock);
491     expectMasterInquiry();
492     savedMasterInquiryRetryTimerCallback();
493     expectMasterIquiryReply();
494     asyncSentinelDatabaseDiscovery->clearStateChangedCb();
495     EXPECT_CALL(*this, stateChangedCb(_))
496         .Times(0);
497     savedDispatcherCommandCb(std::error_code(), masterInquiryReplyMock);
498 }
499
500 TEST_F(AsyncSentinelDatabaseDiscoveryTest, ChangeNotificationFromSentinel)
501 {
502     InSequence dummy;
503     setStateChangedCbExpectsBeforeMasterInquiry();
504     dispatcherConnectAck();
505     expectMasterIquiryReply();
506     expectStateChangedCb(someHost, somePort);
507     savedDispatcherCommandCb(std::error_code(), masterInquiryReplyMock);
508     expectNotificationReply();
509     expectStateChangedCb(someOtherHost, someOtherPort);
510     savedSubscriberCommandCb(std::error_code(), notificationReplyMock);
511 }
512
513 TEST_F(AsyncSentinelDatabaseDiscoveryInListeningModeTest, SubscribeCommandErrorTriggersRetry)
514 {
515     InSequence dummy;
516     expectSubscribeRetryTimer();
517     savedSubscriberCommandCb(getWellKnownErrorCode(), subscribeReplyMock);
518     expectSubscribeNotifications();
519     savedSubscribeRetryTimerCallback();
520 }
521
522 TEST_F(AsyncSentinelDatabaseDiscoveryInListeningModeDeathTest, SubscribeReplyParsingErrorAborts_InvalidReplyType)
523 {
524     InSequence dummy;
525     ON_CALL(notificationReplyMock, getType())
526         .WillByDefault(Return(Reply::Type::NIL));
527     EXPECT_EXIT(savedSubscriberCommandCb(std::error_code(), notificationReplyMock), KilledBySignal(SIGABRT), ".*SUBSCRIBE command reply parsing error");
528 }
529
530 TEST_F(AsyncSentinelDatabaseDiscoveryInListeningModeDeathTest, SubscribeReplyParsingErrorAborts_InvalidKindElementType)
531 {
532     InSequence dummy;
533     setDefaultResponsesForNotificationReplyParsing();
534     ON_CALL(*notificationReplyArrayElement0, getType())
535         .WillByDefault(Return(Reply::Type::NIL));
536     EXPECT_EXIT(savedSubscriberCommandCb(std::error_code(), notificationReplyMock), KilledBySignal(SIGABRT), ".*SUBSCRIBE command reply parsing error");
537 }
538
539 TEST_F(AsyncSentinelDatabaseDiscoveryInListeningModeDeathTest, SubscribeReplyParsingErrorAborts_InvalidKind)
540 {
541     InSequence dummy;
542     setDefaultResponsesForNotificationReplyParsing();
543     std::string invalidKind("invalidKind");
544     Reply::DataItem invalidKindDataItem({invalidKind,ReplyStringLength(invalidKind.length())});
545     ON_CALL(*notificationReplyArrayElement0, getString())
546         .WillByDefault(Return(&invalidKindDataItem));
547     EXPECT_EXIT(savedSubscriberCommandCb(std::error_code(), notificationReplyMock), KilledBySignal(SIGABRT), ".*SUBSCRIBE command reply parsing error");
548 }
549
550 TEST_F(AsyncSentinelDatabaseDiscoveryInListeningModeDeathTest, SubscribeReplyParsingErrorAborts_InvalidMessageElementType)
551 {
552     InSequence dummy;
553     setDefaultResponsesForNotificationReplyParsing();
554     ON_CALL(*notificationReplyArrayElement2, getType())
555         .WillByDefault(Return(Reply::Type::NIL));
556     EXPECT_EXIT(savedSubscriberCommandCb(std::error_code(), notificationReplyMock), KilledBySignal(SIGABRT), ".*SUBSCRIBE command reply parsing error");
557 }
558
559 TEST_F(AsyncSentinelDatabaseDiscoveryInListeningModeDeathTest, SubscribeReplyParsingErrorAborts_InvalidMessageStructure)
560 {
561     InSequence dummy;
562     setDefaultResponsesForNotificationReplyParsing();
563     std::string invalidMessage("mymaster oldHost 1234 5678");
564     auto invalidMessageDataItem(Reply::DataItem({invalidMessage, ReplyStringLength(invalidMessage.length())}));
565     ON_CALL(*notificationReplyArrayElement2, getString())
566         .WillByDefault(Return(&invalidMessageDataItem));
567     EXPECT_EXIT(savedSubscriberCommandCb(std::error_code(), notificationReplyMock), KilledBySignal(SIGABRT), ".*Notification message parsing error");
568 }
569
570 TEST_F(AsyncSentinelDatabaseDiscoveryInListeningModeDeathTest, SubscribeReplyParsingErrorAborts_InvalidPort)
571 {
572     InSequence dummy;
573     setDefaultResponsesForNotificationReplyParsing();
574     std::string invalidMessage("mymaster oldHost 1234 newHost invalidPort");
575     auto invalidMessageDataItem(Reply::DataItem({invalidMessage, ReplyStringLength(invalidMessage.length())}));
576     ON_CALL(*notificationReplyArrayElement2, getString())
577         .WillByDefault(Return(&invalidMessageDataItem));
578     EXPECT_EXIT(savedSubscriberCommandCb(std::error_code(), notificationReplyMock), KilledBySignal(SIGABRT), ".*Notification message parsing error");
579 }
580
581 TEST_F(AsyncSentinelDatabaseDiscoveryInListeningModeTest, SubscriberDisconnectCallbackTriggersSubscriptionRenewal)
582 {
583     InSequence dummy;
584     expectSubscriberWaitConnectedAsync();
585     subscriberDisconnectCb();
586     expectSubscribeNotifications();
587     subscriberConnectAck();
588     expectSubscribeReply();
589     expectDispatcherWaitConnectedAsync();
590     savedSubscriberCommandCb(std::error_code(), subscribeReplyMock);
591     expectMasterInquiry();
592     dispatcherConnectAck();
593     expectMasterIquiryReply();
594     expectStateChangedCb(someHost, somePort);
595     savedDispatcherCommandCb(std::error_code(), masterInquiryReplyMock);
596 }