Add first version
[ric-plt/sdl.git] / tst / asyncredisstorage_test.cpp
diff --git a/tst/asyncredisstorage_test.cpp b/tst/asyncredisstorage_test.cpp
new file mode 100644 (file)
index 0000000..afc4619
--- /dev/null
@@ -0,0 +1,1034 @@
+/*
+   Copyright (c) 2018-2019 Nokia.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+#include <type_traits>
+#include <memory>
+#include <cstdlib>
+#include <gtest/gtest.h>
+#include <arpa/inet.h>
+#include <sdl/emptynamespace.hpp>
+#include <sdl/invalidnamespace.hpp>
+#include <sdl/publisherid.hpp>
+#include "private/createlogger.hpp"
+#include "private/error.hpp"
+#include "private/logger.hpp"
+#include "private/redis/asyncredisstorage.hpp"
+#include "private/redis/contents.hpp"
+#include "private/redis/databaseinfo.hpp"
+#include "private/redis/reply.hpp"
+#include "private/tst/asynccommanddispatchermock.hpp"
+#include "private/tst/asyncdatabasediscoverymock.hpp"
+#include "private/tst/contentsbuildermock.hpp"
+#include "private/tst/enginemock.hpp"
+#include "private/tst/namespaceconfigurationsmock.hpp"
+#include "private/tst/replymock.hpp"
+#include "private/tst/wellknownerrorcode.hpp"
+
+using namespace shareddatalayer;
+using namespace shareddatalayer::redis;
+using namespace shareddatalayer::tst;
+using namespace testing;
+
+namespace
+{
+    std::string getErrorCodeMessage(std::error_code ec)
+    {
+        return ec.message();
+    }
+
+    class AsyncRedisStorageErrorCodeTest: public testing::Test
+    {
+    public:
+        AsyncRedisStorageErrorCodeTest()
+        {
+        }
+
+        virtual ~AsyncRedisStorageErrorCodeTest()
+        {
+        }
+    };
+
+    static const std::string defaultAddress = "address";
+    static const uint16_t defaultPort = 3333;
+    class AsyncRedisStorageTestBase: public testing::Test
+    {
+    public:
+        std::shared_ptr<StrictMock<EngineMock>> engineMock;
+        std::shared_ptr<StrictMock<AsyncDatabaseDiscoveryMock>> discoveryMock;
+        std::shared_ptr<StrictMock<AsyncCommandDispatcherMock>> dispatcherMock;
+        std::unique_ptr<AsyncRedisStorage> sdlStorage;
+        AsyncStorage::Namespace ns;
+        std::shared_ptr<StrictMock<ContentsBuilderMock>> contentsBuilderMock;
+        std::shared_ptr<StrictMock<NamespaceConfigurationsMock>> namespaceConfigurationsMock;
+        Contents contents;
+        Contents publishContentsWithoutPubId;
+        int fd;
+        int discoveryFd;
+        int dispatcherFd;
+        Engine::EventHandler savedDiscoveryEventHandler;
+        Engine::EventHandler savedDispatcherEventHandler;
+        AsyncDatabaseDiscovery::StateChangedCb stateChangedCb;
+        AsyncCommandDispatcher::ConnectAck dispatcherConnectAck;
+        Engine::Callback storedCallback;
+        AsyncCommandDispatcher::CommandCb savedCommandCb;
+        AsyncCommandDispatcher::CommandCb savedPublishCommandCb;
+        AsyncCommandDispatcher::CommandCb savedCommandListQueryCb;
+        ReplyMock replyMock;
+        Reply::ReplyVector replyVector;
+        Reply::ReplyVector commandListReplyVector;
+        Reply::ReplyVector commandListReplyElementVector;
+        std::string expectedStr1;
+        std::string expectedStr2;
+        AsyncStorage::Key key1;
+        AsyncStorage::Key key2;
+        AsyncStorage::Keys keys;
+        AsyncStorage::Keys keysWithNonExistKey;
+        AsyncStorage::Data data1;
+        AsyncStorage::Data data2;
+        AsyncStorage::DataMap dataMap;
+        std::string keyPrefix;
+        std::shared_ptr<Logger> logger;
+
+        AsyncRedisStorageTestBase():
+            engineMock(std::make_shared<StrictMock<EngineMock>>()),
+            discoveryMock(std::make_shared<StrictMock<AsyncDatabaseDiscoveryMock>>()),
+            dispatcherMock(std::make_shared<StrictMock<AsyncCommandDispatcherMock>>()),
+            ns("tag1"),
+            contentsBuilderMock(std::make_shared<StrictMock<ContentsBuilderMock>>(AsyncStorage::SEPARATOR)),
+            namespaceConfigurationsMock(std::make_shared<StrictMock<NamespaceConfigurationsMock>>()),
+            contents({{"aaa","bbb"},{3,3}}),
+            publishContentsWithoutPubId({ { "PUBLISH", ns, "shareddatalayer::NO_PUBLISHER" }, { 7, 4, 29 } }),
+            fd(10),
+            discoveryFd(20),
+            dispatcherFd(30),
+            key1("key1"),
+            key2("key2"),
+            keys({key1,key2}),
+            keysWithNonExistKey({key1,"notfound",key2}),
+            data1({1,2,3}),
+            data2({4,5,6}),
+            dataMap({{key1,data1},{key2,data2}}),
+            keyPrefix("{tag1},*"),
+            logger(createLogger(SDL_LOG_PREFIX))
+        {
+        }
+
+        virtual ~AsyncRedisStorageTestBase() = default;
+
+        std::shared_ptr<AsyncCommandDispatcher> asyncCommandDispatcherCreator(Engine&,
+                                                                              const DatabaseInfo&,
+                                                                              std::shared_ptr<ContentsBuilder>)
+        {
+            newDispatcherCreated();
+            return dispatcherMock;
+        }
+
+        MOCK_METHOD0(newDispatcherCreated, void());
+
+        MOCK_METHOD1(readyAck, void(const std::error_code&));
+
+        MOCK_METHOD1(modifyAck, void(const std::error_code&));
+
+        MOCK_METHOD2(modifyIfAck, void(const std::error_code&, bool status));
+
+        MOCK_METHOD2(getAck, void(const std::error_code&, const AsyncStorage::DataMap&));
+
+        MOCK_METHOD2(findKeysAck, void(const std::error_code&, const AsyncStorage::Keys&));
+
+        DatabaseInfo getDatabaseInfo(DatabaseInfo::Type type = DatabaseInfo::Type::SINGLE,
+                                     DatabaseInfo::Discovery discovery = DatabaseInfo::Discovery::HIREDIS,
+                                     std::string address = defaultAddress,
+                                     uint16_t port = defaultPort)
+        {
+            DatabaseInfo databaseInfo;
+            databaseInfo.type = type;
+            databaseInfo.discovery = discovery;
+            databaseInfo.hosts.push_back({address, htons(port)});
+            databaseInfo.ns = ns;
+            return databaseInfo;
+        }
+
+        void expectDiscoverySetStateChangedCb()
+        {
+            EXPECT_CALL(*discoveryMock, setStateChangedCb(_))
+                .Times(1)
+                .WillOnce(Invoke([this](const AsyncDatabaseDiscovery::StateChangedCb& cb)
+                        {
+                            stateChangedCb = cb;
+                        }));
+        }
+
+        void expectDispatcherWaitConnectedAsync()
+        {
+            EXPECT_CALL(*dispatcherMock, waitConnectedAsync(_))
+                .Times(1)
+                .WillOnce(Invoke([this](const AsyncCommandDispatcher::ConnectAck& connectAck)
+                        {
+                            dispatcherConnectAck = connectAck;
+                        }));
+        }
+
+        void expectDispatcherCreation()
+        {
+            expectDispatcherWaitConnectedAsync();
+        }
+
+        void expectNewDispatcherCreated()
+        {
+            EXPECT_CALL(*this, newDispatcherCreated)
+                .Times(1);
+        }
+
+        void expectNewDispatcherNotCreated()
+        {
+            EXPECT_CALL(*this, newDispatcherCreated)
+                .Times(0);
+        }
+
+        void expectReadyAck(const std::error_code& error)
+        {
+            EXPECT_CALL(*this, readyAck(error))
+                .Times(1);
+        }
+
+        void expectModifyAck(const std::error_code& error)
+        {
+            EXPECT_CALL(*this, modifyAck(error))
+                .Times(1);
+        }
+
+        void expectModifyIfAck(const std::error_code& error, bool status)
+        {
+            EXPECT_CALL(*this, modifyIfAck(error, status))
+                .Times(1);
+        }
+
+        void expectGetAck(const std::error_code& error, const AsyncStorage::DataMap& dataMap)
+        {
+            EXPECT_CALL(*this, getAck(error, dataMap))
+                .Times(1);
+        }
+
+        void expectFindKeysAck(const std::error_code& error, const AsyncStorage::Keys& keys)
+        {
+            EXPECT_CALL(*this, findKeysAck(error, keys))
+                .Times(1);
+        }
+
+        void expectPostCallback()
+        {
+            EXPECT_CALL(*engineMock, postCallback(_))
+                .Times(1)
+                .WillOnce(SaveArg<0>(&storedCallback));
+        }
+
+        void createAsyncStorageInstance(const boost::optional<PublisherId>& pId)
+        {
+            expectDiscoverySetStateChangedCb();
+            if (sdlStorage)
+                expectClearStateChangedCb();
+            sdlStorage.reset(new AsyncRedisStorage(engineMock,
+                                                   discoveryMock,
+                                                   pId,
+                                                   namespaceConfigurationsMock,
+                                                   std::bind(&AsyncRedisStorageTestBase::asyncCommandDispatcherCreator,
+                                                             this,
+                                                             std::placeholders::_1,
+                                                             std::placeholders::_2,
+                                                             std::placeholders::_3),
+                                                   contentsBuilderMock,
+                                                   logger));
+        }
+
+        void createAndConnectAsyncStorageInstance(const boost::optional<PublisherId>& pId)
+        {
+            InSequence dummy;
+            createAsyncStorageInstance(pId);
+            sdlStorage->waitReadyAsync(ns, std::bind(&AsyncRedisStorageTestBase::readyAck, this, std::placeholders::_1));
+            expectNewDispatcherCreated();
+            expectDispatcherCreation();
+            stateChangedCb(getDatabaseInfo());
+            EXPECT_CALL(*this, readyAck(std::error_code()))
+                .Times(1);
+            dispatcherConnectAck();
+        }
+
+        void expectClearStateChangedCb()
+        {
+            EXPECT_CALL(*discoveryMock, clearStateChangedCb())
+                .Times(1);
+        }
+
+        void expectNoDispatchAsync()
+        {
+            EXPECT_CALL(*dispatcherMock, dispatchAsync(_, ns, _))
+                .Times(0);
+        }
+
+        void expectDispatchAsync()
+        {
+            EXPECT_CALL(*dispatcherMock, dispatchAsync(_, ns, contents))
+                .Times(1)
+                .WillOnce(SaveArg<0>(&savedCommandCb));
+        }
+
+        void expectPublishDispatch()
+        {
+            EXPECT_CALL(*dispatcherMock, dispatchAsync(_, ns, contents))
+                .Times(1)
+                .WillOnce(SaveArg<0>(&savedPublishCommandCb));
+        }
+
+        std::shared_ptr<Reply> getMockPtr()
+        {
+            return std::shared_ptr<Reply>(&replyMock, [](Reply*){ });
+        }
+
+        void expectGetType(const Reply::Type& type)
+        {
+            EXPECT_CALL(replyMock, getType())
+                .Times(1)
+                .WillOnce(Return(type));
+        }
+
+        void expectGetDataString(const Reply::DataItem& item)
+        {
+            expectGetType(Reply::Type::STRING);
+            EXPECT_CALL(replyMock, getString())
+                .Times(1)
+                .WillOnce(Return(&item));
+        }
+
+        void expectGetArray()
+        {
+            EXPECT_CALL(replyMock, getArray())
+                .Times(1)
+                .WillOnce(Return(&replyVector));
+        }
+
+        void expectGetInteger(int value)
+        {
+            EXPECT_CALL(replyMock, getInteger())
+                .Times(1)
+                .WillOnce(Return(value));
+        }
+
+        void expectContentsBuild(const std::string& string,
+                                 const AsyncStorage::Key& key,
+                                 const AsyncStorage::Data& data)
+        {
+            EXPECT_CALL(*contentsBuilderMock, build(string, ns, key, data))
+                .Times(1)
+                .WillOnce(Return(contents));
+        }
+
+
+        void expectContentsBuild(const std::string& string,
+                                 const AsyncStorage::Key& key,
+                                 const AsyncStorage::Data& data,
+                                 const std::string& string2,
+                                 const std::string& string3)
+        {
+            EXPECT_CALL(*contentsBuilderMock, build(string, ns, key, data, string2, string3))
+                .Times(1)
+                .WillOnce(Return(contents));
+        }
+
+        void expectContentsBuild(const std::string& string,
+                                 const std::string& string2,
+                                 const std::string& string3)
+        {
+            EXPECT_CALL(*contentsBuilderMock, build(string, string2, string3))
+                .Times(1)
+                .WillOnce(Return(contents));
+        }
+
+        void expectContentsBuild(const std::string& string,
+                                 const AsyncStorage::DataMap& dataMap)
+        {
+            EXPECT_CALL(*contentsBuilderMock, build(string, ns, dataMap))
+                .Times(1)
+                .WillOnce(Return(contents));
+        }
+
+        void expectContentsBuild(const std::string& string,
+                                 const AsyncStorage::DataMap& dataMap,
+                                 const std::string& string2,
+                                 const std::string& string3)
+        {
+            EXPECT_CALL(*contentsBuilderMock, build(string, ns, dataMap, string2, string3))
+                .Times(1)
+                .WillOnce(Return(contents));
+        }
+
+        void expectContentsBuild(const std::string& string,
+                                 const AsyncStorage::Keys& keys)
+        {
+            EXPECT_CALL(*contentsBuilderMock, build(string, ns, keys))
+                .Times(1)
+                .WillOnce(Return(contents));
+        }
+
+        void expectContentsBuild(const std::string& string,
+                                 const AsyncStorage::Keys& keys,
+                                 const std::string& string2,
+                                 const std::string& string3)
+        {
+            EXPECT_CALL(*contentsBuilderMock, build(string, ns, keys, string2, string3))
+                .Times(1)
+                .WillOnce(Return(contents));
+        }
+
+        void expectContentsBuild(const std::string& string,
+                                 const std::string& string2)
+        {
+            EXPECT_CALL(*contentsBuilderMock, build(string, string2))
+                .Times(1)
+                .WillOnce(Return(contents));
+        }
+    };
+
+    class AsyncRedisStorageTest: public AsyncRedisStorageTestBase
+    {
+    public:
+        AsyncRedisStorageTest()
+        {
+            InSequence dummy;
+            for (auto i(0U); i < 3; ++i)
+                replyVector.push_back(getMockPtr());
+            createAndConnectAsyncStorageInstance(boost::none);
+            EXPECT_CALL(*namespaceConfigurationsMock, areNotificationsEnabled(_)).WillRepeatedly(Return(true));
+        }
+
+        ~AsyncRedisStorageTest()
+        {
+            expectClearStateChangedCb();
+            EXPECT_CALL(*dispatcherMock, disableCommandCallbacks())
+                .Times(1);
+        }
+    };
+
+
+    class AsyncRedisStorageTestNotificationsDisabled: public AsyncRedisStorageTestBase
+    {
+    public:
+        AsyncRedisStorageTestNotificationsDisabled()
+        {
+            InSequence dummy;
+            for (auto i(0U); i < 3; ++i)
+                replyVector.push_back(getMockPtr());
+            createAndConnectAsyncStorageInstance(boost::none);
+            EXPECT_CALL(*namespaceConfigurationsMock, areNotificationsEnabled(_)).WillRepeatedly(Return(false));
+        }
+
+        ~AsyncRedisStorageTestNotificationsDisabled()
+        {
+            expectClearStateChangedCb();
+            EXPECT_CALL(*dispatcherMock, disableCommandCallbacks())
+                .Times(1);
+        }
+    };
+
+
+    class AsyncRedisStorageTestDispatcherNotCreated: public AsyncRedisStorageTestBase
+    {
+    public:
+        AsyncRedisStorageTestDispatcherNotCreated()
+        {
+            InSequence dummy;
+            for (auto i(0U); i < 3; ++i)
+                replyVector.push_back(getMockPtr());
+            createAsyncStorageInstance(boost::none);
+        }
+
+        ~AsyncRedisStorageTestDispatcherNotCreated()
+        {
+            expectClearStateChangedCb();
+        }
+    };
+
+    class AsyncRedisStorageDeathTest: public AsyncRedisStorageTestBase
+    {
+    public:
+        AsyncRedisStorageDeathTest()
+        {
+        }
+    };
+}
+
+TEST_F(AsyncRedisStorageErrorCodeTest, AllErrorCodeEnumsHaveCorrectDescriptionMessage)
+{
+    std::error_code ec;
+
+    for (AsyncRedisStorage::ErrorCode aec = AsyncRedisStorage::ErrorCode::SUCCESS; aec != AsyncRedisStorage::ErrorCode::END_MARKER; ++aec)
+    {
+        switch (aec)
+        {
+            case AsyncRedisStorage::ErrorCode::SUCCESS:
+                ec = aec;
+                EXPECT_EQ(std::error_code().message(), getErrorCodeMessage(ec));
+                break;
+            case AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED:
+                ec = aec;
+                EXPECT_EQ("connection to the underlying data storage not yet available", getErrorCodeMessage(ec));
+                break;
+            case AsyncRedisStorage::ErrorCode::INVALID_NAMESPACE:
+                ec = aec;
+                EXPECT_EQ("invalid namespace identifier passed to SDL API", getErrorCodeMessage(ec));
+                break;
+            case AsyncRedisStorage::ErrorCode::END_MARKER:
+                ec = aec;
+                EXPECT_EQ("unsupported error code for message()", getErrorCodeMessage(ec));
+                break;
+            default:
+                FAIL() << "No mapping for AsyncRedisStorage value: " << aec;
+                break;
+        }
+    }
+}
+
+TEST_F(AsyncRedisStorageErrorCodeTest, AllErrorCodeEnumsAreMappedToCorrectSDLInternalError)
+{
+    /* If this test case detects missing error code, remember to add new error code also to AllErrorCodeEnumsAreMappedToCorrectClientErrorCode
+     * test case (and add also mapping implementation from InternalError to Error if needed).
+     */
+    std::error_code ec;
+
+    for (AsyncRedisStorage::ErrorCode aec = AsyncRedisStorage::ErrorCode::SUCCESS; aec != AsyncRedisStorage::ErrorCode::END_MARKER; ++aec)
+    {
+        switch (aec)
+        {
+            case AsyncRedisStorage::ErrorCode::SUCCESS:
+                ec = aec;
+                EXPECT_TRUE(ec == InternalError::SUCCESS);
+                break;
+            case AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED:
+                ec = aec;
+                EXPECT_TRUE(ec == InternalError::SDL_NOT_READY);
+                break;
+            case AsyncRedisStorage::ErrorCode::INVALID_NAMESPACE:
+                ec = aec;
+                EXPECT_TRUE(ec == InternalError::SDL_RECEIVED_INVALID_PARAMETER);
+                break;
+            case AsyncRedisStorage::ErrorCode::END_MARKER:
+                ec = aec;
+                EXPECT_TRUE(ec == InternalError::SDL_ERROR_CODE_LOGIC_ERROR);
+                break;
+            default:
+                FAIL() << "No mapping for AsyncRedisStorage value: " << aec;
+                break;
+        }
+    }
+}
+
+TEST_F(AsyncRedisStorageErrorCodeTest, AllErrorCodeEnumsAreMappedToCorrectClientErrorCode)
+{
+    std::error_code ec;
+
+    ec = AsyncRedisStorage::ErrorCode::SUCCESS;
+    EXPECT_TRUE(ec == shareddatalayer::Error::SUCCESS);
+    ec = AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED;
+    EXPECT_TRUE(ec == shareddatalayer::Error::NOT_CONNECTED);
+    ec = AsyncRedisStorage::ErrorCode::END_MARKER;
+    EXPECT_TRUE(ec == shareddatalayer::Error::BACKEND_FAILURE);
+}
+
+TEST_F(AsyncRedisStorageTest, IsNotCopyable)
+{
+    EXPECT_FALSE(std::is_copy_constructible<AsyncRedisStorage>::value);
+    EXPECT_FALSE(std::is_copy_assignable<AsyncRedisStorage>::value);
+}
+TEST_F(AsyncRedisStorageTest, ImplementsAsyncStorage)
+{
+    EXPECT_TRUE((std::is_base_of<AsyncStorage, AsyncRedisStorage>::value));
+}
+
+TEST_F(AsyncRedisStorageTest, CanGetFd)
+{
+    EXPECT_CALL(*engineMock, fd())
+        .Times(1)
+        .WillOnce(Return(fd));
+    EXPECT_EQ(fd, sdlStorage->fd());
+}
+
+TEST_F(AsyncRedisStorageTest, CanHandleEvents)
+{
+    EXPECT_CALL(*engineMock, handleEvents())
+        .Times(1);
+    sdlStorage->handleEvents();
+}
+
+TEST_F(AsyncRedisStorageTest, ReadyAckIsPassedToAsyncRedisCommandDispatcher)
+{
+    InSequence dummy;
+    EXPECT_CALL(*dispatcherMock, waitConnectedAsync(_))
+        .Times(1)
+        .WillOnce(SaveArg<0>(&dispatcherConnectAck));
+    sdlStorage->waitReadyAsync(ns, std::bind(&AsyncRedisStorageTest::readyAck, this, std::placeholders::_1));
+    expectReadyAck(std::error_code());
+    dispatcherConnectAck();
+}
+
+TEST_F(AsyncRedisStorageTest, PassingEmptyPublisherIdThrows)
+{
+    EXPECT_THROW(sdlStorage.reset(new AsyncRedisStorage(
+                     engineMock,
+                     discoveryMock,
+                     std::string(""),
+                     namespaceConfigurationsMock,
+                     std::bind(&AsyncRedisStorageTest::asyncCommandDispatcherCreator,
+                         this,
+                         std::placeholders::_1,
+                         std::placeholders::_2,
+                         std::placeholders::_3),
+                     contentsBuilderMock,
+                     logger)),
+                 std::invalid_argument);
+}
+
+TEST_F(AsyncRedisStorageTest, PassingInvalidNamespaceToSetAsyncNacks)
+{
+    InSequence dummy;
+    expectPostCallback();
+    sdlStorage->setAsync("ns1,2",
+                         { { key1, { } } },
+                         std::bind(&AsyncRedisStorageTestDispatcherNotCreated::modifyAck,
+                                   this,
+                                   std::placeholders::_1));
+    expectModifyAck(std::error_code(AsyncRedisStorage::ErrorCode::INVALID_NAMESPACE));
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTest, PassingEmptyNamespaceToSetAsyncNacks)
+{
+    InSequence dummy;
+    expectPostCallback();
+    sdlStorage->setAsync("",
+                         { { key1, { } } },
+                         std::bind(&AsyncRedisStorageTestDispatcherNotCreated::modifyAck,
+                                   this,
+                                   std::placeholders::_1));
+    expectModifyAck(std::error_code(AsyncRedisStorage::ErrorCode::INVALID_NAMESPACE));
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTest, SetAsyncSuccessfullyAndErrorIsForwarded)
+{
+    InSequence dummy;
+    expectContentsBuild("MSETPUB", dataMap, ns, shareddatalayer::NO_PUBLISHER);
+    expectDispatchAsync();
+    sdlStorage->setAsync(ns,
+                         dataMap,
+                         std::bind(&AsyncRedisStorageTest::modifyAck, this, std::placeholders::_1));
+    expectModifyAck(std::error_code());
+    savedCommandCb(std::error_code(), replyMock);
+    expectModifyAck(getWellKnownErrorCode());
+    savedCommandCb(getWellKnownErrorCode(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTestNotificationsDisabled, SetAsyncSuccessfullyAndErrorIsForwardedNoPublish)
+{
+    InSequence dummy;
+    expectContentsBuild("MSET", dataMap);
+    expectDispatchAsync();
+    sdlStorage->setAsync(ns,
+                         dataMap,
+                         std::bind(&AsyncRedisStorageTest::modifyAck, this, std::placeholders::_1));
+    expectModifyAck(std::error_code());
+    savedCommandCb(std::error_code(), replyMock);
+    expectModifyAck(getWellKnownErrorCode());
+    savedCommandCb(getWellKnownErrorCode(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTest, EmptyMapIsCheckedInSetAsyncAndAckIsScheduled)
+{
+    InSequence dummy;
+    expectNoDispatchAsync();
+    expectPostCallback();
+    sdlStorage->setAsync(ns,
+                         { },
+                         std::bind(&AsyncRedisStorageTest::modifyAck, this, std::placeholders::_1));
+    expectModifyAck(std::error_code());
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTest, GetAsyncSuccessfullyAndErrorIsForwarded)
+{
+    InSequence dummy;
+    expectContentsBuild("MGET", keysWithNonExistKey);
+    expectDispatchAsync();
+    sdlStorage->getAsync(ns,
+                         keysWithNonExistKey,
+                         std::bind(&AsyncRedisStorageTest::getAck,
+                                   this,
+                                   std::placeholders::_1,
+                                   std::placeholders::_2));
+    expectGetArray();
+    auto expectedDataItem1(Reply::DataItem { std::string(data1.begin(),data1.end()), ReplyStringLength(data1.size()) });
+    auto expectedDataItem2(Reply::DataItem { std::string(data2.begin(),data2.end()), ReplyStringLength(data2.size()) });
+    expectGetDataString(expectedDataItem1);
+    expectGetDataString(expectedDataItem2);
+    expectGetType(Reply::Type::NIL);
+    expectGetAck(std::error_code(), { { key1, data1 }, { key2, data2 } });
+    savedCommandCb(std::error_code(), replyMock);
+    expectGetAck(getWellKnownErrorCode(), { });
+    savedCommandCb(getWellKnownErrorCode(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTest, EmptyEntriesIsCheckedInGetAsyncAndAckIsScheduled)
+{
+    InSequence dummy;
+    expectNoDispatchAsync();
+    expectPostCallback();
+    sdlStorage->getAsync(ns,
+                         { },
+                         std::bind(&AsyncRedisStorageTest::getAck,
+                                   this,
+                                   std::placeholders::_1,
+                                   std::placeholders::_2));
+    expectGetAck(std::error_code(), { });
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTest, RemoveAsyncSuccessfullyAndErrorIsForwarded)
+{
+    InSequence dummy;
+    expectContentsBuild("DELPUB", keys, ns, shareddatalayer::NO_PUBLISHER);
+    expectDispatchAsync();
+    sdlStorage->removeAsync(ns,
+                            keys,
+                            std::bind(&AsyncRedisStorageTest::modifyAck,
+                                      this,
+                                      std::placeholders::_1));
+    expectModifyAck(std::error_code());
+    savedCommandCb(std::error_code(), replyMock);
+    expectModifyAck(getWellKnownErrorCode());
+    savedCommandCb(getWellKnownErrorCode(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTestNotificationsDisabled, RemoveAsyncSuccessfullyAndErrorIsForwardedNoPublish)
+{
+    InSequence dummy;
+    expectContentsBuild("DEL", keys);
+    expectDispatchAsync();
+    sdlStorage->removeAsync(ns,
+                            keys,
+                            std::bind(&AsyncRedisStorageTest::modifyAck,
+                                      this,
+                                      std::placeholders::_1));
+    expectModifyAck(std::error_code());
+    savedCommandCb(std::error_code(), replyMock);
+    expectModifyAck(getWellKnownErrorCode());
+    savedCommandCb(getWellKnownErrorCode(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTest, EmptyEntriesIsCheckedInRemoveAsyncAndAckIsScheduled)
+{
+    InSequence dummy;
+    expectNoDispatchAsync();
+    expectPostCallback();
+    sdlStorage->removeAsync(ns,
+                            { },
+                            std::bind(&AsyncRedisStorageTest::modifyAck, this, std::placeholders::_1));
+    expectModifyAck(std::error_code());
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTest, FindKeysAsyncSuccessfullyAndErrorIsTranslated)
+{
+    InSequence dummy;
+    expectContentsBuild("KEYS", keyPrefix);
+    expectDispatchAsync();
+    sdlStorage->findKeysAsync(ns,
+                              "",
+                              std::bind(&AsyncRedisStorageTest::findKeysAck,
+                                        this,
+                                        std::placeholders::_1,
+                                        std::placeholders::_2));
+    expectGetArray();
+    auto expectedDataItem1(Reply::DataItem { key1, ReplyStringLength(key1.size()) });
+    auto expectedDataItem2(Reply::DataItem { key2, ReplyStringLength(key2.size()) });
+    expectGetDataString(expectedDataItem1);
+    expectGetType(Reply::Type::NIL);
+    expectGetDataString(expectedDataItem2);
+    expectFindKeysAck(std::error_code(), { key1, key2 });
+    savedCommandCb(std::error_code(), replyMock);
+    expectFindKeysAck(getWellKnownErrorCode(), { });
+    savedCommandCb(getWellKnownErrorCode(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTest, RemoveAllAsyncSuccessfully)
+{
+    InSequence dummy;
+    expectContentsBuild("KEYS", keyPrefix);
+    expectDispatchAsync();
+    sdlStorage->removeAllAsync(ns,
+                               std::bind(&AsyncRedisStorageTest::modifyAck, this, std::placeholders::_1));
+    expectGetArray();
+    auto expectedDataItem1(Reply::DataItem { key1, ReplyStringLength(key1.size()) });
+    auto expectedDataItem2(Reply::DataItem { key2, ReplyStringLength(key2.size()) });
+    expectGetDataString(expectedDataItem1);
+    expectGetType(Reply::Type::NIL);
+    expectGetDataString(expectedDataItem2);
+    expectContentsBuild("DELPUB", keys, ns, shareddatalayer::NO_PUBLISHER);
+    expectDispatchAsync();
+    savedCommandCb(std::error_code(), replyMock);
+    expectModifyAck(std::error_code());
+    savedCommandCb(std::error_code(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTestNotificationsDisabled, RemoveAllAsyncSuccessfullyNoPublish)
+{
+    InSequence dummy;
+    expectContentsBuild("KEYS", keyPrefix);
+    expectDispatchAsync();
+    sdlStorage->removeAllAsync(ns,
+                               std::bind(&AsyncRedisStorageTest::modifyAck, this, std::placeholders::_1));
+    expectGetArray();
+    auto expectedDataItem1(Reply::DataItem { key1, ReplyStringLength(key1.size()) });
+    auto expectedDataItem2(Reply::DataItem { key2, ReplyStringLength(key2.size()) });
+    expectGetDataString(expectedDataItem1);
+    expectGetType(Reply::Type::NIL);
+    expectGetDataString(expectedDataItem2);
+    expectContentsBuild("DEL", keys);
+    expectDispatchAsync();
+    savedCommandCb(std::error_code(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTest, NothingIsIssuedToBeRemovedIfNoKeysAreFoundUnderNamespace)
+{
+    InSequence dummy;
+    Reply::ReplyVector empty;
+    expectContentsBuild("KEYS", keyPrefix);
+    expectDispatchAsync();
+    sdlStorage->removeAllAsync(ns,
+                               std::bind(&AsyncRedisStorageTest::modifyAck, this, std::placeholders::_1));
+    EXPECT_CALL(replyMock, getArray())
+        .Times(1)
+        .WillOnce(Return(&empty));
+    EXPECT_CALL(*dispatcherMock, dispatchAsync(_, ns, _))
+        .Times(0);
+    expectModifyAck(std::error_code());
+    savedCommandCb(std::error_code(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTest, RemoveAllAsyncErrorIsForwarded)
+{
+    InSequence dummy;
+    expectContentsBuild("KEYS", keyPrefix);
+    expectDispatchAsync();
+    sdlStorage->removeAllAsync(ns,
+                               std::bind(&AsyncRedisStorageTest::modifyAck,
+                                         this,
+                                         std::placeholders::_1));
+    expectModifyAck(getWellKnownErrorCode());
+    savedCommandCb(getWellKnownErrorCode(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTest, SetIfNotExistsAsyncSuccess)
+{
+    InSequence dummy;
+    expectContentsBuild("SETNXPUB", key1, data1, ns, shareddatalayer::NO_PUBLISHER);
+    expectDispatchAsync();
+    sdlStorage->setIfNotExistsAsync(ns,
+                                    key1,
+                                    data1,
+                                    std::bind(&AsyncRedisStorageTest::modifyIfAck,
+                                              this, std::placeholders::_1,  std::placeholders::_2));
+    expectGetType(Reply::Type::INTEGER);
+    expectGetInteger(1);
+    expectModifyIfAck(std::error_code(), true);
+    savedCommandCb(std::error_code(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTestNotificationsDisabled, SetIfNotExistsAsyncSuccessNoPublish)
+{
+    InSequence dummy;
+    expectContentsBuild("SETNX", key1, data1);
+    expectDispatchAsync();
+    sdlStorage->setIfNotExistsAsync(ns,
+                                    key1,
+                                    data1,
+                                    std::bind(&AsyncRedisStorageTest::modifyIfAck,
+                                              this, std::placeholders::_1,  std::placeholders::_2));
+    expectGetType(Reply::Type::INTEGER);
+    expectGetInteger(1);
+    expectModifyIfAck(std::error_code(), true);
+    savedCommandCb(std::error_code(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTest, SetIfNotExistsAsyncKeyAlreadyExists)
+{
+    InSequence dummy;
+    expectContentsBuild("SETNXPUB", key1, data1, ns, shareddatalayer::NO_PUBLISHER);
+    expectDispatchAsync();
+    sdlStorage->setIfNotExistsAsync(ns,
+                                    key1,
+                                    data1,
+                                    std::bind(&AsyncRedisStorageTest::modifyIfAck,
+                                              this, std::placeholders::_1,  std::placeholders::_2));
+    expectGetType(Reply::Type::INTEGER);
+    expectGetInteger(0);
+    expectModifyIfAck(std::error_code(), false);
+    savedCommandCb(std::error_code(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTest, SetIfNotExistsAsyncErrorResponse)
+{
+    InSequence dummy;
+    expectContentsBuild("SETNXPUB", key1, data1, ns, shareddatalayer::NO_PUBLISHER);
+    expectDispatchAsync();
+    sdlStorage->setIfNotExistsAsync(ns,
+                                    key1,
+                                    data1,
+                                    std::bind(&AsyncRedisStorageTest::modifyIfAck,
+                                              this, std::placeholders::_1,  std::placeholders::_2));
+    expectGetType(Reply::Type::INTEGER);
+    expectModifyIfAck(getWellKnownErrorCode(), false);
+    savedCommandCb(getWellKnownErrorCode(), replyMock);
+}
+
+TEST_F(AsyncRedisStorageTest, RedisSearchPatternCharactersAreCorrectlyEscapedInKeyPrefixSearch)
+{
+    InSequence dummy;
+    const std::string keyPrefixSearchPatternPrefix = '{' + ns + '}' + AsyncStorage::SEPARATOR;
+    std::string builtKeyPrefixSearchPattern;
+    std::string expectedKeyPrefixSearchPattern;
+
+    // empty prefix will not be escaped and it will search all keys
+    expectedKeyPrefixSearchPattern = keyPrefixSearchPatternPrefix + '*';
+    builtKeyPrefixSearchPattern = sdlStorage->buildKeyPrefixSearchPattern(ns, "");
+    ASSERT_STREQ(expectedKeyPrefixSearchPattern.c_str(), builtKeyPrefixSearchPattern.c_str());
+
+    // prefix without search characters is not escaped
+    expectedKeyPrefixSearchPattern = keyPrefixSearchPatternPrefix + "someKnownKeyPrefix" + '*';
+    builtKeyPrefixSearchPattern = sdlStorage->buildKeyPrefixSearchPattern(ns, "someKnownKeyPrefix");
+    ASSERT_STREQ(expectedKeyPrefixSearchPattern.c_str(), builtKeyPrefixSearchPattern.c_str());
+
+    expectedKeyPrefixSearchPattern = keyPrefixSearchPatternPrefix + R"("someKnownKeyPrefix")" + '*';
+    builtKeyPrefixSearchPattern = sdlStorage->buildKeyPrefixSearchPattern(ns, R"("someKnownKeyPrefix")");
+    ASSERT_STREQ(expectedKeyPrefixSearchPattern.c_str(), builtKeyPrefixSearchPattern.c_str());
+
+    // all search characters are correctly escaped with backslash
+    expectedKeyPrefixSearchPattern = keyPrefixSearchPatternPrefix + R"(someKnownKeyPrefix\*)" + '*';
+    builtKeyPrefixSearchPattern = sdlStorage->buildKeyPrefixSearchPattern(ns, "someKnownKeyPrefix*");
+    ASSERT_STREQ(expectedKeyPrefixSearchPattern.c_str(), builtKeyPrefixSearchPattern.c_str());
+
+    expectedKeyPrefixSearchPattern = keyPrefixSearchPatternPrefix + R"(\?some\]Known\[Key\\Prefix\*)" + '*';
+    builtKeyPrefixSearchPattern = sdlStorage->buildKeyPrefixSearchPattern(ns, "?some]Known[Key\\Prefix*");
+    ASSERT_STREQ(expectedKeyPrefixSearchPattern.c_str(), builtKeyPrefixSearchPattern.c_str());
+
+    expectedKeyPrefixSearchPattern = keyPrefixSearchPatternPrefix + R"(\?\*some\[\]Known\[\*\?\\\]Key\\\*Prefix\*\*\*\*)" + '*';
+    builtKeyPrefixSearchPattern = sdlStorage->buildKeyPrefixSearchPattern(ns, "?*some[]Known[*?\\]Key\\*Prefix****");
+    ASSERT_STREQ(expectedKeyPrefixSearchPattern.c_str(), builtKeyPrefixSearchPattern.c_str());
+}
+
+TEST_F(AsyncRedisStorageTestDispatcherNotCreated, ReadyAckNotForwardedIfDispatcherNotYetCreated)
+{
+    InSequence dummy;
+    EXPECT_CALL(*dispatcherMock, waitConnectedAsync(_))
+        .Times(0);
+    sdlStorage->waitReadyAsync(ns, std::bind(&AsyncRedisStorageTestDispatcherNotCreated::readyAck, this, std::placeholders::_1));
+}
+
+TEST_F(AsyncRedisStorageTestDispatcherNotCreated, SetAsyncWithoutDispatcherInstanceNacksWithREDIS_NOT_YET_DISCOVERED)
+{
+    InSequence dummy;
+    expectPostCallback();
+    sdlStorage->setAsync(ns,
+                         { { key1, { } } },
+                         std::bind(&AsyncRedisStorageTestDispatcherNotCreated::modifyAck,
+                                   this,
+                                   std::placeholders::_1));
+    expectModifyAck(std::error_code(AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED));
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTestDispatcherNotCreated, SetIfAsyncWithoutDispatcherInstanceNacksWithREDIS_NOT_YET_DISCOVERED)
+{
+    InSequence dummy;
+    expectPostCallback();
+    sdlStorage->setIfAsync(ns,
+                           key1,
+                           data1,
+                           data2,
+                           std::bind(&AsyncRedisStorageTestDispatcherNotCreated::modifyIfAck,
+                                     this, std::placeholders::_1,  std::placeholders::_2));
+    expectModifyIfAck(std::error_code(AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED), false);
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTestDispatcherNotCreated, SetIfNotExistsAsyncWithoutDispatcherInstanceNacksWithREDIS_NOT_YET_DISCOVERED)
+{
+    InSequence dummy;
+    expectPostCallback();
+    sdlStorage->setIfNotExistsAsync(ns,
+                                    key1,
+                                    data1,
+                                    std::bind(&AsyncRedisStorageTestDispatcherNotCreated::modifyIfAck,
+                                              this, std::placeholders::_1,  std::placeholders::_2));
+    expectModifyIfAck(std::error_code(AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED), false);
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTestDispatcherNotCreated, GetAsyncWithoutDispatcherInstanceNacksWithREDIS_NOT_YET_DISCOVERED)
+{
+    InSequence dummy;
+    expectPostCallback();
+    sdlStorage->getAsync(ns,
+                         {key1},
+                         std::bind(&AsyncRedisStorageTestDispatcherNotCreated::getAck,
+                                   this,
+                                   std::placeholders::_1,
+                                   std::placeholders::_2));
+    expectGetAck(std::error_code(AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED), { });
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTestDispatcherNotCreated, RemoveAsyncWithoutDispatcherInstanceNacksWithREDIS_NOT_YET_DISCOVERED)
+{
+    InSequence dummy;
+    expectPostCallback();
+    sdlStorage->removeAsync(ns,
+                            {key1},
+                            std::bind(&AsyncRedisStorageTestDispatcherNotCreated::modifyAck, this, std::placeholders::_1));
+    expectModifyAck(std::error_code(AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED));
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTestDispatcherNotCreated, FindKeysAsyncWithoutDispatcherInstanceNacksWithREDIS_NOT_YET_DISCOVERED)
+{
+    InSequence dummy;
+    expectPostCallback();
+    sdlStorage->findKeysAsync(ns,
+                              "*",
+                              std::bind(&AsyncRedisStorageTestDispatcherNotCreated::findKeysAck,
+                                        this,
+                                        std::placeholders::_1,
+                                        std::placeholders::_2));
+    expectFindKeysAck(std::error_code(AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED), { });
+    storedCallback();
+}
+
+TEST_F(AsyncRedisStorageTestDispatcherNotCreated, RemoveAllAsyncWithoutDispatcherInstanceNacksWithREDIS_NOT_YET_DISCOVERED)
+{
+    InSequence dummy;
+    expectPostCallback();
+    sdlStorage->removeAllAsync(ns,
+                               std::bind(&AsyncRedisStorageTestDispatcherNotCreated::modifyAck, this, std::placeholders::_1));
+    expectModifyAck(std::error_code(AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED));
+    storedCallback();
+}