blob: e5b2cfa3652057e636548cafb1c2d0211d24f2da [file] [log] [blame]
#include "routing.hpp"
#include <memory>
#include <string>
#include <string_view>
#include <system_error> // NOLINT
#include <gtest/gtest.h> // IWYU pragma: keep
#include "boost/beast/http/message.hpp" // IWYU pragma: keep // NOLINT
#include "boost/beast/http/verb.hpp" // NOLINT
#include "http_request.hpp"
#include "logging.hpp"
#include "utility.hpp"
#include "async_resp.hpp" // IWYU pragma: keep
#include "sessions.hpp"
// IWYU pragma: no_include <boost/beast/http/impl/message.hpp>
// IWYU pragma: no_include "gtest/gtest_pred_impl.h"
// IWYU pragma: no_include <boost/intrusive/detail/list_iterator.hpp>
// IWYU pragma: no_include <gtest/gtest-message.h>
// IWYU pragma: no_include <gtest/gtest-test-part.h>
// IWYU pragma: no_forward_declare bmcweb::AsyncResp
namespace crow {
namespace {
using ::crow::black_magic::getParameterTag;
TEST(Router, AllowHeader) {
// Callback handler that does nothing
auto null_callback = [](const Request&,
const std::shared_ptr<bmcweb::AsyncResp>&) {};
Router router(/*allowSessionEmpty=*/true);
std::error_code ec;
constexpr std::string_view url = "/foo";
Request req{{boost::beast::http::verb::get, url, 11}, ec};
// No route should return no methods.
router.validate();
EXPECT_EQ(router.findRoute(req).allowHeader, "");
EXPECT_EQ(router.findRoute(req).route.rule, nullptr);
router.newRuleTagged<getParameterTag(url)>(url).methods(
boost::beast::http::verb::get)(null_callback);
router.validate();
EXPECT_EQ(router.findRoute(req).allowHeader, "GET");
EXPECT_NE(router.findRoute(req).route.rule, nullptr);
Request patch_req{{boost::beast::http::verb::patch, url, 11}, ec};
EXPECT_EQ(router.findRoute(patch_req).route.rule, nullptr);
router.newRuleTagged<getParameterTag(url)>(url).methods(
boost::beast::http::verb::patch)(null_callback);
router.validate();
EXPECT_EQ(router.findRoute(req).allowHeader, "GET, PATCH");
EXPECT_NE(router.findRoute(req).route.rule, nullptr);
EXPECT_NE(router.findRoute(patch_req).route.rule, nullptr);
}
TEST(Router, 404) {
bool callback_called = false;
// Callback handler that does nothing
auto callback = [&](const Request& req,
const std::shared_ptr<bmcweb::AsyncResp>&) {
BMCWEB_LOG_DEBUG << "=> req: " << req.target();
callback_called = true;
};
Router router(/*allowSessionEmpty=*/true);
std::error_code ec;
constexpr std::string_view url = "/foo/bar";
Request req{{boost::beast::http::verb::get, url, 11}, ec};
req.session = std::make_shared<persistent_data::UserSession>();
req.session->username = "batman";
router.newRuleTagged<getParameterTag(url)>("/foo/<path>")
.methods(boost::beast::http::verb::get)
.notFound()(callback);
router.validate();
{
std::shared_ptr<bmcweb::AsyncResp> async_resp =
std::make_shared<bmcweb::AsyncResp>();
router.handle(req, async_resp);
BMCWEB_LOG_DEBUG << "=> res: "
<< " resultInt: " << async_resp->res.resultInt()
<< " reason: " << async_resp->res.reason();
}
EXPECT_TRUE(callback_called);
}
TEST(Router, 405) {
// Callback handler that does nothing
auto null_callback = [](const Request&,
const std::shared_ptr<bmcweb::AsyncResp>&) {};
bool called = false;
auto not_allowed_callback =
[&called](const Request&, const std::shared_ptr<bmcweb::AsyncResp>&) {
called = true;
};
Router router(/*allowSessionEmpty=*/true);
std::error_code ec;
constexpr std::string_view url = "/foo/bar";
Request req{{boost::beast::http::verb::patch, url, 11}, ec};
req.session = std::make_shared<persistent_data::UserSession>();
req.session->username = "batman";
router.newRuleTagged<getParameterTag(url)>(url).methods(
boost::beast::http::verb::get)(null_callback);
router.newRuleTagged<getParameterTag(url)>("/foo/<path>")
.methodNotAllowed()(not_allowed_callback);
router.validate();
{
std::shared_ptr<bmcweb::AsyncResp> async_resp =
std::make_shared<bmcweb::AsyncResp>();
router.handle(req, async_resp);
BMCWEB_LOG_DEBUG << "=> res: "
<< " resultInt: " << async_resp->res.resultInt()
<< " reason: " << async_resp->res.reason();
}
EXPECT_TRUE(called);
}
TEST(Trie, TrieTestReturnsRuleIndexSuccessfullyWithoutWildcards) {
Trie trie;
trie.add("/redfish", 1);
trie.add("/redfish/v1", 2);
trie.add("/redfish/v1/Chassis", 3);
EXPECT_EQ(trie.find("/redfish").first, 1);
EXPECT_EQ(trie.find("/redfish/v1").first, 2);
EXPECT_EQ(trie.find("/redfish/v1/Chassis").first, 3);
}
TEST(Trie, TrieTestReturnsRuleIndexSuccessfullyWithStringAndPathWildcard) {
Trie trie;
trie.add("/redfish", 1);
trie.add("/redfish/v1", 2);
trie.add("/redfish/v1/Chassis", 3);
trie.add("/redfish/v1/Chassis/<str>", 4);
trie.add("/redfish/<path>", 5);
EXPECT_EQ(trie.find("/redfish/v1/Chassis/test").first, 4);
EXPECT_EQ(trie.find("/redfish/v1/random/path/here").first, 5);
}
#if !defined(PLATFORM_PLUGINS_ENABLED) && \
!defined(GOOGLE_COMMON_PLUGINS_ENABLED) && \
!defined(GOOGLE_PLUGINS_INTERNAL_ENABLED)
TEST(Trie, TrieTestReturnsLowestIndexWhenUrlMatchesMultipleRules) {
Trie trie;
trie.add("/redfish", 1);
trie.add("/redfish/<path>", 2);
trie.add("/redfish/v1", 3);
EXPECT_EQ(trie.find("/redfish/v1").first, 2);
}
#endif
#if defined(PLATFORM_PLUGINS_ENABLED) || \
defined(GOOGLE_COMMON_PLUGINS_ENABLED) || \
defined(GOOGLE_PLUGINS_INTERNAL_ENABLED)
TEST(Trie, TrieReturnsFixedPathBeforeWildCard) {
Trie trie;
trie.add("/redfish/<path>", 0);
trie.add("/redfish", 1);
trie.add("/redfish/v1", 2);
trie.add("/redfish/v1/Chassis", 3);
trie.add("/redfish/v1/Chassis/<str>", 4);
trie.add("/redfish/v1/Chassis/test", 6);
EXPECT_EQ(trie.find("/redfish/v1/Chassis/test").first, 6);
EXPECT_EQ(trie.find("/redfish/v1/Chassis/test1").first, 4);
EXPECT_EQ(trie.find("/redfish/v1/random/path/here").first, 0);
}
TEST(Trie, TrieReturnsFixedPathBeforeWildCardWhenAddedInReverseOrder) {
Trie trie;
trie.add("/redfish/<path>", 0);
trie.add("/redfish", 1);
trie.add("/redfish/v1", 2);
trie.add("/redfish/v1/Chassis", 3);
trie.add("/redfish/v1/Chassis/test", 4);
trie.add("/redfish/v1/Chassis/<str>", 5);
EXPECT_EQ(trie.find("/redfish/v1/Chassis/test").first, 4);
EXPECT_EQ(trie.find("/redfish/v1/Chassis/test1").first, 5);
EXPECT_EQ(trie.find("/redfish/v1/random/path/here").first, 0);
}
#endif
} // namespace
} // namespace crow