diff options
author | Nan Zhou <nanzhoumails@gmail.com> | 2022-09-10 21:11:41 +0300 |
---|---|---|
committer | Nan Zhou <nanzhoumails@gmail.com> | 2022-09-22 02:44:37 +0300 |
commit | c33a039b56fc5789ae71289adaca1e572a48d318 (patch) | |
tree | 8b936ca291537cebebf6bf505e565bff9635f511 /test/http | |
parent | 6ab9ad5492b9203c7dd8fb3e8c59d37bbc455cde (diff) | |
download | bmcweb-c33a039b56fc5789ae71289adaca1e572a48d318.tar.xz |
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 <nanzhoumails@gmail.com>
Change-Id: I4521c7ef5fa03c47cca5c146d322bbb51365ee96
Diffstat (limited to 'test/http')
-rw-r--r-- | test/http/crow_getroutes_test.cpp | 71 | ||||
-rw-r--r-- | test/http/router_test.cpp | 126 | ||||
-rw-r--r-- | test/http/utility_test.cpp | 226 |
3 files changed, 423 insertions, 0 deletions
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 <boost/beast/http/status.hpp> + +#include <memory> + +#include <gmock/gmock.h> // IWYU pragma: keep +#include <gtest/gtest.h> // IWYU pragma: keep + +// IWYU pragma: no_include <gtest/gtest-message.h> +// IWYU pragma: no_include <gtest/gtest-test-part.h> +// IWYU pragma: no_include "gtest/gtest_pred_impl.h" +// IWYU pragma: no_include <gmock/gmock-matchers.h> +// IWYU pragma: no_include <gmock/gmock-more-matchers.h> +// IWYU pragma: no_include <gtest/gtest-matchers.h> + +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 <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; + 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<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 notFoundCalled = false; + // Callback handler that does nothing + auto nullCallback = + [¬FoundCalled](const Request&, + const std::shared_ptr<bmcweb::AsyncResp>&) { + 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<getParameterTag(url)>("/foo/<path>") + .notFound()(nullCallback); + router.validate(); + { + std::shared_ptr<bmcweb::AsyncResp> asyncResp = + std::make_shared<bmcweb::AsyncResp>(); + + router.handle(req, asyncResp); + } + EXPECT_TRUE(notFoundCalled); +} + +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; + std::error_code ec; + + constexpr const std::string_view url = "/foo/bar"; + + Request req{{boost::beast::http::verb::patch, url, 11}, ec}; + + 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); + } + 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 <boost/url/error.hpp> +#include <boost/url/url.hpp> +#include <boost/url/url_view.hpp> +#include <nlohmann/json.hpp> + +#include <cstdint> +#include <ctime> +#include <functional> +#include <limits> +#include <string> +#include <string_view> + +#include <gtest/gtest.h> // IWYU pragma: keep +// IWYU pragma: no_include <gtest/gtest-message.h> +// IWYU pragma: no_include <gtest/gtest-test-part.h> +// 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<boost::urls::url_view> 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("<uint><double><int>")); + EXPECT_EQ(1, getParameterTag("<int>")); + EXPECT_EQ(2, getParameterTag("<uint>")); + EXPECT_EQ(3, getParameterTag("<float>")); + EXPECT_EQ(3, getParameterTag("<double>")); + EXPECT_EQ(4, getParameterTag("<str>")); + EXPECT_EQ(4, getParameterTag("<string>")); + EXPECT_EQ(5, getParameterTag("<path>")); + EXPECT_EQ(6 * 6 + 6 + 1, getParameterTag("<int><int><int>")); + EXPECT_EQ(6 * 6 + 6 + 2, getParameterTag("<uint><int><int>")); + EXPECT_EQ(6 * 6 + 6 * 3 + 2, getParameterTag("<uint><double><int>")); +} + +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 |