#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 Webserver { public: Webserver() { File::setFile(webserverConfigFilename, R"CONFIG( www-data www-data 10 stats.db ../plugins [::1] websocket ::1:9876
::1
8080 http localhost
)CONFIG"); start(); } ~Webserver() { stop(); fs::remove(webserverConfigFilename); } void start() { m_child = bp::child("/usr/bin/webserver"s, "-c"s, webserverConfigFilename.generic_string()); Process::wait_for_pid_listening_on(m_child.id(), 8080); std::this_thread::sleep_for(std::chrono::milliseconds(20)); } void stop() { m_child.terminate(); } private: bp::child m_child; }; class WhiteboardTest: public ::testing::Test { protected: WhiteboardTest(){ } ~WhiteboardTest() override{ } void SetUp() override { File::setFile(testConfigFilename, R"CONFIG( ::1:9876 . 2592000 4 )CONFIG"); std::error_code ec; fs::remove(testDbFilename, ec); m_config = std::make_shared(testConfigFilename); //m_webserver = std::make_shared(webserverConfigFilename); 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; //std::shared_ptr m_webserver; 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() { } 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_; }; TEST_F(WhiteboardTest, connection) { WebsocketClient wc; } TEST_F(WhiteboardTest, 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); }