]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#566,!304] Addressed review comments
authorThomas Markwalder <tmark@isc.org>
Thu, 18 Apr 2019 13:53:35 +0000 (09:53 -0400)
committerThomas Markwalder <tmark@isc.org>
Thu, 18 Apr 2019 14:52:40 +0000 (10:52 -0400)
src/lib/dhcpsrv/tests/Makefile.am
src/lib/dhcpsrv/tests/cb_ctl_dhcp6_unittest.cc [deleted file]
src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc [moved from src/lib/dhcpsrv/tests/cb_ctl_dhcp4_unittest.cc with 55% similarity]

index 313c7bfed1985fa907c03efa4b7ae895c79bbcac..b5e3349d4aeb001cd1606ef64d52e58bdb138388 100644 (file)
@@ -63,8 +63,7 @@ libdhcpsrv_unittests_SOURCES += alloc_engine_hooks_unittest.cc
 libdhcpsrv_unittests_SOURCES += alloc_engine4_unittest.cc
 libdhcpsrv_unittests_SOURCES += alloc_engine6_unittest.cc
 libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
-libdhcpsrv_unittests_SOURCES += cb_ctl_dhcp4_unittest.cc
-libdhcpsrv_unittests_SOURCES += cb_ctl_dhcp6_unittest.cc
+libdhcpsrv_unittests_SOURCES += cb_ctl_dhcp_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_db_access_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_duid_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_expiration_unittest.cc
diff --git a/src/lib/dhcpsrv/tests/cb_ctl_dhcp6_unittest.cc b/src/lib/dhcpsrv/tests/cb_ctl_dhcp6_unittest.cc
deleted file mode 100644 (file)
index 48f7956..0000000
+++ /dev/null
@@ -1,764 +0,0 @@
-// Copyright (C) 2019 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/io_address.h>
-#include <cc/stamped_value.h>
-#include <dhcp/option_string.h>
-#include <dhcpsrv/cb_ctl_dhcp6.h>
-#include <dhcpsrv/cfgmgr.h>
-#include <dhcpsrv/testutils/generic_backend_unittest.h>
-#include <dhcpsrv/testutils/test_config_backend_dhcp6.h>
-#include <boost/date_time/posix_time/posix_time.hpp>
-#include <gtest/gtest.h>
-#include <map>
-#include <string>
-
-using namespace isc::asiolink;
-using namespace isc::db;
-using namespace isc::data;
-using namespace isc::dhcp;
-using namespace isc::dhcp::test;
-using namespace isc::process;
-
-namespace {
-
-/// @brief Base class for testing derivations of the CBControlDHCP.
-class CBControlDHCPTest : public GenericBackendTest {
-public:
-
-    /// @brief Constructor.
-    CBControlDHCPTest()
-        : timestamp_(), object_timestamp_(), audit_entries_() {
-        CfgMgr::instance().clear();
-        CfgMgr::instance().setFamily(AF_INET6);
-        initTimestamps();
-    }
-
-    /// @brief Destructor.
-    virtual ~CBControlDHCPTest() {
-        // Unregister the factory to be tidy.
-        ConfigBackendDHCPv6Mgr::instance().unregisterBackendFactory("memfile");
-        CfgMgr::instance().clear();
-    }
-
-    /// @brief Creates new CREATE audit entry.
-    ///
-    /// The audit entry is added to the @c audit_entries_ collection.
-    ///
-    /// @param object_type Object type to be associated with the audit
-    /// entry.
-    void addCreateAuditEntry(const std::string& object_type) {
-        AuditEntryPtr entry(new AuditEntry(object_type, 1234,
-                                           AuditEntry::ModificationType::CREATE,
-                                           "some log message"));
-        audit_entries_.insert(entry);
-    }
-
-    /// @brief Creates new DELETE audit entry.
-    ///
-    /// The audit entry is added to the @c audit_entries_ collection.
-    ///
-    /// @param object_type Object type to be associated with the audit
-    /// entry.
-    /// @param object_id Identifier of the object to be associated with
-    /// the audit entry.
-    void addDeleteAuditEntry(const std::string& object_type,
-                             const uint64_t object_id) {
-        AuditEntryPtr entry(new AuditEntry(object_type, object_id,
-                                           AuditEntry::ModificationType::DELETE,
-                                           "some log message"));
-        audit_entries_.insert(entry);
-    }
-
-    /// @brief Initializes timestamps used in tests.
-    void initTimestamps() {
-        // Get the current timestamp and move it 30 seconds backwards.
-        auto now = boost::posix_time::second_clock::local_time() -
-            boost::posix_time::seconds(30);
-
-        // Initialize multiple timestamps from the base timestamp. The
-        // values with indexes [-5, 0] are in the past. The remaining
-        // four are in the future.
-        for (int i = -5; i < 5; ++i) {
-            timestamp_[i] = now + boost::posix_time::minutes(i);
-        }
-    }
-
-    /// @brief Returns timestamp associated with a given index.
-    ///
-    /// @param timestamp_index Index of the timestamp to be returned.
-    boost::posix_time::ptime getTimestamp(const int timestamp_index) {
-        return (timestamp_[timestamp_index]);
-    }
-
-    /// @brief Returns timestamp to be associated with a given object type.
-    ///
-    /// The object types correspond to the names of the SQL tables holding
-    /// them, e.g. dhcp6_global_parameter, dhcp6_subnet etc.
-    ///
-    /// @param object_type Object type.
-    boost::posix_time::ptime getTimestamp(const std::string& object_type) {
-        return (object_timestamp_[object_type]);
-    }
-
-    /// @brief Associates object type with a timestamp.
-    ///
-    /// When adding objects to the database, each one is associated with
-    /// a modification time value. This value is setup by unit tests
-    /// via this method.
-    void setTimestamp(const std::string& object_type, const int timestamp_index) {
-        object_timestamp_[object_type] = timestamp_[timestamp_index];
-    }
-
-    /// @brief Sets timestamps for various object types to the same value.
-    ///
-    /// @param timestamp_index Index of the timestamp to be set.
-    virtual void setAllTimestamps(const int timestamp_index) = 0;
-
-    /// @brief Checks if @c databaseConfigApply should fetch updates for specified
-    /// object types.
-    ///
-    /// @param object_type Object type.
-    bool fetchConfigElement(const std::string& object_type) const {
-        if (!audit_entries_.empty()) {
-            const auto& index = audit_entries_.get<AuditEntryObjectTypeTag>();
-            auto range = index.equal_range(object_type);
-            for (auto it = range.first; it != range.second; ++it) {
-                if (((*it)->getModificationType() != AuditEntry::ModificationType::DELETE)) {
-                    return (true);
-                }
-            }
-            return (false);
-        }
-
-        return (true);
-    }
-
-    /// @brief Check if @c databaseConfigApply should delete a given object from the
-    /// local configuration.
-    ///
-    /// @param object_type Object type.
-    /// @param object_id Object identifier.
-    bool deleteConfigElement(const std::string& object_type,
-                             const uint64_t object_id) const {
-        if (!audit_entries_.empty()) {
-            const auto& index = audit_entries_.get<AuditEntryObjectTypeTag>();
-            auto range = index.equal_range(boost::make_tuple(object_type,
-                                                             AuditEntry::ModificationType::DELETE));
-            for (auto it = range.first; it != range.second; ++it) {
-                if ((*it)->getObjectId() == object_id) {
-                    return (true);
-                }
-            }
-        }
-        return (false);
-    }
-
-    /// @brief Holds test timestamps.
-    std::map<int, boost::posix_time::ptime> timestamp_;
-
-    /// @brief Holds mapping of the objects types to their timestamps.
-    std::map<std::string, boost::posix_time::ptime> object_timestamp_;
-
-    /// @brief Collection of audit entries used in the unit tests.
-    AuditEntryCollection audit_entries_;
-};
-
-
-/// @brief Naked @c CBControlDHCPv6 class exposing protected methods.
-class TestCBControlDHCPv6 : public CBControlDHCPv6 {
-public:
-    using CBControlDHCPv6::getInitialAuditEntryTime;
-    using CBControlDHCPv6::databaseConfigApply;
-};
-
-/// @brief Test fixture class for @c CBControlDHCPv6 unit tests.
-class CBControlDHCPv6Test : public CBControlDHCPTest {
-public:
-
-    /// @brief Constructor.
-    CBControlDHCPv6Test()
-        : CBControlDHCPTest(), ctl_() {
-        ConfigBackendDHCPv6Mgr::instance().registerBackendFactory("memfile",
-            [](const DatabaseConnection::ParameterMap& params)
-                -> ConfigBackendDHCPv6Ptr {
-                    return (TestConfigBackendDHCPv6Ptr(new TestConfigBackendDHCPv6(params)));
-             });
-        ConfigBackendDHCPv6Mgr::instance().addBackend("type=memfile");
-
-        // By default, set timestamps for all object types to -4. That leaves
-        // us with the possibility to use index -5 (earlier) to use as lower
-        // bound modification time so as all objects are fetched.
-        setAllTimestamps(-4);
-    }
-
-    /// @brief Sets timestamps of all DHCPv6 specific object types.
-    ///
-    /// @param timestamp_index Index of the timestamp to be set.
-    virtual void setAllTimestamps(const int timestamp_index) {
-        setTimestamp("dhcp6_global_parameter", timestamp_index);
-        setTimestamp("dhcp6_option_def", timestamp_index);
-        setTimestamp("dhcp6_options", timestamp_index);
-        setTimestamp("dhcp6_shared_network", timestamp_index);
-        setTimestamp("dhcp6_subnet", timestamp_index);
-    }
-
-    /// @brief Creates test server configuration and stores it in a test
-    /// configuration backend.
-    ///
-    /// There are pairs of configuration elements stored in the database.
-    /// For example: two global parameters, two option definitions etc.
-    /// Having two elements of each type in the database is useful in tests
-    /// which verify that an element is deleted from the local configuration
-    /// as a result of being deleted from the configuration backend. In that
-    /// case the test verifies that one of the elements of the given type
-    /// is deleted and one is left.
-    void remoteStoreTestConfiguration() {
-        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-
-        // Insert global parameters into a database.
-        StampedValuePtr global_parameter = StampedValue::create("foo", "bar");
-        global_parameter->setModificationTime(getTimestamp("dhcp6_global_parameter"));
-        ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(BackendSelector::UNSPEC(),
-                                                                    ServerSelector::ALL(),
-                                                                    global_parameter));
-
-        global_parameter = StampedValue::create("bar", "teta");
-        global_parameter->setModificationTime(getTimestamp("dhcp6_global_parameter"));
-        ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(BackendSelector::UNSPEC(),
-                                                                    ServerSelector::ALL(),
-                                                                    global_parameter));
-
-        // Insert option definitions into the database.
-        OptionDefinitionPtr def(new OptionDefinition("one", 101, "uint16"));
-        def->setId(1);
-        def->setOptionSpaceName("isc");
-        def->setModificationTime(getTimestamp("dhcp6_option_def"));
-        ASSERT_NO_THROW(mgr.getPool()->createUpdateOptionDef6(BackendSelector::UNSPEC(),
-                                                              ServerSelector::ALL(),
-                                                              def));
-        def.reset(new OptionDefinition("two", 102, "uint16"));
-        def->setId(2);
-        def->setOptionSpaceName("isc");
-        def->setModificationTime(getTimestamp("dhcp6_option_def"));
-        ASSERT_NO_THROW(mgr.getPool()->createUpdateOptionDef6(BackendSelector::UNSPEC(),
-                                                              ServerSelector::ALL(),
-                                                              def));
-
-        // Insert global options into the database.
-        OptionDescriptorPtr opt(new OptionDescriptor(createOption<OptionString>
-                                                     (Option::V6, D6O_BOOTFILE_URL,
-                                                      true, false, "some.bootfile")));
-        opt->setId(1);
-        opt->space_name_ = DHCP6_OPTION_SPACE;
-        opt->setModificationTime(getTimestamp("dhcp6_options"));
-        mgr.getPool()->createUpdateOption6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                           opt);
-
-        opt.reset(new OptionDescriptor(createOption<OptionString>
-                                       (Option::V6, D6O_AFTR_NAME,
-                                        true, true, "some.example.com")));
-        opt->setId(2);
-        opt->space_name_ = DHCP6_OPTION_SPACE;
-        opt->setModificationTime(getTimestamp("dhcp6_options"));
-        mgr.getPool()->createUpdateOption6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                           opt);
-
-        // Insert shared networks into the database.
-        SharedNetwork6Ptr network(new SharedNetwork6("one"));
-        network->setId(1);
-        network->setModificationTime(getTimestamp("dhcp6_shared_network"));
-        mgr.getPool()->createUpdateSharedNetwork6(BackendSelector::UNSPEC(),
-                                                  ServerSelector::ALL(),
-                                                  network);
-
-        network.reset(new SharedNetwork6("two"));
-        network->setId(2);
-        network->setModificationTime(getTimestamp("dhcp6_shared_network"));
-        mgr.getPool()->createUpdateSharedNetwork6(BackendSelector::UNSPEC(),
-                                                  ServerSelector::ALL(),
-                                                  network);
-
-        // Insert subnets into the database.
-        Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4, SubnetID(1)));
-        subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
-        subnet->setSharedNetworkName("one");
-        mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                           subnet);
-
-        subnet.reset(new Subnet6(IOAddress("2001:db8:2::"), 64, 1, 2, 3, 4, SubnetID(2)));
-        subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
-        mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                           subnet);
-    }
-
-    /// @brief Deletes specified global parameter from the configuration
-    /// backend and generates audit entry.
-    ///
-    /// Note that the current Kea implementation does not track database
-    /// identifiers of the global parameters. Therefore, the identifier to
-    /// be used to create the audit entry for the deleted parameter must
-    /// be explicitly specified.
-    ///
-    /// @param parameter_name Parameter name.
-    /// @param id Parameter id.
-    void remoteDeleteGlobalParameter(const std::string& parameter_name,
-                                     const uint64_t id) {
-        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-        mgr.getPool()->deleteGlobalParameter6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                             parameter_name);
-        addDeleteAuditEntry("dhcp6_global_parameter", id);
-    }
-
-    /// @brief Deletes specified option definition from the configuration
-    /// backend and generates audit entry.
-    ///
-    /// @param code Option code.
-    /// @param space Option space.
-    void remoteDeleteOptionDef(const uint16_t code, const std::string& space) {
-        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-
-        auto option_def = mgr.getPool()->getOptionDef6(BackendSelector::UNSPEC(),
-                                                       ServerSelector::ALL(),
-                                                       code, space);
-
-        if (option_def) {
-            mgr.getPool()->deleteOptionDef6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                            code, space);
-            addDeleteAuditEntry("dhcp6_option_def", option_def->getId());
-        }
-    }
-
-    /// @brief Deletes specified global option from the configuration backend
-    /// and generates audit entry.
-    ///
-    /// @param code Option code.
-    /// @param space Option space.
-    void remoteDeleteOption(const uint16_t code, const std::string& space) {
-        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-
-        auto option = mgr.getPool()->getOption6(BackendSelector::UNSPEC(),
-                                                ServerSelector::ALL(),
-                                                code, space);
-
-        if (option) {
-            mgr.getPool()->deleteOptionDef6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                            code, space);
-            addDeleteAuditEntry("dhcp6_option_def", option->getId());
-        }
-    }
-
-    /// @brief Deletes specified shared network from the configuration backend
-    /// and generates audit entry.
-    ///
-    /// @param name Name of the shared network to be deleted.
-    void remoteDeleteSharedNetwork(const std::string& name) {
-        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-
-        auto network = mgr.getPool()->getSharedNetwork6(BackendSelector::UNSPEC(),
-                                                        ServerSelector::ALL(),
-                                                        name);
-
-        if (network) {
-            mgr.getPool()->deleteSharedNetwork6(BackendSelector::UNSPEC(),
-                                                ServerSelector::ALL(),
-                                                name);
-            addDeleteAuditEntry("dhcp6_shared_network", network->getId());
-        }
-    }
-
-    /// @brief Deletes specified subnet from the configuration backend and
-    /// generates audit entry.
-    void remoteDeleteSubnet(const SubnetID& id) {
-        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-
-        mgr.getPool()->deleteSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                     id);
-        addDeleteAuditEntry("dhcp6_subnet", id);
-    }
-
-
-    /// @brief Tests the @c CBControlDHCPv6::databaseConfigApply method.
-    ///
-    /// This test inserts configuration elements of each type into the
-    /// configuration database. Next, it calls the @c databaseConfigApply,
-    /// which should merge each object from the database for which the
-    /// CREATE or UPDATE audit entry is found. The test then verifies
-    /// if the appropriate entries have been merged.
-    ///
-    /// @param lb_modification_time Lower bound modification time to be
-    /// passed to the @c databaseConfigApply.
-    void testDatabaseConfigApply(const boost::posix_time::ptime& lb_modification_time) {
-        remoteStoreTestConfiguration();
-
-        ASSERT_FALSE(audit_entries_.empty())
-            << "Require at least one audit entry. The test is broken!";
-
-        ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                 lb_modification_time, audit_entries_);
-
-        // The updates should have been merged into current configuration.
-        auto srv_cfg = CfgMgr::instance().getCurrentCfg();
-
-        // If there is an audit entry for global parameter and the parameter
-        // modification time is later than last audit entry time it should
-        // be merged.
-        if (fetchConfigElement("dhcp6_global_parameter") &&
-            (getTimestamp("dhcp6_global_parameter") > lb_modification_time)) {
-            checkConfiguredGlobal(srv_cfg, "foo", Element::create("bar"));
-
-        } else {
-            // Otherwise it shouldn't exist.
-            EXPECT_FALSE(srv_cfg->getConfiguredGlobals()->get("foo"));
-        }
-
-        // If there is an audit entry for option definition and the definition
-        // modification time is later than last audit entry time it should
-        // be merged.
-        auto found_def = srv_cfg->getCfgOptionDef()->get("isc", "one");
-        if (fetchConfigElement("dhcp6_option_def") &&
-            getTimestamp("dhcp6_option_def") > lb_modification_time) {
-            ASSERT_TRUE(found_def);
-            EXPECT_EQ(101, found_def->getCode());
-            EXPECT_EQ(OptionDataType::OPT_UINT16_TYPE, found_def->getType());
-
-        } else {
-            EXPECT_FALSE(found_def);
-        }
-
-        // If there is an audit entry for an option and the option
-        // modification time is later than last audit entry time it should
-        // be merged.
-        auto options = srv_cfg->getCfgOption();
-        auto found_opt = options->get("dhcp6", D6O_BOOTFILE_URL);
-        if (fetchConfigElement("dhcp6_options") &&
-            (getTimestamp("dhcp6_options") > lb_modification_time)) {
-            ASSERT_TRUE(found_opt.option_);
-            EXPECT_EQ("some.bootfile", found_opt.option_->toString());
-
-        } else {
-            EXPECT_FALSE(found_opt.option_);
-        }
-
-        // If there is an audit entry for a shared network and the network
-        // modification time is later than last audit entry time it should
-        // be merged.
-        auto networks = srv_cfg->getCfgSharedNetworks6();
-        auto found_network = networks->getByName("one");
-        if (fetchConfigElement("dhcp6_shared_network") &&
-            (getTimestamp("dhcp6_shared_network") > lb_modification_time)) {
-            ASSERT_TRUE(found_network);
-            EXPECT_TRUE(found_network->hasFetchGlobalsFn());
-
-        } else {
-            EXPECT_FALSE(found_network);
-        }
-
-        // If there is an audit entry for a subnet and the subnet modification
-        // time is later than last audit entry time it should be merged.
-        auto subnets = srv_cfg->getCfgSubnets6();
-        auto found_subnet = subnets->getSubnet(1);
-        if (fetchConfigElement("dhcp6_subnet") &&
-            (getTimestamp("dhcp6_subnet") > lb_modification_time)) {
-            ASSERT_TRUE(found_subnet);
-            EXPECT_TRUE(found_subnet->hasFetchGlobalsFn());
-
-        } else {
-            EXPECT_FALSE(found_subnet);
-        }
-    }
-
-    /// @brief Tests deletion of the configuration elements by the
-    /// @c CBControlDHCPv6::databaseConfigApply method.
-    ///
-    /// This test inserts configuration elements of each type into the
-    /// configuration database and calls the @c databaseConfigApply
-    /// to fetch this configuration and merge into the local server
-    /// configuration.
-    ///
-    /// Next, the test calls the specified callback function, i.e.
-    /// @c db_modifications, which deletes selected configuration
-    /// elements from the database and generates appropriate audit
-    /// entries. Finally, it calls the @c databaseConfigApply again
-    /// to process the audit entries and checks if the appropriate
-    /// configuration elements are deleted from the local configuration
-    ///
-    /// @param lb_modification_time Lower bound modification time to be
-    /// passed to the @c databaseConfigApply.
-    /// @param db_modifications Pointer to the callback function which
-    /// applies test specific modifications into the database.
-    void testDatabaseConfigApplyDelete(const boost::posix_time::ptime& lb_modification_time,
-                                       std::function<void()> db_modifications) {
-        // Store initial configuration into the database.
-        remoteStoreTestConfiguration();
-
-        // Since we pass an empty audit collection the server treats this
-        // as if the server is starting up and fetches the entire
-        // configuration from the database.
-        ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                 ctl_.getInitialAuditEntryTime(),
-                                 AuditEntryCollection());
-        // Commit the configuration so as it is moved from the staging
-        // to current.
-        CfgMgr::instance().commit();
-
-        // Run user defined callback which should delete selected configuration
-        // elements from the configuration backend. The appropriate DELETE
-        // audit entries should now be stored in the audit_entries_ collection.
-        if (db_modifications) {
-            db_modifications();
-        }
-
-        // Process the DELETE audit entries.
-        ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
-                                 lb_modification_time, audit_entries_);
-
-        // All changes should have been applied in the current configuration.
-        auto srv_cfg = CfgMgr::instance().getCurrentCfg();
-
-        {
-            SCOPED_TRACE("global parameters");
-            // One of the global parameters should still be there.
-            EXPECT_TRUE(srv_cfg->getConfiguredGlobals()->get("bar"));
-            if (deleteConfigElement("dhcp6_global_parameter", 1)) {
-                EXPECT_FALSE(srv_cfg->getConfiguredGlobals()->get("foo"));
-
-            } else {
-                EXPECT_TRUE(srv_cfg->getConfiguredGlobals()->get("foo"));
-            }
-        }
-
-        {
-            SCOPED_TRACE("option definitions");
-            // One of the option definitions should still be there.
-            EXPECT_TRUE(srv_cfg->getCfgOptionDef()->get("isc", "two"));
-            auto found_def = srv_cfg->getCfgOptionDef()->get("isc", "one");
-            if (deleteConfigElement("dhcp6_option_def", 1)) {
-                EXPECT_FALSE(found_def);
-
-            } else {
-                EXPECT_TRUE(found_def);
-            }
-        }
-
-        {
-            SCOPED_TRACE("global options");
-            // One of the options should still be there.
-            EXPECT_TRUE(srv_cfg->getCfgOption()->get("dhcp6", D6O_AFTR_NAME).option_);
-            auto found_opt = srv_cfg->getCfgOption()->get("dhcp6", D6O_AFTR_NAME);
-            if (deleteConfigElement("dhcp6_options", 1)) {
-                EXPECT_FALSE(found_opt.option_);
-
-            } else {
-                EXPECT_TRUE(found_opt.option_);
-            }
-        }
-
-        {
-            SCOPED_TRACE("shared networks");
-            // One of the shared networks should still be there.
-            EXPECT_TRUE(srv_cfg->getCfgSharedNetworks6()->getByName("two"));
-            auto found_network = srv_cfg->getCfgSharedNetworks6()->getByName("one");
-            if (deleteConfigElement("dhcp6_shared_network", 1)) {
-                EXPECT_FALSE(found_network);
-
-            } else {
-                EXPECT_TRUE(found_network);
-            }
-        }
-
-        {
-            SCOPED_TRACE("subnets");
-            // One of the subnets should still be there.
-            EXPECT_TRUE(srv_cfg->getCfgSubnets6()->getSubnet(2));
-            auto found_subnet = srv_cfg->getCfgSubnets6()->getSubnet(1);
-            if (deleteConfigElement("dhcp6_subnet", 1)) {
-                EXPECT_FALSE(found_subnet);
-                // If the subnet has been deleted, make sure that
-                // it was detached from the shared network it belonged
-                // to, if the shared network still exists.
-                auto found_network = srv_cfg->getCfgSharedNetworks6()->getByName("one");
-                if (found_network) {
-                    EXPECT_TRUE(found_network->getAllSubnets()->empty());
-                }
-
-            } else {
-                EXPECT_TRUE(found_subnet);
-            }
-        }
-    }
-
-    /// @brief Instance of the @c CBControlDHCPv6 used for testing.
-    TestCBControlDHCPv6 ctl_;
-};
-
-
-// This test verifies that the configuration updates for all object
-// types are merged into the current configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyAll) {
-
-    addCreateAuditEntry("dhcp6_global_parameter");
-    addCreateAuditEntry("dhcp6_option_def");
-    addCreateAuditEntry("dhcp6_options");
-    addCreateAuditEntry("dhcp6_shared_network");
-    addCreateAuditEntry("dhcp6_subnet");
-
-    testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that multiple configuration elements are
-// deleted from the local configuration as a result of being
-// deleted from the database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteAll) {
-    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
-        remoteDeleteGlobalParameter("foo", 1);
-        remoteDeleteOptionDef(101, "isc");
-        remoteDeleteOption(D6O_BOOTFILE_URL, DHCP6_OPTION_SPACE);
-        remoteDeleteSharedNetwork("one");
-        remoteDeleteSubnet(SubnetID(1));
-    });
-}
-
-// This test verifies that an attempt to delete non-existing
-// configuration element does not cause an error.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteNonExisting) {
-    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
-        // Add several audit entries instructing to delete the
-        // non-existing configuration elements. The ids are set
-        // to 3, but the only existing elements have ids of 1
-        // and 2.
-        addDeleteAuditEntry("dhcp6_global_parameter", 3);
-        addDeleteAuditEntry("dhcp6_option_def", 3);
-        addDeleteAuditEntry("dhcp6_options", 3);
-        addDeleteAuditEntry("dhcp6_shared_network", 3);
-        addDeleteAuditEntry("dhcp6_subnet", 3);
-    });
-}
-
-// This test verifies that only a global parameter is merged into
-// the current configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyGlobal) {
-    addCreateAuditEntry("dhcp6_global_parameter");
-    testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that the global parameter is deleted from
-// the local configuration as a result of being deleted from the
-// database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteGlobal) {
-    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
-        remoteDeleteGlobalParameter("foo", 1);
-    });
-}
-
-// This test verifies that global parameter is not fetched from the
-// database when the modification time is earlier than the last
-// fetched audit entry.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyGlobalNotFetched) {
-    addCreateAuditEntry("dhcp6_global_parameter");
-    testDatabaseConfigApply(getTimestamp(-3));
-}
-
-// This test verifies that only an option definition is merged into
-// the current configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionDef) {
-    addCreateAuditEntry("dhcp6_option_def");
-    testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that the option definition is deleted from
-// the local configuration as a result of being deleted from the
-// database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteOptionDef) {
-    addDeleteAuditEntry("dhcp6_option_def", 1);
-    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
-        remoteDeleteOptionDef(101, "isc");
-    });
-}
-
-// This test verifies that option definition is not fetched from the
-// database when the modification time is earlier than the last
-// fetched audit entry.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionDefNotFetched) {
-    addCreateAuditEntry("dhcp6_option_def");
-    testDatabaseConfigApply(getTimestamp(-3));
-}
-
-// This test verifies that only a DHCPv6 option is merged into the
-// current configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyOption) {
-    addCreateAuditEntry("dhcp6_options");
-    testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that the global option is deleted from
-// the local configuration as a result of being deleted from the
-// database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteOption) {
-    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
-        remoteDeleteOption(D6O_BOOTFILE_URL, DHCP6_OPTION_SPACE);
-    });
-}
-
-// This test verifies that DHCPv6 option is not fetched from the
-// database when the modification time is earlier than the last
-// fetched audit entry.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionNotFetched) {
-    addCreateAuditEntry("dhcp6_options");
-    testDatabaseConfigApply(getTimestamp(-3));
-}
-
-// This test verifies that only a shared network is merged into the
-// current configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplySharedNetwork) {
-    addCreateAuditEntry("dhcp6_shared_network");
-    testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that the shared network is deleted from
-// the local configuration as a result of being deleted from the
-// database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteSharedNetwork) {
-    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
-        remoteDeleteSharedNetwork("one");
-    });
-}
-
-// This test verifies that shared network is not fetched from the
-// database when the modification time is earlier than the last
-// fetched audit entry.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplySharedNetworkNotFetched) {
-    addCreateAuditEntry("dhcp6_shared_network");
-    testDatabaseConfigApply(getTimestamp(-3));
-}
-
-// This test verifies that only a subnet is merged into the current
-// configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplySubnet) {
-    addCreateAuditEntry("dhcp6_shared_network");
-    addCreateAuditEntry("dhcp6_subnet");
-    testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that the subnet is deleted from the local
-// configuration as a result of being deleted from the database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteSubnet) {
-    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
-        remoteDeleteSubnet(SubnetID(1));
-    });
-}
-
-// This test verifies that subnet is not fetched from the database
-// when the modification time is earlier than the last fetched audit
-// entry.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplySubnetNotFetched) {
-    addCreateAuditEntry("dhcp6_subnet");
-    testDatabaseConfigApply(getTimestamp(-3));
-}
-
-}
similarity index 55%
rename from src/lib/dhcpsrv/tests/cb_ctl_dhcp4_unittest.cc
rename to src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc
index 7dad1b7f3527220a590c7fb578cd076fe781bb04..ff0f8ef362178755b2494b72faafb2b2fa704569 100644 (file)
 #include <cc/stamped_value.h>
 #include <dhcp/option_string.h>
 #include <dhcpsrv/cb_ctl_dhcp4.h>
+#include <dhcpsrv/cb_ctl_dhcp6.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/testutils/generic_backend_unittest.h>
 #include <dhcpsrv/testutils/test_config_backend_dhcp4.h>
+#include <dhcpsrv/testutils/test_config_backend_dhcp6.h>
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <gtest/gtest.h>
 #include <map>
@@ -168,6 +170,8 @@ public:
     AuditEntryCollection audit_entries_;
 };
 
+// ************************ V4 tests *********************
+
 /// @brief Naked @c CBControlDHCPv4 class exposing protected methods.
 class TestCBControlDHCPv4 : public CBControlDHCPv4 {
 public:
@@ -760,4 +764,602 @@ TEST_F(CBControlDHCPv4Test, databaseConfigApplySubnetNotFetched) {
     testDatabaseConfigApply(getTimestamp(-3));
 }
 
+// ************************ V6 tests *********************
+
+/// @brief Naked @c CBControlDHCPv6 class exposing protected methods.
+class TestCBControlDHCPv6 : public CBControlDHCPv6 {
+public:
+    /// @brief Constructor.
+    TestCBControlDHCPv6() {
+        CfgMgr::instance().setFamily(AF_INET6);
+    }
+
+    using CBControlDHCPv6::getInitialAuditEntryTime;
+    using CBControlDHCPv6::databaseConfigApply;
+};
+
+/// @brief Test fixture class for @c CBControlDHCPv6 unit tests.
+class CBControlDHCPv6Test : public CBControlDHCPTest {
+public:
+
+    /// @brief Constructor.
+    CBControlDHCPv6Test()
+        : CBControlDHCPTest(), ctl_() {
+        ConfigBackendDHCPv6Mgr::instance().registerBackendFactory("memfile",
+            [](const DatabaseConnection::ParameterMap& params)
+                -> ConfigBackendDHCPv6Ptr {
+                    return (TestConfigBackendDHCPv6Ptr(new TestConfigBackendDHCPv6(params)));
+             });
+        ConfigBackendDHCPv6Mgr::instance().addBackend("type=memfile");
+
+        // By default, set timestamps for all object types to -4. That leaves
+        // us with the possibility to use index -5 (earlier) to use as lower
+        // bound modification time so as all objects are fetched.
+        setAllTimestamps(-4);
+    }
+
+    /// @brief Sets timestamps of all DHCPv6 specific object types.
+    ///
+    /// @param timestamp_index Index of the timestamp to be set.
+    virtual void setAllTimestamps(const int timestamp_index) {
+        setTimestamp("dhcp6_global_parameter", timestamp_index);
+        setTimestamp("dhcp6_option_def", timestamp_index);
+        setTimestamp("dhcp6_options", timestamp_index);
+        setTimestamp("dhcp6_shared_network", timestamp_index);
+        setTimestamp("dhcp6_subnet", timestamp_index);
+    }
+
+    /// @brief Creates test server configuration and stores it in a test
+    /// configuration backend.
+    ///
+    /// There are pairs of configuration elements stored in the database.
+    /// For example: two global parameters, two option definitions etc.
+    /// Having two elements of each type in the database is useful in tests
+    /// which verify that an element is deleted from the local configuration
+    /// as a result of being deleted from the configuration backend. In that
+    /// case the test verifies that one of the elements of the given type
+    /// is deleted and one is left.
+    void remoteStoreTestConfiguration() {
+        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+        // Insert global parameters into a database.
+        StampedValuePtr global_parameter = StampedValue::create("foo", "bar");
+        global_parameter->setModificationTime(getTimestamp("dhcp6_global_parameter"));
+        ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(BackendSelector::UNSPEC(),
+                                                                    ServerSelector::ALL(),
+                                                                    global_parameter));
+
+        global_parameter = StampedValue::create("bar", "teta");
+        global_parameter->setModificationTime(getTimestamp("dhcp6_global_parameter"));
+        ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(BackendSelector::UNSPEC(),
+                                                                    ServerSelector::ALL(),
+                                                                    global_parameter));
+
+        // Insert option definitions into the database.
+        OptionDefinitionPtr def(new OptionDefinition("one", 101, "uint16"));
+        def->setId(1);
+        def->setOptionSpaceName("isc");
+        def->setModificationTime(getTimestamp("dhcp6_option_def"));
+        ASSERT_NO_THROW(mgr.getPool()->createUpdateOptionDef6(BackendSelector::UNSPEC(),
+                                                              ServerSelector::ALL(),
+                                                              def));
+        def.reset(new OptionDefinition("two", 102, "uint16"));
+        def->setId(2);
+        def->setOptionSpaceName("isc");
+        def->setModificationTime(getTimestamp("dhcp6_option_def"));
+        ASSERT_NO_THROW(mgr.getPool()->createUpdateOptionDef6(BackendSelector::UNSPEC(),
+                                                              ServerSelector::ALL(),
+                                                              def));
+
+        // Insert global options into the database.
+        OptionDescriptorPtr opt(new OptionDescriptor(createOption<OptionString>
+                                                     (Option::V6, D6O_BOOTFILE_URL,
+                                                      true, false, "some.bootfile")));
+        opt->setId(1);
+        opt->space_name_ = DHCP6_OPTION_SPACE;
+        opt->setModificationTime(getTimestamp("dhcp6_options"));
+        mgr.getPool()->createUpdateOption6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                           opt);
+
+        opt.reset(new OptionDescriptor(createOption<OptionString>
+                                       (Option::V6, D6O_AFTR_NAME,
+                                        true, true, "some.example.com")));
+        opt->setId(2);
+        opt->space_name_ = DHCP6_OPTION_SPACE;
+        opt->setModificationTime(getTimestamp("dhcp6_options"));
+        mgr.getPool()->createUpdateOption6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                           opt);
+
+        // Insert shared networks into the database.
+        SharedNetwork6Ptr network(new SharedNetwork6("one"));
+        network->setId(1);
+        network->setModificationTime(getTimestamp("dhcp6_shared_network"));
+        mgr.getPool()->createUpdateSharedNetwork6(BackendSelector::UNSPEC(),
+                                                  ServerSelector::ALL(),
+                                                  network);
+
+        network.reset(new SharedNetwork6("two"));
+        network->setId(2);
+        network->setModificationTime(getTimestamp("dhcp6_shared_network"));
+        mgr.getPool()->createUpdateSharedNetwork6(BackendSelector::UNSPEC(),
+                                                  ServerSelector::ALL(),
+                                                  network);
+
+        // Insert subnets into the database.
+        Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4, SubnetID(1)));
+        subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
+        subnet->setSharedNetworkName("one");
+        mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                           subnet);
+
+        subnet.reset(new Subnet6(IOAddress("2001:db8:2::"), 64, 1, 2, 3, 4, SubnetID(2)));
+        subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
+        mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                           subnet);
+    }
+
+    /// @brief Deletes specified global parameter from the configuration
+    /// backend and generates audit entry.
+    ///
+    /// Note that the current Kea implementation does not track database
+    /// identifiers of the global parameters. Therefore, the identifier to
+    /// be used to create the audit entry for the deleted parameter must
+    /// be explicitly specified.
+    ///
+    /// @param parameter_name Parameter name.
+    /// @param id Parameter id.
+    void remoteDeleteGlobalParameter(const std::string& parameter_name,
+                                     const uint64_t id) {
+        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+        mgr.getPool()->deleteGlobalParameter6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                             parameter_name);
+        addDeleteAuditEntry("dhcp6_global_parameter", id);
+    }
+
+    /// @brief Deletes specified option definition from the configuration
+    /// backend and generates audit entry.
+    ///
+    /// @param code Option code.
+    /// @param space Option space.
+    void remoteDeleteOptionDef(const uint16_t code, const std::string& space) {
+        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+        auto option_def = mgr.getPool()->getOptionDef6(BackendSelector::UNSPEC(),
+                                                       ServerSelector::ALL(),
+                                                       code, space);
+
+        if (option_def) {
+            mgr.getPool()->deleteOptionDef6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                            code, space);
+            addDeleteAuditEntry("dhcp6_option_def", option_def->getId());
+        }
+    }
+
+    /// @brief Deletes specified global option from the configuration backend
+    /// and generates audit entry.
+    ///
+    /// @param code Option code.
+    /// @param space Option space.
+    void remoteDeleteOption(const uint16_t code, const std::string& space) {
+        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+        auto option = mgr.getPool()->getOption6(BackendSelector::UNSPEC(),
+                                                ServerSelector::ALL(),
+                                                code, space);
+
+        if (option) {
+            mgr.getPool()->deleteOptionDef6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                            code, space);
+            addDeleteAuditEntry("dhcp6_option_def", option->getId());
+        }
+    }
+
+    /// @brief Deletes specified shared network from the configuration backend
+    /// and generates audit entry.
+    ///
+    /// @param name Name of the shared network to be deleted.
+    void remoteDeleteSharedNetwork(const std::string& name) {
+        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+        auto network = mgr.getPool()->getSharedNetwork6(BackendSelector::UNSPEC(),
+                                                        ServerSelector::ALL(),
+                                                        name);
+
+        if (network) {
+            mgr.getPool()->deleteSharedNetwork6(BackendSelector::UNSPEC(),
+                                                ServerSelector::ALL(),
+                                                name);
+            addDeleteAuditEntry("dhcp6_shared_network", network->getId());
+        }
+    }
+
+    /// @brief Deletes specified subnet from the configuration backend and
+    /// generates audit entry.
+    void remoteDeleteSubnet(const SubnetID& id) {
+        auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+        mgr.getPool()->deleteSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                     id);
+        addDeleteAuditEntry("dhcp6_subnet", id);
+    }
+
+
+    /// @brief Tests the @c CBControlDHCPv6::databaseConfigApply method.
+    ///
+    /// This test inserts configuration elements of each type into the
+    /// configuration database. Next, it calls the @c databaseConfigApply,
+    /// which should merge each object from the database for which the
+    /// CREATE or UPDATE audit entry is found. The test then verifies
+    /// if the appropriate entries have been merged.
+    ///
+    /// @param lb_modification_time Lower bound modification time to be
+    /// passed to the @c databaseConfigApply.
+    void testDatabaseConfigApply(const boost::posix_time::ptime& lb_modification_time) {
+        remoteStoreTestConfiguration();
+
+        ASSERT_FALSE(audit_entries_.empty())
+            << "Require at least one audit entry. The test is broken!";
+
+        ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                 lb_modification_time, audit_entries_);
+
+        // The updates should have been merged into current configuration.
+        auto srv_cfg = CfgMgr::instance().getCurrentCfg();
+
+        // If there is an audit entry for global parameter and the parameter
+        // modification time is later than last audit entry time it should
+        // be merged.
+        if (fetchConfigElement("dhcp6_global_parameter") &&
+            (getTimestamp("dhcp6_global_parameter") > lb_modification_time)) {
+            checkConfiguredGlobal(srv_cfg, "foo", Element::create("bar"));
+
+        } else {
+            // Otherwise it shouldn't exist.
+            EXPECT_FALSE(srv_cfg->getConfiguredGlobals()->get("foo"));
+        }
+
+        // If there is an audit entry for option definition and the definition
+        // modification time is later than last audit entry time it should
+        // be merged.
+        auto found_def = srv_cfg->getCfgOptionDef()->get("isc", "one");
+        if (fetchConfigElement("dhcp6_option_def") &&
+            getTimestamp("dhcp6_option_def") > lb_modification_time) {
+            ASSERT_TRUE(found_def);
+            EXPECT_EQ(101, found_def->getCode());
+            EXPECT_EQ(OptionDataType::OPT_UINT16_TYPE, found_def->getType());
+
+        } else {
+            EXPECT_FALSE(found_def);
+        }
+
+        // If there is an audit entry for an option and the option
+        // modification time is later than last audit entry time it should
+        // be merged.
+        auto options = srv_cfg->getCfgOption();
+        auto found_opt = options->get("dhcp6", D6O_BOOTFILE_URL);
+        if (fetchConfigElement("dhcp6_options") &&
+            (getTimestamp("dhcp6_options") > lb_modification_time)) {
+            ASSERT_TRUE(found_opt.option_);
+            EXPECT_EQ("some.bootfile", found_opt.option_->toString());
+
+        } else {
+            EXPECT_FALSE(found_opt.option_);
+        }
+
+        // If there is an audit entry for a shared network and the network
+        // modification time is later than last audit entry time it should
+        // be merged.
+        auto networks = srv_cfg->getCfgSharedNetworks6();
+        auto found_network = networks->getByName("one");
+        if (fetchConfigElement("dhcp6_shared_network") &&
+            (getTimestamp("dhcp6_shared_network") > lb_modification_time)) {
+            ASSERT_TRUE(found_network);
+            EXPECT_TRUE(found_network->hasFetchGlobalsFn());
+
+        } else {
+            EXPECT_FALSE(found_network);
+        }
+
+        // If there is an audit entry for a subnet and the subnet modification
+        // time is later than last audit entry time it should be merged.
+        auto subnets = srv_cfg->getCfgSubnets6();
+        auto found_subnet = subnets->getSubnet(1);
+        if (fetchConfigElement("dhcp6_subnet") &&
+            (getTimestamp("dhcp6_subnet") > lb_modification_time)) {
+            ASSERT_TRUE(found_subnet);
+            EXPECT_TRUE(found_subnet->hasFetchGlobalsFn());
+
+        } else {
+            EXPECT_FALSE(found_subnet);
+        }
+    }
+
+    /// @brief Tests deletion of the configuration elements by the
+    /// @c CBControlDHCPv6::databaseConfigApply method.
+    ///
+    /// This test inserts configuration elements of each type into the
+    /// configuration database and calls the @c databaseConfigApply
+    /// to fetch this configuration and merge into the local server
+    /// configuration.
+    ///
+    /// Next, the test calls the specified callback function, i.e.
+    /// @c db_modifications, which deletes selected configuration
+    /// elements from the database and generates appropriate audit
+    /// entries. Finally, it calls the @c databaseConfigApply again
+    /// to process the audit entries and checks if the appropriate
+    /// configuration elements are deleted from the local configuration
+    ///
+    /// @param lb_modification_time Lower bound modification time to be
+    /// passed to the @c databaseConfigApply.
+    /// @param db_modifications Pointer to the callback function which
+    /// applies test specific modifications into the database.
+    void testDatabaseConfigApplyDelete(const boost::posix_time::ptime& lb_modification_time,
+                                       std::function<void()> db_modifications) {
+        // Store initial configuration into the database.
+        remoteStoreTestConfiguration();
+
+        // Since we pass an empty audit collection the server treats this
+        // as if the server is starting up and fetches the entire
+        // configuration from the database.
+        ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                 ctl_.getInitialAuditEntryTime(),
+                                 AuditEntryCollection());
+        // Commit the configuration so as it is moved from the staging
+        // to current.
+        CfgMgr::instance().commit();
+
+        // Run user defined callback which should delete selected configuration
+        // elements from the configuration backend. The appropriate DELETE
+        // audit entries should now be stored in the audit_entries_ collection.
+        if (db_modifications) {
+            db_modifications();
+        }
+
+        // Process the DELETE audit entries.
+        ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+                                 lb_modification_time, audit_entries_);
+
+        // All changes should have been applied in the current configuration.
+        auto srv_cfg = CfgMgr::instance().getCurrentCfg();
+
+        {
+            SCOPED_TRACE("global parameters");
+            // One of the global parameters should still be there.
+            EXPECT_TRUE(srv_cfg->getConfiguredGlobals()->get("bar"));
+            if (deleteConfigElement("dhcp6_global_parameter", 1)) {
+                EXPECT_FALSE(srv_cfg->getConfiguredGlobals()->get("foo"));
+
+            } else {
+                EXPECT_TRUE(srv_cfg->getConfiguredGlobals()->get("foo"));
+            }
+        }
+
+        {
+            SCOPED_TRACE("option definitions");
+            // One of the option definitions should still be there.
+            EXPECT_TRUE(srv_cfg->getCfgOptionDef()->get("isc", "two"));
+            auto found_def = srv_cfg->getCfgOptionDef()->get("isc", "one");
+            if (deleteConfigElement("dhcp6_option_def", 1)) {
+                EXPECT_FALSE(found_def);
+
+            } else {
+                EXPECT_TRUE(found_def);
+            }
+        }
+
+        {
+            SCOPED_TRACE("global options");
+            // One of the options should still be there.
+            EXPECT_TRUE(srv_cfg->getCfgOption()->get("dhcp6", D6O_AFTR_NAME).option_);
+            auto found_opt = srv_cfg->getCfgOption()->get("dhcp6", D6O_AFTR_NAME);
+            if (deleteConfigElement("dhcp6_options", 1)) {
+                EXPECT_FALSE(found_opt.option_);
+
+            } else {
+                EXPECT_TRUE(found_opt.option_);
+            }
+        }
+
+        {
+            SCOPED_TRACE("shared networks");
+            // One of the shared networks should still be there.
+            EXPECT_TRUE(srv_cfg->getCfgSharedNetworks6()->getByName("two"));
+            auto found_network = srv_cfg->getCfgSharedNetworks6()->getByName("one");
+            if (deleteConfigElement("dhcp6_shared_network", 1)) {
+                EXPECT_FALSE(found_network);
+
+            } else {
+                EXPECT_TRUE(found_network);
+            }
+        }
+
+        {
+            SCOPED_TRACE("subnets");
+            // One of the subnets should still be there.
+            EXPECT_TRUE(srv_cfg->getCfgSubnets6()->getSubnet(2));
+            auto found_subnet = srv_cfg->getCfgSubnets6()->getSubnet(1);
+            if (deleteConfigElement("dhcp6_subnet", 1)) {
+                EXPECT_FALSE(found_subnet);
+                // If the subnet has been deleted, make sure that
+                // it was detached from the shared network it belonged
+                // to, if the shared network still exists.
+                auto found_network = srv_cfg->getCfgSharedNetworks6()->getByName("one");
+                if (found_network) {
+                    EXPECT_TRUE(found_network->getAllSubnets()->empty());
+                }
+
+            } else {
+                EXPECT_TRUE(found_subnet);
+            }
+        }
+    }
+
+    /// @brief Instance of the @c CBControlDHCPv6 used for testing.
+    TestCBControlDHCPv6 ctl_;
+};
+
+
+// This test verifies that the configuration updates for all object
+// types are merged into the current configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyAll) {
+
+    addCreateAuditEntry("dhcp6_global_parameter");
+    addCreateAuditEntry("dhcp6_option_def");
+    addCreateAuditEntry("dhcp6_options");
+    addCreateAuditEntry("dhcp6_shared_network");
+    addCreateAuditEntry("dhcp6_subnet");
+
+    testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that multiple configuration elements are
+// deleted from the local configuration as a result of being
+// deleted from the database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteAll) {
+    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+        remoteDeleteGlobalParameter("foo", 1);
+        remoteDeleteOptionDef(101, "isc");
+        remoteDeleteOption(D6O_BOOTFILE_URL, DHCP6_OPTION_SPACE);
+        remoteDeleteSharedNetwork("one");
+        remoteDeleteSubnet(SubnetID(1));
+    });
+}
+
+// This test verifies that an attempt to delete non-existing
+// configuration element does not cause an error.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteNonExisting) {
+    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+        // Add several audit entries instructing to delete the
+        // non-existing configuration elements. The ids are set
+        // to 3, but the only existing elements have ids of 1
+        // and 2.
+        addDeleteAuditEntry("dhcp6_global_parameter", 3);
+        addDeleteAuditEntry("dhcp6_option_def", 3);
+        addDeleteAuditEntry("dhcp6_options", 3);
+        addDeleteAuditEntry("dhcp6_shared_network", 3);
+        addDeleteAuditEntry("dhcp6_subnet", 3);
+    });
+}
+
+// This test verifies that only a global parameter is merged into
+// the current configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyGlobal) {
+    addCreateAuditEntry("dhcp6_global_parameter");
+    testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that the global parameter is deleted from
+// the local configuration as a result of being deleted from the
+// database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteGlobal) {
+    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+        remoteDeleteGlobalParameter("foo", 1);
+    });
+}
+
+// This test verifies that global parameter is not fetched from the
+// database when the modification time is earlier than the last
+// fetched audit entry.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyGlobalNotFetched) {
+    addCreateAuditEntry("dhcp6_global_parameter");
+    testDatabaseConfigApply(getTimestamp(-3));
+}
+
+// This test verifies that only an option definition is merged into
+// the current configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionDef) {
+    addCreateAuditEntry("dhcp6_option_def");
+    testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that the option definition is deleted from
+// the local configuration as a result of being deleted from the
+// database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteOptionDef) {
+    addDeleteAuditEntry("dhcp6_option_def", 1);
+    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+        remoteDeleteOptionDef(101, "isc");
+    });
+}
+
+// This test verifies that option definition is not fetched from the
+// database when the modification time is earlier than the last
+// fetched audit entry.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionDefNotFetched) {
+    addCreateAuditEntry("dhcp6_option_def");
+    testDatabaseConfigApply(getTimestamp(-3));
+}
+
+// This test verifies that only a DHCPv6 option is merged into the
+// current configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyOption) {
+    addCreateAuditEntry("dhcp6_options");
+    testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that the global option is deleted from
+// the local configuration as a result of being deleted from the
+// database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteOption) {
+    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+        remoteDeleteOption(D6O_BOOTFILE_URL, DHCP6_OPTION_SPACE);
+    });
+}
+
+// This test verifies that DHCPv6 option is not fetched from the
+// database when the modification time is earlier than the last
+// fetched audit entry.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionNotFetched) {
+    addCreateAuditEntry("dhcp6_options");
+    testDatabaseConfigApply(getTimestamp(-3));
+}
+
+// This test verifies that only a shared network is merged into the
+// current configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplySharedNetwork) {
+    addCreateAuditEntry("dhcp6_shared_network");
+    testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that the shared network is deleted from
+// the local configuration as a result of being deleted from the
+// database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteSharedNetwork) {
+    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+        remoteDeleteSharedNetwork("one");
+    });
+}
+
+// This test verifies that shared network is not fetched from the
+// database when the modification time is earlier than the last
+// fetched audit entry.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplySharedNetworkNotFetched) {
+    addCreateAuditEntry("dhcp6_shared_network");
+    testDatabaseConfigApply(getTimestamp(-3));
+}
+
+// This test verifies that only a subnet is merged into the current
+// configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplySubnet) {
+    addCreateAuditEntry("dhcp6_shared_network");
+    addCreateAuditEntry("dhcp6_subnet");
+    testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that the subnet is deleted from the local
+// configuration as a result of being deleted from the database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteSubnet) {
+    testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+        remoteDeleteSubnet(SubnetID(1));
+    });
+}
+
+// This test verifies that subnet is not fetched from the database
+// when the modification time is earlier than the last fetched audit
+// entry.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplySubnetNotFetched) {
+    addCreateAuditEntry("dhcp6_subnet");
+    testDatabaseConfigApply(getTimestamp(-3));
+}
+
 }