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