summaryrefslogtreecommitdiffstats
path: root/src/bin/d2/tests/d2_controller_unittests.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:15:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:15:43 +0000
commitf5f56e1a1c4d9e9496fcb9d81131066a964ccd23 (patch)
tree49e44c6f87febed37efb953ab5485aa49f6481a7 /src/bin/d2/tests/d2_controller_unittests.cc
parentInitial commit. (diff)
downloadisc-kea-f5f56e1a1c4d9e9496fcb9d81131066a964ccd23.tar.xz
isc-kea-f5f56e1a1c4d9e9496fcb9d81131066a964ccd23.zip
Adding upstream version 2.4.1.upstream/2.4.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/bin/d2/tests/d2_controller_unittests.cc')
-rw-r--r--src/bin/d2/tests/d2_controller_unittests.cc303
1 files changed, 303 insertions, 0 deletions
diff --git a/src/bin/d2/tests/d2_controller_unittests.cc b/src/bin/d2/tests/d2_controller_unittests.cc
new file mode 100644
index 0000000..0c6e5ec
--- /dev/null
+++ b/src/bin/d2/tests/d2_controller_unittests.cc
@@ -0,0 +1,303 @@
+// 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 <asiolink/testutils/timed_signal.h>
+#include <cc/command_interpreter.h>
+#include <d2srv/testutils/nc_test_utils.h>
+#include <d2/d2_controller.h>
+#include <d2/d2_process.h>
+#include <process/testutils/d_test_stubs.h>
+
+#include <boost/pointer_cast.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+
+#include <sstream>
+
+using namespace isc::asiolink::test;
+using namespace isc::process;
+using namespace boost::posix_time;
+
+namespace isc {
+namespace d2 {
+
+/// @brief Test fixture class for testing D2Controller class.
+///
+/// This class derives from DControllerTest and wraps a D2Controller. Much of
+/// the underlying functionality is in the DControllerBase class which has an
+/// extensive set of unit tests that are independent of DHCP-DDNS.
+/// @TODO Currently These tests are relatively light and duplicate some of
+/// the testing done on the base class. These tests are sufficient to ensure
+/// that D2Controller properly derives from its base class and to test the
+/// logic that is unique to D2Controller. These tests will be augmented and
+/// or new tests added as additional functionality evolves.
+/// Unlike the stub testing, there is no use of SimFailure to induce error
+/// conditions as this is production code.
+class D2ControllerTest : public DControllerTest {
+public:
+ /// @brief Constructor
+ /// Note the constructor passes in the static D2Controller instance
+ /// method.
+ D2ControllerTest() : DControllerTest(D2Controller::instance) {
+ }
+
+ /// @brief Fetches the D2Controller's D2Process
+ ///
+ /// @return A pointer to the process which may be null if it has not yet
+ /// been instantiated.
+ D2ProcessPtr getD2Process() {
+ return (boost::dynamic_pointer_cast<D2Process>(getProcess()));
+ }
+
+ /// @brief Fetches the D2Process's D2Configuration manager
+ ///
+ /// @return A pointer to the manager which may be null if it has not yet
+ /// been instantiated.
+ D2CfgMgrPtr getD2CfgMgr() {
+ D2CfgMgrPtr p;
+ if (getD2Process()) {
+ p = getD2Process()->getD2CfgMgr();
+ }
+
+ return (p);
+ }
+
+ /// @brief Fetches the D2Configuration manager's D2CfgContext
+ ///
+ /// @return A pointer to the context which may be null if it has not yet
+ /// been instantiated.
+ D2CfgContextPtr getD2CfgContext() {
+ D2CfgContextPtr p;
+ if (getD2CfgMgr()) {
+ p = getD2CfgMgr()->getD2CfgContext();
+ }
+
+ return (p);
+ }
+};
+
+/// @brief Basic Controller instantiation testing.
+/// Verifies that the controller singleton gets created and that the
+/// basic derivation from the base class is intact.
+TEST_F(D2ControllerTest, basicInstanceTesting) {
+ // Verify the singleton instance can be fetched and that
+ // it has the correct type.
+ DControllerBasePtr& controller = DControllerTest::getController();
+ ASSERT_TRUE(controller);
+ ASSERT_NO_THROW(boost::dynamic_pointer_cast<D2Controller>(controller));
+
+ // Verify that controller's app name is correct.
+ EXPECT_TRUE(checkAppName(D2Controller::d2_app_name_));
+
+ // Verify that controller's bin name is correct.
+ EXPECT_TRUE(checkBinName(D2Controller::d2_bin_name_));
+
+ // Verify that controller's IOService exists.
+ EXPECT_TRUE(checkIOService());
+
+ // Verify that the Process does NOT exist.
+ EXPECT_FALSE(checkProcess());
+}
+
+/// @brief Tests basic command line processing.
+/// Verifies that:
+/// 1. Standard command line options are supported.
+/// 2. Invalid options are detected.
+TEST_F(D2ControllerTest, commandLineArgs) {
+ char* argv[] = { const_cast<char*>("progName"),
+ const_cast<char*>("-c"),
+ const_cast<char*>(DControllerTest::CFG_TEST_FILE),
+ const_cast<char*>("-d") };
+ int argc = 4;
+
+ // Verify that verbose flag is false initially.
+ EXPECT_TRUE(checkVerbose(false));
+
+ // Verify that standard options can be parsed without error.
+ EXPECT_NO_THROW(parseArgs(argc, argv));
+
+ // Verify that verbose flag is true.
+ EXPECT_TRUE(checkVerbose(true));
+
+ // Verify configuration file name is correct.
+ EXPECT_TRUE(checkConfigFileName(DControllerTest::CFG_TEST_FILE));
+
+ // Verify that an unknown option is detected.
+ char* argv2[] = { const_cast<char*>("progName"),
+ const_cast<char*>("-x") };
+ argc = 2;
+ EXPECT_THROW(parseArgs(argc, argv2), InvalidUsage);
+}
+
+/// @brief Tests application process creation and initialization.
+/// Verifies that the process can be successfully created and initialized.
+TEST_F(D2ControllerTest, initProcessTesting) {
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+}
+
+/// @brief Tests launch and normal shutdown (stand alone mode).
+/// This creates an interval timer to generate a normal shutdown and then
+/// launches with a valid, stand-alone command line and no simulated errors.
+TEST_F(D2ControllerTest, launchNormalShutdown) {
+ // Write valid_d2_config and then run launch() for 1000 ms.
+ time_duration elapsed_time;
+ runWithConfig(valid_d2_config, 1000, elapsed_time);
+
+ // Give a generous margin to accommodate slower test environs.
+ EXPECT_TRUE(elapsed_time.total_milliseconds() >= 800 &&
+ elapsed_time.total_milliseconds() <= 1300);
+}
+
+/// @brief Configuration update event testing.
+/// This really tests just the ability of the handlers to invoke the necessary
+/// chain of methods and handle error conditions. Configuration parsing and
+/// retrieval should be tested as part of the d2 configuration management
+/// implementation.
+/// This test verifies that:
+/// 1. A valid configuration yields a successful parse result.
+/// 2. That an application process error in configuration updating is handled
+/// properly.
+TEST_F(D2ControllerTest, configUpdateTests) {
+ int rcode = -1;
+ isc::data::ConstElementPtr answer;
+
+ // Initialize the application process.
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+
+ // Create a configuration set using a small, valid D2 configuration.
+ isc::data::ElementPtr config_set =
+ isc::data::Element::fromJSON(valid_d2_config);
+
+ // Verify that given a valid config we get a successful update result.
+ answer = updateConfig(config_set);
+ isc::config::parseAnswer(rcode, answer);
+ EXPECT_EQ(0, rcode);
+
+ // Verify that given a valid config we get a successful check result.
+ answer = checkConfig(config_set);
+ isc::config::parseAnswer(rcode, answer);
+ EXPECT_EQ(0, rcode);
+
+ // Use an invalid configuration to verify parsing error return.
+ std::string config = "{ \"ip-address\": 1000 } ";
+ config_set = isc::data::Element::fromJSON(config);
+ answer = updateConfig(config_set);
+ isc::config::parseAnswer(rcode, answer);
+ EXPECT_EQ(1, rcode);
+
+ // Use an invalid configuration to verify checking error return.
+ answer = checkConfig(config_set);
+ isc::config::parseAnswer(rcode, answer);
+ EXPECT_EQ(1, rcode);
+}
+
+// Tests that the original configuration is retained after a SIGHUP triggered
+// reconfiguration fails due to invalid config content.
+TEST_F(D2ControllerTest, invalidConfigReload) {
+ // Schedule to replace the configuration file after launch. This way the
+ // file is updated after we have done the initial configuration.
+ scheduleTimedWrite("{ \"string_test\": BOGUS JSON }", 100);
+
+ // Setup to raise SIGHUP in 200 ms.
+ TimedSignal sighup(*getIOService(), SIGHUP, 200);
+
+ // Write valid_d2_config and then run launch() for a maximum of 500 ms.
+ time_duration elapsed_time;
+ runWithConfig(valid_d2_config, 500, elapsed_time);
+
+ // Context is still available post launch.
+ // Check to see that our configuration matches the original per
+ // valid_d2_config (see src/lib/process/testutils/d_test_stubs.cc)
+ D2CfgMgrPtr d2_cfg_mgr = getD2CfgMgr();
+ D2ParamsPtr d2_params = d2_cfg_mgr->getD2Params();
+ ASSERT_TRUE(d2_params);
+
+ EXPECT_EQ("127.0.0.1", d2_params->getIpAddress().toText());
+ EXPECT_EQ(5031, d2_params->getPort());
+ EXPECT_TRUE(d2_cfg_mgr->forwardUpdatesEnabled());
+ EXPECT_TRUE(d2_cfg_mgr->reverseUpdatesEnabled());
+
+ /// @todo add a way to trap log file and search it
+}
+
+// Tests that the original configuration is replaced after a SIGHUP triggered
+// reconfiguration succeeds.
+TEST_F(D2ControllerTest, validConfigReload) {
+ // Define a replacement config.
+ const char* second_cfg =
+ "{"
+ " \"ip-address\": \"192.168.77.1\" , "
+ " \"port\": 777 , "
+ "\"tsig-keys\": [], "
+ "\"forward-ddns\" : {}, "
+ "\"reverse-ddns\" : {} "
+ "}";
+
+ // Schedule to replace the configuration file after launch. This way the
+ // file is updated after we have done the initial configuration.
+ scheduleTimedWrite(second_cfg, 100);
+
+ // Setup to raise SIGHUP in 200 ms.
+ TimedSignal sighup(*getIOService(), SIGHUP, 200);
+
+ // Write valid_d2_config and then run launch() for a maximum of 500ms.
+ time_duration elapsed_time;
+ runWithConfig(valid_d2_config, 500, elapsed_time);
+
+ // Context is still available post launch.
+ // Check to see that our configuration matches the replacement config.
+ D2CfgMgrPtr d2_cfg_mgr = getD2CfgMgr();
+ D2ParamsPtr d2_params = d2_cfg_mgr->getD2Params();
+ ASSERT_TRUE(d2_params);
+
+ EXPECT_EQ("192.168.77.1", d2_params->getIpAddress().toText());
+ EXPECT_EQ(777, d2_params->getPort());
+ EXPECT_FALSE(d2_cfg_mgr->forwardUpdatesEnabled());
+ EXPECT_FALSE(d2_cfg_mgr->reverseUpdatesEnabled());
+
+ /// @todo add a way to trap log file and search it
+}
+
+// Tests that the SIGINT triggers a normal shutdown.
+TEST_F(D2ControllerTest, sigintShutdown) {
+ // Setup to raise SIGINT in 1 ms.
+ TimedSignal sighup(*getIOService(), SIGINT, 1);
+
+ // Write valid_d2_config and then run launch() for a maximum of 1000 ms.
+ time_duration elapsed_time;
+ runWithConfig(valid_d2_config, 1000, elapsed_time);
+
+ // Signaled shutdown should make our elapsed time much smaller than
+ // the maximum run time. Give generous margin to accommodate slow
+ // test environs.
+ EXPECT_TRUE(elapsed_time.total_milliseconds() < 300);
+
+ /// @todo add a way to trap log file and search it
+}
+
+// Tests that the SIGTERM triggers a normal shutdown.
+TEST_F(D2ControllerTest, sigtermShutdown) {
+ // Setup to raise SIGTERM in 1 ms.
+ TimedSignal sighup(*getIOService(), SIGTERM, 1);
+
+ // Write valid_d2_config and then run launch() for a maximum of 1 s.
+ time_duration elapsed_time;
+ runWithConfig(valid_d2_config, 1000, elapsed_time);
+
+ // Signaled shutdown should make our elapsed time much smaller than
+ // the maximum run time. Give generous margin to accommodate slow
+ // test environs.
+ EXPECT_TRUE(elapsed_time.total_milliseconds() < 300);
+
+ /// @todo add a way to trap log file and search it
+}
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace