summaryrefslogtreecommitdiffstats
path: root/src/lib/config/tests/client_connection_unittests.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/config/tests/client_connection_unittests.cc')
-rw-r--r--src/lib/config/tests/client_connection_unittests.cc183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/lib/config/tests/client_connection_unittests.cc b/src/lib/config/tests/client_connection_unittests.cc
new file mode 100644
index 0000000..3ecca82
--- /dev/null
+++ b/src/lib/config/tests/client_connection_unittests.cc
@@ -0,0 +1,183 @@
+// Copyright (C) 2017-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 <testutils/sandbox.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/testutils/test_server_unix_socket.h>
+#include <cc/json_feed.h>
+#include <config/client_connection.h>
+#include <gtest/gtest.h>
+#include <cstdlib>
+#include <sstream>
+#include <string>
+
+using namespace isc::asiolink;
+using namespace isc::config;
+
+namespace {
+
+/// @brief Test timeout in ms.
+const long TEST_TIMEOUT = 10000;
+
+/// Test fixture class for @ref ClientConnection.
+class ClientConnectionTest : public ::testing::Test {
+public:
+ isc::test::Sandbox sandbox;
+
+ /// @brief Constructor.
+ ///
+ /// Removes unix socket descriptor before the test.
+ ClientConnectionTest() :
+ io_service_(),
+ test_socket_(new test::TestServerUnixSocket(io_service_,
+ unixSocketFilePath())) {
+ removeUnixSocketFile();
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Removes unix socket descriptor after the test.
+ virtual ~ClientConnectionTest() {
+ removeUnixSocketFile();
+ }
+
+ /// @brief Returns socket file path.
+ ///
+ /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the
+ /// socket file is created in the location pointed to by this variable.
+ /// Otherwise, it is created in the build directory.
+ ///
+ /// The KEA_SOCKET_TEST_DIR is typically used to overcome the problem of
+ /// a system limit on the unix socket file path (usually 102 or 103 characters).
+ /// When Kea build is located in the nested directories with absolute path
+ /// exceeding this limit, the test system should be configured to set
+ /// the KEA_SOCKET_TEST_DIR environmental variable to point to an alternative
+ /// location, e.g. /tmp, with an absolute path length being within the
+ /// allowed range.
+ std::string unixSocketFilePath() {
+ std::string socket_path;
+ const char* env = getenv("KEA_SOCKET_TEST_DIR");
+ if (env) {
+ socket_path = std::string(env) + "/test-socket";
+ } else {
+ socket_path = sandbox.join("test-socket");
+ }
+ return (socket_path);
+ }
+
+ /// @brief Removes unix socket descriptor.
+ void removeUnixSocketFile() {
+ static_cast<void>(remove(unixSocketFilePath().c_str()));
+ }
+
+ /// @brief IO service used by the tests.
+ IOService io_service_;
+
+ /// @brief Server side unix socket used in these tests.
+ test::TestServerUnixSocketPtr test_socket_;
+};
+
+// Tests successful transaction: connect, send command and receive a
+// response.
+TEST_F(ClientConnectionTest, success) {
+ // Start timer protecting against test timeouts.
+ test_socket_->startTimer(TEST_TIMEOUT);
+
+ // Start the server.
+ test_socket_->bindServerSocket();
+ test_socket_->generateCustomResponse(2048);
+
+ // Create some valid command.
+ std::string command = "{ \"command\": \"list-commands\" }";
+
+ ClientConnection conn(io_service_);
+
+ // This boolean value will indicate when the callback function is invoked
+ // at the end of the transaction (whether it is successful or unsuccessful).
+ bool handler_invoked = false;
+ conn.start(ClientConnection::SocketPath(unixSocketFilePath()),
+ ClientConnection::ControlCommand(command),
+ [&handler_invoked](const boost::system::error_code& ec,
+ const ConstJSONFeedPtr& feed) {
+ // Indicate that the handler has been called to break from the
+ // while loop below.
+ handler_invoked = true;
+ // The ec should contain no error.
+ ASSERT_FALSE(ec);
+ // The JSONFeed should be present and it should contain a valid
+ // response.
+ ASSERT_TRUE(feed);
+ EXPECT_TRUE(feed->feedOk()) << feed->getErrorMessage();
+ });
+ // Run the connection.
+ while (!handler_invoked && !test_socket_->isStopped()) {
+ io_service_.run_one();
+ }
+}
+
+// This test checks that a timeout is signalled when the communication
+// takes too long.
+TEST_F(ClientConnectionTest, timeout) {
+ // The server will return only partial JSON response (lacking closing
+ // brace). The client will wait for closing brace and eventually the
+ // connection should time out.
+ test_socket_.reset(new test::TestServerUnixSocket(io_service_,
+ unixSocketFilePath(),
+ "{ \"command\": \"foo\""));
+ test_socket_->startTimer(TEST_TIMEOUT);
+
+ // Start the server.
+ test_socket_->bindServerSocket();
+
+ // Command to be sent to the server.
+ std::string command = "{ \"command\": \"list-commands\" }";
+
+ ClientConnection conn(io_service_);
+
+ // This boolean value will be set to true when the callback is invoked.
+ bool handler_invoked = false;
+ conn.start(ClientConnection::SocketPath(unixSocketFilePath()),
+ ClientConnection::ControlCommand(command),
+ [&handler_invoked](const boost::system::error_code& ec,
+ const ConstJSONFeedPtr& /*feed*/) {
+ // Indicate that the callback has been invoked to break the loop
+ // below.
+ handler_invoked = true;
+ ASSERT_TRUE(ec);
+ EXPECT_TRUE(ec.value() == boost::asio::error::timed_out);
+ }, ClientConnection::Timeout(1000));
+
+ while (!handler_invoked && !test_socket_->isStopped()) {
+ io_service_.run_one();
+ }
+}
+
+// This test checks that an error is returned when the client is unable
+// to connect to the server.
+TEST_F(ClientConnectionTest, connectionError) {
+ // Create the new connection but do not bind the server socket.
+ // The connection should be refused and an error returned.
+ ClientConnection conn(io_service_);
+
+ std::string command = "{ \"command\": \"list-commands\" }";
+
+ bool handler_invoked = false;
+ conn.start(ClientConnection::SocketPath(unixSocketFilePath()),
+ ClientConnection::ControlCommand(command),
+ [&handler_invoked](const boost::system::error_code& ec,
+ const ConstJSONFeedPtr& /*feed*/) {
+ handler_invoked = true;
+ ASSERT_TRUE(ec);
+ });
+
+ while (!handler_invoked && !test_socket_->isStopped()) {
+ io_service_.run_one();
+ }
+}
+
+} // end of anonymous namespace