Add extra line about src files are part of RIC platform project
[ric-plt/sdl.git] / tst / configurationreader_test.cpp
1 /*
2    Copyright (c) 2018-2019 Nokia.
3
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
7
8        http://www.apache.org/licenses/LICENSE-2.0
9
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.
15 */
16
17 /*
18  * This source code is part of the near-RT RIC (RAN Intelligent Controller)
19  * platform project (RICP).
20 */
21
22 #include <gtest/gtest.h>
23 #include <gmock/gmock.h>
24 #include <memory>
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"
34
35 using namespace shareddatalayer;
36 using namespace shareddatalayer::tst;
37 using namespace testing;
38
39 namespace
40 {
41     class ConfigurationReaderBaseTest: public testing::Test
42     {
43     public:
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;
51
52         ConfigurationReaderBaseTest(std::string inputSourceName):
53             someKnownInputSource(inputSourceName),
54             logger(createLogger(SDL_LOG_PREFIX))
55         {
56             EXPECT_CALL(databaseConfigurationMock, isEmpty()).WillRepeatedly(Return(true));
57             EXPECT_CALL(namespaceConfigurationsMock, isEmpty()).WillRepeatedly(Return(true));
58         }
59
60         void expectDbTypeConfigurationCheckAndApply(const std::string& type)
61         {
62             EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(type));
63         }
64
65         void expectDBServerAddressConfigurationCheckAndApply(const std::string& address)
66         {
67             EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(address));
68         }
69
70         void expectSentinelAddressConfigurationCheckAndApply(const std::string& address)
71         {
72             EXPECT_CALL(databaseConfigurationMock, checkAndApplySentinelAddress(address));
73         }
74
75         void expectSentinelMasterNameConfigurationCheckAndApply(const std::string& address)
76         {
77             EXPECT_CALL(databaseConfigurationMock, checkAndApplySentinelMasterName(address));
78         }
79
80         void expectDatabaseConfigurationIsEmpty_returnFalse()
81         {
82             EXPECT_CALL(databaseConfigurationMock, isEmpty()).
83                 WillOnce(Return(false));
84         }
85
86         void tryToReadDatabaseConfigurationToNonEmptyContainer()
87         {
88             expectDatabaseConfigurationIsEmpty_returnFalse();
89             configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
90         }
91
92         void expectNamespaceConfigurationIsEmpty_returnFalse()
93         {
94             EXPECT_CALL(namespaceConfigurationsMock, isEmpty()).
95                 WillOnce(Return(false));
96         }
97
98         void tryToReadNamespaceConfigurationToNonEmptyContainer()
99         {
100             expectNamespaceConfigurationIsEmpty_returnFalse();
101             configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
102         }
103
104         void expectAddNamespaceConfiguration(const std::string&  namespacePrefix, bool useDbBackend,
105                                              bool enableNotifications)
106         {
107             NamespaceConfiguration expectedNamespaceConfiguration{namespacePrefix, useDbBackend,
108                                                                   enableNotifications,
109                                                                   someKnownInputSource};
110             EXPECT_CALL(namespaceConfigurationsMock, addNamespaceConfiguration(expectedNamespaceConfiguration));
111         }
112
113         void expectGetEnvironmentString(const char* returnValue)
114         {
115             EXPECT_CALL(systemMock, getenv(_))
116                 .WillOnce(Return(returnValue));
117         }
118
119         void initializeReaderWithoutDirectories()
120         {
121             configurationReader.reset(new ConfigurationReader(noDirectories, systemMock, logger));
122         }
123
124         void initializeReaderWithSDLconfigFileDirectory()
125         {
126             configurationReader.reset(new ConfigurationReader({getTopSrcDir() + "/conf"}, systemMock, logger));
127         }
128     };
129
130     class ConfigurationReaderSDLConfigFileTest: public ConfigurationReaderBaseTest
131     {
132     public:
133
134         ConfigurationReaderSDLConfigFileTest():
135             ConfigurationReaderBaseTest("ConfFileFromSDLrepo")
136         {
137             expectGetEnvironmentString(nullptr);
138             initializeReaderWithSDLconfigFileDirectory();
139         }
140     };
141
142     class ConfigurationReaderInputStreamTest: public ConfigurationReaderBaseTest
143     {
144     public:
145
146         ConfigurationReaderInputStreamTest():
147             ConfigurationReaderBaseTest("<istream>")
148         {
149             expectGetEnvironmentString(nullptr);
150             initializeReaderWithoutDirectories();
151         }
152
153         void readConfigurationAndExpectJsonParserException(const std::istringstream& is)
154         {
155             const std::string expectedError("error in SDL configuration <istream> at line 7: expected ':'");
156
157             EXPECT_THROW( {
158                 try
159                 {
160                     configurationReader->readConfigurationFromInputStream(is);
161                     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
162                     configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
163                 }
164                 catch (const std::exception& e)
165                 {
166                     EXPECT_EQ(expectedError, e.what());
167                     throw;
168                 }
169             }, Exception);
170         }
171
172         void readConfigurationAndExpectBadValueException(const std::istringstream& is,
173                                                          const std::string& param)
174         {
175             std::ostringstream os;
176             os << "Configuration error in " << someKnownInputSource << ": "
177                << "invalid \"" << param << "\": \"bad-value\"";
178
179             EXPECT_THROW( {
180                 try
181                 {
182                     configurationReader->readConfigurationFromInputStream(is);
183                     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
184                     configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
185                 }
186                 catch (const std::exception& e)
187                 {
188                     EXPECT_EQ(os.str(), e.what());
189                     throw;
190                 }
191             }, Exception);
192         }
193
194         void readConfigurationAndExpectDbTypeException(const std::istringstream& is)
195         {
196             std::ostringstream os;
197             os << "Configuration error in " << someKnownInputSource << ": some error";
198
199             EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(_))
200                 .WillOnce(Throw(Exception("some error")));
201
202             EXPECT_THROW( {
203                 try
204                 {
205                     configurationReader->readConfigurationFromInputStream(is);
206                     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
207                 }
208                 catch (const std::exception& e)
209                 {
210                     EXPECT_EQ(os.str(), e.what());
211                     throw;
212                 }
213             }, Exception);
214         }
215
216         void readConfigurationAndExpectAddressException(const std::istringstream& is,
217                                                         const std::string& addressValue)
218         {
219             std::ostringstream os;
220             os << "Configuration error in " << someKnownInputSource << ": "
221                << "invalid \"address\": \"" << addressValue << "\" some error";
222
223             EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(_))
224                 .WillOnce(Throw(Exception("some error")));
225
226             EXPECT_THROW( {
227                 try
228                 {
229                     configurationReader->readConfigurationFromInputStream(is);
230                     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
231                 }
232                 catch (const std::exception& e)
233                 {
234                     EXPECT_EQ(os.str(), e.what());
235                     throw;
236                 }
237             }, Exception);
238         }
239
240         void readConfigurationAndExpectMissingParameterException(const std::istringstream& is, const std::string& param)
241         {
242             std::ostringstream os;
243             os << "Configuration error in " << someKnownInputSource << ": "
244                << "missing \"" << param << "\"";
245
246             EXPECT_THROW( {
247                 try
248                 {
249                     configurationReader->readConfigurationFromInputStream(is);
250                     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
251                     configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
252                 }
253                 catch (const std::exception& e)
254                 {
255                     EXPECT_EQ(os.str(), e.what());
256                     throw;
257                 }
258             }, Exception);
259         }
260
261         void readConfigurationAndExpectNamespacePrefixValidationException(const std::istringstream& is,
262                                                                           const std::string& namespacePrefix)
263         {
264             std::ostringstream os;
265             os << "Configuration error in " << someKnownInputSource << ": "
266                << "\"namespacePrefix\": \"" << namespacePrefix << "\""
267                << " contains some of these disallowed characters: ,{}";
268
269             EXPECT_THROW( {
270                 try
271                 {
272                     configurationReader->readConfigurationFromInputStream(is);
273                     configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
274                 }
275                 catch (const std::exception& e)
276                 {
277                     EXPECT_EQ(os.str(), e.what() );
278                     throw;
279                 }
280             }, Exception);
281         }
282
283         void readConfigurationAndExpectEnableNotificationsValidationException(const std::istringstream& is)
284         {
285             std::ostringstream os;
286             os << "Configuration error in " << someKnownInputSource << ": "
287                << "\"enableNotifications\" cannot be true, when \"useDbBackend\" is false";
288
289             EXPECT_THROW( {
290                 try
291                 {
292                     configurationReader->readConfigurationFromInputStream(is);
293                     configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
294                 }
295                 catch (const std::exception& e)
296                 {
297                     EXPECT_EQ(os.str(), e.what() );
298                     throw;
299                 }
300             }, Exception);
301         }
302
303     };
304 }
305
306 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfiguration)
307 {
308     InSequence dummy;
309     std::istringstream is(R"JSON(
310         {
311             "database":
312             {
313                 "type": "redis-standalone",
314                 "servers":
315                 [
316                     {
317                         "address": "someKnownDbAddress:65535"
318                     }
319                 ]
320             }
321         })JSON");
322     expectDbTypeConfigurationCheckAndApply("redis-standalone");
323     expectDBServerAddressConfigurationCheckAndApply("someKnownDbAddress:65535");
324     configurationReader->readConfigurationFromInputStream(is);
325     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
326 }
327
328 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfigurationWithMultipleServerAddresses)
329 {
330     InSequence dummy;
331     std::istringstream is(R"JSON(
332         {
333             "database":
334             {
335                 "type": "redis-cluster",
336                 "servers":
337                 [
338                     {
339                         "address": "10.20.30.40:50000"
340                     },
341                     {
342                         "address": "10.20.30.50:50001"
343                     }
344                 ]
345             }
346         })JSON");
347
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);
353 }
354
355 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONDatabaseConfigurationWithMultipleReadOperations)
356 {
357     InSequence dummy;
358     std::istringstream isOne(R"JSON(
359         {
360             "database":
361             {
362                 "type": "redis-cluster",
363                 "servers":
364                 [
365                     {
366                         "address": "10.20.30.40:50000"
367                     }
368                 ]
369             }
370         })JSON");
371     std::istringstream isTwo(R"JSON(
372         {
373             "database":
374             {
375                 "type": "redis-cluster",
376                 "servers":
377                 [
378                     {
379                         "address": "10.20.30.50:50001"
380                     }
381                 ]
382             }
383         })JSON");
384
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);
393 }
394
395 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseTypeParameter)
396 {
397     InSequence dummy;
398     std::istringstream is(R"JSON(
399         {
400             "database":
401             {
402                 "servers":
403                 [
404                     {
405                         "address": "10.20.30.50:50001"
406                     }
407                 ]
408             }
409         })JSON");
410
411     readConfigurationAndExpectMissingParameterException(is, "type");
412 }
413
414 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseServersArray)
415 {
416     InSequence dummy;
417     std::istringstream is(R"JSON(
418         {
419             "database":
420             {
421                 "type": "redis-standalone"
422             }
423         })JSON");
424
425     expectDbTypeConfigurationCheckAndApply("redis-standalone");
426     readConfigurationAndExpectMissingParameterException(is, "servers");
427 }
428
429 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryDatabaseServerAddressParameter)
430 {
431     InSequence dummy;
432     std::istringstream is(R"JSON(
433         {
434             "database":
435             {
436                 "type": "redis-standalone",
437                 "servers":
438                 [
439                     {
440                     }
441                 ]
442             }
443         })JSON");
444
445     expectDbTypeConfigurationCheckAndApply("redis-standalone");
446     readConfigurationAndExpectMissingParameterException(is, "address");
447 }
448
449 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowDatabaseConfigurationDbTypeError)
450 {
451     InSequence dummy;
452     std::istringstream is(R"JSON(
453         {
454             "database":
455             {
456                 "type": "someBadType",
457                 "servers":
458                 [
459                     {
460                         "address": "10.20.30.50:50001"
461                     }
462                 ]
463             }
464         })JSON");
465
466     readConfigurationAndExpectDbTypeException(is);
467 }
468
469 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowDatabaseConfigurationAddressError)
470 {
471     InSequence dummy;
472     std::istringstream is(R"JSON(
473         {
474             "database":
475             {
476                 "type": "redis-standalone",
477                 "servers":
478                 [
479                     {
480                         "address": "someBadAddress"
481                     }
482                 ]
483             }
484         })JSON");
485
486     expectDbTypeConfigurationCheckAndApply("redis-standalone");
487     readConfigurationAndExpectAddressException(is, "someBadAddress");
488 }
489
490 TEST_F(ConfigurationReaderInputStreamTest, CanHandleJSONWithoutAnyConfiguration)
491 {
492     InSequence dummy;
493     std::istringstream is(R"JSON(
494         {
495         })JSON");
496
497     EXPECT_CALL(databaseConfigurationMock, checkAndApplyServerAddress(_))
498         .Times(0);
499     EXPECT_CALL(namespaceConfigurationsMock, addNamespaceConfiguration(_))
500         .Times(0);
501     configurationReader->readConfigurationFromInputStream(is);
502 }
503
504 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfiguration)
505 {
506     InSequence dummy;
507     std::istringstream is(R"JSON(
508         {
509             "sharedDataLayer":
510             [
511                 {
512                     "namespacePrefix": "someKnownNamespacePrefix",
513                     "useDbBackend": true,
514                     "enableNotifications": true
515                 },
516                 {
517                     "namespacePrefix": "anotherKnownNamespace",
518                     "useDbBackend": false,
519                     "enableNotifications": false
520                 }
521             ]
522         })JSON");
523
524     expectAddNamespaceConfiguration("anotherKnownNamespace", false, false);
525     expectAddNamespaceConfiguration("someKnownNamespacePrefix", true, true);
526     configurationReader->readConfigurationFromInputStream(is);
527     configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
528 }
529
530 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfigurationWithMultipleReadOperations)
531 {
532     InSequence dummy;
533     std::istringstream isOne(R"JSON(
534         {
535             "sharedDataLayer":
536             [
537                 {
538                     "namespacePrefix": "someKnownNamespacePrefix",
539                     "useDbBackend": true,
540                     "enableNotifications": true
541                 }
542             ]
543         })JSON");
544
545     std::istringstream isTwo(R"JSON(
546         {
547             "sharedDataLayer":
548             [
549                 {
550                     "namespacePrefix": "anotherKnownNamespace",
551                     "useDbBackend": false,
552                     "enableNotifications": false
553                 }
554             ]
555         })JSON");
556
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);
563 }
564
565 TEST_F(ConfigurationReaderInputStreamTest, CanReadJSONSharedDataLayerConfigurationWithEmptyNamespacePrefixValue)
566 {
567     InSequence dummy;
568     std::istringstream is(R"JSON(
569         {
570             "sharedDataLayer":
571             [
572                 {
573                     "namespacePrefix": "",
574                     "useDbBackend": false,
575                     "enableNotifications": false
576                 }
577             ]
578         })JSON");
579
580     expectAddNamespaceConfiguration("", false, false);
581     configurationReader->readConfigurationFromInputStream(is);
582     configurationReader->readNamespaceConfigurations(namespaceConfigurationsMock);
583 }
584
585 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowJSONSyntaxError)
586 {
587     InSequence dummy;
588     std::istringstream is(R"JSON(
589         {
590             "sharedDataLayer":
591             [
592                 {
593                     "abc"
594                 }
595             ]
596         })JSON");
597
598     readConfigurationAndExpectJsonParserException(is);
599 }
600
601 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowParameterUseDbBackendBadValue)
602 {
603     InSequence dummy;
604     std::istringstream is(R"JSON(
605         {
606             "sharedDataLayer":
607             [
608                 {
609                     "namespacePrefix": "someKnownNamespacePrefix",
610                     "useDbBackend": "bad-value",
611                     "enableNotifications": false
612                 }
613             ]
614         })JSON");
615
616     readConfigurationAndExpectBadValueException(is, "useDbBackend");
617 }
618
619 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowParameterEnableNotificationsBadValue)
620 {
621     InSequence dummy;
622     std::istringstream is(R"JSON(
623         {
624             "sharedDataLayer":
625             [
626                 {
627                     "namespacePrefix": "someKnownNamespacePrefix",
628                     "useDbBackend": true,
629                     "enableNotifications": "bad-value"
630                 }
631             ]
632         })JSON");
633
634     readConfigurationAndExpectBadValueException(is, "enableNotifications");
635 }
636
637 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryNamespacePrefixParameter)
638 {
639     InSequence dummy;
640     std::istringstream is(R"JSON(
641         {
642             "sharedDataLayer":
643             [
644                 {
645                     "useDbBackend": true,
646                     "enableNotifications": true
647                 }
648             ]
649         })JSON");
650
651     readConfigurationAndExpectMissingParameterException(is, "namespacePrefix");
652 }
653
654 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryUseDbBackendParameter)
655 {
656     InSequence dummy;
657     std::istringstream is(R"JSON(
658         {
659             "sharedDataLayer":
660             [
661                 {
662                     "namespacePrefix": "someKnownNamespacePrefix",
663                     "enableNotifications": true
664                 }
665             ]
666         })JSON");
667
668     readConfigurationAndExpectMissingParameterException(is, "useDbBackend");
669 }
670
671 TEST_F(ConfigurationReaderInputStreamTest, CanCatchAndThrowMisingMandatoryEnableNotificationsParameter)
672 {
673     InSequence dummy;
674     std::istringstream is(R"JSON(
675         {
676             "sharedDataLayer":
677             [
678                 {
679                     "namespacePrefix": "someKnownNamespacePrefix",
680                     "useDbBackend": true
681                 }
682             ]
683         })JSON");
684
685     readConfigurationAndExpectMissingParameterException(is, "enableNotifications");
686 }
687
688 TEST_F(ConfigurationReaderInputStreamTest, CanThrowValidationErrorForNamespacePrefixWithDisallowedCharacters)
689 {
690     InSequence dummy;
691     std::istringstream is(R"JSON(
692         {
693             "sharedDataLayer":
694             [
695                 {
696                     "namespacePrefix": "a,b{c}",
697                     "useDbBackend": true,
698                     "enableNotifications": true
699                 }
700             ]
701         })JSON");
702
703     readConfigurationAndExpectNamespacePrefixValidationException(is, "a,b{c}");
704 }
705
706 TEST_F(ConfigurationReaderInputStreamTest, CanThrowValidationErrorForEnableNotificationsWithNoDbBackend)
707 {
708     InSequence dummy;
709     std::istringstream is(R"JSON(
710         {
711             "sharedDataLayer":
712             [
713                 {
714                     "namespacePrefix": "someKnownNamespacePrefix",
715                     "useDbBackend": false,
716                     "enableNotifications": true
717                 }
718             ]
719         })JSON");
720
721     readConfigurationAndExpectEnableNotificationsValidationException(is);
722 }
723
724 TEST_F(ConfigurationReaderInputStreamTest, WillNotReadDatabaseConfigurationToNonEmptyContainer)
725 {
726     EXPECT_EXIT(tryToReadDatabaseConfigurationToNonEmptyContainer(),
727         KilledBySignal(SIGABRT), "ABORT.*configurationreader\\.cpp");
728 }
729
730 TEST_F(ConfigurationReaderInputStreamTest, WillNotReadNamespaceConfigurationToNonEmptyContainer)
731 {
732     EXPECT_EXIT(tryToReadNamespaceConfigurationToNonEmptyContainer(),
733         KilledBySignal(SIGABRT), "ABORT.*configurationreader\\.cpp");
734 }
735
736 class ConfigurationReaderEnvironmentVariableTest: public ConfigurationReaderBaseTest
737 {
738 public:
739     std::string dbHostEnvVariableValue;
740     std::string dbPortEnvVariableValue;
741     std::string sentinelPortEnvVariableValue;
742     std::string sentinelMasterNameEnvVariableValue;
743     std::istringstream is{R"JSON(
744         {
745             "database":
746             {
747                 "type": "redis-cluster",
748                 "servers":
749                 [
750                     {
751                         "address": "10.20.30.40:50000"
752                     },
753                     {
754                         "address": "10.20.30.50:50001"
755                     }
756                 ]
757             }
758         })JSON"};
759
760     ConfigurationReaderEnvironmentVariableTest():
761         ConfigurationReaderBaseTest(DB_HOST_ENV_VAR_NAME)
762     {
763     }
764
765     void readEnvironmentConfigurationAndExpectConfigurationErrorException()
766     {
767         std::ostringstream os;
768         os << "Configuration error in " << someKnownInputSource << ": some error";
769
770         EXPECT_CALL(databaseConfigurationMock, checkAndApplyDbType(_))
771             .WillOnce(Throw(Exception("some error")));
772
773         EXPECT_THROW( {
774             try
775             {
776                 EXPECT_CALL(systemMock, getenv(_))
777                     .Times(4)
778                     .WillOnce(Return(dbHostEnvVariableValue.c_str()))
779                     .WillOnce(Return(nullptr));
780                 initializeReaderWithoutDirectories();
781                 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
782             }
783             catch (const std::exception& e)
784             {
785                 EXPECT_EQ(os.str(), e.what());
786                 throw;
787             }
788         }, Exception);
789     }
790 };
791
792 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationCanOverrideJSONDatabaseConfiguration)
793 {
794     InSequence dummy;
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
801
802     expectDbTypeConfigurationCheckAndApply("redis-standalone");
803     expectDBServerAddressConfigurationCheckAndApply("unknownAddress.local:12345");
804     initializeReaderWithSDLconfigFileDirectory();
805     configurationReader->readConfigurationFromInputStream(is);
806     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
807 }
808
809 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithoutPortIsAccepted)
810 {
811     InSequence dummy;
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
817
818     expectDbTypeConfigurationCheckAndApply("redis-standalone");
819     expectDBServerAddressConfigurationCheckAndApply("server.local");
820     initializeReaderWithoutDirectories();
821     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
822 }
823
824 TEST_F(ConfigurationReaderEnvironmentVariableTest, EmptyEnvironmentVariableThrows)
825 {
826     dbHostEnvVariableValue = "";
827     readEnvironmentConfigurationAndExpectConfigurationErrorException();
828 }
829
830 TEST_F(ConfigurationReaderEnvironmentVariableTest, IllegalCharacterInEnvironmentVariableThrows)
831 {
832     dbHostEnvVariableValue = "@";
833     readEnvironmentConfigurationAndExpectConfigurationErrorException();
834 }
835
836 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationAcceptIPv6Address)
837 {
838     InSequence dummy;
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
844
845     expectDbTypeConfigurationCheckAndApply("redis-standalone");
846     expectDBServerAddressConfigurationCheckAndApply("[2001::123]:12345");
847     initializeReaderWithoutDirectories();
848     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
849 }
850
851 TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithSentinel)
852 {
853     InSequence dummy;
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());
862
863     expectDbTypeConfigurationCheckAndApply("redis-sentinel");
864     expectSentinelAddressConfigurationCheckAndApply("sentinelAddress.local:2222");
865     expectSentinelMasterNameConfigurationCheckAndApply(sentinelMasterNameEnvVariableValue);
866     initializeReaderWithoutDirectories();
867     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
868 }