Fix: Router Functions are the same weight as classes

also added some basic tests for it.
This commit is contained in:
Maurice Grönwoldt 2024-05-13 18:23:27 +02:00
parent 516e33d165
commit f001b24749
7 changed files with 138 additions and 13 deletions

View file

@ -6,6 +6,8 @@ set(project_lower vweb)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUGSOFT")
option(BUILD_TESTS "Build tests" OFF)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
@ -46,4 +48,24 @@ configure_file(
install(FILES ${PROJECT_BINARY_DIR}/pkg/${project_lower}-config.cmake
${PROJECT_BINARY_DIR}/${project_lower}-config-version.cmake
DESTINATION lib/${PROJECT_NAME}-${version})
DESTINATION lib/${PROJECT_NAME}-${version})
if (BUILD_TESTS)
add_subdirectory(Tests)
get_property(test_sources GLOBAL PROPERTY Test_SRCS)
set(TESTS_SRCS ${test_sources})
Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0
)
FetchContent_MakeAvailable(Catch2)
add_executable(tests ${SOURCE_FILES} ${TESTS_SRCS})
target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
catch_discover_tests(tests WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Tests)
endif ()

View file

@ -22,4 +22,17 @@ macro(add_headers)
endif ()
endforeach ()
set_property(GLOBAL PROPERTY Headers ${tmp})
endmacro()
macro(add_tests)
get_property(tmp GLOBAL PROPERTY Test_SRCS)
file(RELATIVE_PATH _relPath "${PROJECT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
foreach (_src ${ARGN})
if (_relPath)
list(APPEND tmp "${_relPath}/${_src}")
else ()
list(APPEND tmp "${_src}")
endif ()
endforeach ()
set_property(GLOBAL PROPERTY Test_SRCS ${tmp})
endmacro()

View file

@ -21,6 +21,7 @@ public:
void Stop();
void SetRouter(Ref<Router> &router);
void AddSendResponse(SendData);
void CloseRequest(int id) const;
Ref<MiddleWareHandler> &Middleware() { return m_MiddleWare; }
bool HasBodyParser(const std::string &key) {
return m_ParseFunctions.contains(key);

View file

@ -123,6 +123,10 @@ struct RequestJob : public WorkerJob {
MRequestHandler->AddSendResponse(
{0, (ssize_t)content.size(), MRequest->ID, content});
MMiddleWareHandler->Shutdown(MRequest, response);
} catch (const std::bad_alloc &e) {
// We are out of memory... try to clean up some request...
SocketUtils::Close(MRequest->ID);
MRequestHandler->CloseRequest(MRequest->ID);
} catch (const std::exception &e) {
fprintf(stderr,
"[VWEB] >> Failed to handle Requests... Error thrown\n%s\n",
@ -157,8 +161,11 @@ void RequestHandler::AddSendResponse(SendData sendData) {
auto id = sendData.SocketID;
m_Server->m_OutRequest.Add(id, sendData);
if (!m_SocketManager->SetSendListen(id)) {
SocketUtils::Close(id);
m_Server->m_OutRequest.Remove(id);
CloseRequest(id);
}
}
void RequestHandler::CloseRequest(const int id) const {
SocketUtils::Close(id);
m_Server->m_OutRequest.Remove(id);
}
} // namespace VWeb

View file

@ -156,28 +156,23 @@ struct InlineRoute : Route {
};
void Router::Get(const std::string &path, const RouteFunction &func) {
m_Tree.Add(s_HttpMethodToString[HttpMethod::GET] + path,
(uint32_t)HttpMethod::GET,
m_Tree.Add(path, static_cast<uint32_t>(HttpMethod::GET),
[func] { return std::make_shared<InlineRoute>(func); });
}
void Router::Post(const std::string &path, const RouteFunction &func) {
m_Tree.Add(s_HttpMethodToString[HttpMethod::POST] + path,
(uint32_t)HttpMethod::POST,
m_Tree.Add(path, static_cast<uint32_t>(HttpMethod::POST),
[func] { return std::make_shared<InlineRoute>(func); });
}
void Router::Put(const std::string &path, const RouteFunction &func) {
m_Tree.Add(s_HttpMethodToString[HttpMethod::PUT] + path,
(uint32_t)HttpMethod::PUT,
m_Tree.Add(path, static_cast<uint32_t>(HttpMethod::PUT),
[func] { return std::make_shared<InlineRoute>(func); });
}
void Router::Patch(const std::string &path, const RouteFunction &func) {
m_Tree.Add(s_HttpMethodToString[HttpMethod::PATCH] + path,
(uint32_t)HttpMethod::PATCH,
m_Tree.Add(path, static_cast<uint32_t>(HttpMethod::PATCH),
[func] { return std::make_shared<InlineRoute>(func); });
}
void Router::Delete(const std::string &path, const RouteFunction &func) {
m_Tree.Add(s_HttpMethodToString[HttpMethod::DELETE] + path,
(uint32_t)HttpMethod::DELETE,
m_Tree.Add(path, static_cast<uint32_t>(HttpMethod::DELETE),
[func] { return std::make_shared<InlineRoute>(func); });
}
} // namespace VWeb

3
Tests/CMakeLists.txt Normal file
View file

@ -0,0 +1,3 @@
add_tests(
Router.cpp
)

84
Tests/Router.cpp Normal file
View file

@ -0,0 +1,84 @@
#include "Includes/Router.h"
#include "Includes/Request.h"
#include "Includes/Response.h"
#include <catch2/catch_test_macros.hpp>
namespace VWeb {
class FailureRoute final : public Route {
public:
bool IsAllowed(Request &request) override { return false; }
};
TEST_CASE("route_that_exists_returns_route", "[Router]") {
Router router;
Ref<Request> request = CreateRef<Request>();
request->URI = "/test";
router.Register<Route>("/test");
REQUIRE(router.FindRoute(request) != nullptr);
}
TEST_CASE("route_that_not_exists_returns_nullptr", "[Router]") {
Router router;
Ref<Request> request = CreateRef<Request>();
request->URI = "/testre";
router.Register<Route>("/test");
REQUIRE(router.FindRoute(request) == nullptr);
}
TEST_CASE("route_matches_exactly", "[Router]") {
Router router;
Ref<Request> request = CreateRef<Request>();
request->URI = "/test/id";
router.Register<FailureRoute>("/test/:id");
router.Register<Route>("/test/id");
auto route = router.FindRoute(request);
REQUIRE(route);
REQUIRE(route->IsAllowed(*request));
}
TEST_CASE("route_can_handle_variants", "[Router]") {
Router router;
Ref<Request> request = CreateRef<Request>();
request->URI = "/test/1";
router.Register<FailureRoute>("/test/id");
router.Register<Route>("/test/:id");
auto route = router.FindRoute(request);
REQUIRE(route);
REQUIRE(route->IsAllowed(*request));
}
TEST_CASE("router_returns_function_instead_of_class", "[Router]") {
Router router;
Ref<Request> request = CreateRef<Request>();
request->URI = "/test/id";
router.Register<FailureRoute>("/test/:id");
router.Get("/test/id", [](Request &, Response &) {
return true;
});
auto route = router.FindRoute(request);
REQUIRE(route);
REQUIRE(route->IsAllowed(*request));
}
TEST_CASE("router_returns_class_if_function_is_provided", "[Router]") {
Router router;
Ref<Request> request = CreateRef<Request>();
request->URI = "/test/1";
router.Register<FailureRoute>("/test/:id");
router.Get("/test/id", [](Request &, Response &) {
return true;
});
auto route = router.FindRoute(request);
REQUIRE(route);
REQUIRE_FALSE(route->IsAllowed(*request));
}
} // namespace VWeb