summaryrefslogtreecommitdiffstats
path: root/src/lib/database/tests/audit_entry_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/database/tests/audit_entry_unittest.cc')
-rw-r--r--src/lib/database/tests/audit_entry_unittest.cc362
1 files changed, 362 insertions, 0 deletions
diff --git a/src/lib/database/tests/audit_entry_unittest.cc b/src/lib/database/tests/audit_entry_unittest.cc
new file mode 100644
index 0000000..1d41872
--- /dev/null
+++ b/src/lib/database/tests/audit_entry_unittest.cc
@@ -0,0 +1,362 @@
+// Copyright (C) 2019-2020 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 <database/audit_entry.h>
+#include <exceptions/exceptions.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/make_shared.hpp>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::db;
+
+namespace {
+
+/// @brief Test fixture class for testing @c AuditEntry.
+class AuditEntryTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ AuditEntryTest()
+ : fixed_time_(now()) {
+ }
+
+ /// @brief Returns current time.
+ static boost::posix_time::ptime now() {
+ return (boost::posix_time::microsec_clock::local_time());
+ }
+
+ /// @brief Returns always the same time value.
+ ///
+ /// The value is initialized when the test it started.
+ boost::posix_time::ptime fixedTime() const {
+ return (fixed_time_);
+ }
+
+ /// @brief Checks if the given time value is "close" to the
+ /// current time.
+ ///
+ /// This is useful in tests when the @c AuditEntry class sets the
+ /// modification time to a default value (in its constructor).
+ /// Because the test doesn't know the exact value to which the
+ /// modification time is set, it merely checks that this value
+ /// is earlier than current time and within the range of 1s.
+ ///
+ /// @param t time value to be checked.
+ bool almostEqualTime(const boost::posix_time::ptime& t) const {
+ auto current = now();
+
+ // The provided value must be a valid time.
+ if (t.is_not_a_date_time()) {
+ ADD_FAILURE() << "provided value is not a date time";
+ return (false);
+ }
+
+ // It must be earlier than current time.
+ if (t > current) {
+ ADD_FAILURE() << "provided time value is later than current time";
+ return (false);
+ }
+
+ // The difference must be lower than 1 second.
+ boost::posix_time::time_duration dur = current - t;
+ return (dur.total_milliseconds() < 1000);
+ }
+
+ /// @brief Fixed time value initialized in the constructor.
+ ///
+ /// This is used in tests that require the exact time values.
+ boost::posix_time::ptime fixed_time_;
+};
+
+// Checks that the modification time value can be cast to a number.
+TEST_F(AuditEntryTest, modificationType) {
+ EXPECT_EQ(0, static_cast<int>(AuditEntry::ModificationType::CREATE));
+ EXPECT_EQ(1, static_cast<int>(AuditEntry::ModificationType::UPDATE));
+ EXPECT_EQ(2, static_cast<int>(AuditEntry::ModificationType::DELETE));
+}
+
+// Checks that the audit entry can be created.
+TEST_F(AuditEntryTest, create) {
+
+ AuditEntryPtr audit_entry;
+
+ {
+ SCOPED_TRACE("create with modification time");
+
+ ASSERT_NO_THROW(audit_entry = AuditEntry::create
+ ("dhcp4_subnet", 10, AuditEntry::ModificationType::DELETE,
+ fixedTime(), 123, "deleted subnet 10"));
+ EXPECT_EQ("dhcp4_subnet", audit_entry->getObjectType());
+ EXPECT_EQ(10, audit_entry->getObjectId());
+ EXPECT_EQ(AuditEntry::ModificationType::DELETE, audit_entry->getModificationType());
+ EXPECT_EQ(fixedTime(), audit_entry->getModificationTime());
+ EXPECT_EQ(123, audit_entry->getRevisionId());
+ EXPECT_EQ("deleted subnet 10", audit_entry->getLogMessage());
+ }
+
+ {
+ SCOPED_TRACE("create with default modification time");
+
+ ASSERT_NO_THROW(audit_entry = AuditEntry::create
+ ("dhcp4_option", 123, AuditEntry::ModificationType::CREATE,
+ 234, ""));
+ EXPECT_EQ("dhcp4_option", audit_entry->getObjectType());
+ EXPECT_EQ(123, audit_entry->getObjectId());
+ EXPECT_EQ(AuditEntry::ModificationType::CREATE, audit_entry->getModificationType());
+ EXPECT_TRUE(almostEqualTime(audit_entry->getModificationTime()));
+ EXPECT_EQ(234, audit_entry->getRevisionId());
+ EXPECT_TRUE(audit_entry->getLogMessage().empty());
+ }
+}
+
+// Checks that invalid values for the audit entry are rejected.
+TEST_F(AuditEntryTest, createFailures) {
+ {
+ SCOPED_TRACE("empty object type");
+ EXPECT_THROW(AuditEntry("", 10, AuditEntry::ModificationType::DELETE,
+ fixedTime(), 123, "deleted subnet 10"),
+ BadValue);
+ }
+
+ {
+ SCOPED_TRACE("not a date time");
+ EXPECT_THROW(AuditEntry("dhcp4_subnet", 10,
+ AuditEntry::ModificationType::DELETE,
+ boost::posix_time::ptime(), 123,
+ "deleted subnet 10"),
+ BadValue);
+ }
+}
+
+/// @brief Test fixture class for testing @c AuditEntryCollection.
+class AuditEntryCollectionTest : public AuditEntryTest {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Creates a collection of audit entries used in the tests.
+ AuditEntryCollectionTest()
+ : AuditEntryTest(), audit_entries_() {
+ createTestAuditEntries();
+ }
+
+ /// @brief Returns a time value being being a specified number of
+ /// seconds later or earlier than the time returned by @c fixedTime.
+ ///
+ /// @param secs offset in seconds since the @c fixedTime output. If
+ /// the parameter is negative, the returned time is earlier than the
+ /// fixed time. Otherwise, it is later than fixed time.
+ boost::posix_time::ptime diffTime(const long secs) {
+ if (secs < 0) {
+ return (fixedTime() - boost::posix_time::seconds(-secs));
+ }
+ return (fixedTime() + boost::posix_time::seconds(secs));
+ }
+
+ /// @brief Creates an @c AuditEntry instance and inserts it to
+ /// the @c audit_entries_ collection.
+ ///
+ /// @tparam Args types of the arguments to be passed to the @c AuditEntry
+ /// constructors.
+ /// @param args arguments to be passed to the @c AuditEntry constructors.
+ template<typename... Args>
+ void create(Args&& ...args) {
+ audit_entries_.insert(boost::make_shared<AuditEntry>(args...));
+ }
+
+ /// @brief Creates a collection of @c AuditEntry objects to be used by
+ /// the tests.
+ void createTestAuditEntries() {
+ create("dhcp4_subnet", 10, AuditEntry::ModificationType::CREATE,
+ diffTime(-5), 100, "added subnet 10");
+ create("dhcp4_shared_network", 1, AuditEntry::ModificationType::CREATE,
+ diffTime(-5), 110, "added shared network 1");
+ create("dhcp4_shared_network", 120, AuditEntry::ModificationType::UPDATE,
+ diffTime(-8), 90, "updated shared network 120");
+ create("dhcp4_subnet", 120, AuditEntry::ModificationType::DELETE,
+ diffTime(8), 130, "deleted subnet 120");
+ create("dhcp4_subnet", 1000, AuditEntry::ModificationType::CREATE,
+ diffTime(4), 120, "created subnet 1000");
+ create("dhcp4_option", 15, AuditEntry::ModificationType::UPDATE,
+ diffTime(16), 140, "updated option 15");
+ }
+
+ /// @brief Checks if the returned results range contains an @c AuditEntry
+ /// with a given object type and identifier.
+ ///
+ /// @param object_type expected object type.
+ /// @param object_id expected object id.
+ /// @param begin beginning of the results range to be examined.
+ /// @param end end of the results range to be examined.
+ template<typename Iterator>
+ bool includes(const std::string& object_type, const uint64_t object_id,
+ Iterator begin, Iterator end) {
+ // Iterate over the results range and look for the entry.
+ for (auto it = begin; it != end; ++it) {
+ if (((*it)->getObjectType() == object_type) &&
+ ((*it)->getObjectId() == object_id)) {
+ // Entry found.
+ return (true);
+ }
+ }
+
+ // Entry not found.
+ return (false);
+ }
+
+ /// @brief Checks if the returned results range contains an @c AuditEntry
+ /// with a given object and modification types, and object identifier.
+ ///
+ /// @param object_type expected object type.
+ /// @param object_id expected object id.
+ /// @param modification_type expected modification type.
+ /// @param begin beginning of the results range to be examined.
+ /// @param end end of the results range to be examined.
+ template<typename Iterator>
+ bool includes(const std::string& object_type, const uint64_t object_id,
+ const AuditEntry::ModificationType& modification_type,
+ Iterator begin, Iterator end) {
+ // Iterate over the results range and look for the entry.
+ for (auto it = begin; it != end; ++it) {
+ if (((*it)->getObjectType() == object_type) &&
+ ((*it)->getObjectId() == object_id) &&
+ ((*it)->getModificationType() == modification_type)) {
+ // Entry found.
+ return (true);
+ }
+ }
+
+ // Entry not found.
+ return (false);
+ }
+
+ /// @brief Audit entries used in the tests.
+ AuditEntryCollection audit_entries_;
+
+};
+
+// Checks that entries can be found by object type.
+TEST_F(AuditEntryCollectionTest, getByObjectType) {
+ const auto& object_type_idx = audit_entries_.get<AuditEntryObjectTypeTag>();
+
+ // Search for "dhcp4_subnet" objects.
+ auto range = object_type_idx.equal_range("dhcp4_subnet");
+ ASSERT_EQ(3, std::distance(range.first, range.second));
+ EXPECT_TRUE(includes("dhcp4_subnet", 10, range.first, range.second));
+ EXPECT_TRUE(includes("dhcp4_subnet", 120, range.first, range.second));
+ EXPECT_TRUE(includes("dhcp4_subnet", 1000, range.first, range.second));
+
+ // Search for "dhcp4_shared_network" objects.
+ range = object_type_idx.equal_range("dhcp4_shared_network");
+ ASSERT_EQ(2, std::distance(range.first, range.second));
+ EXPECT_TRUE(includes("dhcp4_shared_network", 1, range.first, range.second));
+ EXPECT_TRUE(includes("dhcp4_shared_network", 120, range.first, range.second));
+
+ // Search for "dhcp4_option" objects.
+ range = object_type_idx.equal_range("dhcp4_option");
+ ASSERT_EQ(1, std::distance(range.first, range.second));
+ EXPECT_TRUE(includes("dhcp4_option", 15, range.first, range.second));
+}
+
+// Checks that entries can be found by modification time.
+TEST_F(AuditEntryCollectionTest, getByModificationTime) {
+ const auto& mod_time_idx = audit_entries_.get<AuditEntryModificationTimeIdTag>();
+
+ // Search for objects later than fixed time - 10s.
+ auto lb = mod_time_idx.lower_bound(diffTime(-10));
+ ASSERT_EQ(6, std::distance(lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 10, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_shared_network", 1, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_shared_network", 120, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end()));
+
+ // Search for objects later than fixed time - 7s.
+ lb = mod_time_idx.lower_bound(diffTime(-7));
+ ASSERT_EQ(5, std::distance(lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 10, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_shared_network", 1, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end()));
+
+ // Search for objects later than fixed time - 1s.
+ lb = mod_time_idx.lower_bound(diffTime(-1));
+ ASSERT_EQ(3, std::distance(lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end()));
+
+ // Search for objects later than fixed time + 6s.
+ lb = mod_time_idx.lower_bound(diffTime(6));
+ ASSERT_EQ(2, std::distance(lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end()));
+
+ // Search for objects later than fixed time + 10s.
+ lb = mod_time_idx.lower_bound(diffTime(10));
+ ASSERT_EQ(1, std::distance(lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end()));
+
+ // Search for objects later than fixed time + 20s.
+ lb = mod_time_idx.lower_bound(diffTime(20));
+ // None found.
+ ASSERT_EQ(0, std::distance(lb, mod_time_idx.end()));
+}
+
+// Checks that entries can be found by modification time and id.
+TEST_F(AuditEntryCollectionTest, getByModificationTimeAndId) {
+ const auto& mod_time_idx = audit_entries_.get<AuditEntryModificationTimeIdTag>();
+
+ // Search for objects later than added added subnet 10.
+ auto mod = boost::make_tuple(diffTime(-5), 100 + 1);
+ auto lb = mod_time_idx.lower_bound(mod);
+ ASSERT_EQ(4, std::distance(lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_shared_network", 1, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end()));
+
+ // Check the order is time first, id after.
+ create("dhcp4_subnet", 1000, AuditEntry::ModificationType::UPDATE,
+ diffTime(-8), 200, "updated subnet 1000");
+ lb = mod_time_idx.lower_bound(mod);
+ ASSERT_EQ(4, std::distance(lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_shared_network", 1, lb, mod_time_idx.end()));
+ EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end()));
+}
+
+// Checks that entries can be found by object id.
+TEST_F(AuditEntryCollectionTest, getByObjectId) {
+ const auto& object_id_idx = audit_entries_.get<AuditEntryObjectIdTag>();
+
+ // Search for object id 10.
+ auto range = object_id_idx.equal_range(10);
+ ASSERT_EQ(1, std::distance(range.first, range.second));
+ EXPECT_TRUE(includes("dhcp4_subnet", 10, range.first, range.second));
+
+ // Add another entry.
+ create("dhcp4_subnet", 10, AuditEntry::ModificationType::UPDATE,
+ diffTime(0), 111, "updated subnet 10");
+
+ // Now search should return two entries.
+ range = object_id_idx.equal_range(10);
+ ASSERT_EQ(2, std::distance(range.first, range.second));
+ EXPECT_TRUE(includes("dhcp4_subnet", 10,
+ AuditEntry::ModificationType::CREATE,
+ range.first, range.second));
+ EXPECT_TRUE(includes("dhcp4_subnet", 10,
+ AuditEntry::ModificationType::UPDATE,
+ range.first, range.second));
+}
+
+}