blob: eae5920a0072a445ba1f6158055da0d98eca3c4a [file] [log] [blame]
#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