Add first version
[ric-plt/sdl.git] / tst / configurationreader_test.cpp
diff --git a/tst/configurationreader_test.cpp b/tst/configurationreader_test.cpp
new file mode 100644 (file)
index 0000000..8120052
--- /dev/null
@@ -0,0 +1,826 @@
+/*
+   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 <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <memory>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include "private/configurationreader.hpp"
+#include "private/createlogger.hpp"
+#include "private/logger.hpp"
+#include "private/tst/databaseconfigurationmock.hpp"
+#include "private/tst/gettopsrcdir.hpp"
+#include "private/tst/namespaceconfigurationsmock.hpp"
+#include "private/tst/systemmock.hpp"
+
+using namespace shareddatalayer;
+using namespace shareddatalayer::tst;
+using namespace testing;
+
+namespace
+{
+    class ConfigurationReaderBaseTest: public testing::Test
+    {
+    public:
+        const std::string someKnownInputSource;
+        const Directories noDirectories;
+        DatabaseConfigurationMock databaseConfigurationMock;
+        NamespaceConfigurationsMock namespaceConfigurationsMock;
+        std::unique_ptr<ConfigurationReader> configurationReader;
+        SystemMock systemMock;
+        std::shared_ptr<Logger> logger;
+
+        ConfigurationReaderBaseTest(std::string inputSourceName):
+            someKnownInputSource(inputSourceName),
+            logger(createLogger(SDL_LOG_PREFIX))
+        {
+            EXPECT_CALL(databaseConfigurationMock, isEmpty()).WillRepeatedly(Return(true));
+            EXPECT_CALL(namespaceConfigurationsMock, isEmpty()).WillRepeatedly(Return(true));
+        }
+
+        void expectDbTypeConfigurationCheckAndApply(const std::string& type)
+        {
+            EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(type));
+        }
+
+        void expectDBServerAddressConfigurationCheckAndApply(const std::string& address)
+        {
+            EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(address));
+        }
+
+        void expectDatabaseConfigurationIsEmpty_returnFalse()
+        {
+            EXPECT_CALL(databaseConfigurationMock, isEmpty()).
+                WillOnce(Return(false));
+        }
+
+        void tryToReadDatabaseConfigurationToNonEmptyContainer()
+        {
+            expectDatabaseConfigurationIsEmpty_returnFalse();
+            configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+        }
+
+        void expectNamespaceConfigurationIsEmpty_returnFalse()
+        {
+            EXPECT_CALL(namespaceConfigurationsMock, isEmpty()).
+                WillOnce(Return(false));
+        }
+
+        void tryToReadNamespaceConfigurationToNonEmptyContainer()
+        {
+            expectNamespaceConfigurationIsEmpty_returnFalse();
+            configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
+        }
+
+        void expectAddNamespaceConfiguration(const std::string&  namespacePrefix, bool useDbBackend,
+                                             bool enableNotifications)
+        {
+            NamespaceConfiguration expectedNamespaceConfiguration{namespacePrefix, useDbBackend,
+                                                                  enableNotifications,
+                                                                  someKnownInputSource};
+            EXPECT_CALL(namespaceConfigurationsMock, addNamespaceConfiguration(expectedNamespaceConfiguration));
+        }
+
+        void expectGetEnvironmentString(const char* returnValue)
+        {
+            EXPECT_CALL(systemMock, getenv(_))
+                .WillOnce(Return(returnValue));
+        }
+
+        void initializeReaderWithoutDirectories()
+        {
+            configurationReader.reset(new ConfigurationReader(noDirectories, systemMock, logger));
+        }
+
+        void initializeReaderWithSDLconfigFileDirectory()
+        {
+            configurationReader.reset(new ConfigurationReader({getTopSrcDir() + "/conf"}, systemMock, logger));
+        }
+    };
+
+    class ConfigurationReaderSDLConfigFileTest: public ConfigurationReaderBaseTest
+    {
+    public:
+
+        ConfigurationReaderSDLConfigFileTest():
+            ConfigurationReaderBaseTest("ConfFileFromSDLrepo")
+        {
+            expectGetEnvironmentString(nullptr);
+            initializeReaderWithSDLconfigFileDirectory();
+        }
+    };
+
+    class ConfigurationReaderInputStreamTest: public ConfigurationReaderBaseTest
+    {
+    public:
+
+        ConfigurationReaderInputStreamTest():
+            ConfigurationReaderBaseTest("<istream>")
+        {
+            expectGetEnvironmentString(nullptr);
+            initializeReaderWithoutDirectories();
+        }
+
+        void readConfigurationAndExpectJsonParserException(const std::istringstream& is)
+        {
+            const std::string expectedError("error in SDL configuration <istream> at line 7: expected ':'");
+
+            EXPECT_THROW( {
+                try
+                {
+                    configurationReader->readConfigurationFromInputStream(is);
+                    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+                    configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
+                }
+                catch (const std::exception& e)
+                {
+                    EXPECT_EQ(expectedError, e.what());
+                    throw;
+                }
+            }, Exception);
+        }
+
+        void readConfigurationAndExpectBadValueException(const std::istringstream& is,
+                                                         const std::string& param)
+        {
+            std::ostringstream os;
+            os << "Configuration error in " << someKnownInputSource << ": "
+               << "invalid \"" << param << "\": \"bad-value\"";
+
+            EXPECT_THROW( {
+                try
+                {
+                    configurationReader->readConfigurationFromInputStream(is);
+                    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+                    configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
+                }
+                catch (const std::exception& e)
+                {
+                    EXPECT_EQ(os.str(), e.what());
+                    throw;
+                }
+            }, Exception);
+        }
+
+        void readConfigurationAndExpectDbTypeException(const std::istringstream& is)
+        {
+            std::ostringstream os;
+            os << "Configuration error in " << someKnownInputSource << ": some error";
+
+            EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(_))
+                .WillOnce(Throw(Exception("some error")));
+
+            EXPECT_THROW( {
+                try
+                {
+                    configurationReader->readConfigurationFromInputStream(is);
+                    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+                }
+                catch (const std::exception& e)
+                {
+                    EXPECT_EQ(os.str(), e.what());
+                    throw;
+                }
+            }, Exception);
+        }
+
+        void readConfigurationAndExpectAddressException(const std::istringstream& is,
+                                                        const std::string& addressValue)
+        {
+            std::ostringstream os;
+            os << "Configuration error in " << someKnownInputSource << ": "
+               << "invalid \"address\": \"" << addressValue << "\" some error";
+
+            EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(_))
+                .WillOnce(Throw(Exception("some error")));
+
+            EXPECT_THROW( {
+                try
+                {
+                    configurationReader->readConfigurationFromInputStream(is);
+                    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+                }
+                catch (const std::exception& e)
+                {
+                    EXPECT_EQ(os.str(), e.what());
+                    throw;
+                }
+            }, Exception);
+        }
+
+        void readConfigurationAndExpectMissingParameterException(const std::istringstream& is, const std::string& param)
+        {
+            std::ostringstream os;
+            os << "Configuration error in " << someKnownInputSource << ": "
+               << "missing \"" << param << "\"";
+
+            EXPECT_THROW( {
+                try
+                {
+                    configurationReader->readConfigurationFromInputStream(is);
+                    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+                    configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
+                }
+                catch (const std::exception& e)
+                {
+                    EXPECT_EQ(os.str(), e.what());
+                    throw;
+                }
+            }, Exception);
+        }
+
+        void readConfigurationAndExpectNamespacePrefixValidationException(const std::istringstream& is,
+                                                                          const std::string& namespacePrefix)
+        {
+            std::ostringstream os;
+            os << "Configuration error in " << someKnownInputSource << ": "
+               << "\"namespacePrefix\": \"" << namespacePrefix << "\""
+               << " contains some of these disallowed characters: ,{}";
+
+            EXPECT_THROW( {
+                try
+                {
+                    configurationReader->readConfigurationFromInputStream(is);
+                    configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
+                }
+                catch (const std::exception& e)
+                {
+                    EXPECT_EQ(os.str(), e.what() );
+                    throw;
+                }
+            }, Exception);
+        }
+
+        void readConfigurationAndExpectEnableNotificationsValidationException(const std::istringstream& is)
+        {
+            std::ostringstream os;
+            os << "Configuration error in " << someKnownInputSource << ": "
+               << "\"enableNotifications\" cannot be true, when \"useDbBackend\" is false";
+
+            EXPECT_THROW( {
+                try
+                {
+                    configurationReader->readConfigurationFromInputStream(is);
+                    configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
+                }
+                catch (const std::exception& e)
+                {
+                    EXPECT_EQ(os.str(), e.what() );
+                    throw;
+                }
+            }, Exception);
+        }
+
+    };
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfiguration)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "database":
+            {
+                "type": "redis-standalone",
+                "servers":
+                [
+                    {
+                        "address": "someKnownDbAddress:65535"
+                    }
+                ]
+            }
+        })JSON");
+    expectDbTypeConfigurationCheckAndApply("redis-standalone");
+    expectDBServerAddressConfigurationCheckAndApply("someKnownDbAddress:65535");
+    configurationReader->readConfigurationFromInputStream(is);
+    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfigurationWithMultipleServerAddresses)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "database":
+            {
+                "type": "redis-cluster",
+                "servers":
+                [
+                    {
+                        "address": "10.20.30.40:50000"
+                    },
+                    {
+                        "address": "10.20.30.50:50001"
+                    }
+                ]
+            }
+        })JSON");
+
+    expectDbTypeConfigurationCheckAndApply("redis-cluster");
+    expectDBServerAddressConfigurationCheckAndApply("10.20.30.40:50000");
+    expectDBServerAddressConfigurationCheckAndApply("10.20.30.50:50001");
+    configurationReader->readConfigurationFromInputStream(is);
+    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfigurationWithMultipleReadOperations)
+{
+    InSequence dummy;
+    std::istringstream isOne(R"JSON(
+        {
+            "database":
+            {
+                "type": "redis-cluster",
+                "servers":
+                [
+                    {
+                        "address": "10.20.30.40:50000"
+                    }
+                ]
+            }
+        })JSON");
+    std::istringstream isTwo(R"JSON(
+        {
+            "database":
+            {
+                "type": "redis-cluster",
+                "servers":
+                [
+                    {
+                        "address": "10.20.30.50:50001"
+                    }
+                ]
+            }
+        })JSON");
+
+    expectDbTypeConfigurationCheckAndApply("redis-cluster");
+    expectDBServerAddressConfigurationCheckAndApply("10.20.30.40:50000");
+    expectDbTypeConfigurationCheckAndApply("redis-cluster");
+    expectDBServerAddressConfigurationCheckAndApply("10.20.30.50:50001");
+    configurationReader->readConfigurationFromInputStream(isOne);
+    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+    configurationReader->readConfigurationFromInputStream(isTwo);
+    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseTypeParameter)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "database":
+            {
+                "servers":
+                [
+                    {
+                        "address": "10.20.30.50:50001"
+                    }
+                ]
+            }
+        })JSON");
+
+    readConfigurationAndExpectMissingParameterException(is, "type");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseServersArray)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "database":
+            {
+                "type": "redis-standalone"
+            }
+        })JSON");
+
+    expectDbTypeConfigurationCheckAndApply("redis-standalone");
+    readConfigurationAndExpectMissingParameterException(is, "servers");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseServerAddressParameter)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "database":
+            {
+                "type": "redis-standalone",
+                "servers":
+                [
+                    {
+                    }
+                ]
+            }
+        })JSON");
+
+    expectDbTypeConfigurationCheckAndApply("redis-standalone");
+    readConfigurationAndExpectMissingParameterException(is, "address");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowDatabaseConfigurationDbTypeError)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "database":
+            {
+                "type": "someBadType",
+                "servers":
+                [
+                    {
+                        "address": "10.20.30.50:50001"
+                    }
+                ]
+            }
+        })JSON");
+
+    readConfigurationAndExpectDbTypeException(is);
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowDatabaseConfigurationAddressError)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "database":
+            {
+                "type": "redis-standalone",
+                "servers":
+                [
+                    {
+                        "address": "someBadAddress"
+                    }
+                ]
+            }
+        })JSON");
+
+    expectDbTypeConfigurationCheckAndApply("redis-standalone");
+    readConfigurationAndExpectAddressException(is, "someBadAddress");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanHandleJSONWithoutAnyConfiguration)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+        })JSON");
+
+    EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(_))
+        .Times(0);
+    EXPECT_CALL(namespaceConfigurationsMock, addNamespaceConfiguration(_))
+        .Times(0);
+    configurationReader->readConfigurationFromInputStream(is);
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfiguration)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "namespacePrefix": "someKnownNamespacePrefix",
+                    "useDbBackend": true,
+                    "enableNotifications": true
+                },
+                {
+                    "namespacePrefix": "anotherKnownNamespace",
+                    "useDbBackend": false,
+                    "enableNotifications": false
+                }
+            ]
+        })JSON");
+
+    expectAddNamespaceConfiguration("anotherKnownNamespace", false, false);
+    expectAddNamespaceConfiguration("someKnownNamespacePrefix", true, true);
+    configurationReader->readConfigurationFromInputStream(is);
+    configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfigurationWithMultipleReadOperations)
+{
+    InSequence dummy;
+    std::istringstream isOne(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "namespacePrefix": "someKnownNamespacePrefix",
+                    "useDbBackend": true,
+                    "enableNotifications": true
+                }
+            ]
+        })JSON");
+
+    std::istringstream isTwo(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "namespacePrefix": "anotherKnownNamespace",
+                    "useDbBackend": false,
+                    "enableNotifications": false
+                }
+            ]
+        })JSON");
+
+    expectAddNamespaceConfiguration("someKnownNamespacePrefix", true, true);
+    expectAddNamespaceConfiguration("anotherKnownNamespace", false, false);
+    configurationReader->readConfigurationFromInputStream(isOne);
+    configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
+    configurationReader->readConfigurationFromInputStream(isTwo);
+    configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfigurationWithEmptyNamespacePrefixValue)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "namespacePrefix": "",
+                    "useDbBackend": false,
+                    "enableNotifications": false
+                }
+            ]
+        })JSON");
+
+    expectAddNamespaceConfiguration("", false, false);
+    configurationReader->readConfigurationFromInputStream(is);
+    configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowJSONSyntaxError)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "abc"
+                }
+            ]
+        })JSON");
+
+    readConfigurationAndExpectJsonParserException(is);
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowParameterUseDbBackendBadValue)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "namespacePrefix": "someKnownNamespacePrefix",
+                    "useDbBackend": "bad-value",
+                    "enableNotifications": false
+                }
+            ]
+        })JSON");
+
+    readConfigurationAndExpectBadValueException(is, "useDbBackend");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowParameterEnableNotificationsBadValue)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "namespacePrefix": "someKnownNamespacePrefix",
+                    "useDbBackend": true,
+                    "enableNotifications": "bad-value"
+                }
+            ]
+        })JSON");
+
+    readConfigurationAndExpectBadValueException(is, "enableNotifications");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryNamespacePrefixParameter)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "useDbBackend": true,
+                    "enableNotifications": true
+                }
+            ]
+        })JSON");
+
+    readConfigurationAndExpectMissingParameterException(is, "namespacePrefix");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryUseDbBackendParameter)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "namespacePrefix": "someKnownNamespacePrefix",
+                    "enableNotifications": true
+                }
+            ]
+        })JSON");
+
+    readConfigurationAndExpectMissingParameterException(is, "useDbBackend");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryEnableNotificationsParameter)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "namespacePrefix": "someKnownNamespacePrefix",
+                    "useDbBackend": true
+                }
+            ]
+        })JSON");
+
+    readConfigurationAndExpectMissingParameterException(is, "enableNotifications");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanThrowValidationErrorForNamespacePrefixWithDisallowedCharacters)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "namespacePrefix": "a,b{c}",
+                    "useDbBackend": true,
+                    "enableNotifications": true
+                }
+            ]
+        })JSON");
+
+    readConfigurationAndExpectNamespacePrefixValidationException(is, "a,b{c}");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, CanThrowValidationErrorForEnableNotificationsWithNoDbBackend)
+{
+    InSequence dummy;
+    std::istringstream is(R"JSON(
+        {
+            "sharedDataLayer":
+            [
+                {
+                    "namespacePrefix": "someKnownNamespacePrefix",
+                    "useDbBackend": false,
+                    "enableNotifications": true
+                }
+            ]
+        })JSON");
+
+    readConfigurationAndExpectEnableNotificationsValidationException(is);
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, WillNotReadDatabaseConfigurationToNonEmptyContainer)
+{
+    EXPECT_EXIT(tryToReadDatabaseConfigurationToNonEmptyContainer(),
+        KilledBySignal(SIGABRT), "ABORT.*configurationreader\\.cpp");
+}
+
+TEST_F(ConfigurationReaderInputStreamTest, WillNotReadNamespaceConfigurationToNonEmptyContainer)
+{
+    EXPECT_EXIT(tryToReadNamespaceConfigurationToNonEmptyContainer(),
+        KilledBySignal(SIGABRT), "ABORT.*configurationreader\\.cpp");
+}
+
+class ConfigurationReaderEnvironmentVariableTest: public ConfigurationReaderBaseTest
+{
+public:
+    std::string dbHostEnvVariableValue;
+    std::string dbPortEnvVariableValue;
+    std::istringstream is{R"JSON(
+        {
+            "database":
+            {
+                "type": "redis-cluster",
+                "servers":
+                [
+                    {
+                        "address": "10.20.30.40:50000"
+                    },
+                    {
+                        "address": "10.20.30.50:50001"
+                    }
+                ]
+            }
+        })JSON"};
+
+    ConfigurationReaderEnvironmentVariableTest():
+        ConfigurationReaderBaseTest(DB_HOST_ENV_VAR_NAME)
+    {
+    }
+
+    void readEnvironmentConfigurationAndExpectConfigurationErrorException()
+    {
+        std::ostringstream os;
+        os << "Configuration error in " << someKnownInputSource << ": some error";
+
+        EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(_))
+            .WillOnce(Throw(Exception("some error")));
+
+        EXPECT_THROW( {
+            try
+            {
+                EXPECT_CALL(systemMock, getenv(_))
+                                   .Times(2)
+                    .WillOnce(Return(dbHostEnvVariableValue.c_str()))
+                    .WillOnce(Return(nullptr));
+                initializeReaderWithoutDirectories();
+                configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+            }
+            catch (const std::exception& e)
+            {
+                EXPECT_EQ(os.str(), e.what());
+                throw;
+            }
+        }, Exception);
+    }
+};
+
+TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationCanOverrideJSONDatabaseConfiguration)
+{
+    InSequence dummy;
+    dbHostEnvVariableValue = "unknownAddress.local";
+    expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
+    dbPortEnvVariableValue = "12345";
+    expectGetEnvironmentString(dbPortEnvVariableValue.c_str());
+
+    expectDbTypeConfigurationCheckAndApply("redis-standalone");
+    expectDBServerAddressConfigurationCheckAndApply("unknownAddress.local:12345");
+    initializeReaderWithSDLconfigFileDirectory();
+    configurationReader->readConfigurationFromInputStream(is);
+    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+}
+
+TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithoutPortIsAccepted)
+{
+    InSequence dummy;
+    dbHostEnvVariableValue = "server.local";
+    expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
+    expectGetEnvironmentString(nullptr);
+
+    expectDbTypeConfigurationCheckAndApply("redis-standalone");
+    expectDBServerAddressConfigurationCheckAndApply("server.local");
+    initializeReaderWithoutDirectories();
+    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+}
+
+TEST_F(ConfigurationReaderEnvironmentVariableTest, EmptyEnvironmentVariableThrows)
+{
+    dbHostEnvVariableValue = "";
+    readEnvironmentConfigurationAndExpectConfigurationErrorException();
+}
+
+TEST_F(ConfigurationReaderEnvironmentVariableTest, IllegalCharacterInEnvironmentVariableThrows)
+{
+    dbHostEnvVariableValue = "@";
+    readEnvironmentConfigurationAndExpectConfigurationErrorException();
+}
+
+TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationAcceptIPv6Address)
+{
+    InSequence dummy;
+    dbHostEnvVariableValue = "[2001::123]:12345";
+    expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
+    expectGetEnvironmentString(nullptr);
+
+    expectDbTypeConfigurationCheckAndApply("redis-standalone");
+    expectDBServerAddressConfigurationCheckAndApply("[2001::123]:12345");
+    initializeReaderWithoutDirectories();
+    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+}