diff options
Diffstat (limited to 'src/lib/process/tests/d_cfg_mgr_unittests.cc')
-rw-r--r-- | src/lib/process/tests/d_cfg_mgr_unittests.cc | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/src/lib/process/tests/d_cfg_mgr_unittests.cc b/src/lib/process/tests/d_cfg_mgr_unittests.cc new file mode 100644 index 0000000..faa3e92 --- /dev/null +++ b/src/lib/process/tests/d_cfg_mgr_unittests.cc @@ -0,0 +1,375 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <cc/command_interpreter.h> +#include <exceptions/exceptions.h> +#include <process/testutils/d_test_stubs.h> +#include <process/d_cfg_mgr.h> +#include <process/redact_config.h> + +#include <boost/foreach.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <gtest/gtest.h> + +#include <sstream> + +using namespace std; +using namespace isc; +using namespace isc::config; +using namespace isc::process; +using namespace isc::data; +using namespace boost::posix_time; + +namespace { + +/// @brief Test Class for verifying that configuration context cannot be null +/// during construction. +class DCtorTestCfgMgr : public DCfgMgrBase { +public: + /// @brief Constructor - Note that is passes in an empty configuration + /// pointer to the base class constructor. + DCtorTestCfgMgr() : DCfgMgrBase(ConfigPtr()) { + } + + /// @brief Destructor + virtual ~DCtorTestCfgMgr() { + } + + /// @brief Dummy implementation as this method is abstract. + virtual ConfigPtr createNewContext() { + return (ConfigPtr()); + } + + /// @brief Returns summary of configuration in the textual format. + virtual std::string getConfigSummary(const uint32_t) { + return (""); + } +}; + +/// @brief Test fixture class for testing DCfgMgrBase class. +/// It maintains an member instance of DStubCfgMgr and derives from +/// ConfigParseTest fixture, thus providing methods for converting JSON +/// strings to configuration element sets, checking parse results, +/// accessing the configuration context and trying to unparse. +class DStubCfgMgrTest : public ConfigParseTest { +public: + + /// @brief Constructor + DStubCfgMgrTest():cfg_mgr_(new DStubCfgMgr) { + } + + /// @brief Destructor + ~DStubCfgMgrTest() { + } + + /// @brief Convenience method which returns a DStubContextPtr to the + /// configuration context. + /// + /// @return returns a DStubContextPtr. + DStubContextPtr getStubContext() { + return (boost::dynamic_pointer_cast<DStubContext> + (cfg_mgr_->getContext())); + } + + /// @brief Configuration manager instance. + DStubCfgMgrPtr cfg_mgr_; +}; + +///@brief Tests basic construction/destruction of configuration manager. +/// Verifies that: +/// 1. Proper construction succeeds. +/// 2. Configuration context is initialized by construction. +/// 3. Destruction works properly. +/// 4. Construction with a null context is not allowed. +TEST(DCfgMgrBase, construction) { + DCfgMgrBasePtr cfg_mgr; + + // Verify that configuration manager constructions without error. + ASSERT_NO_THROW(cfg_mgr.reset(new DStubCfgMgr())); + + // Verify that the context can be retrieved and is not null. + ConfigPtr context = cfg_mgr->getContext(); + EXPECT_TRUE(context); + + // Verify that the manager can be destructed without error. + EXPECT_NO_THROW(cfg_mgr.reset()); + + // Verify that an attempt to construct a manger with a null context fails. + ASSERT_THROW(DCtorTestCfgMgr(), DCfgMgrBaseError); +} + +///@brief Tests fundamental aspects of configuration parsing. +/// Verifies that: +/// 1. A correctly formed simple configuration parses without error. +/// 2. An error building the element is handled. +/// 3. An error committing the element is handled. +/// 4. An unknown element error is handled. +TEST_F(DStubCfgMgrTest, basicParseTest) { + // Create a simple configuration. + string config = "{ \"test-value\": [] } "; + ASSERT_TRUE(fromJSON(config)); + + // Verify that we can parse a simple configuration. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + EXPECT_TRUE(checkAnswer(0)); + + // Verify that we can check a simple configuration. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, true); + EXPECT_TRUE(checkAnswer(0)); +} + +/// @brief Tests that element ids supported by the base class as well as those +/// added by the derived class function properly. +/// This test verifies that: +/// 1. Boolean parameters can be parsed and retrieved. +/// 2. Uint32 parameters can be parsed and retrieved. +/// 3. String parameters can be parsed and retrieved. +/// 4. Map elements can be parsed and retrieved. +/// 5. List elements can be parsed and retrieved. +/// 6. Parsing a second configuration, updates the existing context values +/// correctly. +TEST_F(DStubCfgMgrTest, simpleTypesTest) { + // Create a configuration with all of the parameters. + string config = "{ \"bool_test\": true , " + " \"uint32_test\": 77 , " + " \"string_test\": \"hmmm chewy\" , " + " \"map_test\" : {} , " + " \"list_test\": [] }"; + ASSERT_TRUE(fromJSON(config)); + + // Verify that the configuration parses without error. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + ASSERT_TRUE(checkAnswer(0)); + DStubContextPtr context = getStubContext(); + ASSERT_TRUE(context); + + // Create a configuration which "updates" all of the parameter values. + string config2 = "{ \"bool_test\": false , " + " \"uint32_test\": 88 , " + " \"string_test\": \"ewww yuk!\" , " + " \"map_test2\" : {} , " + " \"list_test2\": [] }"; + ASSERT_TRUE(fromJSON(config2)); + + // Verify that the configuration parses without error. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + EXPECT_TRUE(checkAnswer(0)); + context = getStubContext(); + ASSERT_TRUE(context); +} + +/// @brief Tests that the configuration context is preserved after failure +/// during parsing causes a rollback. +/// 1. Verifies configuration context rollback. +TEST_F(DStubCfgMgrTest, rollBackTest) { + // Create a configuration with all of the parameters. + string config = "{ \"bool_test\": true , " + " \"uint32_test\": 77 , " + " \"string_test\": \"hmmm chewy\" , " + " \"map_test\" : {} , " + " \"list_test\": [] }"; + ASSERT_TRUE(fromJSON(config)); + + // Verify that the configuration parses without error. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + EXPECT_TRUE(checkAnswer(0)); + DStubContextPtr context = getStubContext(); + ASSERT_TRUE(context); + + // Create a configuration which "updates" all of the parameter values + // plus one unknown at the end. + string config2 = "{ \"bool_test\": false , " + " \"uint32_test\": 88 , " + " \"string_test\": \"ewww yuk!\" , " + " \"map_test2\" : {} , " + " \"list_test2\": [] , " + " \"zeta_unknown\": 33 } "; + ASSERT_TRUE(fromJSON(config2)); +} + +/// @brief Tests that the configuration context is preserved during +/// check only parsing. +TEST_F(DStubCfgMgrTest, checkOnly) { + // Create a configuration with all of the parameters. + string config = "{ \"bool_test\": true , " + " \"uint32_test\": 77 , " + " \"string_test\": \"hmmm chewy\" , " + " \"map_test\" : {} , " + " \"list_test\": [] }"; + ASSERT_TRUE(fromJSON(config)); + + // Verify that the configuration parses without error. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + EXPECT_TRUE(checkAnswer(0)); + DStubContextPtr context = getStubContext(); + ASSERT_TRUE(context); + + + // Create a configuration which "updates" all of the parameter values. + string config2 = "{ \"bool_test\": false , " + " \"uint32_test\": 88 , " + " \"string_test\": \"ewww yuk!\" , " + " \"map_test2\" : {} , " + " \"list_test2\": [] }"; + ASSERT_TRUE(fromJSON(config2)); + + answer_ = cfg_mgr_->simpleParseConfig(config_set_, true); + EXPECT_TRUE(checkAnswer(0)); + context = getStubContext(); + ASSERT_TRUE(context); + +} + +// Tests that configuration element position is returned by getParam variants. +TEST_F(DStubCfgMgrTest, paramPosition) { + // Create a configuration with one of each scalar types. We end them + // with line feeds so we can test position value. + string config = "{ \"bool_test\": true , \n" + " \"uint32_test\": 77 , \n" + " \"string_test\": \"hmmm chewy\" }"; + ASSERT_TRUE(fromJSON(config)); + + // Verify that the configuration parses without error. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + ASSERT_TRUE(checkAnswer(0)); + DStubContextPtr context = getStubContext(); + ASSERT_TRUE(context); + +} + +// This tests if some aspects of simpleParseConfig are behaving properly. +// Thorough testing is only possible for specific implementations. This +// is done for control agent (see CtrlAgentControllerTest tests in +// src/bin/agent/tests/ctrl_agent_controller_unittest.cc for example). +// Also, shell tests in src/bin/agent/ctrl_agent_process_tests.sh test +// the whole CA process that uses simpleParseConfig. The alternative +// would be to implement whole parser that would set the context +// properly. The ROI for this is not worth the effort. +TEST_F(DStubCfgMgrTest, simpleParseConfig) { + using namespace isc::data; + + // Passing just null pointer should result in error return code + answer_ = cfg_mgr_->simpleParseConfig(ConstElementPtr(), false); + EXPECT_TRUE(checkAnswer(1)); + + // Ok, now try with a dummy, but valid json code + string config = "{ \"bool_test\": true , \n" + " \"uint32_test\": 77 , \n" + " \"string_test\": \"hmmm chewy\" }"; + ASSERT_NO_THROW(fromJSON(config)); + + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + EXPECT_TRUE(checkAnswer(0)); +} + +// This test checks that the post configuration callback function is +// executed by the simpleParseConfig function. +TEST_F(DStubCfgMgrTest, simpleParseConfigWithCallback) { + string config = "{ \"bool_test\": true , \n" + " \"uint32_test\": 77 , \n" + " \"string_test\": \"hmmm chewy\" }"; + ASSERT_NO_THROW(fromJSON(config)); + + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false, + []() { + isc_throw(Unexpected, "unexpected configuration error"); + }); + EXPECT_TRUE(checkAnswer(1)); +} + +// This test checks that redactConfig works as expected. +TEST_F(DStubCfgMgrTest, redactConfig) { + // Basic case. + string config = "{ \"foo\": 1 }"; + ConstElementPtr elem; + ASSERT_NO_THROW(elem = Element::fromJSON(config)); + ConstElementPtr ret; + ASSERT_NO_THROW(ret = redactConfig(elem)); + EXPECT_EQ(ret->str(), elem->str()); + + // Verify redaction. + config = "{ \"password\": \"foo\", \"secret\": \"bar\" }"; + ASSERT_NO_THROW(elem = Element::fromJSON(config)); + ASSERT_NO_THROW(ret = redactConfig(elem)); + string expected = "{ \"password\": \"*****\", \"secret\": \"*****\" }"; + EXPECT_EQ(expected, ret->str()); + + // Verify that user context are skipped. + config = "{ \"user-context\": { \"password\": \"foo\" } }"; + ASSERT_NO_THROW(elem = Element::fromJSON(config)); + ASSERT_NO_THROW(ret = redactConfig(elem)); + EXPECT_EQ(ret->str(), elem->str()); + + // Verify that only given subtrees are handled. + list<string> keys = { "foo" }; + config = "{ \"foo\": { \"password\": \"foo\" }, "; + config += "\"next\": { \"secret\": \"bar\" } }"; + ASSERT_NO_THROW(elem = Element::fromJSON(config)); + ASSERT_NO_THROW(ret = redactConfig(elem, keys)); + expected = "{ \"foo\": { \"password\": \"*****\" }, "; + expected += "\"next\": { \"secret\": \"bar\" } }"; + EXPECT_EQ(expected, ret->str()); +} + +// Test that user context is not touched when configuration is redacted. +TEST(RedactConfig, userContext) { + ConstElementPtr const config(Element::fromJSON(R"( + { + "some-database": { + "password": "sensitive", + "secret": "sensitive", + "user": "keatest", + "nested-map": { + "password": "sensitive", + "secret": "sensitive", + "user": "keatest" + } + }, + "user-context": { + "password": "keatest", + "secret": "keatest", + "user": "keatest", + "nested-map": { + "password": "keatest", + "secret": "keatest", + "user": "keatest" + } + } + } + )")); + ConstElementPtr const expected(Element::fromJSON(R"( + { + "some-database": { + "password": "*****", + "secret": "*****", + "user": "keatest", + "nested-map": { + "password": "*****", + "secret": "*****", + "user": "keatest" + } + }, + "user-context": { + "password": "keatest", + "secret": "keatest", + "user": "keatest", + "nested-map": { + "password": "keatest", + "secret": "keatest", + "user": "keatest" + } + } + } + )")); + ConstElementPtr redacted(redactConfig(config)); + EXPECT_TRUE(isEquivalent(redacted, expected)) + << "Actual:\n" << prettyPrint(redacted) << "\n" + "Expected:\n" << prettyPrint(expected); +} + +} // end of anonymous namespace |