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 <gmock/gmock.h>
20 #include <boost/property_tree/ptree.hpp>
21 #include <boost/property_tree/json_parser.hpp>
22 #include "private/configurationreader.hpp"
23 #include "private/createlogger.hpp"
24 #include "private/logger.hpp"
25 #include "private/tst/databaseconfigurationmock.hpp"
26 #include "private/tst/gettopsrcdir.hpp"
27 #include "private/tst/namespaceconfigurationsmock.hpp"
28 #include "private/tst/systemmock.hpp"
30 using namespace shareddatalayer;
31 using namespace shareddatalayer::tst;
32 using namespace testing;
36 class ConfigurationReaderBaseTest: public testing::Test
39 const std::string someKnownInputSource;
40 const Directories noDirectories;
41 DatabaseConfigurationMock databaseConfigurationMock;
42 NamespaceConfigurationsMock namespaceConfigurationsMock;
43 std::unique_ptr<ConfigurationReader> configurationReader;
44 SystemMock systemMock;
45 std::shared_ptr<Logger> logger;
47 ConfigurationReaderBaseTest(std::string inputSourceName):
48 someKnownInputSource(inputSourceName),
49 logger(createLogger(SDL_LOG_PREFIX))
51 EXPECT_CALL(databaseConfigurationMock, isEmpty()).WillRepeatedly(Return(true));
52 EXPECT_CALL(namespaceConfigurationsMock, isEmpty()).WillRepeatedly(Return(true));
55 void expectDbTypeConfigurationCheckAndApply(const std::string& type)
57 EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(type));
60 void expectDBServerAddressConfigurationCheckAndApply(const std::string& address)
62 EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(address));
65 void expectSentinelAddressConfigurationCheckAndApply(const std::string& address)
67 EXPECT_CALL(databaseConfigurationMock, checkAndApplySentinelAddress(address));
70 void expectSentinelMasterNameConfigurationCheckAndApply(const std::string& address)
72 EXPECT_CALL(databaseConfigurationMock, checkAndApplySentinelMasterName(address));
75 void expectDatabaseConfigurationIsEmpty_returnFalse()
77 EXPECT_CALL(databaseConfigurationMock, isEmpty()).
78 WillOnce(Return(false));
81 void tryToReadDatabaseConfigurationToNonEmptyContainer()
83 expectDatabaseConfigurationIsEmpty_returnFalse();
84 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
87 void expectNamespaceConfigurationIsEmpty_returnFalse()
89 EXPECT_CALL(namespaceConfigurationsMock, isEmpty()).
90 WillOnce(Return(false));
93 void tryToReadNamespaceConfigurationToNonEmptyContainer()
95 expectNamespaceConfigurationIsEmpty_returnFalse();
96 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
99 void expectAddNamespaceConfiguration(const std::string& namespacePrefix, bool useDbBackend,
100 bool enableNotifications)
102 NamespaceConfiguration expectedNamespaceConfiguration{namespacePrefix, useDbBackend,
104 someKnownInputSource};
105 EXPECT_CALL(namespaceConfigurationsMock, addNamespaceConfiguration(expectedNamespaceConfiguration));
108 void expectGetEnvironmentString(const char* returnValue)
110 EXPECT_CALL(systemMock, getenv(_))
111 .WillOnce(Return(returnValue));
114 void initializeReaderWithoutDirectories()
116 configurationReader.reset(new ConfigurationReader(noDirectories, systemMock, logger));
119 void initializeReaderWithSDLconfigFileDirectory()
121 configurationReader.reset(new ConfigurationReader({getTopSrcDir() + "/conf"}, systemMock, logger));
125 class ConfigurationReaderSDLConfigFileTest: public ConfigurationReaderBaseTest
129 ConfigurationReaderSDLConfigFileTest():
130 ConfigurationReaderBaseTest("ConfFileFromSDLrepo")
132 expectGetEnvironmentString(nullptr);
133 initializeReaderWithSDLconfigFileDirectory();
137 class ConfigurationReaderInputStreamTest: public ConfigurationReaderBaseTest
141 ConfigurationReaderInputStreamTest():
142 ConfigurationReaderBaseTest("<istream>")
144 expectGetEnvironmentString(nullptr);
145 initializeReaderWithoutDirectories();
148 void readConfigurationAndExpectJsonParserException(const std::istringstream& is)
150 const std::string expectedError("error in SDL configuration <istream> at line 7: expected ':'");
155 configurationReader->readConfigurationFromInputStream(is);
156 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
157 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
159 catch (const std::exception& e)
161 EXPECT_EQ(expectedError, e.what());
167 void readConfigurationAndExpectBadValueException(const std::istringstream& is,
168 const std::string& param)
170 std::ostringstream os;
171 os << "Configuration error in " << someKnownInputSource << ": "
172 << "invalid \"" << param << "\": \"bad-value\"";
177 configurationReader->readConfigurationFromInputStream(is);
178 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
179 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
181 catch (const std::exception& e)
183 EXPECT_EQ(os.str(), e.what());
189 void readConfigurationAndExpectDbTypeException(const std::istringstream& is)
191 std::ostringstream os;
192 os << "Configuration error in " << someKnownInputSource << ": some error";
194 EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(_))
195 .WillOnce(Throw(Exception("some error")));
200 configurationReader->readConfigurationFromInputStream(is);
201 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
203 catch (const std::exception& e)
205 EXPECT_EQ(os.str(), e.what());
211 void readConfigurationAndExpectAddressException(const std::istringstream& is,
212 const std::string& addressValue)
214 std::ostringstream os;
215 os << "Configuration error in " << someKnownInputSource << ": "
216 << "invalid \"address\": \"" << addressValue << "\" some error";
218 EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(_))
219 .WillOnce(Throw(Exception("some error")));
224 configurationReader->readConfigurationFromInputStream(is);
225 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
227 catch (const std::exception& e)
229 EXPECT_EQ(os.str(), e.what());
235 void readConfigurationAndExpectMissingParameterException(const std::istringstream& is, const std::string& param)
237 std::ostringstream os;
238 os << "Configuration error in " << someKnownInputSource << ": "
239 << "missing \"" << param << "\"";
244 configurationReader->readConfigurationFromInputStream(is);
245 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
246 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
248 catch (const std::exception& e)
250 EXPECT_EQ(os.str(), e.what());
256 void readConfigurationAndExpectNamespacePrefixValidationException(const std::istringstream& is,
257 const std::string& namespacePrefix)
259 std::ostringstream os;
260 os << "Configuration error in " << someKnownInputSource << ": "
261 << "\"namespacePrefix\": \"" << namespacePrefix << "\""
262 << " contains some of these disallowed characters: ,{}";
267 configurationReader->readConfigurationFromInputStream(is);
268 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
270 catch (const std::exception& e)
272 EXPECT_EQ(os.str(), e.what() );
278 void readConfigurationAndExpectEnableNotificationsValidationException(const std::istringstream& is)
280 std::ostringstream os;
281 os << "Configuration error in " << someKnownInputSource << ": "
282 << "\"enableNotifications\" cannot be true, when \"useDbBackend\" is false";
287 configurationReader->readConfigurationFromInputStream(is);
288 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
290 catch (const std::exception& e)
292 EXPECT_EQ(os.str(), e.what() );
301 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfiguration)
304 std::istringstream is(R"JSON(
308 "type": "redis-standalone",
312 "address": "someKnownDbAddress:65535"
317 expectDbTypeConfigurationCheckAndApply("redis-standalone");
318 expectDBServerAddressConfigurationCheckAndApply("someKnownDbAddress:65535");
319 configurationReader->readConfigurationFromInputStream(is);
320 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
323 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfigurationWithMultipleServerAddresses)
326 std::istringstream is(R"JSON(
330 "type": "redis-cluster",
334 "address": "10.20.30.40:50000"
337 "address": "10.20.30.50:50001"
343 expectDbTypeConfigurationCheckAndApply("redis-cluster");
344 expectDBServerAddressConfigurationCheckAndApply("10.20.30.40:50000");
345 expectDBServerAddressConfigurationCheckAndApply("10.20.30.50:50001");
346 configurationReader->readConfigurationFromInputStream(is);
347 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
350 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfigurationWithMultipleReadOperations)
353 std::istringstream isOne(R"JSON(
357 "type": "redis-cluster",
361 "address": "10.20.30.40:50000"
366 std::istringstream isTwo(R"JSON(
370 "type": "redis-cluster",
374 "address": "10.20.30.50:50001"
380 expectDbTypeConfigurationCheckAndApply("redis-cluster");
381 expectDBServerAddressConfigurationCheckAndApply("10.20.30.40:50000");
382 expectDbTypeConfigurationCheckAndApply("redis-cluster");
383 expectDBServerAddressConfigurationCheckAndApply("10.20.30.50:50001");
384 configurationReader->readConfigurationFromInputStream(isOne);
385 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
386 configurationReader->readConfigurationFromInputStream(isTwo);
387 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
390 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseTypeParameter)
393 std::istringstream is(R"JSON(
400 "address": "10.20.30.50:50001"
406 readConfigurationAndExpectMissingParameterException(is, "type");
409 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseServersArray)
412 std::istringstream is(R"JSON(
416 "type": "redis-standalone"
420 expectDbTypeConfigurationCheckAndApply("redis-standalone");
421 readConfigurationAndExpectMissingParameterException(is, "servers");
424 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseServerAddressParameter)
427 std::istringstream is(R"JSON(
431 "type": "redis-standalone",
440 expectDbTypeConfigurationCheckAndApply("redis-standalone");
441 readConfigurationAndExpectMissingParameterException(is, "address");
444 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowDatabaseConfigurationDbTypeError)
447 std::istringstream is(R"JSON(
451 "type": "someBadType",
455 "address": "10.20.30.50:50001"
461 readConfigurationAndExpectDbTypeException(is);
464 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowDatabaseConfigurationAddressError)
467 std::istringstream is(R"JSON(
471 "type": "redis-standalone",
475 "address": "someBadAddress"
481 expectDbTypeConfigurationCheckAndApply("redis-standalone");
482 readConfigurationAndExpectAddressException(is, "someBadAddress");
485 TEST_F(ConfigurationReaderInputStreamTest, CanHandleJSONWithoutAnyConfiguration)
488 std::istringstream is(R"JSON(
492 EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(_))
494 EXPECT_CALL(namespaceConfigurationsMock, addNamespaceConfiguration(_))
496 configurationReader->readConfigurationFromInputStream(is);
499 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfiguration)
502 std::istringstream is(R"JSON(
507 "namespacePrefix": "someKnownNamespacePrefix",
508 "useDbBackend": true,
509 "enableNotifications": true
512 "namespacePrefix": "anotherKnownNamespace",
513 "useDbBackend": false,
514 "enableNotifications": false
519 expectAddNamespaceConfiguration("anotherKnownNamespace", false, false);
520 expectAddNamespaceConfiguration("someKnownNamespacePrefix", true, true);
521 configurationReader->readConfigurationFromInputStream(is);
522 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
525 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfigurationWithMultipleReadOperations)
528 std::istringstream isOne(R"JSON(
533 "namespacePrefix": "someKnownNamespacePrefix",
534 "useDbBackend": true,
535 "enableNotifications": true
540 std::istringstream isTwo(R"JSON(
545 "namespacePrefix": "anotherKnownNamespace",
546 "useDbBackend": false,
547 "enableNotifications": false
552 expectAddNamespaceConfiguration("someKnownNamespacePrefix", true, true);
553 expectAddNamespaceConfiguration("anotherKnownNamespace", false, false);
554 configurationReader->readConfigurationFromInputStream(isOne);
555 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
556 configurationReader->readConfigurationFromInputStream(isTwo);
557 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
560 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfigurationWithEmptyNamespacePrefixValue)
563 std::istringstream is(R"JSON(
568 "namespacePrefix": "",
569 "useDbBackend": false,
570 "enableNotifications": false
575 expectAddNamespaceConfiguration("", false, false);
576 configurationReader->readConfigurationFromInputStream(is);
577 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
580 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowJSONSyntaxError)
583 std::istringstream is(R"JSON(
593 readConfigurationAndExpectJsonParserException(is);
596 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowParameterUseDbBackendBadValue)
599 std::istringstream is(R"JSON(
604 "namespacePrefix": "someKnownNamespacePrefix",
605 "useDbBackend": "bad-value",
606 "enableNotifications": false
611 readConfigurationAndExpectBadValueException(is, "useDbBackend");
614 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowParameterEnableNotificationsBadValue)
617 std::istringstream is(R"JSON(
622 "namespacePrefix": "someKnownNamespacePrefix",
623 "useDbBackend": true,
624 "enableNotifications": "bad-value"
629 readConfigurationAndExpectBadValueException(is, "enableNotifications");
632 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryNamespacePrefixParameter)
635 std::istringstream is(R"JSON(
640 "useDbBackend": true,
641 "enableNotifications": true
646 readConfigurationAndExpectMissingParameterException(is, "namespacePrefix");
649 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryUseDbBackendParameter)
652 std::istringstream is(R"JSON(
657 "namespacePrefix": "someKnownNamespacePrefix",
658 "enableNotifications": true
663 readConfigurationAndExpectMissingParameterException(is, "useDbBackend");
666 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryEnableNotificationsParameter)
669 std::istringstream is(R"JSON(
674 "namespacePrefix": "someKnownNamespacePrefix",
680 readConfigurationAndExpectMissingParameterException(is, "enableNotifications");
683 TEST_F(ConfigurationReaderInputStreamTest, CanThrowValidationErrorForNamespacePrefixWithDisallowedCharacters)
686 std::istringstream is(R"JSON(
691 "namespacePrefix": "a,b{c}",
692 "useDbBackend": true,
693 "enableNotifications": true
698 readConfigurationAndExpectNamespacePrefixValidationException(is, "a,b{c}");
701 TEST_F(ConfigurationReaderInputStreamTest, CanThrowValidationErrorForEnableNotificationsWithNoDbBackend)
704 std::istringstream is(R"JSON(
709 "namespacePrefix": "someKnownNamespacePrefix",
710 "useDbBackend": false,
711 "enableNotifications": true
716 readConfigurationAndExpectEnableNotificationsValidationException(is);
719 TEST_F(ConfigurationReaderInputStreamTest, WillNotReadDatabaseConfigurationToNonEmptyContainer)
721 EXPECT_EXIT(tryToReadDatabaseConfigurationToNonEmptyContainer(),
722 KilledBySignal(SIGABRT), "ABORT.*configurationreader\\.cpp");
725 TEST_F(ConfigurationReaderInputStreamTest, WillNotReadNamespaceConfigurationToNonEmptyContainer)
727 EXPECT_EXIT(tryToReadNamespaceConfigurationToNonEmptyContainer(),
728 KilledBySignal(SIGABRT), "ABORT.*configurationreader\\.cpp");
731 class ConfigurationReaderEnvironmentVariableTest: public ConfigurationReaderBaseTest
734 std::string dbHostEnvVariableValue;
735 std::string dbPortEnvVariableValue;
736 std::string sentinelPortEnvVariableValue;
737 std::string sentinelMasterNameEnvVariableValue;
738 std::istringstream is{R"JSON(
742 "type": "redis-cluster",
746 "address": "10.20.30.40:50000"
749 "address": "10.20.30.50:50001"
755 ConfigurationReaderEnvironmentVariableTest():
756 ConfigurationReaderBaseTest(DB_HOST_ENV_VAR_NAME)
760 void readEnvironmentConfigurationAndExpectConfigurationErrorException()
762 std::ostringstream os;
763 os << "Configuration error in " << someKnownInputSource << ": some error";
765 EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(_))
766 .WillOnce(Throw(Exception("some error")));
771 EXPECT_CALL(systemMock, getenv(_))
773 .WillOnce(Return(dbHostEnvVariableValue.c_str()))
774 .WillOnce(Return(nullptr));
775 initializeReaderWithoutDirectories();
776 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
778 catch (const std::exception& e)
780 EXPECT_EQ(os.str(), e.what());
787 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationCanOverrideJSONDatabaseConfiguration)
790 dbHostEnvVariableValue = "unknownAddress.local";
791 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
792 dbPortEnvVariableValue = "12345";
793 expectGetEnvironmentString(dbPortEnvVariableValue.c_str());
794 expectGetEnvironmentString(nullptr); //SENTINEL_PORT_ENV_VAR_NAME
795 expectGetEnvironmentString(nullptr); //SENTINEL_MASTER_NAME_ENV_VAR_NAME
797 expectDbTypeConfigurationCheckAndApply("redis-standalone");
798 expectDBServerAddressConfigurationCheckAndApply("unknownAddress.local:12345");
799 initializeReaderWithSDLconfigFileDirectory();
800 configurationReader->readConfigurationFromInputStream(is);
801 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
804 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithoutPortIsAccepted)
807 dbHostEnvVariableValue = "server.local";
808 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
809 expectGetEnvironmentString(nullptr); //DB_PORT_ENV_VAR_NAME
810 expectGetEnvironmentString(nullptr); //SENTINEL_PORT_ENV_VAR_NAME
811 expectGetEnvironmentString(nullptr); //SENTINEL_MASTER_NAME_ENV_VAR_NAME
813 expectDbTypeConfigurationCheckAndApply("redis-standalone");
814 expectDBServerAddressConfigurationCheckAndApply("server.local");
815 initializeReaderWithoutDirectories();
816 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
819 TEST_F(ConfigurationReaderEnvironmentVariableTest, EmptyEnvironmentVariableThrows)
821 dbHostEnvVariableValue = "";
822 readEnvironmentConfigurationAndExpectConfigurationErrorException();
825 TEST_F(ConfigurationReaderEnvironmentVariableTest, IllegalCharacterInEnvironmentVariableThrows)
827 dbHostEnvVariableValue = "@";
828 readEnvironmentConfigurationAndExpectConfigurationErrorException();
831 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationAcceptIPv6Address)
834 dbHostEnvVariableValue = "[2001::123]:12345";
835 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
836 expectGetEnvironmentString(nullptr); //DB_PORT_ENV_VAR_NAME
837 expectGetEnvironmentString(nullptr); //SENTINEL_PORT_ENV_VAR_NAME
838 expectGetEnvironmentString(nullptr); //SENTINEL_MASTER_NAME_ENV_VAR_NAME
840 expectDbTypeConfigurationCheckAndApply("redis-standalone");
841 expectDBServerAddressConfigurationCheckAndApply("[2001::123]:12345");
842 initializeReaderWithoutDirectories();
843 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
846 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithSentinel)
849 dbHostEnvVariableValue = "sentinelAddress.local";
850 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
851 dbPortEnvVariableValue = "1111";
852 expectGetEnvironmentString(dbPortEnvVariableValue.c_str());
853 sentinelPortEnvVariableValue = "2222";
854 expectGetEnvironmentString(sentinelPortEnvVariableValue.c_str());
855 sentinelMasterNameEnvVariableValue = "mymaster";
856 expectGetEnvironmentString(sentinelMasterNameEnvVariableValue.c_str());
858 expectDbTypeConfigurationCheckAndApply("redis-sentinel");
859 expectSentinelAddressConfigurationCheckAndApply("sentinelAddress.local:2222");
860 expectSentinelMasterNameConfigurationCheckAndApply(sentinelMasterNameEnvVariableValue);
861 initializeReaderWithoutDirectories();
862 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);