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.
18 * This source code is part of the near-RT RIC (RAN Intelligent Controller)
19 * platform project (RICP).
22 #include <gtest/gtest.h>
23 #include <gmock/gmock.h>
25 #include <boost/property_tree/ptree.hpp>
26 #include <boost/property_tree/json_parser.hpp>
27 #include "private/configurationreader.hpp"
28 #include "private/createlogger.hpp"
29 #include "private/logger.hpp"
30 #include "private/tst/databaseconfigurationmock.hpp"
31 #include "private/tst/gettopsrcdir.hpp"
32 #include "private/tst/namespaceconfigurationsmock.hpp"
33 #include "private/tst/systemmock.hpp"
35 using namespace shareddatalayer;
36 using namespace shareddatalayer::tst;
37 using namespace testing;
41 class ConfigurationReaderBaseTest: public testing::Test
44 const std::string someKnownInputSource;
45 const Directories noDirectories;
46 DatabaseConfigurationMock databaseConfigurationMock;
47 NamespaceConfigurationsMock namespaceConfigurationsMock;
48 std::unique_ptr<ConfigurationReader> configurationReader;
49 SystemMock systemMock;
50 std::shared_ptr<Logger> logger;
52 ConfigurationReaderBaseTest(std::string inputSourceName):
53 someKnownInputSource(inputSourceName),
54 logger(createLogger(SDL_LOG_PREFIX))
56 EXPECT_CALL(databaseConfigurationMock, isEmpty()).WillRepeatedly(Return(true));
57 EXPECT_CALL(namespaceConfigurationsMock, isEmpty()).WillRepeatedly(Return(true));
60 void expectDbTypeConfigurationCheckAndApply(const std::string& type)
62 EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(type));
65 void expectDBServerAddressConfigurationCheckAndApply(const std::string& address)
67 EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(address));
70 void expectSentinelAddressConfigurationCheckAndApply(const std::string& address)
72 EXPECT_CALL(databaseConfigurationMock, checkAndApplySentinelAddress(address));
75 void expectSentinelMasterNameConfigurationCheckAndApply(const std::string& address)
77 EXPECT_CALL(databaseConfigurationMock, checkAndApplySentinelMasterName(address));
80 void expectDatabaseConfigurationIsEmpty_returnFalse()
82 EXPECT_CALL(databaseConfigurationMock, isEmpty()).
83 WillOnce(Return(false));
86 void tryToReadDatabaseConfigurationToNonEmptyContainer()
88 expectDatabaseConfigurationIsEmpty_returnFalse();
89 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
92 void expectNamespaceConfigurationIsEmpty_returnFalse()
94 EXPECT_CALL(namespaceConfigurationsMock, isEmpty()).
95 WillOnce(Return(false));
98 void tryToReadNamespaceConfigurationToNonEmptyContainer()
100 expectNamespaceConfigurationIsEmpty_returnFalse();
101 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
104 void expectAddNamespaceConfiguration(const std::string& namespacePrefix, bool useDbBackend,
105 bool enableNotifications)
107 NamespaceConfiguration expectedNamespaceConfiguration{namespacePrefix, useDbBackend,
109 someKnownInputSource};
110 EXPECT_CALL(namespaceConfigurationsMock, addNamespaceConfiguration(expectedNamespaceConfiguration));
113 void expectGetEnvironmentString(const char* returnValue)
115 EXPECT_CALL(systemMock, getenv(_))
116 .WillOnce(Return(returnValue));
119 void initializeReaderWithoutDirectories()
121 configurationReader.reset(new ConfigurationReader(noDirectories, systemMock, logger));
124 void initializeReaderWithSDLconfigFileDirectory()
126 configurationReader.reset(new ConfigurationReader({getTopSrcDir() + "/conf"}, systemMock, logger));
130 class ConfigurationReaderSDLConfigFileTest: public ConfigurationReaderBaseTest
134 ConfigurationReaderSDLConfigFileTest():
135 ConfigurationReaderBaseTest("ConfFileFromSDLrepo")
137 expectGetEnvironmentString(nullptr);
138 initializeReaderWithSDLconfigFileDirectory();
142 class ConfigurationReaderInputStreamTest: public ConfigurationReaderBaseTest
146 ConfigurationReaderInputStreamTest():
147 ConfigurationReaderBaseTest("<istream>")
149 expectGetEnvironmentString(nullptr);
150 initializeReaderWithoutDirectories();
153 void readConfigurationAndExpectJsonParserException(const std::istringstream& is)
155 const std::string expectedError("error in SDL configuration <istream> at line 7: expected ':'");
160 configurationReader->readConfigurationFromInputStream(is);
161 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
162 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
164 catch (const std::exception& e)
166 EXPECT_EQ(expectedError, e.what());
172 void readConfigurationAndExpectBadValueException(const std::istringstream& is,
173 const std::string& param)
175 std::ostringstream os;
176 os << "Configuration error in " << someKnownInputSource << ": "
177 << "invalid \"" << param << "\": \"bad-value\"";
182 configurationReader->readConfigurationFromInputStream(is);
183 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
184 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
186 catch (const std::exception& e)
188 EXPECT_EQ(os.str(), e.what());
194 void readConfigurationAndExpectDbTypeException(const std::istringstream& is)
196 std::ostringstream os;
197 os << "Configuration error in " << someKnownInputSource << ": some error";
199 EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(_))
200 .WillOnce(Throw(Exception("some error")));
205 configurationReader->readConfigurationFromInputStream(is);
206 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
208 catch (const std::exception& e)
210 EXPECT_EQ(os.str(), e.what());
216 void readConfigurationAndExpectAddressException(const std::istringstream& is,
217 const std::string& addressValue)
219 std::ostringstream os;
220 os << "Configuration error in " << someKnownInputSource << ": "
221 << "invalid \"address\": \"" << addressValue << "\" some error";
223 EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(_))
224 .WillOnce(Throw(Exception("some error")));
229 configurationReader->readConfigurationFromInputStream(is);
230 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
232 catch (const std::exception& e)
234 EXPECT_EQ(os.str(), e.what());
240 void readConfigurationAndExpectMissingParameterException(const std::istringstream& is, const std::string& param)
242 std::ostringstream os;
243 os << "Configuration error in " << someKnownInputSource << ": "
244 << "missing \"" << param << "\"";
249 configurationReader->readConfigurationFromInputStream(is);
250 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
251 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
253 catch (const std::exception& e)
255 EXPECT_EQ(os.str(), e.what());
261 void readConfigurationAndExpectNamespacePrefixValidationException(const std::istringstream& is,
262 const std::string& namespacePrefix)
264 std::ostringstream os;
265 os << "Configuration error in " << someKnownInputSource << ": "
266 << "\"namespacePrefix\": \"" << namespacePrefix << "\""
267 << " contains some of these disallowed characters: ,{}";
272 configurationReader->readConfigurationFromInputStream(is);
273 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
275 catch (const std::exception& e)
277 EXPECT_EQ(os.str(), e.what() );
283 void readConfigurationAndExpectEnableNotificationsValidationException(const std::istringstream& is)
285 std::ostringstream os;
286 os << "Configuration error in " << someKnownInputSource << ": "
287 << "\"enableNotifications\" cannot be true, when \"useDbBackend\" is false";
292 configurationReader->readConfigurationFromInputStream(is);
293 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
295 catch (const std::exception& e)
297 EXPECT_EQ(os.str(), e.what() );
306 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfiguration)
309 std::istringstream is(R"JSON(
313 "type": "redis-standalone",
317 "address": "someKnownDbAddress:65535"
322 expectDbTypeConfigurationCheckAndApply("redis-standalone");
323 expectDBServerAddressConfigurationCheckAndApply("someKnownDbAddress:65535");
324 configurationReader->readConfigurationFromInputStream(is);
325 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
328 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfigurationWithMultipleServerAddresses)
331 std::istringstream is(R"JSON(
335 "type": "redis-cluster",
339 "address": "10.20.30.40:50000"
342 "address": "10.20.30.50:50001"
348 expectDbTypeConfigurationCheckAndApply("redis-cluster");
349 expectDBServerAddressConfigurationCheckAndApply("10.20.30.40:50000");
350 expectDBServerAddressConfigurationCheckAndApply("10.20.30.50:50001");
351 configurationReader->readConfigurationFromInputStream(is);
352 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
355 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfigurationWithMultipleReadOperations)
358 std::istringstream isOne(R"JSON(
362 "type": "redis-cluster",
366 "address": "10.20.30.40:50000"
371 std::istringstream isTwo(R"JSON(
375 "type": "redis-cluster",
379 "address": "10.20.30.50:50001"
385 expectDbTypeConfigurationCheckAndApply("redis-cluster");
386 expectDBServerAddressConfigurationCheckAndApply("10.20.30.40:50000");
387 expectDbTypeConfigurationCheckAndApply("redis-cluster");
388 expectDBServerAddressConfigurationCheckAndApply("10.20.30.50:50001");
389 configurationReader->readConfigurationFromInputStream(isOne);
390 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
391 configurationReader->readConfigurationFromInputStream(isTwo);
392 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
395 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseTypeParameter)
398 std::istringstream is(R"JSON(
405 "address": "10.20.30.50:50001"
411 readConfigurationAndExpectMissingParameterException(is, "type");
414 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseServersArray)
417 std::istringstream is(R"JSON(
421 "type": "redis-standalone"
425 expectDbTypeConfigurationCheckAndApply("redis-standalone");
426 readConfigurationAndExpectMissingParameterException(is, "servers");
429 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseServerAddressParameter)
432 std::istringstream is(R"JSON(
436 "type": "redis-standalone",
445 expectDbTypeConfigurationCheckAndApply("redis-standalone");
446 readConfigurationAndExpectMissingParameterException(is, "address");
449 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowDatabaseConfigurationDbTypeError)
452 std::istringstream is(R"JSON(
456 "type": "someBadType",
460 "address": "10.20.30.50:50001"
466 readConfigurationAndExpectDbTypeException(is);
469 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowDatabaseConfigurationAddressError)
472 std::istringstream is(R"JSON(
476 "type": "redis-standalone",
480 "address": "someBadAddress"
486 expectDbTypeConfigurationCheckAndApply("redis-standalone");
487 readConfigurationAndExpectAddressException(is, "someBadAddress");
490 TEST_F(ConfigurationReaderInputStreamTest, CanHandleJSONWithoutAnyConfiguration)
493 std::istringstream is(R"JSON(
497 EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(_))
499 EXPECT_CALL(namespaceConfigurationsMock, addNamespaceConfiguration(_))
501 configurationReader->readConfigurationFromInputStream(is);
504 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfiguration)
507 std::istringstream is(R"JSON(
512 "namespacePrefix": "someKnownNamespacePrefix",
513 "useDbBackend": true,
514 "enableNotifications": true
517 "namespacePrefix": "anotherKnownNamespace",
518 "useDbBackend": false,
519 "enableNotifications": false
524 expectAddNamespaceConfiguration("anotherKnownNamespace", false, false);
525 expectAddNamespaceConfiguration("someKnownNamespacePrefix", true, true);
526 configurationReader->readConfigurationFromInputStream(is);
527 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
530 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfigurationWithMultipleReadOperations)
533 std::istringstream isOne(R"JSON(
538 "namespacePrefix": "someKnownNamespacePrefix",
539 "useDbBackend": true,
540 "enableNotifications": true
545 std::istringstream isTwo(R"JSON(
550 "namespacePrefix": "anotherKnownNamespace",
551 "useDbBackend": false,
552 "enableNotifications": false
557 expectAddNamespaceConfiguration("someKnownNamespacePrefix", true, true);
558 expectAddNamespaceConfiguration("anotherKnownNamespace", false, false);
559 configurationReader->readConfigurationFromInputStream(isOne);
560 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
561 configurationReader->readConfigurationFromInputStream(isTwo);
562 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
565 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfigurationWithEmptyNamespacePrefixValue)
568 std::istringstream is(R"JSON(
573 "namespacePrefix": "",
574 "useDbBackend": false,
575 "enableNotifications": false
580 expectAddNamespaceConfiguration("", false, false);
581 configurationReader->readConfigurationFromInputStream(is);
582 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
585 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowJSONSyntaxError)
588 std::istringstream is(R"JSON(
598 readConfigurationAndExpectJsonParserException(is);
601 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowParameterUseDbBackendBadValue)
604 std::istringstream is(R"JSON(
609 "namespacePrefix": "someKnownNamespacePrefix",
610 "useDbBackend": "bad-value",
611 "enableNotifications": false
616 readConfigurationAndExpectBadValueException(is, "useDbBackend");
619 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowParameterEnableNotificationsBadValue)
622 std::istringstream is(R"JSON(
627 "namespacePrefix": "someKnownNamespacePrefix",
628 "useDbBackend": true,
629 "enableNotifications": "bad-value"
634 readConfigurationAndExpectBadValueException(is, "enableNotifications");
637 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryNamespacePrefixParameter)
640 std::istringstream is(R"JSON(
645 "useDbBackend": true,
646 "enableNotifications": true
651 readConfigurationAndExpectMissingParameterException(is, "namespacePrefix");
654 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryUseDbBackendParameter)
657 std::istringstream is(R"JSON(
662 "namespacePrefix": "someKnownNamespacePrefix",
663 "enableNotifications": true
668 readConfigurationAndExpectMissingParameterException(is, "useDbBackend");
671 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryEnableNotificationsParameter)
674 std::istringstream is(R"JSON(
679 "namespacePrefix": "someKnownNamespacePrefix",
685 readConfigurationAndExpectMissingParameterException(is, "enableNotifications");
688 TEST_F(ConfigurationReaderInputStreamTest, CanThrowValidationErrorForNamespacePrefixWithDisallowedCharacters)
691 std::istringstream is(R"JSON(
696 "namespacePrefix": "a,b{c}",
697 "useDbBackend": true,
698 "enableNotifications": true
703 readConfigurationAndExpectNamespacePrefixValidationException(is, "a,b{c}");
706 TEST_F(ConfigurationReaderInputStreamTest, CanThrowValidationErrorForEnableNotificationsWithNoDbBackend)
709 std::istringstream is(R"JSON(
714 "namespacePrefix": "someKnownNamespacePrefix",
715 "useDbBackend": false,
716 "enableNotifications": true
721 readConfigurationAndExpectEnableNotificationsValidationException(is);
724 TEST_F(ConfigurationReaderInputStreamTest, WillNotReadDatabaseConfigurationToNonEmptyContainer)
726 EXPECT_EXIT(tryToReadDatabaseConfigurationToNonEmptyContainer(),
727 KilledBySignal(SIGABRT), "ABORT.*configurationreader\\.cpp");
730 TEST_F(ConfigurationReaderInputStreamTest, WillNotReadNamespaceConfigurationToNonEmptyContainer)
732 EXPECT_EXIT(tryToReadNamespaceConfigurationToNonEmptyContainer(),
733 KilledBySignal(SIGABRT), "ABORT.*configurationreader\\.cpp");
736 class ConfigurationReaderEnvironmentVariableTest: public ConfigurationReaderBaseTest
739 std::string dbHostEnvVariableValue;
740 std::string dbPortEnvVariableValue;
741 std::string sentinelPortEnvVariableValue;
742 std::string sentinelMasterNameEnvVariableValue;
743 std::istringstream is{R"JSON(
747 "type": "redis-cluster",
751 "address": "10.20.30.40:50000"
754 "address": "10.20.30.50:50001"
760 ConfigurationReaderEnvironmentVariableTest():
761 ConfigurationReaderBaseTest(DB_HOST_ENV_VAR_NAME)
765 void readEnvironmentConfigurationAndExpectConfigurationErrorException()
767 std::ostringstream os;
768 os << "Configuration error in " << someKnownInputSource << ": some error";
770 EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(_))
771 .WillOnce(Throw(Exception("some error")));
776 EXPECT_CALL(systemMock, getenv(_))
778 .WillOnce(Return(dbHostEnvVariableValue.c_str()))
779 .WillOnce(Return(nullptr));
780 initializeReaderWithoutDirectories();
781 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
783 catch (const std::exception& e)
785 EXPECT_EQ(os.str(), e.what());
792 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationCanOverrideJSONDatabaseConfiguration)
795 dbHostEnvVariableValue = "unknownAddress.local";
796 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
797 dbPortEnvVariableValue = "12345";
798 expectGetEnvironmentString(dbPortEnvVariableValue.c_str());
799 expectGetEnvironmentString(nullptr); //SENTINEL_PORT_ENV_VAR_NAME
800 expectGetEnvironmentString(nullptr); //SENTINEL_MASTER_NAME_ENV_VAR_NAME
802 expectDbTypeConfigurationCheckAndApply("redis-standalone");
803 expectDBServerAddressConfigurationCheckAndApply("unknownAddress.local:12345");
804 initializeReaderWithSDLconfigFileDirectory();
805 configurationReader->readConfigurationFromInputStream(is);
806 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
809 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithoutPortIsAccepted)
812 dbHostEnvVariableValue = "server.local";
813 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
814 expectGetEnvironmentString(nullptr); //DB_PORT_ENV_VAR_NAME
815 expectGetEnvironmentString(nullptr); //SENTINEL_PORT_ENV_VAR_NAME
816 expectGetEnvironmentString(nullptr); //SENTINEL_MASTER_NAME_ENV_VAR_NAME
818 expectDbTypeConfigurationCheckAndApply("redis-standalone");
819 expectDBServerAddressConfigurationCheckAndApply("server.local");
820 initializeReaderWithoutDirectories();
821 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
824 TEST_F(ConfigurationReaderEnvironmentVariableTest, EmptyEnvironmentVariableThrows)
826 dbHostEnvVariableValue = "";
827 readEnvironmentConfigurationAndExpectConfigurationErrorException();
830 TEST_F(ConfigurationReaderEnvironmentVariableTest, IllegalCharacterInEnvironmentVariableThrows)
832 dbHostEnvVariableValue = "@";
833 readEnvironmentConfigurationAndExpectConfigurationErrorException();
836 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationAcceptIPv6Address)
839 dbHostEnvVariableValue = "[2001::123]:12345";
840 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
841 expectGetEnvironmentString(nullptr); //DB_PORT_ENV_VAR_NAME
842 expectGetEnvironmentString(nullptr); //SENTINEL_PORT_ENV_VAR_NAME
843 expectGetEnvironmentString(nullptr); //SENTINEL_MASTER_NAME_ENV_VAR_NAME
845 expectDbTypeConfigurationCheckAndApply("redis-standalone");
846 expectDBServerAddressConfigurationCheckAndApply("[2001::123]:12345");
847 initializeReaderWithoutDirectories();
848 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
851 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithSentinel)
854 dbHostEnvVariableValue = "sentinelAddress.local";
855 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
856 dbPortEnvVariableValue = "1111";
857 expectGetEnvironmentString(dbPortEnvVariableValue.c_str());
858 sentinelPortEnvVariableValue = "2222";
859 expectGetEnvironmentString(sentinelPortEnvVariableValue.c_str());
860 sentinelMasterNameEnvVariableValue = "mymaster";
861 expectGetEnvironmentString(sentinelMasterNameEnvVariableValue.c_str());
863 expectDbTypeConfigurationCheckAndApply("redis-sentinel");
864 expectSentinelAddressConfigurationCheckAndApply("sentinelAddress.local:2222");
865 expectSentinelMasterNameConfigurationCheckAndApply(sentinelMasterNameEnvVariableValue);
866 initializeReaderWithoutDirectories();
867 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);