From 42cee50cc0d676e3120f8d35bfc667ea21e5a5fa Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Fri, 11 Jul 2025 10:30:30 +0200 Subject: [PATCH] (WIP) Add unit-tests for HttpServerConnection (and later others) --- test/CMakeLists.txt | 42 +++ test/base-configuration-fixture.hpp | 53 ++++ test/base-sslcert-fixture.hpp | 47 ++++ test/base-tlsstream-fixture.hpp | 91 +++++++ test/remote-httpmessage.cpp | 103 ++++++++ test/remote-httpserverconnection.cpp | 380 +++++++++++++++++++++++++++ 6 files changed, 716 insertions(+) create mode 100644 test/base-configuration-fixture.hpp create mode 100644 test/base-sslcert-fixture.hpp create mode 100644 test/base-tlsstream-fixture.hpp create mode 100644 test/remote-httpmessage.cpp create mode 100644 test/remote-httpserverconnection.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c87679b08..a74d50e3b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -88,6 +88,8 @@ set(base_test_SOURCES icinga-perfdata.cpp methods-pluginnotificationtask.cpp remote-configpackageutility.cpp + remote-httpserverconnection.cpp + remote-httpmessage.cpp remote-url.cpp ${base_OBJS} $ @@ -271,6 +273,20 @@ add_boost_test(base icinga_perfdata/parse_edgecases icinga_perfdata/empty_warn_crit_min_max methods_pluginnotificationtask/truncate_long_output + remote_httpmessage/request_parse + # remote_httpmessage/request_params + remote_httpserverconnection/setup_certs + remote_httpserverconnection/expect_100_continue + remote_httpserverconnection/bad_request + remote_httpserverconnection/error_access_control + remote_httpserverconnection/error_accept_header + remote_httpserverconnection/error_authenticate_cn + remote_httpserverconnection/error_authenticate_passwd + remote_httpserverconnection/error_authenticate_wronguser + remote_httpserverconnection/error_authenticate_wrongpasswd + remote_httpserverconnection/reuse_connection_and_wg_abort + remote_httpserverconnection/liveness_disconnect + remote_httpserverconnection/cleanup_certs remote_configpackageutility/ValidateName remote_url/id_and_path remote_url/parameters @@ -279,6 +295,32 @@ add_boost_test(base remote_url/illegal_legal_strings ) +if(BUILD_TESTING) + set_tests_properties( + base-remote_httpmessage/request_parse + base-remote_httpserverconnection/expect_100_continue + base-remote_httpserverconnection/bad_request + base-remote_httpserverconnection/error_access_control + base-remote_httpserverconnection/error_accept_header + base-remote_httpserverconnection/error_authenticate_cn + base-remote_httpserverconnection/error_authenticate_passwd + base-remote_httpserverconnection/error_authenticate_wronguser + base-remote_httpserverconnection/error_authenticate_wrongpasswd + base-remote_httpserverconnection/reuse_connection_and_wg_abort + base-remote_httpserverconnection/liveness_disconnect + PROPERTIES FIXTURES_REQUIRED ssl_certs) + + set_tests_properties( + base-remote_httpserverconnection/setup_certs + PROPERTIES FIXTURES_SETUP ssl_certs + ) + + set_tests_properties( + base-remote_httpserverconnection/cleanup_certs + PROPERTIES FIXTURES_CLEANUP ssl_certs + ) +endif() + if(ICINGA2_WITH_LIVESTATUS) set(livestatus_test_SOURCES icingaapplication-fixture.cpp diff --git a/test/base-configuration-fixture.hpp b/test/base-configuration-fixture.hpp new file mode 100644 index 000000000..dcfbe8d43 --- /dev/null +++ b/test/base-configuration-fixture.hpp @@ -0,0 +1,53 @@ +/* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ + +#ifndef CONFIGURATION_FIXTURE_H +#define CONFIGURATION_FIXTURE_H + +#include "base/configuration.hpp" +#include "base/utility.hpp" +#include +#include + +namespace icinga { + +struct ConfigurationDataDirFixture +{ + ConfigurationDataDirFixture() + { + Utility::MkDir(tmpDir, 0700); + } + + String tmpDir = boost::filesystem::temp_directory_path().append("icinga2").string(); + String previousDataDir = std::exchange(Configuration::DataDir, tmpDir); +}; + +struct ConfigurationDataDirCleanupFixture: ConfigurationDataDirFixture +{ + ~ConfigurationDataDirCleanupFixture() + { + Utility::RemoveDirRecursive(std::exchange(Configuration::DataDir, previousDataDir)); + } +}; + +struct ConfigurationCacheDirFixture +{ + ConfigurationCacheDirFixture() + { + Utility::MkDir(tmpCacheDir, 0700); + } + + String tmpCacheDir = boost::filesystem::temp_directory_path().append("icinga2").append("cache").string(); + String previousCacheDir = std::exchange(Configuration::CacheDir, tmpCacheDir); +}; + +struct ConfigurationCacheDirCleanupFixture: ConfigurationCacheDirFixture +{ + ~ConfigurationCacheDirCleanupFixture() + { + Utility::RemoveDirRecursive(std::exchange(Configuration::CacheDir, previousCacheDir)); + } +}; + +} // namespace icinga + +#endif // CONFIGURATION_FIXTURE_H diff --git a/test/base-sslcert-fixture.hpp b/test/base-sslcert-fixture.hpp new file mode 100644 index 000000000..4a4867d68 --- /dev/null +++ b/test/base-sslcert-fixture.hpp @@ -0,0 +1,47 @@ +/* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ + +#ifndef SSLCERT_FIXTURE_H +#define SSLCERT_FIXTURE_H + +#include "remote/apilistener.hpp" +#include "remote/pkiutility.hpp" +#include "test/base-configuration-fixture.hpp" +#include + +namespace icinga{ + +struct SslCertificateFixture: ConfigurationDataDirFixture +{ + SslCertificateFixture() + { + caDir = ApiListener::GetCaDir(); + Utility::MkDir(caDir, 0700); + certsDir = ApiListener::GetCertsDir(); + Utility::MkDir(certsDir, 0700); + + PkiUtility::NewCa(); + if (!Utility::PathExists(certsDir+"ca.crt")) { + Utility::CopyFile(caDir + "ca.crt", certsDir + "ca.crt"); + } + } + + ~SslCertificateFixture() {} + + void EnsureCertFor(const String& name) + { + auto certKeyFileName = certsDir + name + ".crt"; + + if (!Utility::PathExists(certKeyFileName)) { + PkiUtility::NewCert(name, certsDir + name + ".key", certsDir + name + ".csr", + certsDir + name + ".crt"); + PkiUtility::SignCsr(certsDir + name + ".csr", certsDir + name + ".crt"); + } + } + + String caDir; + String certsDir; +}; + +} // namespace icinga + +#endif // SSLCERT_FIXTURE_H diff --git a/test/base-tlsstream-fixture.hpp b/test/base-tlsstream-fixture.hpp new file mode 100644 index 000000000..6655f45f1 --- /dev/null +++ b/test/base-tlsstream-fixture.hpp @@ -0,0 +1,91 @@ +/* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ + +#ifndef TLSSTREAM_FIXTURE_H +#define TLSSTREAM_FIXTURE_H + +#include "test/base-sslcert-fixture.hpp" +#include "base/tlsstream.hpp" +#include "base/io-engine.hpp" +#include + +namespace icinga { + +/** + * Creates a pair of TLS Streams on a random unused port. + */ +struct TlsStreamFixture: SslCertificateFixture +{ + TlsStreamFixture() + { + using namespace boost::asio::ip; + using handshake_type = boost::asio::ssl::stream_base::handshake_type; + + EnsureCertFor("remote"); + EnsureCertFor("local"); + + auto& localContext = IoEngine::Get().GetIoContext(); + + remoteSslCtx = SetupSslContext(certsDir + "remote.crt", certsDir + "remote.key", caDir+"ca.crt", "", DEFAULT_TLS_CIPHERS, + DEFAULT_TLS_PROTOCOLMIN, DebugInfo()); + client = Shared::Make(IoEngine::Get().GetIoContext(), *remoteSslCtx); + + localSslCtx = SetupSslContext(certsDir + "local.crt", certsDir + "local.key", caDir+"ca.crt", + "", DEFAULT_TLS_CIPHERS, DEFAULT_TLS_PROTOCOLMIN, DebugInfo()); + server = Shared::Make(localContext, *localSslCtx); + + std::mutex handshakeMutex; + std::condition_variable handshakeCv; + bool handshakeDone = false; + + tcp::acceptor acceptor{localContext, tcp::endpoint{make_address_v4("127.0.0.1"), 0}}; + acceptor.listen(); + acceptor.async_accept(server->lowest_layer(), [&, this](const boost::system::error_code& ec) { + if (ec) { + std::cout << "Local Accept Error: " << ec.message() << std::endl; + return; + } + server->next_layer().async_handshake(handshake_type::server, [&, this](const boost::system::error_code& ec) { + if (ec) { + std::cout << "Local Handshake Error: " << ec.message() << std::endl; + return; + } + + handshakeMutex.lock(); + handshakeDone = true; + handshakeMutex.unlock(); + handshakeCv.notify_all(); + }); + }); + + boost::system::error_code ec; + if (client->lowest_layer().connect(acceptor.local_endpoint(), ec)) { + std::cout << "Client Connect error: " << ec.message() << std::endl; + } + + if (client->next_layer().handshake(handshake_type::client, ec)) { + std::cout << "Client Handshake error: " << ec.message() << std::endl; + } + if (!client->next_layer().IsVerifyOK()) { + std::cout << "Verify failed for connection" << std::endl; + throw; + } + + std::unique_lock lock{handshakeMutex}; + handshakeCv.wait(lock, [&](){return handshakeDone;}); + } + + ~TlsStreamFixture() + { + std::cout << "TlsStreamFixture done" << std::endl; + } + + Shared::Ptr remoteSslCtx; + Shared::Ptr client; + + Shared::Ptr localSslCtx; + Shared::Ptr server; +}; + +} // namespace icinga + +#endif // HTTPSERVERCONNECTION_FIXTURE_H diff --git a/test/remote-httpmessage.cpp b/test/remote-httpmessage.cpp new file mode 100644 index 000000000..819ef4a57 --- /dev/null +++ b/test/remote-httpmessage.cpp @@ -0,0 +1,103 @@ +/* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ + +#include "test/base-tlsstream-fixture.hpp" +#include "test/icingaapplication-fixture.hpp" +#include "base/base64.hpp" +#include "base/json.hpp" +#include "config/configitem.hpp" +#include "config/configcompiler.hpp" +#include "remote/httphandler.hpp" +#include +#include +#include +#include + +using namespace icinga; +using namespace boost::beast; + +struct HttpMessageFixture: TlsStreamFixture +{ +}; + +BOOST_FIXTURE_TEST_SUITE(remote_httpmessage, HttpMessageFixture) + +BOOST_AUTO_TEST_CASE(request_parse) +{ + std::atomic done = false; + + http::request requestOut; + requestOut.method(http::verb::get); + requestOut.target("https://localhost:5665/v1/test"); + requestOut.set(http::field::authorization, "Basic " + Base64::Encode("invalid:invalid")); + requestOut.set(http::field::accept, "application/json"); + requestOut.set(http::field::connection, "close"); + requestOut.content_length(0); + + auto & io = IoEngine::Get(); + io.SpawnCoroutine(io.GetIoContext(), [&, this](boost::asio::yield_context yc) { + boost::beast::flat_buffer buf; + HttpRequest request(server); + server->async_fill(yc); + BOOST_REQUIRE_NO_THROW(request.ParseHeader(buf, yc)); + + for (auto & field : requestOut.base()) { + BOOST_REQUIRE(request.count(field.name())); + } + + request.ParseBody(buf, yc); + + server->next_layer().async_shutdown(yc); + }); + + http::write(*client, requestOut); + client->flush(); + + BOOST_REQUIRE_NO_THROW(client->next_layer().shutdown()); +} + +BOOST_AUTO_TEST_CASE(request_params) +{ + HttpRequest request(client); + request.body() = JsonEncode(new Dictionary{{"test1", false}, {"test2", true}}); + std::cout << request.body(); + request.target("https://localhost:1234/v1/test?test1&test3&test3"); + request.DecodeParams(); + + BOOST_REQUIRE_EQUAL(request.Params()->Get("test2"), "true"); + BOOST_REQUIRE(request.Params()->Get("test1").IsObjectType()); + BOOST_REQUIRE_EQUAL(request.GetLastParameter("test1"), "true"); + BOOST_REQUIRE(request.Params()->Get("test3").IsObjectType()); + BOOST_REQUIRE_EQUAL(request.GetLastParameter("test3"), "true"); +} + +BOOST_AUTO_TEST_CASE(response_stream_operator) +{ + +} + +BOOST_AUTO_TEST_CASE(response_body_reader) +{ + +} + +BOOST_AUTO_TEST_CASE(response_body_writer) +{ + +} + +BOOST_AUTO_TEST_CASE(response_write_fixed) +{ + +} + +BOOST_AUTO_TEST_CASE(response_write_chunked) +{ + +} + +BOOST_AUTO_TEST_CASE(response_sendjsonbody) +{ + +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/remote-httpserverconnection.cpp b/test/remote-httpserverconnection.cpp new file mode 100644 index 000000000..feaad4670 --- /dev/null +++ b/test/remote-httpserverconnection.cpp @@ -0,0 +1,380 @@ +/* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ + +#include "test/base-tlsstream-fixture.hpp" +#include "test/icingaapplication-fixture.hpp" +#include "base/base64.hpp" +#include "base/json.hpp" +#include "config/configitem.hpp" +#include "config/configcompiler.hpp" +#include "remote/httphandler.hpp" +#include +#include +#include +#include +#include + +using namespace icinga; +using namespace boost::beast; +using namespace boost::unit_test_framework; + +struct HttpServerConnectionFixture : + TlsStreamFixture, IcingaApplicationFixture, ConfigurationCacheDirCleanupFixture +{ + HttpServerConnectionFixture() + : wg(new StoppableWaitGroup) + { + Logger::SetConsoleLogSeverity(icinga::LogDebug); + } + + ~HttpServerConnectionFixture() {}; + + static void CreateApiListener(){ + ConfigItem::RunWithActivationContext(new Function("CreateTestUser", []() { + String config = R"CONFIG( +const NodeName = "local" +object Endpoint "local" {} +object Zone "local" { +endpoints = [ "local" ] +} +object ApiListener "api" { + accept_config = false + accept_commands = false + + bind_host = "localhost" + bind_port = 123456 + ticket_salt = "test" + access_control_allow_origin = ["127.0.0.1"] +} +)CONFIG"; + std::unique_ptr expr = ConfigCompiler::CompileText("", config); + expr->Evaluate(*ScriptFrame::GetCurrentFrame()); + })); + } + + static void CreateTestUsers() + { + ApiUser::Ptr user = new ApiUser; + user->SetName("remote"); + user->SetClientCN("remote"); + user->SetPermissions(new Array{"*"}); + user->Register(); + + user = new ApiUser; + user->SetName("test"); + user->SetPassword("test"); + user->SetPermissions(new Array{"*"}); + user->Register(); + } + + void SetupHttpServerConnection(bool authenticated) + { + String identity = authenticated ? "remote" : "invalid"; + conn = new HttpServerConnection(wg, identity, authenticated, server); + conn->Start(); + } + + void ShutdownTlsConn() + { + boost::system::error_code ec; + if (client->next_layer().shutdown(ec)) { + BOOST_TEST_MESSAGE(ec.message()); + throw; + } + } + + boost::asio::deadline_timer timeout{IoEngine::Get().GetIoContext()}; + HttpServerConnection::Ptr conn; + StoppableWaitGroup::Ptr wg; +}; + +class UnitTestHandler final: public HttpHandler +{ + bool HandleRequest(const WaitGroup::Ptr& waitGroup, HttpRequest& request, HttpResponse& response, + boost::asio::yield_context& yc) override + { + response.result(boost::beast::http::status::ok); + response.body() << "test"; + return true; + } +}; + +REGISTER_URLHANDLER("/v1/test", UnitTestHandler); + +BOOST_FIXTURE_TEST_SUITE(remote_httpserverconnection, HttpServerConnectionFixture) + +BOOST_FIXTURE_TEST_CASE(setup_certs, SslCertificateFixture) +{ + EnsureCertFor("local"); + EnsureCertFor("remote"); +} + +BOOST_AUTO_TEST_CASE(expect_100_continue) +{ + CreateTestUsers(); + SetupHttpServerConnection(true); + + http::request request; + request.method(http::verb::get); + request.version(11); + request.target("https://localhost:5665/v1/test"); + request.set(http::field::expect, "100-continue"); + request.set(http::field::accept, "application/json"); + request.set(http::field::connection, "close"); + request.content_length(0); + http::request_serializer sr(request); + http::write_header(*client, sr); + client->flush(); + + flat_buffer buf; + http::response response; + http::read(*client, buf, response); + + BOOST_REQUIRE_EQUAL(response.version(), 11); + BOOST_REQUIRE_EQUAL(response.result(), http::status::continue_); + + http::write(*client, sr); + client->flush(); + + http::read(*client, buf, response); + BOOST_REQUIRE_EQUAL(response.version(), 11); + BOOST_REQUIRE_EQUAL(response.result(), http::status::ok); + BOOST_REQUIRE_EQUAL(response.body(), "test"); + + BOOST_REQUIRE_NO_THROW(ShutdownTlsConn()); +} + +BOOST_AUTO_TEST_CASE(bad_request) +{ + CreateTestUsers(); + SetupHttpServerConnection(true); + + http::request request; + request.method(http::verb::get); + request.version(12); + request.target("https://localhost:5665/v1/test"); + request.set(http::field::accept, "application/json"); + request.set(http::field::connection, "close"); + request.content_length(0); + http::write(*client, request); + client->flush(); + + flat_buffer buf; + http::response response; + http::read(*client, buf, response); + + BOOST_REQUIRE_EQUAL(response.result(), http::status::bad_request); + BOOST_REQUIRE_NE(response.body().find("

Bad Request

"), std::string::npos); + + BOOST_REQUIRE_NO_THROW(ShutdownTlsConn()); +} + +BOOST_AUTO_TEST_CASE(error_access_control) +{ + CreateTestUsers(); + CreateApiListener(); + SetupHttpServerConnection(true); + + http::request request; + request.method(http::verb::options); + request.target("https://localhost:5665/v1/test"); + request.set(http::field::origin, "127.0.0.1"); + request.set(http::field::access_control_request_method, "GET"); + request.set(http::field::connection, "close"); + request.content_length(0); + http::write(*client, request); + client->flush(); + + flat_buffer buf; + http::response response; + http::read(*client, buf, response); + + BOOST_REQUIRE_EQUAL(response.version(), 11); + BOOST_REQUIRE_EQUAL(response.result(), http::status::ok); + BOOST_REQUIRE_EQUAL(response.body(), "Preflight OK"); + + boost::container::flat_set sv; + boost::container::flat_set options{"GET", "POST", "PUT", "DELETE", "PUSH"}; + + BOOST_REQUIRE_NE(response[http::field::access_control_allow_methods], ""); + BOOST_REQUIRE_NE(response[http::field::access_control_allow_headers], ""); + + BOOST_REQUIRE_NO_THROW(ShutdownTlsConn()); +} + +BOOST_AUTO_TEST_CASE(error_accept_header) +{ + CreateTestUsers(); + SetupHttpServerConnection(true); + + http::request request; + request.method(http::verb::post); + request.target("https://localhost:5665/v1/test"); + request.set(http::field::accept, "text/html"); + request.set(http::field::connection, "close"); + request.content_length(0); + http::write(*client, request); + client->flush(); + + flat_buffer buf; + http::response response; + http::read(*client, buf, response); + + BOOST_REQUIRE_EQUAL(response.version(), 11); + BOOST_REQUIRE_EQUAL(response.result(), http::status::bad_request); + BOOST_REQUIRE_EQUAL(response.body(), "

Accept header is missing or not set to 'application/json'.

\r\n"); + + BOOST_REQUIRE_NO_THROW(ShutdownTlsConn()); +} + +BOOST_AUTO_TEST_CASE(error_authenticate_cn) +{ + CreateTestUsers(); + SetupHttpServerConnection(true); + + http::request request; + request.method(http::verb::get); + request.target("https://localhost:5665/v1/test"); + request.set(http::field::accept, "application/json"); + request.set(http::field::connection, "close"); + request.content_length(0); + http::write(*client, request); + client->flush(); + + flat_buffer buf; + http::response response; + http::read(*client, buf, response); + + BOOST_REQUIRE_EQUAL(response.version(), 11); + BOOST_REQUIRE_EQUAL(response.result(), http::status::ok); + + BOOST_REQUIRE_NO_THROW(ShutdownTlsConn()); +} + +BOOST_AUTO_TEST_CASE(error_authenticate_passwd) +{ + CreateTestUsers(); + SetupHttpServerConnection(false); + + http::request request; + request.method(http::verb::get); + request.target("https://localhost:5665/v1/test"); + request.set(http::field::authorization, "Basic " + Base64::Encode("test:test")); + request.set(http::field::accept, "application/json"); + request.set(http::field::connection, "close"); + request.content_length(0); + http::write(*client, request); + client->flush(); + + flat_buffer buf; + http::response response; + http::read(*client, buf, response); + + BOOST_REQUIRE_EQUAL(response.version(), 11); + BOOST_REQUIRE_EQUAL(response.result(), http::status::ok); + + BOOST_REQUIRE_NO_THROW(ShutdownTlsConn()); +} + +BOOST_AUTO_TEST_CASE(error_authenticate_wronguser) +{ + CreateTestUsers(); + SetupHttpServerConnection(false); + + http::request request; + request.method(http::verb::get); + request.target("https://localhost:5665/v1/test"); + request.set(http::field::authorization, "Basic " + Base64::Encode("invalid:invalid")); + request.set(http::field::accept, "application/json"); + request.set(http::field::connection, "close"); + request.content_length(0); + http::write(*client, request); + client->flush(); + + flat_buffer buf; + http::response response; + http::read(*client, buf, response); + + BOOST_REQUIRE_EQUAL(response.version(), 11); + BOOST_REQUIRE_EQUAL(response.result(), http::status::unauthorized); + Dictionary::Ptr body = JsonDecode(response.body()); + BOOST_REQUIRE(body); + BOOST_REQUIRE_EQUAL(body->Get("error"), 401); + + BOOST_REQUIRE_NO_THROW(ShutdownTlsConn()); +} + +BOOST_AUTO_TEST_CASE(error_authenticate_wrongpasswd) +{ + CreateTestUsers(); + SetupHttpServerConnection(false); + + http::request request; + request.method(http::verb::get); + request.target("https://localhost:5665/v1/test"); + request.set(http::field::authorization, "Basic " + Base64::Encode("test:invalid")); + request.set(http::field::accept, "application/json"); + request.set(http::field::connection, "close"); + request.content_length(0); + http::write(*client, request); + client->flush(); + + flat_buffer buf; + http::response response; + http::read(*client, buf, response); + + BOOST_REQUIRE_EQUAL(response.version(), 11); + BOOST_REQUIRE_EQUAL(response.result(), http::status::unauthorized); + Dictionary::Ptr body = JsonDecode(response.body()); + BOOST_REQUIRE(body); + BOOST_REQUIRE_EQUAL(body->Get("error"), 401); + + BOOST_REQUIRE_NO_THROW(ShutdownTlsConn()); +} + +BOOST_AUTO_TEST_CASE(reuse_connection_and_wg_abort) +{ + CreateTestUsers(); + SetupHttpServerConnection(true); + + http::request request; + request.method(http::verb::get); + request.target("https://localhost:5665/v1/test"); + request.set(http::field::accept, "application/json"); + // request.keep_alive(true); + http::write(*client, request); + client->flush(); + + flat_buffer buf; + http::response response; + http::read(*client, buf, response); + + BOOST_REQUIRE_EQUAL(response.version(), 11); + BOOST_REQUIRE_EQUAL(response.result(), http::status::ok); + BOOST_REQUIRE_EQUAL(response.body(), "test"); + + http::write(*client, request); + client->flush(); + + wg->Join(); + + response.body() = ""; + http::read(*client, buf, response); + BOOST_REQUIRE_EQUAL(response.result(), http::status::ok); + BOOST_REQUIRE_EQUAL(response.body(), "test"); + + BOOST_REQUIRE_NO_THROW(ShutdownTlsConn()); +} + +BOOST_AUTO_TEST_CASE(liveness_disconnect) +{ + SetupHttpServerConnection(false); + + Utility::Sleep(11); + BOOST_REQUIRE(conn->Disconnected()); + + BOOST_REQUIRE_NO_THROW(ShutdownTlsConn()); +} + +BOOST_FIXTURE_TEST_CASE(cleanup_certs, ConfigurationDataDirCleanupFixture) {} + +BOOST_AUTO_TEST_SUITE_END()