Fix posting build action to start it later; add basic test for the route
This commit is contained in:
parent
0c8f0b04c2
commit
3894d911d7
|
@ -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 <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
@ -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<std::pair<string, WebClient::Session::Handler>> &routes);
|
||||
void testBasicNetworking();
|
||||
std::shared_ptr<WebAPI::Response> invokeRouteHandler(
|
||||
void (*handler)(const Params ¶ms, ResponseHandler &&handler), std::vector<std::pair<std::string_view, std::string_view>> &&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<std::pair<std::string, WebClient::Session::Handler>> &routes)
|
||||
{
|
||||
// get first route
|
||||
|
@ -83,9 +97,6 @@ void WebAPITests::testRoutes(const std::list<std::pair<std::string, WebClient::S
|
|||
|
||||
// define function to stop server
|
||||
const auto stopServer = [&] {
|
||||
if (server->m_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::list<std::pair<std::string, WebClient::S
|
|||
handleResponse = [&](WebClient::Session &session, const WebClient::HttpClientError &error) {
|
||||
currentRoute->second(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<std::pair<std::string, WebClient::S
|
|||
m_setup.webServer.ioContext.run();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Checks a few basic routes using the Boost.Beast-based HTTP server and client to test whether basic
|
||||
* networking and HTTP processing works.
|
||||
*/
|
||||
void WebAPITests::testBasicNetworking()
|
||||
{
|
||||
testRoutes({
|
||||
|
@ -123,7 +138,6 @@ void WebAPITests::testBasicNetworking()
|
|||
const auto &response = get<Response>(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<Response> WebAPITests::invokeRouteHandler(
|
||||
void (*handler)(const Params &, ResponseHandler &&), std::vector<std::pair<std::string_view, std::string_view>> &&queryParams)
|
||||
{
|
||||
auto &ioc = m_setup.webServer.ioContext;
|
||||
auto session = std::make_shared<WebAPI::Session>(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<WebAPI::Response>();
|
||||
session->assignEmptyRequest();
|
||||
std::invoke(handler, params, [&response](std::shared_ptr<WebAPI::Response> &&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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,13 @@ using namespace CppUtilities;
|
|||
namespace LibRepoMgr {
|
||||
namespace WebAPI {
|
||||
|
||||
Url::Url(std::string_view path, std::string_view hash, std::vector<std::pair<std::string_view, std::string_view>> &¶ms)
|
||||
: path(path)
|
||||
, hash(hash)
|
||||
, params(std::move(params))
|
||||
{
|
||||
}
|
||||
|
||||
Url::Url(const Request &request)
|
||||
{
|
||||
const auto target = request.target();
|
||||
|
|
|
@ -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<std::pair<std::string_view, std::string_view>> &¶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();
|
||||
|
|
|
@ -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<BuildAction>(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) {
|
||||
|
|
|
@ -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<BuildActionIdType> &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<BuildActionIdType> &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
|
||||
|
|
|
@ -18,13 +18,14 @@ namespace WebAPI {
|
|||
|
||||
class Session : public std::enable_shared_from_this<Session> {
|
||||
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> &&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<void> 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<RequestParser>();
|
||||
}
|
||||
|
||||
inline boost::asio::ip::tcp::socket &Session::socket()
|
||||
{
|
||||
return m_socket;
|
||||
|
|
Loading…
Reference in New Issue