#include "expresscpp/console.hpp"
#include "expresscpp/expresscpp.hpp"
#include "expresscpp/fetch.hpp"
#include "expresscpp/impl/routing_stack.hpp"
#include "gtest/gtest.h"
#include "nlohmann/json.hpp"
#include "test_utils.hpp"
using namespace expresscpp;
using namespace std::string_literals;
constexpr std::size_t port = 8081u;
std::shared_ptr RoutePath(ExpressCpp &app, std::string_view path, std::string_view test_data,
HttpMethod method) {
auto req = std::make_shared(path, method);
auto res = std::make_shared(nullptr);
std::string name = "special_header";
req->setHeader(name, test_data.data());
app.HandleRequest(req, res, nullptr);
return res;
}
TEST(BasicTests, RunAndStop) {
ExpressCpp expresscpp;
std::thread runner([&expresscpp]() { expresscpp.Run(); });
std::this_thread::sleep_for(std::chrono::milliseconds(100));
expresscpp.Stop();
runner.join();
}
TEST(BasicTests, DumpStack) {
{
ExpressCpp app;
app.Get("/", [](auto /*req*/, auto res, auto /*next*/) { res->Send("/"); });
const auto rs = app.Stack();
auto contains = [&]() {
for (const auto &r : rs) {
Console::Debug(fmt::format(R"(contains "{}")", r.path));
if (r.path == "/") {
return true;
}
}
return false;
}();
EXPECT_TRUE(contains);
}
{
ExpressCpp app;
app.Get("/a", [](auto /*req*/, auto res, auto /*next*/) { res->Send("/"); });
app.Get("/b", [](auto /*req*/, auto res, auto /*next*/) { res->Send("/"); });
const auto rs = app.Stack();
auto contains_a = [&]() {
for (const auto &r : rs) {
if (r.path == "/a") {
EXPECT_EQ(r.method, HttpMethod::Get);
return true;
}
}
return false;
}();
EXPECT_TRUE(contains_a);
auto contains_b = [&]() {
for (const auto &r : rs) {
if (r.path == "/b") {
EXPECT_EQ(r.method, HttpMethod::Get);
return true;
}
}
return false;
}();
EXPECT_TRUE(contains_b);
}
}
TEST(BasicTests, DumpStackWithMiddleware) {
ExpressCpp app;
auto logger = [](auto /*req*/, auto /*res*/, auto next) {
Console::Debug("function called");
next();
};
app.Use(logger);
app.Get("/", [](auto /*req*/, auto res, auto /*next*/) { res->Send("/"); });
const auto rs = app.Stack();
std::size_t number_of_root_routes = 0;
auto contains_routes_in_stack = [&]() {
for (const auto &r : rs) {
if (r.path == "/") {
number_of_root_routes++;
// logger has method all, route has method get
EXPECT_TRUE(r.method == HttpMethod::Get || r.method == HttpMethod::All);
}
}
if (number_of_root_routes == 2) {
return true;
}
return false;
}();
EXPECT_TRUE(contains_routes_in_stack);
}
TEST(BasicTests, SingleRouteWithParams) {
ExpressCpp app;
app.Get("/:id", [](auto req, auto res, auto /*next*/) {
EXPECT_EQ(req->GetParams().size(), 1);
EXPECT_EQ(req->GetParams().at("id"), "10");
res->Json(R"({"status": 1 })");
});
app.Listen(port, [=](auto ec) {
EXPECT_FALSE(ec);
auto r = fetch(fmt::format("localhost:{}/10", port), { HttpMethod::Get});
const auto expected = nlohmann::json::parse(r);
EXPECT_EQ(expected["status"], 1);
});
}
TEST(BasicTests, SingleRouteWithRangeParams) {
ExpressCpp app;
app.Get("/things/:from-:to", [](auto req, auto res, auto /*next*/) {
EXPECT_EQ(req->GetParams().size(), 2);
EXPECT_EQ(req->GetParams().at("from"), "157");
EXPECT_EQ(req->GetParams().at("to"), "2158");
res->Json(R"({"status": 1 })");
});
app.Listen(8081, [](auto ec) {
EXPECT_FALSE(ec);
auto r = fetch(fmt::format("http://localhost:{}/things/157-2158", port), { HttpMethod::Get});
const auto expected = nlohmann::json::parse(r);
EXPECT_EQ(expected["status"], 1);
});
}
TEST(BasicTests, SingleRouteWithParamsAndQueryParams) {
ExpressCpp app;
app.Get("/things/:id", [](auto req, auto res, auto /*next*/) {
EXPECT_EQ(req->GetParams().size(), 1);
EXPECT_EQ(req->GetParams().at("id"), "1234");
EXPECT_EQ(req->GetQueryParams().size(), 1);
EXPECT_EQ(req->GetQueryParams().at("key1"), "value1");
res->Json(R"({"status": 1 })");
});
app.Listen(8081, [](auto ec) {
EXPECT_FALSE(ec);
auto r = fetch(fmt::format("http://localhost:{}/things/1234?key1=value1", port), { HttpMethod::Get});
const auto expected = nlohmann::json::parse(r);
EXPECT_EQ(expected["status"], 1);
});
}
TEST(BasicTests, SingleRouteWithQueryParams) {
ExpressCpp app;
app.Get("/", [](auto req, auto res, auto /*next*/) {
EXPECT_EQ(req->GetParams().size(), 0);
EXPECT_EQ(req->GetQueryParams().size(), 1);
EXPECT_EQ(req->GetQueryParams().at("key1"), "value1");
res->Json(R"({"status": 1 })");
});
app.Listen(8081, [](auto ec) {
EXPECT_FALSE(ec);
auto r = fetch(fmt::format("http://localhost:{}/?key1=value1", port), { HttpMethod::Get});
const auto expected = nlohmann::json::parse(r);
EXPECT_EQ(expected["status"], 1);
});
}
TEST(BasicTests, SingleRouteJson) {
TestCallSleeper sleeper(1);
ExpressCpp app;
app.Get("/", [&](auto /*req*/, auto res, auto /*next*/) {
sleeper.Call();
res->Json(R"({"status": 1 })");
});
app.Listen(port, [](auto ec) {
EXPECT_FALSE(ec);
auto r = fetch(fmt::format("localhost:{}/", port), { HttpMethod::Get});
const auto expected = nlohmann::json::parse(r);
EXPECT_EQ(expected["status"], 1);
});
EXPECT_TRUE(sleeper.Wait());
}
TEST(BasicTests, SingleRouteWithoutBeast) {
{
ExpressCpp app;
app.Get("/", [](auto req, auto res, auto /*next*/) {
auto headers = req->getHeaders();
EXPECT_NE(headers.find("special_header"), headers.end());
auto value = headers.at("special_header");
EXPECT_EQ(value, "special_header_value");
EXPECT_ANY_THROW(res->Json(R"({"status": 1 })"););
});
auto res = RoutePath(app, "/", "special_header_value", HttpMethod::Get);
EXPECT_EQ(res->response_sent_, true);
}
{
ExpressCpp app;
app.Get("/aaa", [](auto req, auto res, auto /*next*/) {
auto headers = req->getHeaders();
EXPECT_NE(headers.find("special_header"), headers.end());
auto value = headers.at("special_header");
EXPECT_EQ(value, "special_header_value");
EXPECT_ANY_THROW(res->Json(R"({"status": 1 })"););
});
auto res = RoutePath(app, "/aaa", "special_header_value", HttpMethod::Get);
EXPECT_EQ(res->response_sent_, true);
}
{
ExpressCpp app;
app.Post("/aaa", [](auto req, auto res, auto /*next*/) {
auto headers = req->getHeaders();
EXPECT_NE(headers.find("special_header"), headers.end());
auto value = headers.at("special_header");
EXPECT_EQ(value, "special_header_value");
EXPECT_ANY_THROW(res->Json(R"({"status": 1 })"););
});
auto res = RoutePath(app, "/aaa", "special_header_value", HttpMethod::Post);
EXPECT_EQ(res->response_sent_, true);
}
}
TEST(BasicTests, PostRouteWithoutBeast) {
ExpressCpp app;
app.Post("/aaa", [](auto req, auto res, auto /*next*/) {
auto headers = req->getHeaders();
EXPECT_NE(headers.find("special_header"), headers.end());
auto value = headers.at("special_header");
EXPECT_EQ(value, "special_header_value");
EXPECT_ANY_THROW(res->Json(R"({"status": 1 })"););
});
auto res = RoutePath(app, "/aaa", "special_header_value", HttpMethod::Post);
EXPECT_EQ(res->response_sent_, true);
}
TEST(BasicTests, MultiRoute) {
ExpressCpp app;
app.Get("/", [](auto /*req*/, auto res, auto /*next*/) { res->Send("/"); });
app.Get("/b", [](auto /*req*/, auto res, auto /*next*/) { res->Send("/b"); });
app.Listen(8081, [](auto ec) {
EXPECT_FALSE(ec);
auto r = fetch(fmt::format("localhost:{}/", port), { HttpMethod::Get});
auto rb = fetch(fmt::format("localhost:{}/b", port), { HttpMethod::Get});
EXPECT_EQ(r, "/");
EXPECT_EQ(rb, "/b");
});
}
TEST(BasicTests, MultipleListenCalls) {
ExpressCpp app;
app.Get("/a", [](auto /*req*/, auto res, auto /*next*/) { res->Send("/a"); });
app.Listen(port, [](const auto ec) { EXPECT_EQ(ec.value(), 0); });
app.Listen(port, [](const auto ec) { EXPECT_EQ(ec, std::errc::already_connected); });
}
TEST(BasicTests, StartMultipleApps) {
{
ExpressCpp app1;
constexpr std::size_t port1 = 8081u;
app1.Get("/a", [](auto /*req*/, auto res, auto /*next*/) { res->Send("/1"); });
app1.Listen(port1, [&](std::error_code ec) {
EXPECT_EQ(ec.value(), 0);
auto r1 = fetch(fmt::format("localhost:{}/a", port1), { HttpMethod::Get});
EXPECT_EQ(r1, "/1");
});
ExpressCpp app2;
constexpr std::size_t port2 = 8082u;
app2.Get("/a", [](auto /*req*/, auto res, auto /*next*/) { res->Send("/2"); });
app2.Listen(port2, [&](std::error_code ec) {
EXPECT_EQ(ec.value(), 0);
auto r2 = fetch(fmt::format("localhost:{}/a", port2), { HttpMethod::Get});
EXPECT_EQ(r2, "/2");
});
}
}