diff options
Diffstat (limited to 'src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc')
-rw-r--r-- | src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc new file mode 100644 index 0000000..738ddfc --- /dev/null +++ b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc @@ -0,0 +1,338 @@ +// Copyright (C) 2016-2022 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/data.h> +#include <database/dbaccess_parser.h> +#include <dhcpsrv/cfg_db_access.h> +#include <dhcpsrv/host_data_source_factory.h> +#include <dhcpsrv/host_mgr.h> +#include <dhcpsrv/lease_mgr.h> +#include <dhcpsrv/lease_mgr_factory.h> +#include <testutils/test_to_element.h> +#include <gtest/gtest.h> + +#if defined HAVE_MYSQL +#include <mysql/testutils/mysql_schema.h> +#endif + +#if defined HAVE_PGSQL +#include <pgsql/testutils/pgsql_schema.h> +#endif + +using namespace isc; +using namespace isc::dhcp; +using namespace isc::test; + +namespace { + +// Test default values of the lease and host database access strings. +TEST(CfgDbAccessTest, defaults) { + CfgDbAccess cfg; + EXPECT_EQ("type=memfile", cfg.getLeaseDbAccessString()); + std::string expected = "{ \"type\": \"memfile\" }"; + runToElementTest<CfgLeaseDbAccess>(expected, CfgLeaseDbAccess(cfg)); + + EXPECT_TRUE(cfg.getHostDbAccessString().empty()); + runToElementTest<CfgHostDbAccess>("[ ]", CfgHostDbAccess(cfg)); +} + +// This test verifies that it is possible to set the lease database +// string. +TEST(CfgDbAccessTest, setLeaseDbAccessString) { + CfgDbAccess cfg; + ASSERT_NO_THROW(cfg.setLeaseDbAccessString("type=mysql")); + EXPECT_EQ("type=mysql", cfg.getLeaseDbAccessString()); + + // Check unparse + std::string expected = "{ \"type\": \"mysql\" }"; + runToElementTest<CfgLeaseDbAccess>(expected, CfgLeaseDbAccess(cfg)); + + // Append additional parameter. + ASSERT_NO_THROW(cfg.setAppendedParameters("universe=4")); + EXPECT_EQ("type=mysql universe=4", cfg.getLeaseDbAccessString()); + + // Additional parameters are not in lease_db_access_ + runToElementTest<CfgLeaseDbAccess>(expected, CfgLeaseDbAccess(cfg)); + + // If access string is empty, no parameters will be appended. + ASSERT_NO_THROW(cfg.setLeaseDbAccessString("")); + EXPECT_TRUE(cfg.getLeaseDbAccessString().empty()); +} + + +// This test verifies that it is possible to set the host database +// string. +TEST(CfgDbAccessTest, setHostDbAccessString) { + CfgDbAccess cfg; + ASSERT_NO_THROW(cfg.setHostDbAccessString("type=mysql")); + EXPECT_EQ("type=mysql", cfg.getHostDbAccessString()); + + // Check unparse + std::string expected = "[ { \"type\": \"mysql\" } ]"; + runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg)); + + // Append additional parameter. + ASSERT_NO_THROW(cfg.setAppendedParameters("universe=4")); + EXPECT_EQ("type=mysql universe=4", cfg.getHostDbAccessString()); + + // Additional parameters are not in host_db_access_ + runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg)); + + // If access string is empty, no parameters will be appended. + CfgDbAccess cfg1; + ASSERT_NO_THROW(cfg1.setHostDbAccessString("")); + EXPECT_TRUE(cfg1.getHostDbAccessString().empty()); +} + +// This test verifies that it is possible to set multiple host +// database string. +TEST(CfgDbAccessTest, pushHostDbAccessString) { + // Push a string. + CfgDbAccess cfg; + ASSERT_NO_THROW(cfg.setHostDbAccessString("type=foo")); + + // Push another in front. + ASSERT_NO_THROW(cfg.setHostDbAccessString("type=mysql", true)); + EXPECT_EQ("type=mysql", cfg.getHostDbAccessString()); + + // Push a third string. + ASSERT_NO_THROW(cfg.setHostDbAccessString("type=bar")); + + // Check unparse + std::string expected = "[ { \"type\": \"mysql\" }, "; + expected += "{ \"type\": \"foo\" }, { \"type\": \"bar\" } ]"; + runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg)); + + // Check access strings + std::list<std::string> hal = cfg.getHostDbAccessStringList(); + ASSERT_EQ(3, hal.size()); + std::list<std::string>::const_iterator it = hal.cbegin(); + ASSERT_NE(hal.cend(), it); + EXPECT_EQ("type=mysql", *it); + ASSERT_NE(hal.cend(), ++it); + EXPECT_EQ("type=foo", *it); + ASSERT_NE(hal.cend(), ++it); + EXPECT_EQ("type=bar", *it); + + // Build a similar list with the first string empty so it will be ignored. + CfgDbAccess cfg1; + ASSERT_NO_THROW(cfg1.setHostDbAccessString("")); + ASSERT_NO_THROW(cfg1.setHostDbAccessString("type=foo")); + ASSERT_NO_THROW(cfg1.setHostDbAccessString("type=bar")); + expected = "[ { \"type\": \"foo\" }, { \"type\": \"bar\" } ]"; + runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg1)); + hal = cfg1.getHostDbAccessStringList(); + ASSERT_EQ(2, hal.size()); + EXPECT_EQ("type=foo", hal.front()); + EXPECT_EQ("type=bar", hal.back()); +} + +// Tests that lease manager can be created from a specified configuration. +TEST(CfgDbAccessTest, createLeaseMgr) { + CfgDbAccess cfg; + ASSERT_NO_THROW(cfg.setLeaseDbAccessString("type=memfile persist=false universe=4")); + ASSERT_NO_THROW(cfg.createManagers()); + + ASSERT_NO_THROW({ + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); + EXPECT_EQ("memfile",lease_mgr.getType()); + }); +} + +// The following tests require MySQL enabled. +#if defined HAVE_MYSQL + +/// @brief Test fixture class for testing @ref CfgDbAccessTest using MySQL +/// backend. +class CfgMySQLDbAccessTest : public ::testing::Test { +public: + + /// @brief Constructor. + CfgMySQLDbAccessTest() { + // Ensure we have the proper schema with no transient data. + db::test::createMySQLSchema(); + } + + /// @brief Destructor. + virtual ~CfgMySQLDbAccessTest() { + // If data wipe enabled, delete transient data otherwise destroy the schema + db::test::destroyMySQLSchema(); + LeaseMgrFactory::destroy(); + } +}; + + +// Tests that MySQL lease manager and host data source can be created from a +// specified configuration. +TEST_F(CfgMySQLDbAccessTest, createManagers) { + CfgDbAccess cfg; + ASSERT_NO_THROW(cfg.setLeaseDbAccessString(db::test::validMySQLConnectionString())); + ASSERT_NO_THROW(cfg.setHostDbAccessString(db::test::validMySQLConnectionString())); + ASSERT_NO_THROW(cfg.createManagers()); + + ASSERT_NO_THROW({ + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); + EXPECT_EQ("mysql", lease_mgr.getType()); + }); + + ASSERT_NO_THROW({ + const HostDataSourcePtr& host_data_source = + HostMgr::instance().getHostDataSource(); + ASSERT_TRUE(host_data_source); + EXPECT_EQ("mysql", host_data_source->getType()); + }); + + // Because of the lazy initialization of the HostMgr instance, it is + // possible that the first call to the instance() function tosses + // existing connection to the database created by the call to + // createManagers(). Let's make sure that this doesn't happen. + ASSERT_NO_THROW(HostMgr::instance()); + + ASSERT_NO_THROW({ + const HostDataSourcePtr& host_data_source = + HostMgr::instance().getHostDataSource(); + ASSERT_TRUE(host_data_source); + EXPECT_EQ("mysql", host_data_source->getType()); + }); + + EXPECT_TRUE(HostMgr::instance().getIPReservationsUnique()); +} + +// Tests that the createManagers function utilizes the setting in the +// CfgDbAccess class which controls whether the IP reservations must +// be unique or can be non-unique. +TEST_F(CfgMySQLDbAccessTest, createManagersIPResrvUnique) { + CfgDbAccess cfg; + + cfg.setIPReservationsUnique(false); + + ASSERT_NO_THROW(cfg.setLeaseDbAccessString(db::test::validMySQLConnectionString())); + ASSERT_NO_THROW(cfg.setHostDbAccessString(db::test::validMySQLConnectionString())); + ASSERT_NO_THROW(cfg.createManagers()); + + ASSERT_NO_THROW({ + const HostDataSourcePtr& host_data_source = + HostMgr::instance().getHostDataSource(); + ASSERT_TRUE(host_data_source); + EXPECT_EQ("mysql", host_data_source->getType()); + }); + + // Because of the lazy initialization of the HostMgr instance, it is + // possible that the first call to the instance() function tosses + // existing connection to the database created by the call to + // createManagers(). Let's make sure that this doesn't happen. + ASSERT_NO_THROW(HostMgr::instance()); + + ASSERT_NO_THROW({ + const HostDataSourcePtr& host_data_source = + HostMgr::instance().getHostDataSource(); + ASSERT_TRUE(host_data_source); + EXPECT_EQ("mysql", host_data_source->getType()); + }); + + EXPECT_FALSE(HostMgr::instance().getIPReservationsUnique()); +} + +#endif + +// The following tests require PgSQL enabled. +#if defined HAVE_PGSQL + +/// @brief Test fixture class for testing @ref CfgDbAccessTest using PgSQL +/// backend. +class CfgPgSQLDbAccessTest : public ::testing::Test { +public: + + /// @brief Constructor. + CfgPgSQLDbAccessTest() { + // Ensure we have the proper schema with no transient data. + db::test::createPgSQLSchema(); + } + + /// @brief Destructor. + virtual ~CfgPgSQLDbAccessTest() { + // If data wipe enabled, delete transient data otherwise destroy the schema + db::test::destroyPgSQLSchema(); + LeaseMgrFactory::destroy(); + } +}; + + +// Tests that PgSQL lease manager and host data source can be created from a +// specified configuration. +TEST_F(CfgPgSQLDbAccessTest, createManagers) { + CfgDbAccess cfg; + ASSERT_NO_THROW(cfg.setLeaseDbAccessString(db::test::validPgSQLConnectionString())); + ASSERT_NO_THROW(cfg.setHostDbAccessString(db::test::validPgSQLConnectionString())); + ASSERT_NO_THROW(cfg.createManagers()); + + ASSERT_NO_THROW({ + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); + EXPECT_EQ("postgresql", lease_mgr.getType()); + }); + + ASSERT_NO_THROW({ + const HostDataSourcePtr& host_data_source = + HostMgr::instance().getHostDataSource(); + ASSERT_TRUE(host_data_source); + EXPECT_EQ("postgresql", host_data_source->getType()); + }); + + // Because of the lazy initialization of the HostMgr instance, it is + // possible that the first call to the instance() function tosses + // existing connection to the database created by the call to + // createManagers(). Let's make sure that this doesn't happen. + ASSERT_NO_THROW(HostMgr::instance()); + + ASSERT_NO_THROW({ + const HostDataSourcePtr& host_data_source = + HostMgr::instance().getHostDataSource(); + ASSERT_TRUE(host_data_source); + EXPECT_EQ("postgresql", host_data_source->getType()); + }); + + EXPECT_TRUE(HostMgr::instance().getIPReservationsUnique()); +} + +// Tests that the createManagers function utilizes the setting in the +// CfgDbAccess class which controls whether the IP reservations must +// be unique or can be non-unique. +TEST_F(CfgPgSQLDbAccessTest, createManagersIPResrvUnique) { + CfgDbAccess cfg; + + cfg.setIPReservationsUnique(false); + + ASSERT_NO_THROW(cfg.setLeaseDbAccessString(db::test::validPgSQLConnectionString())); + ASSERT_NO_THROW(cfg.setHostDbAccessString(db::test::validPgSQLConnectionString())); + ASSERT_NO_THROW(cfg.createManagers()); + + ASSERT_NO_THROW({ + const HostDataSourcePtr& host_data_source = + HostMgr::instance().getHostDataSource(); + ASSERT_TRUE(host_data_source); + EXPECT_EQ("postgresql", host_data_source->getType()); + }); + + // Because of the lazy initialization of the HostMgr instance, it is + // possible that the first call to the instance() function tosses + // existing connection to the database created by the call to + // createManagers(). Let's make sure that this doesn't happen. + ASSERT_NO_THROW(HostMgr::instance()); + + ASSERT_NO_THROW({ + const HostDataSourcePtr& host_data_source = + HostMgr::instance().getHostDataSource(); + ASSERT_TRUE(host_data_source); + EXPECT_EQ("postgresql", host_data_source->getType()); + }); + + EXPECT_FALSE(HostMgr::instance().getIPReservationsUnique()); +} + +#endif + +} // end of anonymous namespace |