From c33a039b56fc5789ae71289adaca1e572a48d318 Mon Sep 17 00:00:00 2001 From: Nan Zhou Date: Sat, 10 Sep 2022 18:11:41 +0000 Subject: treewide: reorganize unit tests Like other C++ projects, unit tests normally are in a separate repo and respect the folder structure of the file under test. This commit deleted all "ut" folder and move tests to a "test" folder. The test folder also has similar structure as the main folder. This commit also made neccessary include changes to make codes compile. Unused tests are untouched. Tested: unit test passed. Reference: [1] https://github.com/grpc/grpc/tree/master/test [2] https://github.com/boostorg/core/tree/414dfb466878af427d33b36e6ccf84d21c0e081b/test [3] Many other OpenBMC repos: https://github.com/openbmc/entity-manager/tree/master/test [4] https://stackoverflow.com/questions/2360734/whats-a-good-directory-structure-for-larger-c-projects-using-makefile Signed-off-by: Nan Zhou Change-Id: I4521c7ef5fa03c47cca5c146d322bbb51365ee96 --- test/http/crow_getroutes_test.cpp | 71 ++++++++++++ test/http/router_test.cpp | 126 +++++++++++++++++++++ test/http/utility_test.cpp | 226 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 423 insertions(+) create mode 100644 test/http/crow_getroutes_test.cpp create mode 100644 test/http/router_test.cpp create mode 100644 test/http/utility_test.cpp (limited to 'test/http') diff --git a/test/http/crow_getroutes_test.cpp b/test/http/crow_getroutes_test.cpp new file mode 100644 index 0000000000..23a511e735 --- /dev/null +++ b/test/http/crow_getroutes_test.cpp @@ -0,0 +1,71 @@ +#include "app.hpp" +#include "routing.hpp" + +#include + +#include + +#include // IWYU pragma: keep +#include // IWYU pragma: keep + +// IWYU pragma: no_include +// IWYU pragma: no_include +// IWYU pragma: no_include "gtest/gtest_pred_impl.h" +// IWYU pragma: no_include +// IWYU pragma: no_include +// IWYU pragma: no_include + +namespace crow +{ +namespace +{ + +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::Pointee; +using ::testing::UnorderedElementsAre; + +TEST(GetRoutes, TestEmptyRoutes) +{ + App app; + app.validate(); + + EXPECT_THAT(app.getRoutes(), IsEmpty()); +} + +// Tests that static urls are correctly passed +TEST(GetRoutes, TestOneRoute) +{ + App app; + + BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); + + // TODO: "/" doesn't get reported in |getRoutes| today. Uncomment this once + // it is fixed + // EXPECT_THAT(app.getRoutes(), + // testing::ElementsAre(Pointee(Eq("/")))); +} + +// Tests that static urls are correctly passed +TEST(GetRoutes, TestlotsOfRoutes) +{ + App app; + BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); + BMCWEB_ROUTE(app, "/foo")([]() { return boost::beast::http::status::ok; }); + BMCWEB_ROUTE(app, "/bar")([]() { return boost::beast::http::status::ok; }); + BMCWEB_ROUTE(app, "/baz")([]() { return boost::beast::http::status::ok; }); + BMCWEB_ROUTE(app, "/boo")([]() { return boost::beast::http::status::ok; }); + BMCWEB_ROUTE(app, "/moo")([]() { return boost::beast::http::status::ok; }); + + app.validate(); + + // TODO: "/" doesn't get reported in |getRoutes| today. Uncomment this once + // it is fixed + EXPECT_THAT(app.getRoutes(), UnorderedElementsAre( + // Pointee(Eq("/")), + Pointee(Eq("/foo")), Pointee(Eq("/bar")), + Pointee(Eq("/baz")), Pointee(Eq("/boo")), + Pointee(Eq("/moo")))); +} +} // namespace +} // namespace crow diff --git a/test/http/router_test.cpp b/test/http/router_test.cpp new file mode 100644 index 0000000000..9b5d9bec98 --- /dev/null +++ b/test/http/router_test.cpp @@ -0,0 +1,126 @@ +#include "async_resp.hpp" // IWYU pragma: keep +#include "http_request.hpp" +#include "routing.hpp" +#include "utility.hpp" + +#include // IWYU pragma: keep +#include + +#include +#include +#include +#include + +#include // IWYU pragma: keep + +// IWYU pragma: no_include +// IWYU pragma: no_include "gtest/gtest_pred_impl.h" +// IWYU pragma: no_include +// IWYU pragma: no_include +// IWYU pragma: no_include +// 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&) {}; + + Router router; + std::error_code ec; + + constexpr const 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(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(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 notFoundCalled = false; + // Callback handler that does nothing + auto nullCallback = + [¬FoundCalled](const Request&, + const std::shared_ptr&) { + notFoundCalled = true; + }; + + Router router; + std::error_code ec; + + constexpr const std::string_view url = "/foo/bar"; + + Request req{{boost::beast::http::verb::get, url, 11}, ec}; + + router.newRuleTagged("/foo/") + .notFound()(nullCallback); + router.validate(); + { + std::shared_ptr asyncResp = + std::make_shared(); + + router.handle(req, asyncResp); + } + EXPECT_TRUE(notFoundCalled); +} + +TEST(Router, 405) +{ + // Callback handler that does nothing + auto nullCallback = [](const Request&, + const std::shared_ptr&) {}; + bool called = false; + auto notAllowedCallback = + [&called](const Request&, const std::shared_ptr&) { + called = true; + }; + + Router router; + std::error_code ec; + + constexpr const std::string_view url = "/foo/bar"; + + Request req{{boost::beast::http::verb::patch, url, 11}, ec}; + + router.newRuleTagged(std::string(url)) + .methods(boost::beast::http::verb::get)(nullCallback); + router.newRuleTagged("/foo/") + .methodNotAllowed()(notAllowedCallback); + router.validate(); + { + std::shared_ptr asyncResp = + std::make_shared(); + + router.handle(req, asyncResp); + } + EXPECT_TRUE(called); +} +} // namespace +} // namespace crow diff --git a/test/http/utility_test.cpp b/test/http/utility_test.cpp new file mode 100644 index 0000000000..8eef93f4a3 --- /dev/null +++ b/test/http/utility_test.cpp @@ -0,0 +1,226 @@ +#include "bmcweb_config.h" + +#include "utility.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep +// IWYU pragma: no_include +// IWYU pragma: no_include +// IWYU pragma: no_include "gtest/gtest_pred_impl.h" + +namespace crow::utility +{ +namespace +{ + +using ::crow::black_magic::getParameterTag; + +TEST(Utility, Base64DecodeAuthString) +{ + std::string authString("dXNlcm40bWU6cGFzc3cwcmQ="); + std::string result; + EXPECT_TRUE(base64Decode(authString, result)); + EXPECT_EQ(result, "usern4me:passw0rd"); +} + +TEST(Utility, Base64DecodeNonAscii) +{ + std::string junkString("\xff\xee\xdd\xcc\x01\x11\x22\x33"); + std::string result; + EXPECT_FALSE(base64Decode(junkString, result)); +} + +TEST(Utility, Base64EncodeString) +{ + using namespace std::string_literals; + std::string encoded; + + encoded = base64encode(""); + EXPECT_EQ(encoded, ""); + + encoded = base64encode("f"); + EXPECT_EQ(encoded, "Zg=="); + + encoded = base64encode("f0"); + EXPECT_EQ(encoded, "ZjA="); + + encoded = base64encode("f0\0"s); + EXPECT_EQ(encoded, "ZjAA"); + + encoded = base64encode("f0\0 "s); + EXPECT_EQ(encoded, "ZjAAIA=="); + + encoded = base64encode("f0\0 B"s); + EXPECT_EQ(encoded, "ZjAAIEI="); + + encoded = base64encode("f0\0 Ba"s); + EXPECT_EQ(encoded, "ZjAAIEJh"); + + encoded = base64encode("f0\0 Bar"s); + EXPECT_EQ(encoded, "ZjAAIEJhcg=="); +} + +TEST(Utility, Base64EncodeDecodeString) +{ + using namespace std::string_literals; + std::string data("Data fr\0m 90 reading a \nFile"s); + std::string encoded = base64encode(data); + std::string decoded; + EXPECT_TRUE(base64Decode(encoded, decoded)); + EXPECT_EQ(data, decoded); +} + +TEST(Utility, UrlFromPieces) +{ + boost::urls::url url = urlFromPieces("redfish", "v1", "foo"); + EXPECT_EQ(std::string_view(url.data(), url.size()), "/redfish/v1/foo"); + + url = urlFromPieces("/", "badString"); + EXPECT_EQ(std::string_view(url.data(), url.size()), "/%2f/badString"); + + url = urlFromPieces("bad?tring"); + EXPECT_EQ(std::string_view(url.data(), url.size()), "/bad%3ftring"); + + url = urlFromPieces("/", "bad&tring"); + EXPECT_EQ(std::string_view(url.data(), url.size()), "/%2f/bad&tring"); +} + +TEST(Utility, readUrlSegments) +{ + boost::urls::result parsed = + boost::urls::parse_relative_ref("/redfish/v1/Chassis#/Fans/0/Reading"); + + EXPECT_TRUE(readUrlSegments(*parsed, "redfish", "v1", "Chassis")); + + EXPECT_FALSE(readUrlSegments(*parsed, "FOOBAR", "v1", "Chassis")); + + EXPECT_FALSE(readUrlSegments(*parsed, "redfish", "v1")); + + EXPECT_FALSE( + readUrlSegments(*parsed, "redfish", "v1", "Chassis", "FOOBAR")); + + std::string out1; + std::string out2; + std::string out3; + EXPECT_TRUE(readUrlSegments(*parsed, "redfish", "v1", std::ref(out1))); + EXPECT_EQ(out1, "Chassis"); + + out1 = out2 = out3 = ""; + EXPECT_TRUE(readUrlSegments(*parsed, std::ref(out1), std::ref(out2), + std::ref(out3))); + EXPECT_EQ(out1, "redfish"); + EXPECT_EQ(out2, "v1"); + EXPECT_EQ(out3, "Chassis"); + + out1 = out2 = out3 = ""; + EXPECT_TRUE(readUrlSegments(*parsed, "redfish", std::ref(out1), "Chassis")); + EXPECT_EQ(out1, "v1"); + + out1 = out2 = out3 = ""; + EXPECT_TRUE(readUrlSegments(*parsed, std::ref(out1), "v1", std::ref(out2))); + EXPECT_EQ(out1, "redfish"); + EXPECT_EQ(out2, "Chassis"); + + EXPECT_FALSE(readUrlSegments(*parsed, "too", "short")); + + EXPECT_FALSE(readUrlSegments(*parsed, "too", "long", "too", "long")); + + EXPECT_FALSE( + readUrlSegments(*parsed, std::ref(out1), "v2", std::ref(out2))); + + EXPECT_FALSE(readUrlSegments(*parsed, "redfish", std::ref(out1), + std::ref(out2), std::ref(out3))); + + parsed = boost::urls::parse_relative_ref("/absolute/url"); + EXPECT_TRUE(readUrlSegments(*parsed, "absolute", "url")); + + parsed = boost::urls::parse_relative_ref("not/absolute/url"); + EXPECT_FALSE(readUrlSegments(*parsed, "not", "absolute", "url")); + + parsed = boost::urls::parse_relative_ref("/excellent/path"); + + EXPECT_TRUE(readUrlSegments(*parsed, "excellent", "path", OrMorePaths())); + EXPECT_TRUE(readUrlSegments(*parsed, "excellent", OrMorePaths())); + EXPECT_TRUE(readUrlSegments(*parsed, OrMorePaths())); +} + +TEST(Utility, ValidateAndSplitUrlPositive) +{ + std::string host; + std::string urlProto; + uint16_t port = 0; + std::string path; + ASSERT_TRUE(validateAndSplitUrl("https://foo.com:18080/bar", urlProto, host, + port, path)); + EXPECT_EQ(host, "foo.com"); + EXPECT_EQ(urlProto, "https"); + EXPECT_EQ(port, 18080); + + EXPECT_EQ(path, "/bar"); + + // query string + ASSERT_TRUE(validateAndSplitUrl("https://foo.com:18080/bar?foobar=1", + urlProto, host, port, path)); + EXPECT_EQ(path, "/bar?foobar=1"); + + // fragment + ASSERT_TRUE(validateAndSplitUrl("https://foo.com:18080/bar#frag", urlProto, + host, port, path)); + EXPECT_EQ(path, "/bar#frag"); + + // Missing port + ASSERT_TRUE( + validateAndSplitUrl("https://foo.com/bar", urlProto, host, port, path)); + EXPECT_EQ(port, 443); + + // Missing path defaults to "/" + ASSERT_TRUE( + validateAndSplitUrl("https://foo.com/", urlProto, host, port, path)); + EXPECT_EQ(path, "/"); + + // If http push eventing is allowed, allow http and pick a default port of + // 80, if it's not, parse should fail. + ASSERT_EQ( + validateAndSplitUrl("http://foo.com/bar", urlProto, host, port, path), + bmcwebInsecureEnableHttpPushStyleEventing); + if constexpr (bmcwebInsecureEnableHttpPushStyleEventing) + { + EXPECT_EQ(port, 80); + } +} + +TEST(Router, ParameterTagging) +{ + EXPECT_EQ(6 * 6 + 6 * 3 + 2, getParameterTag("")); + EXPECT_EQ(1, getParameterTag("")); + EXPECT_EQ(2, getParameterTag("")); + EXPECT_EQ(3, getParameterTag("")); + EXPECT_EQ(3, getParameterTag("")); + EXPECT_EQ(4, getParameterTag("")); + EXPECT_EQ(4, getParameterTag("")); + EXPECT_EQ(5, getParameterTag("")); + EXPECT_EQ(6 * 6 + 6 + 1, getParameterTag("")); + EXPECT_EQ(6 * 6 + 6 + 2, getParameterTag("")); + EXPECT_EQ(6 * 6 + 6 * 3 + 2, getParameterTag("")); +} + +TEST(URL, JsonEncoding) +{ + std::string urlString = "/foo"; + EXPECT_EQ(nlohmann::json(boost::urls::url(urlString)), urlString); + EXPECT_EQ(nlohmann::json(boost::urls::url_view(urlString)), urlString); +} + +} // namespace +} // namespace crow::utility -- cgit v1.2.3