| #include "app.hpp" |
| #include "app_singleton.hpp" |
| #include "async_resp.hpp" |
| #include "http_request.hpp" |
| #include "macros.hpp" |
| #include "privileges.hpp" |
| #include "query.hpp" |
| #include "registries/privilege_registry.hpp" |
| #include "utility.hpp" |
| |
| #include <boost/asio/io_context.hpp> |
| #include <boost/beast/http/verb.hpp> |
| #include <nlohmann/json.hpp> |
| |
| #include <memory> |
| #include <string_view> |
| |
| #include <gtest/gtest.h> |
| |
| namespace crow |
| { |
| namespace |
| { |
| |
| class PluginMacrosTest : public testing::Test |
| { |
| public: |
| PluginMacrosTest() |
| { |
| io = std::make_shared<boost::asio::io_context>(); |
| app = std::make_unique<crow::App>(io); |
| crow::globalBmcWebApp = app.get(); |
| } |
| |
| private: |
| std::shared_ptr<boost::asio::io_context> io; |
| std::unique_ptr<crow::App> app; |
| }; |
| |
| TEST_F(PluginMacrosTest, AddedHandlerCanBeInvoked) |
| { |
| bool called = false; |
| auto callback = [&called](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) { |
| called = true; |
| }; |
| |
| REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, |
| redfish::privileges::getChassis, std::move(callback)); |
| |
| crow::globalBmcWebApp->validate(); |
| |
| constexpr std::string_view url = "/hello/"; |
| std::error_code ec; |
| Request req{{boost::beast::http::verb::get, url, 11}, ec}; |
| |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); |
| crow::globalBmcWebApp->handle(req, asyncResp); |
| |
| EXPECT_TRUE(called); |
| } |
| |
| TEST_F(PluginMacrosTest, DuplicateHandlerThrows) |
| { |
| auto callback = [](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) {}; |
| |
| REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, |
| redfish::privileges::getChassis, std::move(callback)); |
| REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, |
| redfish::privileges::getChassis, std::move(callback)); |
| |
| EXPECT_ANY_THROW(crow::globalBmcWebApp->validate()); |
| } |
| |
| TEST_F(PluginMacrosTest, ReplacedNonExistHandlerThrows) |
| { |
| auto callback = [](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) {}; |
| EXPECT_ANY_THROW(REDFISH_HANDLER_REPLACE("/hello/", |
| boost::beast::http::verb::get, |
| std::move(callback));); |
| } |
| |
| TEST_F(PluginMacrosTest, ReplacedHandlerCanBeInvoked) |
| { |
| bool originalCalled = false; |
| auto originalCallback = |
| [&originalCalled](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) { |
| originalCalled = true; |
| }; |
| |
| bool replacedCalled = false; |
| auto replacedCallback = |
| [&replacedCalled](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) { |
| replacedCalled = true; |
| }; |
| |
| REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, |
| redfish::privileges::getChassis, |
| std::move(originalCallback)); |
| REDFISH_HANDLER_REPLACE("/hello/", boost::beast::http::verb::get, |
| std::move(replacedCallback)); |
| crow::globalBmcWebApp->validate(); |
| |
| constexpr std::string_view url = "/hello/"; |
| std::error_code ec; |
| Request req{{boost::beast::http::verb::get, url, 11}, ec}; |
| |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); |
| crow::globalBmcWebApp->handle(req, asyncResp); |
| |
| EXPECT_FALSE(originalCalled); |
| EXPECT_TRUE(replacedCalled); |
| } |
| |
| TEST_F(PluginMacrosTest, RemoveNonExistHandlerThrows) |
| { |
| EXPECT_ANY_THROW( |
| REDFISH_HANDLER_REMOVE("/hello/", boost::beast::http::verb::get)); |
| } |
| |
| TEST_F(PluginMacrosTest, RemovedHandlerNotBeInvoked) |
| { |
| bool originalCalled = false; |
| auto originalCallback = |
| [&originalCalled](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) { |
| originalCalled = true; |
| }; |
| |
| REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, |
| redfish::privileges::getChassis, |
| std::move(originalCallback)); |
| REDFISH_HANDLER_REMOVE("/hello/", boost::beast::http::verb::get); |
| crow::globalBmcWebApp->validate(); |
| |
| constexpr std::string_view url = "/hello/"; |
| std::error_code ec; |
| Request req{{boost::beast::http::verb::get, url, 11}, ec}; |
| |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); |
| crow::globalBmcWebApp->handle(req, asyncResp); |
| |
| EXPECT_FALSE(originalCalled); |
| } |
| |
| TEST_F(PluginMacrosTest, AppendNonExistHandlerThrows) |
| { |
| auto callback = [](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) {}; |
| |
| EXPECT_ANY_THROW(REDFISH_HANDLER_APPEND( |
| "/hello/", boost::beast::http::verb::get, std::move(callback))); |
| } |
| |
| TEST_F(PluginMacrosTest, AppendRemovedHandlerThrows) |
| { |
| bool originalCalled = false; |
| auto originalCallback = |
| [&originalCalled](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) { |
| originalCalled = true; |
| }; |
| bool appendedCalled = false; |
| auto appendedCallback = |
| [&appendedCalled](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) { |
| appendedCalled = true; |
| }; |
| REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, |
| redfish::privileges::getChassis, |
| std::move(originalCallback)); |
| REDFISH_HANDLER_APPEND("/hello/", boost::beast::http::verb::get, |
| std::move(appendedCallback)); |
| REDFISH_HANDLER_REMOVE("/hello/", boost::beast::http::verb::get); |
| |
| constexpr std::string_view url = "/hello/"; |
| std::error_code ec; |
| Request req{{boost::beast::http::verb::get, url, 11}, ec}; |
| |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); |
| crow::globalBmcWebApp->handle(req, asyncResp); |
| |
| EXPECT_FALSE(originalCalled); |
| EXPECT_FALSE(appendedCalled); |
| } |
| |
| TEST_F(PluginMacrosTest, AppendedHandlerCanBeCompletelyRemoved) |
| { |
| auto callback = [](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) {}; |
| REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, |
| redfish::privileges::getChassis, std::move(callback)); |
| REDFISH_HANDLER_REMOVE("/hello/", boost::beast::http::verb::get); |
| EXPECT_ANY_THROW(REDFISH_HANDLER_APPEND( |
| "/hello/", boost::beast::http::verb::get, std::move(callback))); |
| } |
| |
| TEST_F(PluginMacrosTest, ReplacedHandlerCanBeAppended) |
| { |
| |
| bool replacedCalled = false; |
| auto replacedCallback = |
| [&replacedCalled](const Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& resp) { |
| EXPECT_TRUE(redfish::setUpRedfishRouteOnGlobalApp(req, resp)); |
| replacedCalled = true; |
| }; |
| |
| bool appendedCalled = false; |
| auto appendedCallback = |
| [&appendedCalled](const Request&, |
| const std::shared_ptr<bmcweb::AsyncResp>&) { |
| appendedCalled = true; |
| }; |
| |
| REDFISH_HANDLER_ADD( |
| "/hello/", boost::beast::http::verb::get, |
| redfish::privileges::getChassis, |
| [](const Request&, const std::shared_ptr<bmcweb::AsyncResp>&) {}); |
| REDFISH_HANDLER_REPLACE("/hello/", boost::beast::http::verb::get, |
| std::move(replacedCallback)); |
| REDFISH_HANDLER_APPEND("/hello/", boost::beast::http::verb::get, |
| std::move(appendedCallback)); |
| crow::globalBmcWebApp->validate(); |
| |
| constexpr std::string_view url = "/hello/"; |
| std::error_code ec; |
| Request req{{boost::beast::http::verb::get, url, 11}, ec}; |
| |
| // This is to make sure |asyncResp| destructs |
| { |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); |
| crow::globalBmcWebApp->handle(req, asyncResp); |
| } |
| |
| EXPECT_TRUE(appendedCalled); |
| EXPECT_TRUE(replacedCalled); |
| } |
| |
| // The test makes sure the execution order is: |
| // 1. originalCallback |
| // 2. appendedCallback (if multiple appends exist, the order is LIFO; please |
| // note multiple appends should be rarely used) |
| // 3. callback setup by setUpRedfishRoute |
| TEST_F(PluginMacrosTest, AppendedHandlerBeInvokedInOrder) |
| { |
| crow::Logger::setLogLevel(crow::LogLevel::Debug); |
| |
| bool originalCalled = false; |
| bool appendedOneCalled = false; |
| bool appendedTwoCalled = false; |
| bool onlyCalled = false; |
| auto originalCallback = |
| [&originalCalled, &appendedOneCalled, &appendedTwoCalled, |
| &onlyCalled](const Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& resp) { |
| EXPECT_TRUE(redfish::setUpRedfishRouteOnGlobalApp(req, resp)); |
| EXPECT_FALSE(appendedOneCalled); |
| EXPECT_FALSE(appendedTwoCalled); |
| EXPECT_FALSE(onlyCalled); |
| originalCalled = true; |
| resp->res.jsonValue = R"( |
| { |
| "Members": [ |
| { |
| "@odata.id": "/only" |
| } |
| ] |
| } |
| )"_json; |
| }; |
| |
| auto appendedOneCallback = |
| [&originalCalled, &appendedOneCalled, &appendedTwoCalled, &onlyCalled]( |
| const Request&, const std::shared_ptr<bmcweb::AsyncResp>&) { |
| EXPECT_TRUE(originalCalled); |
| // this tests that the callback setup by setUpRedfishRoute hasn't run |
| EXPECT_FALSE(onlyCalled); |
| // the order is LIFO |
| EXPECT_TRUE(appendedTwoCalled); |
| appendedOneCalled = true; |
| }; |
| |
| auto appendedTwoCallback = |
| [&originalCalled, &appendedOneCalled, &appendedTwoCalled, &onlyCalled]( |
| const Request&, const std::shared_ptr<bmcweb::AsyncResp>&) { |
| EXPECT_TRUE(originalCalled); |
| // this tests that the callback setup by setUpRedfishRoute hasn't run |
| EXPECT_FALSE(onlyCalled); |
| EXPECT_FALSE(appendedOneCalled); |
| appendedTwoCalled = true; |
| }; |
| |
| auto onlyCallback = |
| [&originalCalled, &appendedOneCalled, &appendedTwoCalled, &onlyCalled]( |
| const Request&, const std::shared_ptr<bmcweb::AsyncResp>&) { |
| EXPECT_TRUE(originalCalled); |
| EXPECT_TRUE(appendedOneCalled); |
| EXPECT_TRUE(appendedTwoCalled); |
| onlyCalled = true; |
| }; |
| |
| REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, |
| redfish::privileges::getChassis, |
| std::move(originalCallback)); |
| REDFISH_HANDLER_ADD("/only", boost::beast::http::verb::get, |
| redfish::privileges::getChassis, |
| std::move(onlyCallback)); |
| REDFISH_HANDLER_APPEND("/hello/", boost::beast::http::verb::get, |
| std::move(appendedOneCallback)); |
| REDFISH_HANDLER_APPEND("/hello/", boost::beast::http::verb::get, |
| std::move(appendedTwoCallback)); |
| crow::globalBmcWebApp->validate(); |
| |
| constexpr std::string_view url = "/hello/?only"; |
| std::error_code ec; |
| Request req{{boost::beast::http::verb::get, url, 11}, ec}; |
| |
| // This is to make sure |asyncResp| destructs |
| { |
| auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); |
| asyncResp->res.setCompleteRequestHandler([](crow::Response&) {}); |
| crow::globalBmcWebApp->handle(req, asyncResp); |
| } |
| |
| EXPECT_TRUE(onlyCalled); |
| } |
| |
| } // namespace |
| } // namespace crow |