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