From c9fa963e71258c5adfb71cf1996cd1bcb33df0bb Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sun, 26 Feb 2023 08:54:17 +0100 Subject: Start with copy of whiteboard --- tests/test-whiteboard.cpp | 255 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 tests/test-whiteboard.cpp (limited to 'tests/test-whiteboard.cpp') diff --git a/tests/test-whiteboard.cpp b/tests/test-whiteboard.cpp new file mode 100644 index 0000000..3f70bcf --- /dev/null +++ b/tests/test-whiteboard.cpp @@ -0,0 +1,255 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libreichwein/file.h" +#include "libreichwein/process.h" + +#include "config.h" +#include "storage.h" +#include "whiteboard.h" + +namespace bp = boost::process; +namespace fs = std::filesystem; +using namespace Reichwein; +using namespace std::string_literals; + +namespace { + const fs::path webserverConfigFilename{"./webserver.conf"}; + + const fs::path testConfigFilename{"./whiteboard.conf"}; + const fs::path testDbFilename{"./whiteboard.db3"}; +} + +class WhiteboardTest: public ::testing::Test +{ +protected: + WhiteboardTest(){ + } + + ~WhiteboardTest() override{ + } + + void SetUp() override + { + File::setFile(testConfigFilename, R"CONFIG( + + ::1:9876 + . + 2592000 + 4 + 3 + +)CONFIG"); + std::error_code ec; + fs::remove(testDbFilename, ec); + + m_config = std::make_shared(testConfigFilename); + + m_pid = fork(); + if (m_pid == -1) { + throw std::runtime_error("Error on fork(): "s + strerror(errno)); + } else if (m_pid == 0) { // child + Whiteboard whiteboard; + std::vector argvv{{"whiteboard", "-c", testConfigFilename.generic_string()}}; + char* argv[] = {argvv[0].data(), argvv[1].data(), argvv[2].data(), nullptr}; + whiteboard.run(argvv.size(), argv); + exit(0); + } + Process::wait_for_pid_listening_on(m_pid, 9876); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + + void TearDown() override + { + if (m_pid == 0) + throw std::runtime_error("Whiteboard not running on requesting SIGTERM"); + + if (kill(m_pid, SIGTERM) != 0) + throw std::runtime_error("Unable to SIGTERM Whiteboard"); + + if (int result = waitpid(m_pid, NULL, 0); result != m_pid) + throw std::runtime_error("waitpid returned "s + std::to_string(result)); + + std::error_code ec; + fs::remove(testDbFilename, ec); + fs::remove(testConfigFilename, ec); + } + + std::shared_ptr m_config; + pid_t m_pid{}; +}; + +class WebsocketClient +{ +public: + WebsocketClient() + { + std::string host = "::1"; + auto const port = "9876" ; + + // These objects perform our I/O + boost::asio::ip::tcp::resolver resolver{ioc_}; + ws_ = std::make_unique>(ioc_); + + // Look up the domain name + resolver_results_ = resolver.resolve(host, port); + + connect(); + handshake(); + } + + void connect() + { + // Make the connection on the IP address we get from a lookup + ep_ = boost::asio::connect(boost::beast::get_lowest_layer(*ws_), resolver_results_); + } + + void handshake() + { + // Update the host_ string. This will provide the value of the + // Host HTTP header during the WebSocket handshake. + // See https://tools.ietf.org/html/rfc7230#section-5.4 + std::string host{"[::1]:9876"}; + + // Set a decorator to change the User-Agent of the handshake + ws_->set_option(boost::beast::websocket::stream_base::decorator( + [](boost::beast::websocket::request_type& req) + { + req.set(boost::beast::http::field::user_agent, + std::string("Reichwein.IT Test Websocket Client")); + })); + + // Perform the websocket handshake + ws_->handshake(host, "/"); + + } + + void write(const std::string& data) + { + ws_->write(boost::asio::buffer(data)); + } + + std::string read() + { + boost::beast::flat_buffer buffer; + ws_->read(buffer); + return {boost::asio::buffers_begin(buffer.data()), boost::asio::buffers_end(buffer.data())}; + } + + ~WebsocketClient() + { + } + + bool is_open() + { + return ws_->is_open(); + } + +private: + boost::asio::io_context ioc_; + boost::asio::ip::tcp::resolver::results_type resolver_results_; + std::unique_ptr> ws_; + boost::asio::ip::tcp::endpoint ep_; +}; + +// +// tests via websocket server in separate process (hides coverage) +// + +TEST_F(WhiteboardTest, websocket_server_connection) +{ + WebsocketClient wc; +} + +TEST_F(WhiteboardTest, websocket_server_generate_id) +{ + WebsocketClient wc; + + wc.write("newid"); + std::string result0 {wc.read()}; + ASSERT_TRUE(boost::algorithm::starts_with(result0, "newid")); + ASSERT_TRUE(boost::algorithm::ends_with(result0, "")); + ASSERT_EQ(result0.size(), 58); + + wc.write("newid"); + std::string result1 {wc.read()}; + ASSERT_TRUE(boost::algorithm::starts_with(result1, "newid")); + ASSERT_TRUE(boost::algorithm::ends_with(result1, "")); + ASSERT_EQ(result1.size(), 58); + + ASSERT_NE(result0, result1); +} + +// check number of threads as configured +TEST_F(WhiteboardTest, threads) +{ + ASSERT_GE(Process::number_of_threads(m_pid), 4); +} + +TEST_F(WhiteboardTest, max_connections) +{ + WebsocketClient wc1; + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + ASSERT_TRUE(wc1.is_open()); + + WebsocketClient wc2; + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + ASSERT_TRUE(wc2.is_open()); + + WebsocketClient wc3; + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + ASSERT_TRUE(wc3.is_open()); + + ASSERT_THROW(WebsocketClient wc4, std::exception); +} + +TEST_F(WhiteboardTest, id) +{ + WebsocketClient wc; + + wc.write("getfile1"); + std::string result {wc.read()}; + + EXPECT_EQ(result, "getfile00"); + + wc.write("getfile"); + result = wc.read(); + EXPECT_EQ(result, "errorMessage handling error: Invalid id (empty)"); + + wc.write("getfile01234567890123456789"); + result = wc.read(); + EXPECT_EQ(result, "errorMessage handling error: Invalid id (size > 16)"); + + wc.write("getfileX"); + result = wc.read(); + EXPECT_EQ(result, "errorMessage handling error: Invalid id char: X"); + + wc.write("getfilea."); + result = wc.read(); + EXPECT_EQ(result, "errorMessage handling error: Invalid id char: ."); + + wc.write("getfilea$b"); + result = wc.read(); + EXPECT_EQ(result, "errorMessage handling error: Invalid id char: $"); +} + -- cgit v1.2.3