summaryrefslogtreecommitdiff
path: root/test/http
diff options
context:
space:
mode:
authorNan Zhou <nanzhoumails@gmail.com>2022-09-10 21:11:41 +0300
committerNan Zhou <nanzhoumails@gmail.com>2022-09-22 02:44:37 +0300
commitc33a039b56fc5789ae71289adaca1e572a48d318 (patch)
tree8b936ca291537cebebf6bf505e565bff9635f511 /test/http
parent6ab9ad5492b9203c7dd8fb3e8c59d37bbc455cde (diff)
downloadbmcweb-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.cpp71
-rw-r--r--test/http/router_test.cpp126
-rw-r--r--test/http/utility_test.cpp226
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 =
+ [&notFoundCalled](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