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 expectDatabaseConfigurationIsEmpty_returnFalse()
67 EXPECT_CALL(databaseConfigurationMock, isEmpty()).
68 WillOnce(Return(false));
71 void tryToReadDatabaseConfigurationToNonEmptyContainer()
73 expectDatabaseConfigurationIsEmpty_returnFalse();
74 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
77 void expectNamespaceConfigurationIsEmpty_returnFalse()
79 EXPECT_CALL(namespaceConfigurationsMock, isEmpty()).
80 WillOnce(Return(false));
83 void tryToReadNamespaceConfigurationToNonEmptyContainer()
85 expectNamespaceConfigurationIsEmpty_returnFalse();
86 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
89 void expectAddNamespaceConfiguration(const std::string& namespacePrefix, bool useDbBackend,
90 bool enableNotifications)
92 NamespaceConfiguration expectedNamespaceConfiguration{namespacePrefix, useDbBackend,
94 someKnownInputSource};
95 EXPECT_CALL(namespaceConfigurationsMock, addNamespaceConfiguration(expectedNamespaceConfiguration));
98 void expectGetEnvironmentString(const char* returnValue)
100 EXPECT_CALL(systemMock, getenv(_))
101 .WillOnce(Return(returnValue));
104 void initializeReaderWithoutDirectories()
106 configurationReader.reset(new ConfigurationReader(noDirectories, systemMock, logger));
109 void initializeReaderWithSDLconfigFileDirectory()
111 configurationReader.reset(new ConfigurationReader({getTopSrcDir() + "/conf"}, systemMock, logger));
115 class ConfigurationReaderSDLConfigFileTest: public ConfigurationReaderBaseTest
119 ConfigurationReaderSDLConfigFileTest():
120 ConfigurationReaderBaseTest("ConfFileFromSDLrepo")
122 expectGetEnvironmentString(nullptr);
123 initializeReaderWithSDLconfigFileDirectory();
127 class ConfigurationReaderInputStreamTest: public ConfigurationReaderBaseTest
131 ConfigurationReaderInputStreamTest():
132 ConfigurationReaderBaseTest("<istream>")
134 expectGetEnvironmentString(nullptr);
135 initializeReaderWithoutDirectories();
138 void readConfigurationAndExpectJsonParserException(const std::istringstream& is)
140 const std::string expectedError("error in SDL configuration <istream> at line 7: expected ':'");
145 configurationReader->readConfigurationFromInputStream(is);
146 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
147 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
149 catch (const std::exception& e)
151 EXPECT_EQ(expectedError, e.what());
157 void readConfigurationAndExpectBadValueException(const std::istringstream& is,
158 const std::string& param)
160 std::ostringstream os;
161 os << "Configuration error in " << someKnownInputSource << ": "
162 << "invalid \"" << param << "\": \"bad-value\"";
167 configurationReader->readConfigurationFromInputStream(is);
168 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
169 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
171 catch (const std::exception& e)
173 EXPECT_EQ(os.str(), e.what());
179 void readConfigurationAndExpectDbTypeException(const std::istringstream& is)
181 std::ostringstream os;
182 os << "Configuration error in " << someKnownInputSource << ": some error";
184 EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(_))
185 .WillOnce(Throw(Exception("some error")));
190 configurationReader->readConfigurationFromInputStream(is);
191 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
193 catch (const std::exception& e)
195 EXPECT_EQ(os.str(), e.what());
201 void readConfigurationAndExpectAddressException(const std::istringstream& is,
202 const std::string& addressValue)
204 std::ostringstream os;
205 os << "Configuration error in " << someKnownInputSource << ": "
206 << "invalid \"address\": \"" << addressValue << "\" some error";
208 EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(_))
209 .WillOnce(Throw(Exception("some error")));
214 configurationReader->readConfigurationFromInputStream(is);
215 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
217 catch (const std::exception& e)
219 EXPECT_EQ(os.str(), e.what());
225 void readConfigurationAndExpectMissingParameterException(const std::istringstream& is, const std::string& param)
227 std::ostringstream os;
228 os << "Configuration error in " << someKnownInputSource << ": "
229 << "missing \"" << param << "\"";
234 configurationReader->readConfigurationFromInputStream(is);
235 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
236 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
238 catch (const std::exception& e)
240 EXPECT_EQ(os.str(), e.what());
246 void readConfigurationAndExpectNamespacePrefixValidationException(const std::istringstream& is,
247 const std::string& namespacePrefix)
249 std::ostringstream os;
250 os << "Configuration error in " << someKnownInputSource << ": "
251 << "\"namespacePrefix\": \"" << namespacePrefix << "\""
252 << " contains some of these disallowed characters: ,{}";
257 configurationReader->readConfigurationFromInputStream(is);
258 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
260 catch (const std::exception& e)
262 EXPECT_EQ(os.str(), e.what() );
268 void readConfigurationAndExpectEnableNotificationsValidationException(const std::istringstream& is)
270 std::ostringstream os;
271 os << "Configuration error in " << someKnownInputSource << ": "
272 << "\"enableNotifications\" cannot be true, when \"useDbBackend\" is false";
277 configurationReader->readConfigurationFromInputStream(is);
278 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
280 catch (const std::exception& e)
282 EXPECT_EQ(os.str(), e.what() );
291 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfiguration)
294 std::istringstream is(R"JSON(
298 "type": "redis-standalone",
302 "address": "someKnownDbAddress:65535"
307 expectDbTypeConfigurationCheckAndApply("redis-standalone");
308 expectDBServerAddressConfigurationCheckAndApply("someKnownDbAddress:65535");
309 configurationReader->readConfigurationFromInputStream(is);
310 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
313 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfigurationWithMultipleServerAddresses)
316 std::istringstream is(R"JSON(
320 "type": "redis-cluster",
324 "address": "10.20.30.40:50000"
327 "address": "10.20.30.50:50001"
333 expectDbTypeConfigurationCheckAndApply("redis-cluster");
334 expectDBServerAddressConfigurationCheckAndApply("10.20.30.40:50000");
335 expectDBServerAddressConfigurationCheckAndApply("10.20.30.50:50001");
336 configurationReader->readConfigurationFromInputStream(is);
337 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
340 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfigurationWithMultipleReadOperations)
343 std::istringstream isOne(R"JSON(
347 "type": "redis-cluster",
351 "address": "10.20.30.40:50000"
356 std::istringstream isTwo(R"JSON(
360 "type": "redis-cluster",
364 "address": "10.20.30.50:50001"
370 expectDbTypeConfigurationCheckAndApply("redis-cluster");
371 expectDBServerAddressConfigurationCheckAndApply("10.20.30.40:50000");
372 expectDbTypeConfigurationCheckAndApply("redis-cluster");
373 expectDBServerAddressConfigurationCheckAndApply("10.20.30.50:50001");
374 configurationReader->readConfigurationFromInputStream(isOne);
375 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
376 configurationReader->readConfigurationFromInputStream(isTwo);
377 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
380 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseTypeParameter)
383 std::istringstream is(R"JSON(
390 "address": "10.20.30.50:50001"
396 readConfigurationAndExpectMissingParameterException(is, "type");
399 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseServersArray)
402 std::istringstream is(R"JSON(
406 "type": "redis-standalone"
410 expectDbTypeConfigurationCheckAndApply("redis-standalone");
411 readConfigurationAndExpectMissingParameterException(is, "servers");
414 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseServerAddressParameter)
417 std::istringstream is(R"JSON(
421 "type": "redis-standalone",
430 expectDbTypeConfigurationCheckAndApply("redis-standalone");
431 readConfigurationAndExpectMissingParameterException(is, "address");
434 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowDatabaseConfigurationDbTypeError)
437 std::istringstream is(R"JSON(
441 "type": "someBadType",
445 "address": "10.20.30.50:50001"
451 readConfigurationAndExpectDbTypeException(is);
454 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowDatabaseConfigurationAddressError)
457 std::istringstream is(R"JSON(
461 "type": "redis-standalone",
465 "address": "someBadAddress"
471 expectDbTypeConfigurationCheckAndApply("redis-standalone");
472 readConfigurationAndExpectAddressException(is, "someBadAddress");
475 TEST_F(ConfigurationReaderInputStreamTest, CanHandleJSONWithoutAnyConfiguration)
478 std::istringstream is(R"JSON(
482 EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(_))
484 EXPECT_CALL(namespaceConfigurationsMock, addNamespaceConfiguration(_))
486 configurationReader->readConfigurationFromInputStream(is);
489 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfiguration)
492 std::istringstream is(R"JSON(
497 "namespacePrefix": "someKnownNamespacePrefix",
498 "useDbBackend": true,
499 "enableNotifications": true
502 "namespacePrefix": "anotherKnownNamespace",
503 "useDbBackend": false,
504 "enableNotifications": false
509 expectAddNamespaceConfiguration("anotherKnownNamespace", false, false);
510 expectAddNamespaceConfiguration("someKnownNamespacePrefix", true, true);
511 configurationReader->readConfigurationFromInputStream(is);
512 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
515 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfigurationWithMultipleReadOperations)
518 std::istringstream isOne(R"JSON(
523 "namespacePrefix": "someKnownNamespacePrefix",
524 "useDbBackend": true,
525 "enableNotifications": true
530 std::istringstream isTwo(R"JSON(
535 "namespacePrefix": "anotherKnownNamespace",
536 "useDbBackend": false,
537 "enableNotifications": false
542 expectAddNamespaceConfiguration("someKnownNamespacePrefix", true, true);
543 expectAddNamespaceConfiguration("anotherKnownNamespace", false, false);
544 configurationReader->readConfigurationFromInputStream(isOne);
545 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
546 configurationReader->readConfigurationFromInputStream(isTwo);
547 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
550 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfigurationWithEmptyNamespacePrefixValue)
553 std::istringstream is(R"JSON(
558 "namespacePrefix": "",
559 "useDbBackend": false,
560 "enableNotifications": false
565 expectAddNamespaceConfiguration("", false, false);
566 configurationReader->readConfigurationFromInputStream(is);
567 configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
570 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowJSONSyntaxError)
573 std::istringstream is(R"JSON(
583 readConfigurationAndExpectJsonParserException(is);
586 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowParameterUseDbBackendBadValue)
589 std::istringstream is(R"JSON(
594 "namespacePrefix": "someKnownNamespacePrefix",
595 "useDbBackend": "bad-value",
596 "enableNotifications": false
601 readConfigurationAndExpectBadValueException(is, "useDbBackend");
604 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowParameterEnableNotificationsBadValue)
607 std::istringstream is(R"JSON(
612 "namespacePrefix": "someKnownNamespacePrefix",
613 "useDbBackend": true,
614 "enableNotifications": "bad-value"
619 readConfigurationAndExpectBadValueException(is, "enableNotifications");
622 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryNamespacePrefixParameter)
625 std::istringstream is(R"JSON(
630 "useDbBackend": true,
631 "enableNotifications": true
636 readConfigurationAndExpectMissingParameterException(is, "namespacePrefix");
639 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryUseDbBackendParameter)
642 std::istringstream is(R"JSON(
647 "namespacePrefix": "someKnownNamespacePrefix",
648 "enableNotifications": true
653 readConfigurationAndExpectMissingParameterException(is, "useDbBackend");
656 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryEnableNotificationsParameter)
659 std::istringstream is(R"JSON(
664 "namespacePrefix": "someKnownNamespacePrefix",
670 readConfigurationAndExpectMissingParameterException(is, "enableNotifications");
673 TEST_F(ConfigurationReaderInputStreamTest, CanThrowValidationErrorForNamespacePrefixWithDisallowedCharacters)
676 std::istringstream is(R"JSON(
681 "namespacePrefix": "a,b{c}",
682 "useDbBackend": true,
683 "enableNotifications": true
688 readConfigurationAndExpectNamespacePrefixValidationException(is, "a,b{c}");
691 TEST_F(ConfigurationReaderInputStreamTest, CanThrowValidationErrorForEnableNotificationsWithNoDbBackend)
694 std::istringstream is(R"JSON(
699 "namespacePrefix": "someKnownNamespacePrefix",
700 "useDbBackend": false,
701 "enableNotifications": true
706 readConfigurationAndExpectEnableNotificationsValidationException(is);
709 TEST_F(ConfigurationReaderInputStreamTest, WillNotReadDatabaseConfigurationToNonEmptyContainer)
711 EXPECT_EXIT(tryToReadDatabaseConfigurationToNonEmptyContainer(),
712 KilledBySignal(SIGABRT), "ABORT.*configurationreader\\.cpp");
715 TEST_F(ConfigurationReaderInputStreamTest, WillNotReadNamespaceConfigurationToNonEmptyContainer)
717 EXPECT_EXIT(tryToReadNamespaceConfigurationToNonEmptyContainer(),
718 KilledBySignal(SIGABRT), "ABORT.*configurationreader\\.cpp");
721 class ConfigurationReaderEnvironmentVariableTest: public ConfigurationReaderBaseTest
724 std::string dbHostEnvVariableValue;
725 std::string dbPortEnvVariableValue;
726 std::istringstream is{R"JSON(
730 "type": "redis-cluster",
734 "address": "10.20.30.40:50000"
737 "address": "10.20.30.50:50001"
743 ConfigurationReaderEnvironmentVariableTest():
744 ConfigurationReaderBaseTest(DB_HOST_ENV_VAR_NAME)
748 void readEnvironmentConfigurationAndExpectConfigurationErrorException()
750 std::ostringstream os;
751 os << "Configuration error in " << someKnownInputSource << ": some error";
753 EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(_))
754 .WillOnce(Throw(Exception("some error")));
759 EXPECT_CALL(systemMock, getenv(_))
761 .WillOnce(Return(dbHostEnvVariableValue.c_str()))
762 .WillOnce(Return(nullptr));
763 initializeReaderWithoutDirectories();
764 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
766 catch (const std::exception& e)
768 EXPECT_EQ(os.str(), e.what());
775 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationCanOverrideJSONDatabaseConfiguration)
778 dbHostEnvVariableValue = "unknownAddress.local";
779 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
780 dbPortEnvVariableValue = "12345";
781 expectGetEnvironmentString(dbPortEnvVariableValue.c_str());
783 expectDbTypeConfigurationCheckAndApply("redis-standalone");
784 expectDBServerAddressConfigurationCheckAndApply("unknownAddress.local:12345");
785 initializeReaderWithSDLconfigFileDirectory();
786 configurationReader->readConfigurationFromInputStream(is);
787 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
790 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithoutPortIsAccepted)
793 dbHostEnvVariableValue = "server.local";
794 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
795 expectGetEnvironmentString(nullptr);
797 expectDbTypeConfigurationCheckAndApply("redis-standalone");
798 expectDBServerAddressConfigurationCheckAndApply("server.local");
799 initializeReaderWithoutDirectories();
800 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
803 TEST_F(ConfigurationReaderEnvironmentVariableTest, EmptyEnvironmentVariableThrows)
805 dbHostEnvVariableValue = "";
806 readEnvironmentConfigurationAndExpectConfigurationErrorException();
809 TEST_F(ConfigurationReaderEnvironmentVariableTest, IllegalCharacterInEnvironmentVariableThrows)
811 dbHostEnvVariableValue = "@";
812 readEnvironmentConfigurationAndExpectConfigurationErrorException();
815 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationAcceptIPv6Address)
818 dbHostEnvVariableValue = "[2001::123]:12345";
819 expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
820 expectGetEnvironmentString(nullptr);
822 expectDbTypeConfigurationCheckAndApply("redis-standalone");
823 expectDBServerAddressConfigurationCheckAndApply("[2001::123]:12345");
824 initializeReaderWithoutDirectories();
825 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);