blob: 1d368791177c0847d56dfb72867354d59303d782 [file]
#include "control.hpp"
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/functional/any_invocable.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "dbus_utility.hpp"
#include "test/redfish-core/lib/snapshot_fixture.hpp"
#include <nlohmann/json.hpp>
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "test/g3/mock_managed_store.hpp"
#include "test/g3/mock_managed_store_test.hpp"
#include "sdbusplus/message/native_types.hpp"
namespace redfish {
namespace {
using ::dbus::utility::DbusVariantType;
using ::managedStore::KeyType;
using ::managedStore::ManagedType;
using ::managedStore::SimulateFailedAsyncPostDbusCallThreadSafeAction;
using ::managedStore::SimulateSuccessfulAsyncPostDbusCallThreadSafeAction;
using ::testing::_;
using ::testing::An;
constexpr absl::string_view kChassisId = "platform1";
constexpr absl::string_view kControlId = "PL1";
constexpr absl::string_view kControlId2 = "PL2";
constexpr absl::string_view kServiceName = "xyz.openbmc_project.ControlService";
constexpr absl::string_view kInterfaceName =
"xyz.openbmc_project.Chassis.Control";
class ControlTest : public SnapshotFixture {
protected:
void TearDown() override {
auto store = dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore());
if (store != nullptr) {
for (const auto& key : registered_keys_) {
EXPECT_TRUE(store->evictMockObjectFromManagedStore(key).ok());
}
}
}
void UpsertMockObject(const KeyType& key,
const std::shared_ptr<managedStore::ValueType>& value) {
auto store = dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore());
ASSERT_NE(store, nullptr);
ASSERT_TRUE(store->upsertMockObjectIntoManagedStore(key, value).ok());
registered_keys_.push_back(key);
}
std::string GetControlPath(absl::string_view control_id) const {
return absl::StrCat("/xyz/openbmc_project/control/", kChassisId, "/",
control_id);
}
void MockControlPaths(const std::vector<std::string>& control_ids) {
std::vector<std::string> interfaces = {std::string(kInterfaceName)};
KeyType key(ManagedType::kManagedSubtreePaths,
absl::StrCat("/xyz/openbmc_project/control/", kChassisId), 0,
interfaces);
dbus::utility::MapperGetSubTreePathsResponse mockPaths;
for (const auto& cid : control_ids) {
mockPaths.push_back(GetControlPath(cid));
}
std::shared_ptr<managedStore::ValueType> value =
managedStore::MockManagedStoreTest::CreateValueType(mockPaths);
UpsertMockObject(key, value);
}
void MockMapperObject(absl::string_view control_id) {
std::vector<std::string> interfaces = {std::string(kInterfaceName)};
KeyType mapperKey(ManagedType::kManagedMapperObject,
GetControlPath(control_id), interfaces);
dbus::utility::MapperGetObject mapperObject = {
{std::string(kServiceName), {std::string(kInterfaceName)}}};
std::shared_ptr<managedStore::ValueType> mapperValue =
managedStore::MockManagedStoreTest::CreateValueType(mapperObject);
UpsertMockObject(mapperKey, mapperValue);
}
void MockMapperObjectEmpty(absl::string_view control_id) {
std::vector<std::string> interfaces = {std::string(kInterfaceName)};
KeyType mapperKey(ManagedType::kManagedMapperObject,
GetControlPath(control_id), interfaces);
dbus::utility::MapperGetObject mapperObject = {};
std::shared_ptr<managedStore::ValueType> mapperValue =
managedStore::MockManagedStoreTest::CreateValueType(mapperObject);
UpsertMockObject(mapperKey, mapperValue);
}
void MockProperties(absl::string_view control_id,
const dbus::utility::DBusPropertiesMap& props) {
KeyType propsKey(
ManagedType::kManagedPropertyMap, kServiceName,
sdbusplus::message::object_path(GetControlPath(control_id)),
kInterfaceName);
std::shared_ptr<managedStore::ValueType> propsValue =
managedStore::MockManagedStoreTest::CreateValueType(props);
UpsertMockObject(propsKey, propsValue);
}
void MockPropertiesError(absl::string_view control_id) {
KeyType propsKey(
ManagedType::kManagedPropertyMap, kServiceName,
sdbusplus::message::object_path(GetControlPath(control_id)),
kInterfaceName);
dbus::utility::DBusPropertiesMap emptyProps = {};
std::shared_ptr<managedStore::ValueType> propsErrorValue =
managedStore::MockManagedStoreTest::CreateErrorValueType(
emptyProps,
boost::system::errc::make_error_code(
boost::system::errc::io_error));
UpsertMockObject(propsKey, propsErrorValue);
}
void MockMapperObjectError(absl::string_view control_id) {
std::vector<std::string> interfaces = {std::string(kInterfaceName)};
KeyType mapperKey(ManagedType::kManagedMapperObject,
GetControlPath(control_id), interfaces);
dbus::utility::MapperGetObject emptyObject = {};
std::shared_ptr<managedStore::ValueType> mapperErrorValue =
managedStore::MockManagedStoreTest::CreateErrorValueType(
emptyObject,
boost::system::errc::make_error_code(
boost::system::errc::io_error));
UpsertMockObject(mapperKey, mapperErrorValue);
}
std::vector<KeyType> registered_keys_;
};
TEST_F(ControlTest, GetControlCollectionTestReturnsCorrectResponse) {
MockControlPaths({std::string(kControlId), std::string(kControlId2)});
getControlCollection(app_, CreateRequest(), share_async_resp_,
std::string(kChassisId));
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json, nlohmann::json::parse(R"json(
{
"@odata.id": "/redfish/v1/Chassis/platform1/Controls",
"@odata.type": "#ControlCollection.v1_0_0.ControlCollection",
"Members": [
{
"@odata.id": "/redfish/v1/Chassis/platform1/Controls/PL1"
},
{
"@odata.id": "/redfish/v1/Chassis/platform1/Controls/PL2"
}
],
"Members@odata.count": 2,
"Name": "Control Collection"
})json"));
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(ControlTest, GetControlConfigurationReturnsCorrectResponse) {
MockMapperObject(kControlId);
MockProperties(kControlId, {{"SetPoint", uint32_t{240}},
{"SetPointUnits", std::string("W")},
{"ControlType", std::string("Power")},
{"PhysicalContext", std::string("Processor")}});
getControlConfiguration(app_, CreateRequest(), share_async_resp_,
std::string(kChassisId), std::string(kControlId));
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json, nlohmann::json::parse(R"json(
{
"@odata.id": "/redfish/v1/Chassis/platform1/Controls/PL1",
"@odata.type": "#Control.v1_5_1.Control",
"ControlMode": "Manual",
"ControlType": "Power",
"Id": "PL1",
"Name": "Chassis Control PL1",
"PhysicalContext": "Processor",
"SetPoint": 240,
"SetPointUnits": "W"
})json"));
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(ControlTest, GetControlConfigurationDisabledControl) {
MockMapperObject(kControlId2);
MockProperties(kControlId2, {{"SetPoint", uint32_t{0}},
{"SetPointUnits", std::string("W")}});
getControlConfiguration(app_, CreateRequest(), share_async_resp_,
std::string(kChassisId), std::string(kControlId2));
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json, nlohmann::json::parse(R"json(
{
"@odata.id": "/redfish/v1/Chassis/platform1/Controls/PL2",
"@odata.type": "#Control.v1_5_1.Control",
"ControlMode": "Disabled",
"Id": "PL2",
"Name": "Chassis Control PL2"
})json"));
EXPECT_FALSE(json.contains("SetPointUnits"));
EXPECT_FALSE(json.contains("SetPoint"));
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(ControlTest, GetControlConfigurationNotFound) {
MockMapperObjectEmpty(kControlId);
getControlConfiguration(app_, CreateRequest(), share_async_resp_,
std::string(kChassisId), std::string(kControlId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(ControlTest, GetControlConfigurationDBusError) {
MockMapperObject(kControlId);
MockPropertiesError(kControlId);
getControlConfiguration(app_, CreateRequest(), share_async_resp_,
std::string(kChassisId), std::string(kControlId));
RunIoUntilDone();
// Verify that internalError was invoked and returned 500
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(ControlTest, PatchControlConfigurationSuccess) {
MockMapperObject(kControlId);
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
_, An<absl::AnyInvocable<void(const boost::system::error_code&)>&&>(),
std::string(kServiceName), GetControlPath(kControlId),
"org.freedesktop.DBus.Properties", "Set", std::string(kInterfaceName),
"SetPoint", DbusVariantType(uint32_t{250})))
.Times(1)
.WillOnce(SimulateSuccessfulAsyncPostDbusCallThreadSafeAction::
SimulateSuccessfulAsyncPostDbusCall());
patchControlConfiguration(app_, CreateRequest(R"({"SetPoint": 250})"),
share_async_resp_, std::string(kChassisId),
std::string(kControlId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(ControlTest, PatchControlConfigurationSetPropertyError) {
MockMapperObject(kControlId);
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
_, An<absl::AnyInvocable<void(const boost::system::error_code&)>&&>(),
std::string(kServiceName), GetControlPath(kControlId),
"org.freedesktop.DBus.Properties", "Set", std::string(kInterfaceName),
"SetPoint", DbusVariantType(uint32_t{250})))
.Times(1)
.WillOnce(SimulateFailedAsyncPostDbusCallThreadSafeAction::
SimulateFailedAsyncPostDbusCall());
patchControlConfiguration(app_, CreateRequest(R"({"SetPoint": 250})"),
share_async_resp_, std::string(kChassisId),
std::string(kControlId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(ControlTest, PatchControlConfigurationMissingSetPoint) {
patchControlConfiguration(app_, CreateRequest(R"({})"),
share_async_resp_, std::string(kChassisId),
std::string(kControlId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::bad_request);
}
TEST_F(ControlTest, PatchControlConfigurationNullSetPoint) {
patchControlConfiguration(app_, CreateRequest(R"({"SetPoint": null})"),
share_async_resp_, std::string(kChassisId),
std::string(kControlId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::bad_request);
}
TEST_F(ControlTest, PatchControlConfigurationUnknownProperty) {
patchControlConfiguration(app_, CreateRequest(R"({"OtherProperty": 250})"),
share_async_resp_, std::string(kChassisId),
std::string(kControlId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::bad_request);
}
TEST_F(ControlTest, PatchControlConfigurationInvalidSetPoint) {
patchControlConfiguration(
app_, CreateRequest(R"({"SetPoint": "invalid_string"})"),
share_async_resp_, std::string(kChassisId), std::string(kControlId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::bad_request);
}
TEST_F(ControlTest, PatchControlConfigurationNotFound) {
MockMapperObjectEmpty(kControlId);
patchControlConfiguration(app_, CreateRequest(R"({"SetPoint": 250})"),
share_async_resp_, std::string(kChassisId),
std::string(kControlId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(ControlTest, PatchControlConfigurationDBusMapperError) {
MockMapperObjectError(kControlId);
patchControlConfiguration(app_, CreateRequest(R"({"SetPoint": 250})"),
share_async_resp_, std::string(kChassisId),
std::string(kControlId));
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
} // namespace
} // namespace redfish