summaryrefslogtreecommitdiffstats
path: root/src/lib/database/tests/database_connection_unittest.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/lib/database/tests/database_connection_unittest.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/lib/database/tests/database_connection_unittest.cc')
-rw-r--r--src/lib/database/tests/database_connection_unittest.cc643
1 files changed, 643 insertions, 0 deletions
diff --git a/src/lib/database/tests/database_connection_unittest.cc b/src/lib/database/tests/database_connection_unittest.cc
new file mode 100644
index 0000000..b34f2e8
--- /dev/null
+++ b/src/lib/database/tests/database_connection_unittest.cc
@@ -0,0 +1,643 @@
+// Copyright (C) 2015-2023 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/cfg_to_element.h>
+#include <cc/data.h>
+#include <database/database_connection.h>
+#include <database/dbaccess_parser.h>
+#include <exceptions/exceptions.h>
+#include <gtest/gtest.h>
+
+#include <functional>
+
+using namespace isc::data;
+using namespace isc::db;
+using namespace isc::util;
+namespace ph = std::placeholders;
+
+/// @brief Test fixture for exercising DbLostCallback invocation
+class DatabaseConnectionCallbackTest : public ::testing::Test {
+public:
+ /// Constructor
+ DatabaseConnectionCallbackTest()
+ : db_reconnect_ctl_(0) {
+ DatabaseConnection::db_lost_callback_ = 0;
+ DatabaseConnection::db_recovered_callback_ = 0;
+ DatabaseConnection::db_failed_callback_ = 0;
+ }
+
+ /// Destructor
+ ~DatabaseConnectionCallbackTest() {
+ DatabaseConnection::db_lost_callback_ = 0;
+ DatabaseConnection::db_recovered_callback_ = 0;
+ DatabaseConnection::db_failed_callback_ = 0;
+ }
+
+ /// @brief Callback to register with a DatabaseConnection
+ ///
+ /// @param db_reconnect_ctl ReconnectCtl containing reconnect
+ /// parameters
+ bool dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
+ if (!db_reconnect_ctl) {
+ isc_throw(isc::BadValue, "db_reconnect_ctl should not be null");
+ }
+
+ db_reconnect_ctl_ = db_reconnect_ctl;
+ return (true);
+ }
+
+ /// @brief Callback to register with a DatabaseConnection
+ ///
+ /// @param db_reconnect_ctl ReconnectCtl containing reconnect
+ /// parameters
+ bool dbRecoveredCallback(ReconnectCtlPtr db_reconnect_ctl) {
+ if (!db_reconnect_ctl) {
+ isc_throw(isc::BadValue, "db_reconnect_ctl should not be null");
+ }
+
+ db_reconnect_ctl_ = db_reconnect_ctl;
+ db_reconnect_ctl_->resetRetries();
+ return (true);
+ }
+
+ /// @brief Callback to register with a DatabaseConnection
+ ///
+ /// @param db_reconnect_ctl ReconnectCtl containing reconnect
+ /// parameters
+ bool dbFailedCallback(ReconnectCtlPtr db_reconnect_ctl) {
+ if (!db_reconnect_ctl) {
+ isc_throw(isc::BadValue, "db_reconnect_ctl should not be null");
+ }
+
+ db_reconnect_ctl_ = db_reconnect_ctl;
+ db_reconnect_ctl_->resetRetries();
+ return (false);
+ }
+
+ /// @brief Retainer for the control passed into the callback
+ ReconnectCtlPtr db_reconnect_ctl_;
+};
+
+/// @brief getParameter test
+///
+/// This test checks if the LeaseMgr can be instantiated and that it
+/// parses parameters string properly.
+TEST(DatabaseConnectionTest, getParameter) {
+
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("param1")] = std::string("value1");
+ pmap[std::string("param2")] = std::string("value2");
+ DatabaseConnection datasrc(pmap);
+
+ EXPECT_EQ("value1", datasrc.getParameter("param1"));
+ EXPECT_EQ("value2", datasrc.getParameter("param2"));
+ EXPECT_THROW(datasrc.getParameter("param3"), isc::BadValue);
+}
+
+/// @brief NoDbLostCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbLostCallback
+/// returns false if the connection has no registered DbLostCallback.
+TEST_F(DatabaseConnectionCallbackTest, NoDbLostCallback) {
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60000");
+ DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
+
+ bool ret = false;
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl()));
+ EXPECT_FALSE(ret);
+ EXPECT_FALSE(db_reconnect_ctl_);
+}
+
+/// @brief NoDbRecoveredCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbRecoveredCallback
+/// returns false if the connection has no registered DbRecoveredCallback.
+TEST_F(DatabaseConnectionCallbackTest, NoDbRecoveredCallback) {
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60000");
+ DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
+
+ bool ret = false;
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbRecoveredCallback(datasrc.reconnectCtl()));
+ EXPECT_FALSE(ret);
+ EXPECT_FALSE(db_reconnect_ctl_);
+}
+
+/// @brief NoDbFailedCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbFailedCallback
+/// returns false if the connection has no registered DbFailedCallback.
+TEST_F(DatabaseConnectionCallbackTest, NoDbFailedCallback) {
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60000");
+ DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
+
+ bool ret = false;
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbFailedCallback(datasrc.reconnectCtl()));
+ EXPECT_FALSE(ret);
+ EXPECT_FALSE(db_reconnect_ctl_);
+}
+
+/// @brief dbLostCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbLostCallback
+/// safely invokes the registered DbCallback. It also tests
+/// operation of DbReconnectCtl retry accounting methods.
+TEST_F(DatabaseConnectionCallbackTest, dbLostCallback) {
+ /// Create a Database configuration that includes the reconnect
+ /// control parameters.
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60000");
+
+ /// Install the callback.
+ DatabaseConnection::db_lost_callback_ =
+ std::bind(&DatabaseConnectionCallbackTest::dbLostCallback, this, ph::_1);
+ /// Create the connection..
+ DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
+ bool ret = false;
+
+ /// We should be able to invoke the callback and get
+ /// the correct reconnect control parameters from it.
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl()));
+ EXPECT_TRUE(ret);
+ ASSERT_TRUE(db_reconnect_ctl_);
+ ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ("timer", db_reconnect_ctl_->timerName());
+ ASSERT_EQ(3, db_reconnect_ctl_->maxRetries());
+ ASSERT_EQ(3, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval());
+
+ /// Verify that checkRetries() correctly decrements
+ /// down to zero, and that retriesLeft() returns
+ /// the correct value.
+ for (int i = 3; i > 1 ; --i) {
+ ASSERT_EQ(i, db_reconnect_ctl_->retriesLeft());
+ ASSERT_TRUE(db_reconnect_ctl_->checkRetries());
+ }
+
+ /// Retries are exhausted, verify that's reflected.
+ EXPECT_FALSE(db_reconnect_ctl_->checkRetries());
+ EXPECT_EQ(0, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
+}
+
+/// @brief dbRecoveredCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbRecoveredCallback
+/// safely invokes the registered DbRecoveredCallback. It also tests
+/// operation of DbReconnectCtl retry reset method.
+TEST_F(DatabaseConnectionCallbackTest, dbRecoveredCallback) {
+ /// Create a Database configuration that includes the reconnect
+ /// control parameters.
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60000");
+
+ /// Install the callback.
+ DatabaseConnection::db_lost_callback_ =
+ std::bind(&DatabaseConnectionCallbackTest::dbLostCallback, this, ph::_1);
+ DatabaseConnection::db_recovered_callback_ =
+ std::bind(&DatabaseConnectionCallbackTest::dbRecoveredCallback, this, ph::_1);
+ /// Create the connection..
+ DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
+ bool ret = false;
+
+ /// We should be able to invoke the callback and get
+ /// the correct reconnect control parameters from it.
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl()));
+ EXPECT_TRUE(ret);
+ ASSERT_TRUE(db_reconnect_ctl_);
+ ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ("timer", db_reconnect_ctl_->timerName());
+ ASSERT_EQ(3, db_reconnect_ctl_->maxRetries());
+ ASSERT_EQ(3, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval());
+
+ /// Verify that checkRetries() correctly decrements
+ /// down to zero, and that retriesLeft() returns
+ /// the correct value.
+ for (int i = 3; i > 1 ; --i) {
+ ASSERT_EQ(i, db_reconnect_ctl_->retriesLeft());
+ ASSERT_TRUE(db_reconnect_ctl_->checkRetries());
+ }
+
+ /// Retries are exhausted, verify that's reflected.
+ EXPECT_FALSE(db_reconnect_ctl_->checkRetries());
+ EXPECT_EQ(0, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
+
+ /// Reset the reconnect ctl object to verify that it is set again.
+ db_reconnect_ctl_.reset();
+
+ /// We should be able to invoke the callback and get
+ /// the correct reconnect control parameters from it.
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbRecoveredCallback(datasrc.reconnectCtl()));
+ EXPECT_TRUE(ret);
+ ASSERT_TRUE(db_reconnect_ctl_);
+ ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ("timer", db_reconnect_ctl_->timerName());
+ EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval());
+
+ /// Retries are reset, verify that's reflected.
+ EXPECT_EQ(3, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
+}
+
+/// @brief dbFailedCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbFailedCallback
+/// safely invokes the registered DbFailedCallback.
+TEST_F(DatabaseConnectionCallbackTest, dbFailedCallback) {
+ /// Create a Database configuration that includes the reconnect
+ /// control parameters.
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60000");
+
+ /// Install the callback.
+ DatabaseConnection::db_lost_callback_ =
+ std::bind(&DatabaseConnectionCallbackTest::dbLostCallback, this, ph::_1);
+ DatabaseConnection::db_failed_callback_ =
+ std::bind(&DatabaseConnectionCallbackTest::dbFailedCallback, this, ph::_1);
+ /// Create the connection..
+ DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
+ bool ret = false;
+
+ /// We should be able to invoke the callback and get
+ /// the correct reconnect control parameters from it.
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl()));
+ EXPECT_TRUE(ret);
+ ASSERT_TRUE(db_reconnect_ctl_);
+ ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ("timer", db_reconnect_ctl_->timerName());
+ ASSERT_EQ(3, db_reconnect_ctl_->maxRetries());
+ ASSERT_EQ(3, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval());
+
+ /// Verify that checkRetries() correctly decrements
+ /// down to zero, and that retriesLeft() returns
+ /// the correct value.
+ for (int i = 3; i > 1 ; --i) {
+ ASSERT_EQ(i, db_reconnect_ctl_->retriesLeft());
+ ASSERT_TRUE(db_reconnect_ctl_->checkRetries());
+ }
+
+ /// Retries are exhausted, verify that's reflected.
+ EXPECT_FALSE(db_reconnect_ctl_->checkRetries());
+ EXPECT_EQ(0, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
+
+ /// Reset the reconnect ctl object to verify that it is set again.
+ db_reconnect_ctl_.reset();
+
+ /// We should be able to invoke the callback and get
+ /// the correct reconnect control parameters from it.
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbFailedCallback(datasrc.reconnectCtl()));
+ EXPECT_FALSE(ret);
+ ASSERT_TRUE(db_reconnect_ctl_);
+ ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ("timer", db_reconnect_ctl_->timerName());
+ EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval());
+
+ /// Retries are reset, verify that's reflected.
+ EXPECT_EQ(3, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
+}
+
+// This test checks that a database access string can be parsed correctly.
+TEST(DatabaseConnectionTest, parse) {
+
+ DatabaseConnection::ParameterMap parameters = DatabaseConnection::parse(
+ "user=me password='forbidden' name=kea somethingelse= type=mysql");
+
+ EXPECT_EQ(5, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("forbidden", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+ EXPECT_EQ("", parameters["somethingelse"]);
+}
+
+// This test checks that it is allowed to specify password including whitespaces
+// assuming that the password is enclosed in ''.
+TEST(DatabaseConnectionTest, parsePasswordWithWhitespace) {
+
+ // Case 1: password in the middle.
+ DatabaseConnection::ParameterMap parameters = DatabaseConnection::parse(
+ "user=me password='forbidden with space' name=kea type=mysql");
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("forbidden with space", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // Case 2: password at the end.
+ parameters = DatabaseConnection::parse("user=me name=kea type=mysql password='forbidden with space'");
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("forbidden with space", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // Case 3: Empty password at the end.
+ parameters = DatabaseConnection::parse("user=me name=kea type=mysql password=''");
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // Case 4: password at the beginning.
+ parameters = DatabaseConnection::parse("password='forbidden with space' user=me name=kea type=mysql");
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("forbidden with space", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // Case 5: Empty password at the beginning.
+ parameters = DatabaseConnection::parse("password='' user=me name=kea type=mysql");
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+
+ // Case 6: Password is a sole parameter.
+ parameters = DatabaseConnection::parse("password='forbidden with spaces'");
+
+ EXPECT_EQ(1, parameters.size());
+ EXPECT_EQ("forbidden with spaces", parameters["password"]);
+}
+
+// This test checks that an invalid database access string behaves as expected.
+TEST(DatabaseConnectionTest, parseInvalid) {
+
+ // No tokens in the string, so we expect no parameters
+ std::string invalid = "";
+ DatabaseConnection::ParameterMap parameters = DatabaseConnection::parse(invalid);
+ EXPECT_EQ(0, parameters.size());
+
+ // With spaces, there are some tokens so we expect invalid parameter
+ // as there are no equals signs.
+ invalid = " \t ";
+ EXPECT_THROW(DatabaseConnection::parse(invalid), isc::InvalidParameter);
+
+ invalid = " noequalshere ";
+ EXPECT_THROW(DatabaseConnection::parse(invalid), isc::InvalidParameter);
+
+ // Mismatched single quote.
+ invalid = "password='xyz";
+ EXPECT_THROW(DatabaseConnection::parse(invalid), isc::InvalidParameter);
+
+ // A single "=" is valid string, but is placed here as the result is
+ // expected to be nothing.
+ invalid = "=";
+ parameters = DatabaseConnection::parse(invalid);
+ EXPECT_EQ(1, parameters.size());
+ EXPECT_EQ("", parameters[""]);
+}
+
+/// @brief redactedAccessString test
+///
+/// Checks that the redacted configuration string includes the password only
+/// as a set of asterisks.
+TEST(DatabaseConnectionTest, redactAccessString) {
+
+ DatabaseConnection::ParameterMap parameters =
+ DatabaseConnection::parse("user=me password=forbidden name=kea type=mysql");
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("forbidden", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // Redact the result. To check, break the redacted string down into its
+ // components.
+ std::string redacted = DatabaseConnection::redactedAccessString(parameters);
+ parameters = DatabaseConnection::parse(redacted);
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("*****", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+}
+
+/// @brief redactedAccessString test - empty password
+///
+/// Checks that the redacted configuration string includes the password only
+/// as a set of asterisks, even if the password is null.
+TEST(DatabaseConnectionTest, redactAccessStringEmptyPassword) {
+
+ DatabaseConnection::ParameterMap parameters =
+ DatabaseConnection::parse("user=me name=kea type=mysql password=");
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // Redact the result. To check, break the redacted string down into its
+ // components.
+ std::string redacted = DatabaseConnection::redactedAccessString(parameters);
+ parameters = DatabaseConnection::parse(redacted);
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("*****", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // ... and again to check that the position of the empty password in the
+ // string does not matter.
+ parameters = DatabaseConnection::parse("user=me password= name=kea type=mysql");
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ redacted = DatabaseConnection::redactedAccessString(parameters);
+ parameters = DatabaseConnection::parse(redacted);
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("*****", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+}
+
+/// @brief redactedAccessString test - no password
+///
+/// Checks that the redacted configuration string excludes the password if there
+/// was no password to begin with.
+TEST(DatabaseConnectionTest, redactAccessStringNoPassword) {
+
+ DatabaseConnection::ParameterMap parameters =
+ DatabaseConnection::parse("user=me name=kea type=mysql");
+ EXPECT_EQ(3, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // Redact the result. To check, break the redacted string down into its
+ // components.
+ std::string redacted = DatabaseConnection::redactedAccessString(parameters);
+ parameters = DatabaseConnection::parse(redacted);
+
+ EXPECT_EQ(3, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+}
+
+// Check that the toElementDbAccessString() handles all valid parameters
+// Note that because toElementDbAccessString() utilizes
+// toElement() this tests both.
+TEST(DatabaseConnection, toElementDbAccessStringValid) {
+ const char* configs[] = {
+ "{\n"
+ "\"connect-timeout\": 200, \n"
+ "\"on-fail\": \"stop-retry-exit\", \n"
+ "\"lfc-interval\" : 100, \n"
+ "\"host\": \"whatevah\", \n"
+ "\"max-reconnect-tries\": 5, \n"
+ "\"name\": \"name_str\", \n"
+ "\"password\": \"password_str\", \n"
+ "\"persist\" : true, \n"
+ "\"port\" : 300, \n"
+ "\"readonly\" : false, \n"
+ "\"reconnect-wait-time\": 99, \n"
+ "\"type\": \"memfile\", \n"
+ "\"user\": \"user_str\", \n"
+ "\"max-row-errors\": 50, \n"
+ "\"trust-anchor\": \"my-ca\", \n"
+ "\"cert-file\": \"my-cert.crt\", \n"
+ "\"key-file\": \"my-key.key\", \n"
+ "\"cipher-list\": \"AES\" \n"
+ "}\n"
+ };
+
+ DbAccessParser parser;
+ std::string access_str;
+ ConstElementPtr json_elements;
+
+ ASSERT_NO_THROW(json_elements = Element::fromJSON(configs[0]));
+ ASSERT_NO_THROW(parser.parse(access_str, json_elements));
+
+ ElementPtr round_trip = DatabaseConnection::toElementDbAccessString(access_str);
+
+ ASSERT_TRUE(json_elements->equals(*round_trip));
+}
+
+// Check that the toElementDbAccessString() handles Postgres backend
+// specific parameters.
+TEST(DatabaseConnection, toElementDbAccessStringValidPostgresql) {
+ const char* configs[] = {
+ "{\n"
+ "\"connect-timeout\": 200, \n"
+ "\"tcp-user-timeout\": 10, \n"
+ "\"type\": \"postgresql\", \n"
+ "\"user\": \"user_str\" \n"
+ "}\n"
+ };
+
+ DbAccessParser parser;
+ std::string access_str;
+ ConstElementPtr json_elements;
+
+ ASSERT_NO_THROW(json_elements = Element::fromJSON(configs[0]));
+ ASSERT_NO_THROW(parser.parse(access_str, json_elements));
+
+ ElementPtr round_trip = DatabaseConnection::toElementDbAccessString(access_str);
+
+ ASSERT_TRUE(json_elements->equals(*round_trip));
+}
+
+// Check that the toElementDbAccessString() handles MySQL backend
+// specific parameters.
+TEST(DatabaseConnection, toElementDbAccessStringValidMySql) {
+ const char* configs[] = {
+ "{\n"
+ "\"connect-timeout\": 200, \n"
+ "\"read-timeout\": 10, \n"
+ "\"write-timeout\": 10, \n"
+ "\"type\": \"mysql\", \n"
+ "\"user\": \"user_str\" \n"
+ "}\n"
+ };
+
+ DbAccessParser parser;
+ std::string access_str;
+ ConstElementPtr json_elements;
+
+ ASSERT_NO_THROW(json_elements = Element::fromJSON(configs[0]));
+ ASSERT_NO_THROW(parser.parse(access_str, json_elements));
+
+ ElementPtr round_trip = DatabaseConnection::toElementDbAccessString(access_str);
+
+ ASSERT_TRUE(json_elements->equals(*round_trip));
+}
+
+// Check that toElementDbAccessString() catches invalid parameters.
+// Note that because toElementDbAccessString() utilizes
+// toElement() this tests both.
+//
+// Test has been disabled. The recent change turned handling of unknown connection
+// string params. Instead of throwing, it logs an error and continues. This gives
+// us better resiliency. However, we don't have any means implemented to
+// test whether it was printed or not. It's reasonably easy to implement such a
+// check, but we don't have time for this.
+TEST(DatabaseConnection, DISABLED_toElementDbAccessStringInvalid) {
+ std::vector<std::string> access_strs = {
+ "bogus-param=memfile",
+ "lfc-interval=not-an-integer",
+ "connect-timeout=not-an-integer",
+ "port=not-an-integer",
+ "persist=not-boolean",
+ "readonly=not-boolean"
+ };
+
+ for (auto access_str : access_strs) {
+ /// @todo: verify that an ERROR is logged.
+ ASSERT_NO_THROW(DatabaseConnection::toElementDbAccessString(access_str));
+ }
+}
+
+// Check that toElementDbAccessString() handles empty access string
+// Note that because toElementDbAccessString() utilizes
+// toElement() this tests both.
+TEST(DatabaseConnection, toElementDbAccessStringEmpty) {
+ ConstElementPtr elements;
+ ASSERT_NO_THROW(elements = DatabaseConnection::toElementDbAccessString(""));
+ ASSERT_TRUE(elements);
+ ASSERT_EQ(0, elements->size());
+}