| #include "async_resp.hpp" // IWYU pragma: keep |
| #include "http_request.hpp" |
| #include "routing.hpp" |
| #include "utility.hpp" |
| |
| #include <boost/beast/http/message.hpp> // IWYU pragma: keep |
| #include <boost/beast/http/verb.hpp> |
| |
| #include <memory> |
| #include <string> |
| #include <string_view> |
| #include <system_error> |
| |
| #include <gtest/gtest.h> // IWYU pragma: keep |
| |
| // 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 nullCallback = [](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)>(std::string(url)) |
| .methods(boost::beast::http::verb::get)(nullCallback); |
| router.validate(); |
| EXPECT_EQ(router.findRoute(req).allowHeader, "GET"); |
| EXPECT_NE(router.findRoute(req).route.rule, nullptr); |
| |
| Request patchReq{{boost::beast::http::verb::patch, url, 11}, ec}; |
| EXPECT_EQ(router.findRoute(patchReq).route.rule, nullptr); |
| |
| router.newRuleTagged<getParameterTag(url)>(std::string(url)) |
| .methods(boost::beast::http::verb::patch)(nullCallback); |
| router.validate(); |
| EXPECT_EQ(router.findRoute(req).allowHeader, "GET, PATCH"); |
| EXPECT_NE(router.findRoute(req).route.rule, nullptr); |
| EXPECT_NE(router.findRoute(patchReq).route.rule, nullptr); |
| } |
| |
| TEST(Router, 404) |
| { |
| bool callbackCalled = false; |
| // Callback handler that does nothing |
| auto callback = |
| [&](const Request& req, const std::shared_ptr<bmcweb::AsyncResp>&) { |
| BMCWEB_LOG_DEBUG << "=> req: " << req.target(); |
| callbackCalled = 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> asyncResp = |
| std::make_shared<bmcweb::AsyncResp>(); |
| router.handle(req, asyncResp); |
| BMCWEB_LOG_DEBUG << "=> res: " |
| << " resultInt: " << asyncResp->res.resultInt() |
| << " reason: " << asyncResp->res.reason(); |
| } |
| |
| EXPECT_TRUE(callbackCalled); |
| } |
| |
| TEST(Router, 405) |
| { |
| // Callback handler that does nothing |
| auto nullCallback = [](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) {}; |
| bool called = false; |
| auto notAllowedCallback = |
| [&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)>(std::string(url)) |
| .methods(boost::beast::http::verb::get)(nullCallback); |
| router.newRuleTagged<getParameterTag(url)>("/foo/<path>") |
| .methodNotAllowed()(notAllowedCallback); |
| router.validate(); |
| { |
| std::shared_ptr<bmcweb::AsyncResp> asyncResp = |
| std::make_shared<bmcweb::AsyncResp>(); |
| |
| router.handle(req, asyncResp); |
| BMCWEB_LOG_DEBUG << "=> res: " |
| << " resultInt: " << asyncResp->res.resultInt() |
| << " reason: " << asyncResp->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); |
| } |
| |
| #ifndef PLATFORM_PLUGINS_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 |
| |
| #ifdef PLATFORM_PLUGINS_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 |