diff --git a/librepomgr/tests/webapi.cpp b/librepomgr/tests/webapi.cpp index b98a2d7..7e1c005 100644 --- a/librepomgr/tests/webapi.cpp +++ b/librepomgr/tests/webapi.cpp @@ -1,5 +1,9 @@ +#include "../buildactions/buildaction.h" #include "../serversetup.h" +#include "../webapi/params.h" +#include "../webapi/routes.h" #include "../webapi/server.h" +#include "../webapi/session.h" #include "../webclient/session.h" #include "../../libpkg/data/config.h" @@ -13,6 +17,8 @@ #include #include +#include + #include #include #include @@ -21,6 +27,7 @@ using namespace std; using namespace CPPUNIT_NS; using namespace CppUtilities; +using namespace CppUtilities::Literals; using namespace LibRepoMgr; using namespace LibRepoMgr::WebAPI; @@ -28,6 +35,7 @@ using namespace LibRepoMgr::WebAPI; class WebAPITests : public TestFixture { CPPUNIT_TEST_SUITE(WebAPITests); CPPUNIT_TEST(testBasicNetworking); + CPPUNIT_TEST(testPostingBuildAction); CPPUNIT_TEST_SUITE_END(); public: @@ -37,6 +45,9 @@ public: void testRoutes(const std::list> &routes); void testBasicNetworking(); + std::shared_ptr invokeRouteHandler( + void (*handler)(const Params ¶ms, ResponseHandler &&handler), std::vector> &&queryParams); + void testPostingBuildAction(); private: ServiceSetup m_setup; @@ -46,7 +57,7 @@ private: CPPUNIT_TEST_SUITE_REGISTRATION(WebAPITests); -unsigned short randomPort() +static unsigned short randomPort() { random_device dev; default_random_engine engine(dev()); @@ -68,6 +79,9 @@ void WebAPITests::tearDown() { } +/*! + * \brief Runs the Boost.Asio/Beast server and client to simulte accessing the specified \a routes. + */ void WebAPITests::testRoutes(const std::list> &routes) { // get first route @@ -83,9 +97,6 @@ void WebAPITests::testRoutes(const std::listm_socket.is_open()) { - server->m_socket.cancel(); - } if (server->m_acceptor.is_open()) { server->m_acceptor.cancel(); } @@ -104,7 +115,7 @@ void WebAPITests::testRoutes(const std::listsecond(session, error); if (++currentRoute == routes.end()) { - boost::asio::post(server->m_socket.get_executor(), stopServer); + boost::asio::post(server->m_acceptor.get_executor(), stopServer); return; } testNextRoute(); @@ -115,6 +126,10 @@ void WebAPITests::testRoutes(const std::list(session.response); CPPUNIT_ASSERT(!error); CPPUNIT_ASSERT(!response.body().empty()); - cout << "index: " << response.body() << endl; } }, { "/foo", [](const WebClient::Session &session, const WebClient::HttpClientError &error) { @@ -145,7 +159,67 @@ void WebAPITests::testBasicNetworking() CPPUNIT_ASSERT(!error); CPPUNIT_ASSERT(!response.body().empty()); CPPUNIT_ASSERT_EQUAL("application/json"s, response[boost::beast::http::field::content_type].to_string()); - cout << "status: " << response.body() << endl; } }, }); } + +/*! + * \brief Invokes the specified route \a handler with the specified \a queryParams and returns the response. + */ +std::shared_ptr WebAPITests::invokeRouteHandler( + void (*handler)(const Params &, ResponseHandler &&), std::vector> &&queryParams) +{ + auto &ioc = m_setup.webServer.ioContext; + auto session = std::make_shared(boost::asio::ip::tcp::socket(ioc), m_setup); + auto params = WebAPI::Params(m_setup, *session, WebAPI::Url(std::string_view(), std::string_view(), std::move(queryParams))); + auto response = std::shared_ptr(); + session->assignEmptyRequest(); + std::invoke(handler, params, [&response](std::shared_ptr &&r) { response = r; }); + return response; +} + +/*! + * \brief Parses the specified \a json as build action storing results in \a buildAction. + */ +static void parseBuildAction(BuildAction &buildAction, std::string_view json) +{ + const auto doc = ReflectiveRapidJSON::JsonReflector::parseJsonDocFromString(json.data(), json.size()); + if (!doc.IsObject()) { + CPPUNIT_FAIL("json document is no object"); + } + auto errors = ReflectiveRapidJSON::JsonDeserializationErrors(); + ReflectiveRapidJSON::JsonReflector::pull(buildAction, doc.GetObject(), &errors); + CPPUNIT_ASSERT_EQUAL_MESSAGE("response is valid json", 0_st, errors.size()); +} + +/*! + * \brief Tests the handler to post a build action. + * \remarks Only covers a very basic use so far. + */ +void WebAPITests::testPostingBuildAction() +{ + { + const auto response = invokeRouteHandler(&WebAPI::Routes::postBuildAction, {}); + CPPUNIT_ASSERT_MESSAGE("got response", response); + CPPUNIT_ASSERT_EQUAL_MESSAGE("response body", "need exactly either one type or one task parameter"s, response->body()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("response ok", boost::beast::http::status::bad_request, response->result()); + } + { + const auto response = invokeRouteHandler(&WebAPI::Routes::postBuildAction, + { + { "type"sv, "prepare-build"sv }, + { "start-condition"sv, "manually"sv }, + }); + CPPUNIT_ASSERT_MESSAGE("got response", response); + + auto buildAction = BuildAction(); + parseBuildAction(buildAction, response->body()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("expected build action type returned", BuildActionType::PrepareBuild, buildAction.type); + + const auto createdBuildAction = m_setup.building.getBuildAction(buildAction.id); + CPPUNIT_ASSERT_MESSAGE("build action actually created", createdBuildAction); + CPPUNIT_ASSERT_EQUAL_MESSAGE("build action not started yet", BuildActionStatus::Created, createdBuildAction->status); + CPPUNIT_ASSERT_EQUAL_MESSAGE("build action has no result yet", BuildActionResult::None, createdBuildAction->result); + CPPUNIT_ASSERT_EQUAL_MESSAGE("response ok", boost::beast::http::status::ok, response->result()); + } +} diff --git a/librepomgr/webapi/params.cpp b/librepomgr/webapi/params.cpp index 0410acf..a2790ea 100644 --- a/librepomgr/webapi/params.cpp +++ b/librepomgr/webapi/params.cpp @@ -9,6 +9,13 @@ using namespace CppUtilities; namespace LibRepoMgr { namespace WebAPI { +Url::Url(std::string_view path, std::string_view hash, std::vector> &¶ms) + : path(path) + , hash(hash) + , params(std::move(params)) +{ +} + Url::Url(const Request &request) { const auto target = request.target(); diff --git a/librepomgr/webapi/params.h b/librepomgr/webapi/params.h index 2ee980b..c21b7b5 100644 --- a/librepomgr/webapi/params.h +++ b/librepomgr/webapi/params.h @@ -35,6 +35,7 @@ inline BadRequest::BadRequest(const char *message) } struct LIBREPOMGR_EXPORT Url { + Url(std::string_view path, std::string_view hash, std::vector> &¶ms); Url(const Request &request); std::string_view path; std::string_view hash; @@ -55,6 +56,7 @@ inline bool Url::hasPrettyFlag() const struct LIBREPOMGR_EXPORT Params { Params(ServiceSetup &setup, Session &session); + Params(ServiceSetup &setup, Session &session, Url &&target); ServiceSetup &setup; Session &session; const Url target; @@ -70,6 +72,13 @@ inline Params::Params(ServiceSetup &setup, Session &session) { } +inline Params::Params(ServiceSetup &setup, Session &session, Url &&target) + : setup(setup) + , session(session) + , target(std::move(target)) +{ +} + inline const Request &Params::request() const { return session.request(); diff --git a/librepomgr/webapi/routes.cpp b/librepomgr/webapi/routes.cpp index c9b8ff4..f9bfe86 100644 --- a/librepomgr/webapi/routes.cpp +++ b/librepomgr/webapi/routes.cpp @@ -620,7 +620,7 @@ void postBuildAction(const Params ¶ms, ResponseHandler &&handler) auto buildLock = params.setup.building.lockToWrite(); const auto id = params.setup.building.allocateBuildActionID(); auto startsAfterBuildActions = params.setup.building.getBuildActions(startAfterIds); - const auto startNow = startImmediately || BuildAction::haveSucceeded(startsAfterBuildActions); + const auto startNow = startImmediately || (!startsAfterBuildActions.empty() && BuildAction::haveSucceeded(startsAfterBuildActions)); buildLock.unlock(); auto buildAction = std::make_shared(id); if (!directories.empty()) { @@ -734,7 +734,7 @@ void postBuildActionsFromTask(const Params ¶ms, ResponseHandler &&handler, c auto &building = params.setup.building; auto buildLock = building.lockToWrite(); auto startsAfterBuildActions = building.getBuildActions(startAfterIds); - const auto startNow = startImmediately || BuildAction::haveSucceeded(startsAfterBuildActions); + const auto startNow = startImmediately || (!startsAfterBuildActions.empty() && BuildAction::haveSucceeded(startsAfterBuildActions)); for (auto &newBuildAction : newBuildActions) { newBuildAction->id = building.allocateBuildActionID(); if (lastBuildAction) { diff --git a/librepomgr/webapi/routes.h b/librepomgr/webapi/routes.h index 2dc5b3a..a780903 100644 --- a/librepomgr/webapi/routes.h +++ b/librepomgr/webapi/routes.h @@ -1,6 +1,7 @@ #ifndef LIBREPOMGR_ROUTES_H #define LIBREPOMGR_ROUTES_H +#include "../global.h" #include "./typedefs.h" #include "../buildactions/buildactionfwd.h" @@ -9,27 +10,27 @@ namespace LibRepoMgr { namespace WebAPI { namespace Routes { -void getRoot(const Params ¶ms, ResponseHandler &&handler); -void getVersion(const Params ¶ms, ResponseHandler &&handler); -void getStatus(const Params ¶ms, ResponseHandler &&handler); -void getDatabases(const Params ¶ms, ResponseHandler &&handler); -void getUnresolved(const Params ¶ms, ResponseHandler &&handler); -void getPackages(const Params ¶ms, ResponseHandler &&handler); -void getBuildActions(const Params ¶ms, ResponseHandler &&handler); -void getBuildActionDetails(const Params ¶ms, ResponseHandler &&handler); -void getBuildActionOutput(const Params ¶ms, ResponseHandler &&handler); -void getBuildActionLogFile(const Params ¶ms, ResponseHandler &&handler); -void getBuildActionArtefact(const Params ¶ms, ResponseHandler &&handler); -void postLoadPackages(const Params ¶ms, ResponseHandler &&handler); -void postDumpCacheFile(const Params ¶ms, ResponseHandler &&handler); -void postBuildAction(const Params ¶ms, ResponseHandler &&handler); -void postBuildActionsFromTask(const Params ¶ms, ResponseHandler &&handler, const std::string &taskName, const std::string &directory, - const std::vector &startAfterIds, bool startImmediately); -void deleteBuildActions(const Params ¶ms, ResponseHandler &&handler); -void postCloneBuildActions(const Params ¶ms, ResponseHandler &&handler); -void postStartBuildActions(const Params ¶ms, ResponseHandler &&handler); -void postStopBuildActions(const Params ¶ms, ResponseHandler &&handler); -void postQuit(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getRoot(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getVersion(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getStatus(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getDatabases(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getUnresolved(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getPackages(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getBuildActions(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getBuildActionDetails(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getBuildActionOutput(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getBuildActionLogFile(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void getBuildActionArtefact(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void postLoadPackages(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void postDumpCacheFile(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void postBuildAction(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void postBuildActionsFromTask(const Params ¶ms, ResponseHandler &&handler, const std::string &taskName, + const std::string &directory, const std::vector &startAfterIds, bool startImmediately); +LIBREPOMGR_EXPORT void deleteBuildActions(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void postCloneBuildActions(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void postStartBuildActions(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void postStopBuildActions(const Params ¶ms, ResponseHandler &&handler); +LIBREPOMGR_EXPORT void postQuit(const Params ¶ms, ResponseHandler &&handler); } // namespace Routes } // namespace WebAPI diff --git a/librepomgr/webapi/session.h b/librepomgr/webapi/session.h index 54d339d..1685653 100644 --- a/librepomgr/webapi/session.h +++ b/librepomgr/webapi/session.h @@ -18,13 +18,14 @@ namespace WebAPI { class Session : public std::enable_shared_from_this { public: - Session(boost::asio::ip::tcp::socket socket, ServiceSetup &config); + Session(boost::asio::ip::tcp::socket &&socket, ServiceSetup &config); void receive(); void respond(std::shared_ptr &&response); void respond(const char *localFilePath, const char *mimeType, std::string_view urlPath); void close(); const Request &request() const; + void assignEmptyRequest(); boost::asio::ip::tcp::socket &socket(); void received(boost::system::error_code ec, std::size_t bytesTransferred); void responded(boost::system::error_code ec, std::size_t bytesTransferred, bool shouldClose); @@ -38,7 +39,7 @@ private: std::shared_ptr m_res; }; -inline Session::Session(boost::asio::ip::tcp::socket socket, ServiceSetup &setup) +inline Session::Session(boost::asio::ip::tcp::socket &&socket, ServiceSetup &setup) : m_socket(std::move(socket)) , m_strand(m_socket.get_executor()) , m_setup(setup) @@ -50,6 +51,11 @@ inline const Request &Session::request() const return m_parser->get(); } +inline void Session::assignEmptyRequest() +{ + m_parser = std::make_unique(); +} + inline boost::asio::ip::tcp::socket &Session::socket() { return m_socket;