2 Copyright (c) 2018-2019 Nokia.
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <gtest/gtest.h>
18 #include <arpa/inet.h>
19 #include <sdl/asyncstorage.hpp>
20 #include "private/createlogger.hpp"
21 #include "private/hostandport.hpp"
22 #include "private/timer.hpp"
23 #include "private/redis/asyncsentineldatabasediscovery.hpp"
24 #include "private/tst/asynccommanddispatchermock.hpp"
25 #include "private/tst/contentsbuildermock.hpp"
26 #include "private/tst/enginemock.hpp"
27 #include "private/tst/replymock.hpp"
28 #include "private/tst/wellknownerrorcode.hpp"
30 using namespace shareddatalayer;
31 using namespace shareddatalayer::redis;
32 using namespace shareddatalayer::tst;
33 using namespace testing;
37 class AsyncSentinelDatabaseDiscoveryBaseTest: public testing::Test
40 std::unique_ptr<AsyncSentinelDatabaseDiscovery> asyncSentinelDatabaseDiscovery;
41 std::shared_ptr<StrictMock<EngineMock>> engineMock;
42 std::shared_ptr<StrictMock<AsyncCommandDispatcherMock>> dispatcherMock;
43 std::shared_ptr<StrictMock<ContentsBuilderMock>> contentsBuilderMock;
44 std::shared_ptr<Logger> logger;
46 AsyncCommandDispatcher::ConnectAck dispatcherConnectAck;
47 AsyncCommandDispatcher::CommandCb savedCommandCb;
51 Reply::DataItem hostDataItem;
52 Reply::DataItem portDataItem;
53 std::shared_ptr<ReplyMock> masterInquiryReplyHost;
54 std::shared_ptr<ReplyMock> masterInquiryReplyPort;
55 Reply::ReplyVector masterInquiryReply;
56 Timer::Duration expectedMasterInquiryRetryTimerDuration;
57 Timer::Callback savedConnectionRetryTimerCallback;
59 AsyncSentinelDatabaseDiscoveryBaseTest():
60 engineMock(std::make_shared<StrictMock<EngineMock>>()),
61 dispatcherMock(std::make_shared<StrictMock<AsyncCommandDispatcherMock>>()),
62 contentsBuilderMock(std::make_shared<StrictMock<ContentsBuilderMock>>(AsyncStorage::SEPARATOR)),
63 logger(createLogger(SDL_LOG_PREFIX)),
64 contents({{"aaa","bbb"},{3,3}}),
67 hostDataItem({someHost,ReplyStringLength(someHost.length())}),
68 portDataItem({std::to_string(somePort),ReplyStringLength(std::to_string(somePort).length())}),
69 masterInquiryReplyHost(std::make_shared<ReplyMock>()),
70 masterInquiryReplyPort(std::make_shared<ReplyMock>()),
71 expectedMasterInquiryRetryTimerDuration(std::chrono::seconds(1))
73 masterInquiryReply.push_back(masterInquiryReplyHost);
74 masterInquiryReply.push_back(masterInquiryReplyPort);
77 virtual ~AsyncSentinelDatabaseDiscoveryBaseTest()
81 std::shared_ptr<AsyncCommandDispatcher> asyncCommandDispatcherCreator(Engine&,
83 std::shared_ptr<ContentsBuilder>)
85 // @TODO Add database info checking when configuration support for sentinel is added.
86 newDispatcherCreated();
87 return dispatcherMock;
90 MOCK_METHOD0(newDispatcherCreated, void());
92 void expectNewDispatcherCreated()
94 EXPECT_CALL(*this, newDispatcherCreated())
98 void expectDispatcherWaitConnectedAsync()
100 EXPECT_CALL(*dispatcherMock, waitConnectedAsync(_))
102 .WillOnce(Invoke([this](const AsyncCommandDispatcher::ConnectAck& connectAck)
104 dispatcherConnectAck = connectAck;
108 void expectContentsBuild(const std::string& string,
109 const std::string& string2,
110 const std::string& string3)
112 EXPECT_CALL(*contentsBuilderMock, build(string, string2, string3))
114 .WillOnce(Return(contents));
117 void expectDispatchAsync()
119 EXPECT_CALL(*dispatcherMock, dispatchAsync(_, _, contents))
121 .WillOnce(SaveArg<0>(&savedCommandCb));
124 void expectMasterInquiry()
126 expectContentsBuild("SENTINEL", "get-master-addr-by-name", "mymaster");
127 expectDispatchAsync();
130 MOCK_METHOD1(stateChangedCb, void(const DatabaseInfo&));
132 void expectStateChangedCb()
134 EXPECT_CALL(*this, stateChangedCb(_))
136 .WillOnce(Invoke([this](const DatabaseInfo& databaseInfo)
138 EXPECT_THAT(DatabaseConfiguration::Addresses({ HostAndPort(someHost, htons(somePort)) }),
139 ContainerEq(databaseInfo.hosts));
140 EXPECT_EQ(DatabaseInfo::Type::SINGLE, databaseInfo.type);
141 EXPECT_EQ(boost::none, databaseInfo.ns);
142 EXPECT_EQ(DatabaseInfo::Discovery::SENTINEL, databaseInfo.discovery);
146 void expectGetReplyType(ReplyMock& mock, const Reply::Type& type)
148 EXPECT_CALL(mock, getType())
150 .WillOnce(Return(type));
153 void expectGetReplyArray_ReturnMasterInquiryReply()
155 EXPECT_CALL(replyMock, getArray())
157 .WillOnce(Return(&masterInquiryReply));
160 void expectGetReplyString(ReplyMock& mock, const Reply::DataItem& item)
162 EXPECT_CALL(mock, getString())
164 .WillOnce(Return(&item));
167 void expectMasterIquiryReply()
169 expectGetReplyType(replyMock, Reply::Type::ARRAY);
170 expectGetReplyArray_ReturnMasterInquiryReply();
171 expectGetReplyType(*masterInquiryReplyHost, Reply::Type::STRING);
172 expectGetReplyString(*masterInquiryReplyHost, hostDataItem);
173 expectGetReplyType(*masterInquiryReplyPort, Reply::Type::STRING);
174 expectGetReplyString(*masterInquiryReplyPort, portDataItem);
177 void expectMasterInquiryRetryTimer()
179 EXPECT_CALL(*engineMock, armTimer(_, expectedMasterInquiryRetryTimerDuration, _))
181 .WillOnce(SaveArg<2>(&savedConnectionRetryTimerCallback));
184 void setDefaultResponsesForMasterInquiryReplyParsing()
186 ON_CALL(replyMock, getType())
187 .WillByDefault(Return(Reply::Type::ARRAY));
188 ON_CALL(replyMock, getArray())
189 .WillByDefault(Return(&masterInquiryReply));
190 ON_CALL(*masterInquiryReplyHost, getType())
191 .WillByDefault(Return(Reply::Type::STRING));
192 ON_CALL(*masterInquiryReplyHost, getString())
193 .WillByDefault(Return(&hostDataItem));
194 ON_CALL(*masterInquiryReplyPort, getType())
195 .WillByDefault(Return(Reply::Type::STRING));
196 ON_CALL(*masterInquiryReplyHost, getString())
197 .WillByDefault(Return(&portDataItem));
201 class AsyncSentinelDatabaseDiscoveryTest: public AsyncSentinelDatabaseDiscoveryBaseTest
204 AsyncSentinelDatabaseDiscoveryTest()
206 expectNewDispatcherCreated();
207 asyncSentinelDatabaseDiscovery.reset(
208 new AsyncSentinelDatabaseDiscovery(
211 std::bind(&AsyncSentinelDatabaseDiscoveryBaseTest::asyncCommandDispatcherCreator,
213 std::placeholders::_1,
214 std::placeholders::_2,
215 std::placeholders::_3),
216 contentsBuilderMock));
220 using AsyncSentinelDatabaseDiscoveryDeathTest = AsyncSentinelDatabaseDiscoveryTest;
223 TEST_F(AsyncSentinelDatabaseDiscoveryBaseTest, IsNotCopyable)
226 EXPECT_FALSE(std::is_copy_constructible<AsyncSentinelDatabaseDiscovery>::value);
227 EXPECT_FALSE(std::is_copy_assignable<AsyncSentinelDatabaseDiscovery>::value);
230 TEST_F(AsyncSentinelDatabaseDiscoveryBaseTest, ImplementsAsyncDatabaseDiscovery)
233 EXPECT_TRUE((std::is_base_of<AsyncDatabaseDiscovery, AsyncSentinelDatabaseDiscovery>::value));
236 TEST_F(AsyncSentinelDatabaseDiscoveryTest, RedisMasterIsInquiredFromSentinel)
239 expectDispatcherWaitConnectedAsync();
240 asyncSentinelDatabaseDiscovery->setStateChangedCb(std::bind(&AsyncSentinelDatabaseDiscoveryTest::stateChangedCb,
242 std::placeholders::_1));
243 expectMasterInquiry();
244 dispatcherConnectAck();
245 expectMasterIquiryReply();
246 expectStateChangedCb();
247 savedCommandCb(std::error_code(), replyMock);
250 TEST_F(AsyncSentinelDatabaseDiscoveryTest, RedisMasterInquiryErrorTriggersRetry)
253 expectDispatcherWaitConnectedAsync();
254 asyncSentinelDatabaseDiscovery->setStateChangedCb(std::bind(&AsyncSentinelDatabaseDiscoveryTest::stateChangedCb,
256 std::placeholders::_1));
257 expectMasterInquiry();
258 dispatcherConnectAck();
259 expectMasterInquiryRetryTimer();
260 savedCommandCb(getWellKnownErrorCode(), replyMock);
261 expectMasterInquiry();
262 savedConnectionRetryTimerCallback();
263 expectMasterIquiryReply();
264 expectStateChangedCb();
265 savedCommandCb(std::error_code(), replyMock);
268 TEST_F(AsyncSentinelDatabaseDiscoveryDeathTest, MasterInquiryParsingErrorAborts_InvalidReplyType)
271 expectDispatcherWaitConnectedAsync();
272 asyncSentinelDatabaseDiscovery->setStateChangedCb(std::bind(&AsyncSentinelDatabaseDiscoveryTest::stateChangedCb,
274 std::placeholders::_1));
275 expectMasterInquiry();
276 dispatcherConnectAck();
277 ON_CALL(replyMock, getType())
278 .WillByDefault(Return(Reply::Type::NIL));
279 EXPECT_EXIT(savedCommandCb(std::error_code(), replyMock), KilledBySignal(SIGABRT), ".*Master inquiry reply parsing error");
282 TEST_F(AsyncSentinelDatabaseDiscoveryDeathTest, MasterInquiryParsingErrorAborts_InvalidHostElementType)
285 expectDispatcherWaitConnectedAsync();
286 asyncSentinelDatabaseDiscovery->setStateChangedCb(std::bind(&AsyncSentinelDatabaseDiscoveryTest::stateChangedCb,
288 std::placeholders::_1));
289 expectMasterInquiry();
290 dispatcherConnectAck();
291 setDefaultResponsesForMasterInquiryReplyParsing();
292 ON_CALL(*masterInquiryReplyHost, getType())
293 .WillByDefault(Return(Reply::Type::NIL));
294 EXPECT_EXIT(savedCommandCb(std::error_code(), replyMock), KilledBySignal(SIGABRT), ".*Master inquiry reply parsing error");
297 TEST_F(AsyncSentinelDatabaseDiscoveryDeathTest, MasterInquiryParsingErrorAborts_InvalidPortElementType)
300 expectDispatcherWaitConnectedAsync();
301 asyncSentinelDatabaseDiscovery->setStateChangedCb(std::bind(&AsyncSentinelDatabaseDiscoveryTest::stateChangedCb,
303 std::placeholders::_1));
304 expectMasterInquiry();
305 dispatcherConnectAck();
306 setDefaultResponsesForMasterInquiryReplyParsing();
307 ON_CALL(*masterInquiryReplyPort, getType())
308 .WillByDefault(Return(Reply::Type::NIL));
309 EXPECT_EXIT(savedCommandCb(std::error_code(), replyMock), KilledBySignal(SIGABRT), ".*Master inquiry reply parsing error");
312 TEST_F(AsyncSentinelDatabaseDiscoveryDeathTest, MasterInquiryParsingErrorAborts_PortCantBeCastedToInt)
315 expectDispatcherWaitConnectedAsync();
316 asyncSentinelDatabaseDiscovery->setStateChangedCb(std::bind(&AsyncSentinelDatabaseDiscoveryTest::stateChangedCb,
318 std::placeholders::_1));
319 expectMasterInquiry();
320 dispatcherConnectAck();
321 setDefaultResponsesForMasterInquiryReplyParsing();
322 std::string invalidPort("invalidPort");
323 Reply::DataItem invalidPortDataItem({invalidPort,ReplyStringLength(invalidPort.length())});
324 ON_CALL(*masterInquiryReplyPort, getString())
325 .WillByDefault(Return(&invalidPortDataItem));
326 EXPECT_EXIT(savedCommandCb(std::error_code(), replyMock), KilledBySignal(SIGABRT), ".*Master inquiry reply parsing error");
329 TEST_F(AsyncSentinelDatabaseDiscoveryTest, CallbackIsNotCalledAfterCleared)
332 expectDispatcherWaitConnectedAsync();
333 asyncSentinelDatabaseDiscovery->setStateChangedCb(std::bind(&AsyncSentinelDatabaseDiscoveryTest::stateChangedCb,
335 std::placeholders::_1));
336 expectMasterInquiry();
337 dispatcherConnectAck();
338 expectMasterInquiryRetryTimer();
339 savedCommandCb(getWellKnownErrorCode(), replyMock);
340 expectMasterInquiry();
341 savedConnectionRetryTimerCallback();
342 expectMasterIquiryReply();
343 asyncSentinelDatabaseDiscovery->clearStateChangedCb();
344 EXPECT_CALL(*this, stateChangedCb(_))
346 savedCommandCb(std::error_code(), replyMock);