blob: 115221dea62bd1020e319c1a176ffbb7e7d0113a [file]
#include <cstdint>
#include <filesystem> //NOLINT
#include <memory>
#include <string>
#include <system_error> // NOLINT
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/functional/any_invocable.h"
#include "http_request.hpp"
#include "http_response.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "managers.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"
namespace redfish
{
namespace
{
using ::dbus::utility::DBusPropertiesMap;
using ::dbus::utility::DbusVariantType;
using ::dbus::utility::MapperGetSubTreeResponse;
using ::dbus::utility::MapperServiceMap;
using ::managedStore::KeyType;
using ::managedStore::ManagedType;
using ::managedStore::ValueType;
constexpr char kClassType[] = "Temperature";
constexpr char kSensor[] = "sensor0";
constexpr double kInput = 43.0;
constexpr double kOutput = 4000.0;
constexpr double kSetpoint = 65.0;
constexpr uint8_t kTestData = 15;
// std::shared_ptr<ValueType> CreateMockSubtreeResponse()
// {
// MapperGetSubTreeResponse mockSubtreeResponse{{std::make_pair(
// "/xyz/openbmc_project/inventory/system/component/BMC_flash",
// MapperServiceMap{{std::make_pair(
// "xyz.openbmc_project.EntityManager",
// std::vector<std::string>{
// "xyz.openbmc_project.Inventory.Decorator.LocationCode"})}})}};
// return managedStore::MockManagedStoreTest::CreateValueType(
// std::move(mockSubtreeResponse));
// }
// TODO(edwarddl): Uncomment after handleManagerGet is updated
// TEST_F(SnapshotFixture, handleManagerGetTest)
// {
// KeyType key(ManagedType::kManagedSubtree,
// "/xyz/openbmc_project/inventory", 0,
// {"xyz.openbmc_project.Inventory.Item.Bmc"});
// ASSERT_TRUE(
// dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
// managedStore::GetManagedObjectStore())
// ->upsertMockObjectIntoManagedStore(
// key, CreateMockSubtreeResponse())
// .ok());
// handleManagerGet(app_, CreateRequest(), share_async_resp_);
//
// RunIoUntilDone();
//
// nlohmann::json& json = share_async_resp_->res.jsonValue;
//
// LOG(INFO) << json.dump(2);
// EXPECT_EQ(json["@odata.id"], "/redfish/v1/Managers/bmc");
// EXPECT_EQ(json["Location"]["PartLocation"]["ServiceLabel"], "BMC");
// EXPECT_EQ(json["Location"]["PartLocation"]["LocationType"], "Embedded");
// EXPECT_EQ(json["Location"]["PartLocationContext"], "DC_SCM");
// EXPECT_EQ(json["Location"]["Oem"]["Google"]["EmbeddedLocationContext"],
// "openbmc_manager");
// EXPECT_EQ(json["Links"]["ManagerForServers@odata.count"], 1);
// EXPECT_EQ(json["Links"]["ManagerForServers"][0]["@odata.id"],
// "/redfish/v1/Systems/system");
// EXPECT_EQ(share_async_resp_->res.result(),
// boost::beast::http::status::ok);
// }
std::shared_ptr<ValueType> CreatePidDebugInfoProperty()
{
DBusPropertiesMap pidDebugInfoProperty{
{std::make_pair("ClassType", DbusVariantType(kClassType))},
{std::make_pair("Input", DbusVariantType(kInput))},
{std::make_pair("Leader", DbusVariantType(kSensor))},
{std::make_pair("Output", DbusVariantType(kOutput))},
{std::make_pair("Setpoint", DbusVariantType(kSetpoint))},
{std::make_pair("TestData", DbusVariantType(kTestData))}};
return managedStore::MockManagedStoreTest::CreateValueType(
std::move(pidDebugInfoProperty));
}
TEST_F(SnapshotFixture, updateZoneLeaderInfoGetNull)
{
const std::string zoneIndex = "0";
const std::string name = "Zone_0";
const std::string leader = "sensor_PID";
const std::string pidDebugInfoPath =
"/xyz/openbmc_project/settings/fanctrl/zone" + zoneIndex + "/" + leader;
auto response = std::make_shared<bmcweb::AsyncResp>();
nlohmann::json& json = response->res.jsonValue;
managedStore::ManagedObjectStoreContext context(response);
// Test for null dbus response
updateZoneLeaderInfo(response, zoneIndex, name, leader, context);
RunIoUntilDone();
EXPECT_TRUE(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["ClassType"]
.is_null());
EXPECT_TRUE(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["Input"]
.is_null());
EXPECT_TRUE(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["Output"]
.is_null());
EXPECT_TRUE(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["Sensor"]
.is_null());
EXPECT_TRUE(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["Setpoint"]
.is_null());
}
TEST_F(SnapshotFixture, updateZoneLeaderInfoSuccess)
{
const std::string zoneIndex = "0";
const std::string name = "Zone_0";
const std::string leader = "sensor_PID";
const std::string pidDebugInfoPath =
"/xyz/openbmc_project/settings/fanctrl/zone" + zoneIndex + "/" + leader;
auto response = std::make_shared<bmcweb::AsyncResp>();
nlohmann::json& json = response->res.jsonValue;
managedStore::ManagedObjectStoreContext context(response);
// kManagedPropertyMap
KeyType key(ManagedType::kManagedPropertyMap,
"xyz.openbmc_project.State.FanCtrl", pidDebugInfoPath,
"xyz.openbmc_project.Debug.Pid.ThermalPower");
ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, CreatePidDebugInfoProperty())
.ok());
updateZoneLeaderInfo(response, zoneIndex, name, leader, context);
RunIoUntilDone();
EXPECT_EQ(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["ClassType"],
kClassType);
EXPECT_EQ(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["Input"],
kInput);
EXPECT_EQ(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["Sensor"],
kSensor);
EXPECT_EQ(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["Output"],
kOutput);
EXPECT_EQ(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["Setpoint"],
kSetpoint);
EXPECT_EQ(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][name]["Leader"]["TestData"],
"n/a");
}
TEST_F(SnapshotFixture, PostManagerResetActionGood)
{
// using expected call to mitigate the memory issue in valgrind
// https://b.corp.google.com/issues/416295689
// https://b.corp.google.com/issues/416608762
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
testing::_,
testing::An<
absl::AnyInvocable<void(const boost::system::error_code&)>&&>(),
"xyz.openbmc_project.State.Chassis",
testing::An<const std::string&>(),
"org.freedesktop.DBus.Properties", "Set",
"xyz.openbmc_project.State.Chassis", "RequestedPowerTransition",
dbus::utility::DbusVariantType(
"xyz.openbmc_project.State.Chassis.Transition.PowerCycle")))
.WillOnce(
managedStore::SimulateSuccessfulAsyncPostDbusCallThreadSafeAction::
SimulateSuccessfulAsyncPostDbusCall());
handlePostManagerResetAction(
app_, CreateRequest("{\"ResetType\":\"TrayPowerCycle\"} "),
share_async_resp_);
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture, PostManagerResetActionBadResetType)
{
handlePostManagerResetAction(
app_, CreateRequest("{\"ResetType\":\"Typo\"} "), share_async_resp_);
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::bad_request);
}
TEST_F(SnapshotFixture, handleManagerResetActionInfoAllowableValues)
{
handleManagerResetActionInfo(app_, CreateRequest(""), share_async_resp_);
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
auto allowable_values =
share_async_resp_->res.jsonValue["Parameters"][0]["AllowableValues"];
EXPECT_THAT(allowable_values,
testing::ElementsAre("GracefulRestart", "ForceRestart",
"TrayPowerCycle"));
}
// TODO(edwarddl): Uncomment after handleManagerGet is updated
// TEST_F(SnapshotFixture, handleManagerGetMultiSystem)
// {
// KeyType key(ManagedType::kManagedSubtreePaths, "/", 0,
// {"xyz.openbmc_project.Inventory.Item.System"});
//
// std::vector<std::string> path = {
// "/xyz/system1",
// "/xyz/system2",
// };
// std::shared_ptr<ValueType> test_value = CreateValueType(path);
// ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
// managedStore::GetManagedObjectStore())
// ->upsertMockObjectIntoManagedStore(key, test_value)
// .ok());
//
// handleManagerGet(app_, CreateRequest(), share_async_resp_);
//
// RunIoUntilDone();
//
// nlohmann::json& json = share_async_resp_->res.jsonValue;
//
// EXPECT_EQ(json["Links"]["ManagerForServers@odata.count"], 2);
// EXPECT_EQ(json["Links"]["ManagerForServers"][0]["@odata.id"],
// "/redfish/v1/Systems/system1");
// EXPECT_EQ(json["Links"]["ManagerForServers"][1]["@odata.id"],
// "/redfish/v1/Systems/system2");
// EXPECT_EQ(share_async_resp_->res.result(),
// boost::beast::http::status::ok);
// }
TEST_F(SnapshotFixture, handleManagerGetFruDeviceOemLink) {
std::error_code ec;
auto origPath = std::filesystem::current_path(ec);
std::filesystem::current_path(std::filesystem::temp_directory_path(ec), ec);
handleManagerGet(app_, CreateRequest(), share_async_resp_, "bmc");
RunIoUntilDone();
std::filesystem::current_path(origPath, ec);
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["Oem"]["Google"]["FruDevice"]["@odata.id"],
"/redfish/v1/Managers/bmc/Oem/Google/FruDevice");
}
TEST_F(SnapshotFixture, handleFruDeviceGet) {
handleFruDeviceGet(app_, CreateRequest(), share_async_resp_);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json, nlohmann::json::parse(R"({
"@odata.id": "/redfish/v1/Managers/bmc/Oem/Google/FruDevice",
"@odata.type": "#GoogleFruDevice.v1_0_0.GoogleFruDeviceService",
"Id": "FruDevice",
"IpmiFrus": {
"@odata.id": "/redfish/v1/Managers/bmc/Oem/Google/FruDevice/IpmiFrus"
},
"Name": "Google FRU Device Service",
"RawEeproms": {
"@odata.id": "/redfish/v1/Managers/bmc/Oem/Google/FruDevice/RawEeproms"
}
})"));
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture, handleIpmiFruCollectionGet) {
KeyType key(ManagedType::kManagedSubtreePaths,
"/xyz/openbmc_project/FruDevice", 0,
{"xyz.openbmc_project.FruDevice"});
std::vector<std::string> paths = {
"/xyz/openbmc_project/FruDevice/TestFru1",
"/xyz/openbmc_project/FruDevice/TestFru2",
};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(paths))
.ok());
handleIpmiFruCollectionGet(app_, CreateRequest(), share_async_resp_);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json, nlohmann::json::parse(R"({
"@odata.id": "/redfish/v1/Managers/bmc/Oem/Google/FruDevice/IpmiFrus",
"@odata.type": "#GoogleFruDevice.v1_0_0.GoogleIpmiFruCollection",
"Id": "IpmiFrus",
"Members": [
{
"@odata.id": "/redfish/v1/Managers/bmc/Oem/Google/FruDevice/IpmiFrus/TestFru1"
},
{
"@odata.id": "/redfish/v1/Managers/bmc/Oem/Google/FruDevice/IpmiFrus/TestFru2"
}
],
"Members@odata.count": 2,
"Name": "Google IPMI FRU Collection"
})"));
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture, handleRawEepromCollectionGet) {
KeyType key(ManagedType::kManagedSubtreePaths,
"/xyz/openbmc_project/FruDevice", 0,
{"xyz.openbmc_project.Inventory.Item.I2CDevice"});
std::vector<std::string> paths = {
"/xyz/openbmc_project/FruDevice/Eeprom1",
};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(paths))
.ok());
handleRawEepromCollectionGet(app_, CreateRequest(), share_async_resp_);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json, nlohmann::json::parse(R"({
"@odata.id": "/redfish/v1/Managers/bmc/Oem/Google/FruDevice/RawEeproms",
"@odata.type": "#GoogleFruDevice.v1_0_0.GoogleRawEepromCollection",
"Id": "RawEeproms",
"Members": [
{
"@odata.id": "/redfish/v1/Managers/bmc/Oem/Google/FruDevice/RawEeproms/Eeprom1"
}
],
"Members@odata.count": 1,
"Name": "Google Raw EEPROM Collection"
})"));
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST(ToTitleCaseTest, ConvertsPropertyNamesToTitleCase) {
EXPECT_EQ(toTitleCase("BOARD_PRODUCT_NAME"), "BoardProductName");
EXPECT_EQ(toTitleCase("BUS"), "Bus");
EXPECT_EQ(toTitleCase("ADDRESS"), "Address");
EXPECT_EQ(toTitleCase("PRODUCT_MANUFACTURER"), "ProductManufacturer");
EXPECT_EQ(toTitleCase("PRODUCT_SERIAL_NUMBER"), "ProductSerialNumber");
EXPECT_EQ(toTitleCase("foo_bar_baz"), "FooBarBaz");
EXPECT_EQ(toTitleCase(""), "");
}
// Tests handleIpmiFruMemberGet with dynamic ObjectMapper service discovery:
// 1. Registers a mock Mapper GetObject response (kManagedMapperObject) mapping
// the FRU path to its owning service ("xyz.openbmc_project.FruDevice").
// 2. Registers mock D-Bus properties (kManagedPropertyMap) on that service.
// 3. Verifies that handleIpmiFruMemberGet queries the discovered service and
// populates the Redfish response correctly.
TEST_F(SnapshotFixture, handleIpmiFruMemberGet) {
std::string fruId = "TestFru1";
KeyType objKey(ManagedType::kManagedMapperObject,
"/xyz/openbmc_project/FruDevice/" + fruId,
std::vector<std::string>{"xyz.openbmc_project.FruDevice"});
dbus::utility::MapperGetObject objVal = {
{"xyz.openbmc_project.FruDevice", {"xyz.openbmc_project.FruDevice"}}};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
objKey,
managedStore::MockManagedStoreTest::CreateValueType(objVal))
.ok());
KeyType key(ManagedType::kManagedPropertyMap, "xyz.openbmc_project.FruDevice",
"/xyz/openbmc_project/FruDevice/" + fruId,
"xyz.openbmc_project.FruDevice");
DBusPropertiesMap props = {
{std::make_pair("BUS", DbusVariantType(static_cast<uint32_t>(1)))},
{std::make_pair("ADDRESS", DbusVariantType(static_cast<uint32_t>(80)))},
{std::make_pair("BOARD_PRODUCT_NAME", DbusVariantType("BoardName"))}};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(props))
.ok());
handleIpmiFruMemberGet(app_, CreateRequest(), share_async_resp_, fruId);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json, nlohmann::json::parse(R"({
"@odata.id": "/redfish/v1/Managers/bmc/Oem/Google/FruDevice/IpmiFrus/TestFru1",
"@odata.type": "#GoogleFruDevice.v1_0_0.IpmiFru",
"Id": "TestFru1",
"Name": "Google IPMI FRU",
"Bus": 1,
"Address": 80,
"BoardProductName": "BoardName"
})"));
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
// Tests handleIpmiFruMemberGet when the requested FRU ID does not exist in
// the ObjectMapper, verifying that HTTP 404 (ResourceNotFound) is returned.
TEST_F(SnapshotFixture, handleIpmiFruMemberGetNotFound) {
handleIpmiFruMemberGet(app_, CreateRequest(), share_async_resp_,
"NonExistentFru");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
// Tests end-to-end HTTP request dispatch through Crow router to both IpmiFrus
// and RawEeproms member endpoints:
// 1. Verifies that the Crow router dispatches GET requests for /IpmiFrus/<id>
// and /RawEeproms/<id> (without trailing slashes) correctly.
// 2. Verifies dynamic ObjectMapper resolution (kManagedMapperObject) and
// properties population for IpmiFrus.
// 3. Verifies dynamic ObjectMapper resolution and GetRawFru D-Bus method call
// mock invocation for RawEeproms.
TEST_F(SnapshotFixture, requestRoutesFruDevicesRouteMatching) {
requestRoutesFruDevices(app_);
app_.validate();
std::string fruId = "TestFru1";
KeyType fruObjKey(ManagedType::kManagedMapperObject,
"/xyz/openbmc_project/FruDevice/" + fruId,
std::vector<std::string>{"xyz.openbmc_project.FruDevice"});
dbus::utility::MapperGetObject fruObjVal = {
{"xyz.openbmc_project.FruDevice", {"xyz.openbmc_project.FruDevice"}}};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
fruObjKey,
managedStore::MockManagedStoreTest::CreateValueType(fruObjVal))
.ok());
KeyType key(ManagedType::kManagedPropertyMap, "xyz.openbmc_project.FruDevice",
"/xyz/openbmc_project/FruDevice/" + fruId,
"xyz.openbmc_project.FruDevice");
DBusPropertiesMap props = {
{std::make_pair("BUS", DbusVariantType(static_cast<uint32_t>(1)))},
{std::make_pair("ADDRESS", DbusVariantType(static_cast<uint32_t>(80)))},
{std::make_pair("BOARD_PRODUCT_NAME", DbusVariantType("BoardName"))}};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(props))
.ok());
boost::beast::http::request<boost::beast::http::string_body> reqIn{
boost::beast::http::verb::get,
"/redfish/v1/Managers/bmc/Oem/Google/FruDevice/IpmiFrus/TestFru1", 11};
std::error_code ec;
crow::Request req(reqIn, ec);
auto async_resp = std::make_shared<bmcweb::AsyncResp>();
app_.handle(req, async_resp);
RunIoUntilDone();
EXPECT_EQ(async_resp->res.result(), boost::beast::http::status::ok);
EXPECT_EQ(async_resp->res.jsonValue["@odata.id"],
"/redfish/v1/Managers/bmc/Oem/Google/FruDevice/IpmiFrus/TestFru1");
EXPECT_EQ(async_resp->res.jsonValue["Id"], "TestFru1");
// Verify RawEeproms member route with dynamic ObjectMapper resolution
std::string rawId = "Eeprom1";
KeyType rawObjKey(
ManagedType::kManagedMapperObject,
"/xyz/openbmc_project/FruDevice/" + rawId,
std::vector<std::string>{
"xyz.openbmc_project.Inventory.Item.I2CDevice"});
dbus::utility::MapperGetObject rawObjVal = {
{"xyz.openbmc_project.FruDevice",
{"xyz.openbmc_project.Inventory.Item.I2CDevice"}}};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
rawObjKey,
managedStore::MockManagedStoreTest::CreateValueType(rawObjVal))
.ok());
KeyType rawKey(ManagedType::kManagedPropertyMap,
"xyz.openbmc_project.FruDevice",
"/xyz/openbmc_project/FruDevice/" + rawId,
"xyz.openbmc_project.Inventory.Item.I2CDevice");
DBusPropertiesMap rawProps = {
{std::make_pair("Bus", DbusVariantType(static_cast<uint32_t>(2)))},
{std::make_pair("Address", DbusVariantType(static_cast<uint32_t>(81)))},
};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
rawKey,
managedStore::MockManagedStoreTest::CreateValueType(rawProps))
.ok());
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
testing::_,
testing::An<
absl::AnyInvocable<void(const boost::system::error_code&,
const std::vector<uint8_t>&)>&&>(),
"xyz.openbmc_project.FruDevice", "/xyz/openbmc_project/FruDevice",
"xyz.openbmc_project.FruDeviceManager", "GetRawFru",
testing::Eq(static_cast<uint16_t>(2)),
testing::Eq(static_cast<uint8_t>(81))))
.WillOnce(
managedStore::
SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction<
std::vector<uint8_t>>::
SimulateSuccessfulAsyncPostDbusCallWithValue(
std::make_shared<std::vector<uint8_t>>(
std::vector<uint8_t>{0x01, 0x02, 0x03})));
boost::beast::http::request<boost::beast::http::string_body> rawReqIn{
boost::beast::http::verb::get,
"/redfish/v1/Managers/bmc/Oem/Google/FruDevice/RawEeproms/Eeprom1", 11};
crow::Request rawReq(rawReqIn, ec);
auto raw_async_resp = std::make_shared<bmcweb::AsyncResp>();
app_.handle(rawReq, raw_async_resp);
RunIoUntilDone();
EXPECT_EQ(raw_async_resp->res.result(), boost::beast::http::status::ok);
EXPECT_EQ(raw_async_resp->res.jsonValue["@odata.id"],
"/redfish/v1/Managers/bmc/Oem/Google/FruDevice/RawEeproms/Eeprom1");
EXPECT_EQ(raw_async_resp->res.jsonValue["Id"], "Eeprom1");
}
// Tests handleRawEepromMemberGet with dynamic ObjectMapper service discovery:
// 1. Registers mock Mapper GetObject mapping (kManagedMapperObject) for I2CDevice.
// 2. Registers Bus/Address properties on the discovered service.
// 3. Expects GetRawFru D-Bus call to be issued to the discovered service.
TEST_F(SnapshotFixture, handleRawEepromMemberGet) {
std::string rawId = "Eeprom1";
KeyType rawObjKey(
ManagedType::kManagedMapperObject,
"/xyz/openbmc_project/FruDevice/" + rawId,
std::vector<std::string>{
"xyz.openbmc_project.Inventory.Item.I2CDevice"});
dbus::utility::MapperGetObject rawObjVal = {
{"xyz.openbmc_project.FruDevice",
{"xyz.openbmc_project.Inventory.Item.I2CDevice"}}};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
rawObjKey,
managedStore::MockManagedStoreTest::CreateValueType(rawObjVal))
.ok());
KeyType key(ManagedType::kManagedPropertyMap, "xyz.openbmc_project.FruDevice",
"/xyz/openbmc_project/FruDevice/" + rawId,
"xyz.openbmc_project.Inventory.Item.I2CDevice");
DBusPropertiesMap props = {
{std::make_pair("Bus", DbusVariantType(static_cast<uint32_t>(2)))},
{std::make_pair("Address", DbusVariantType(static_cast<uint32_t>(81)))},
};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(props))
.ok());
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
testing::_,
testing::An<
absl::AnyInvocable<void(const boost::system::error_code&,
const std::vector<uint8_t>&)>&&>(),
"xyz.openbmc_project.FruDevice", "/xyz/openbmc_project/FruDevice",
"xyz.openbmc_project.FruDeviceManager", "GetRawFru",
testing::Eq(static_cast<uint16_t>(2)),
testing::Eq(static_cast<uint8_t>(81))))
.WillOnce(
managedStore::
SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction<
std::vector<uint8_t>>::
SimulateSuccessfulAsyncPostDbusCallWithValue(
std::make_shared<std::vector<uint8_t>>(
std::vector<uint8_t>{1, 2, 3})));
handleRawEepromMemberGet(app_, CreateRequest(), share_async_resp_, rawId);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json, nlohmann::json::parse(R"({
"@odata.id": "/redfish/v1/Managers/bmc/Oem/Google/FruDevice/RawEeproms/Eeprom1",
"@odata.type": "#GoogleFruDevice.v1_0_0.RawEeprom",
"Id": "Eeprom1",
"Name": "Google Raw EEPROM",
"Bus": 2,
"Address": 81,
"RawData": [1, 2, 3],
"HexdumpFormattedRawData": "0000: 01 02 03 | ... |\n"
})"));
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture, handleRawEepromMemberGetZeroLength) {
std::string rawId = "Eeprom1";
KeyType key(ManagedType::kManagedPropertyMap, "xyz.openbmc_project.FruDevice",
"/xyz/openbmc_project/FruDevice/" + rawId,
"xyz.openbmc_project.Inventory.Item.I2CDevice");
DBusPropertiesMap props = {
{std::make_pair("Bus", DbusVariantType(static_cast<uint32_t>(2)))},
{std::make_pair("Address", DbusVariantType(static_cast<uint32_t>(81)))},
};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(props))
.ok());
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
testing::_,
testing::An<
absl::AnyInvocable<void(const boost::system::error_code&,
const std::vector<uint8_t>&)>&&>(),
"xyz.openbmc_project.FruDevice", "/xyz/openbmc_project/FruDevice",
"xyz.openbmc_project.FruDeviceManager", "GetRawFru",
testing::Eq(static_cast<uint16_t>(2)),
testing::Eq(static_cast<uint8_t>(81))))
.WillOnce(
managedStore::
SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction<
std::vector<uint8_t>>::
SimulateSuccessfulAsyncPostDbusCallWithValue(
std::make_shared<std::vector<uint8_t>>(
std::vector<uint8_t>{})));
handleRawEepromMemberGet(app_, CreateRequest(), share_async_resp_, rawId);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["RawData"], std::vector<uint8_t>{});
EXPECT_EQ(json["HexdumpFormattedRawData"], "");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture, handleRawEepromMemberGetMultipleLines) {
std::string rawId = "Eeprom1";
KeyType key(ManagedType::kManagedPropertyMap, "xyz.openbmc_project.FruDevice",
"/xyz/openbmc_project/FruDevice/" + rawId,
"xyz.openbmc_project.Inventory.Item.I2CDevice");
DBusPropertiesMap props = {
{std::make_pair("Bus", DbusVariantType(static_cast<uint32_t>(2)))},
{std::make_pair("Address", DbusVariantType(static_cast<uint32_t>(81)))},
};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(props))
.ok());
std::vector<uint8_t> rawData;
for (int i = 0; i < 20; ++i) {
rawData.push_back(static_cast<uint8_t>('A' + i));
}
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
testing::_,
testing::An<
absl::AnyInvocable<void(const boost::system::error_code&,
const std::vector<uint8_t>&)>&&>(),
"xyz.openbmc_project.FruDevice", "/xyz/openbmc_project/FruDevice",
"xyz.openbmc_project.FruDeviceManager", "GetRawFru",
testing::Eq(static_cast<uint16_t>(2)),
testing::Eq(static_cast<uint8_t>(81))))
.WillOnce(
managedStore::
SimulateSuccessfulAsyncPostDbusCallThreadSafeWithValueAction<
std::vector<uint8_t>>::
SimulateSuccessfulAsyncPostDbusCallWithValue(
std::make_shared<std::vector<uint8_t>>(rawData)));
handleRawEepromMemberGet(app_, CreateRequest(), share_async_resp_, rawId);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["RawData"], rawData);
std::string expectedHexdump =
"0000: 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 | "
"ABCDEFGHIJKLMNOP |\n"
"0010: 51 52 53 54 | QRST |\n";
EXPECT_EQ(json["HexdumpFormattedRawData"], expectedHexdump);
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture, handleRawEepromMemberGetNotFound) {
handleRawEepromMemberGet(app_, CreateRequest(), share_async_resp_,
"NonExistentEeprom");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(SnapshotFixture, handleIpmiFruCollectionGetInvalidOdataVersion) {
boost::beast::http::request<boost::beast::http::string_body> reqIn;
reqIn.set("OData-Version", "3.0");
std::error_code ec;
crow::Request req(reqIn, ec);
handleIpmiFruCollectionGet(app_, req, share_async_resp_);
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::precondition_failed);
}
TEST_F(SnapshotFixture, handleRawEepromCollectionGetInvalidOdataVersion) {
boost::beast::http::request<boost::beast::http::string_body> reqIn;
reqIn.set("OData-Version", "3.0");
std::error_code ec;
crow::Request req(reqIn, ec);
handleRawEepromCollectionGet(app_, req, share_async_resp_);
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::precondition_failed);
}
TEST_F(SnapshotFixture, handleIpmiFruMemberGetInvalidId) {
std::string fruId = "Test-Fru";
handleIpmiFruMemberGet(app_, CreateRequest(), share_async_resp_, fruId);
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(SnapshotFixture, handleRawEepromMemberGetInvalidId) {
std::string rawId = "Eeprom.1";
handleRawEepromMemberGet(app_, CreateRequest(), share_async_resp_, rawId);
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(SnapshotFixture, asyncPopulatePidZoneChassisValid) {
const std::string connection = "xyz.openbmc_project.EntityManager";
const std::string path = "/xyz/openbmc_project/inventory";
const std::string zoneName = "Zone_0";
const std::string chassisName = "TestChassis";
// Mock ManagedObjectType for asyncPopulatePid
dbus::utility::ManagedObjectType managedObj = {
{sdbusplus::message::object_path(
"/xyz/openbmc_project/inventory/system/chassis/" + chassisName +
"/" + zoneName),
{{"xyz.openbmc_project.Configuration.Pid.Zone",
{{"Name", DbusVariantType(zoneName)},
{"Class", DbusVariantType("temp")},
{"ZoneIndex", DbusVariantType(0.0)},
{"MinThermalOutput", DbusVariantType(3000.0)}}}}}};
KeyType objKey(ManagedType::kManagedObject, connection, path);
ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
objKey, managedStore::MockManagedStoreTest::CreateValueType(
managedObj))
.ok());
// Mock chassis subtree paths containing the matching chassis
std::vector<std::string> chassisPaths = {
"/xyz/openbmc_project/inventory/system/chassis/" + chassisName,
};
KeyType chassisSubtreeKey(
ManagedType::kManagedSubtreePaths, "/xyz/openbmc_project/inventory", 0,
{"xyz.openbmc_project.Inventory.Item.Board",
"xyz.openbmc_project.Inventory.Item.Chassis"});
ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
chassisSubtreeKey,
managedStore::MockManagedStoreTest::CreateValueType(
chassisPaths))
.ok());
asyncPopulatePid(connection, path, "", {}, share_async_resp_);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][zoneName]["Chassis"]["@odata.id"],
"/redfish/v1/Chassis/" + chassisName);
}
TEST_F(SnapshotFixture, asyncPopulatePidZoneChassisInvalid) {
const std::string connection = "xyz.openbmc_project.EntityManager";
const std::string path = "/xyz/openbmc_project/inventory";
const std::string zoneName = "Zone_0";
const std::string chassisName = "InvalidChassis";
// Mock ManagedObjectType with an invalid chassis name in the path
dbus::utility::ManagedObjectType managedObj = {
{sdbusplus::message::object_path(
"/xyz/openbmc_project/inventory/system/chassis/" + chassisName +
"/" + zoneName),
{{"xyz.openbmc_project.Configuration.Pid.Zone",
{{"Name", DbusVariantType(zoneName)},
{"Class", DbusVariantType("temp")},
{"ZoneIndex", DbusVariantType(0.0)},
{"MinThermalOutput", DbusVariantType(3000.0)}}}}}};
KeyType objKey(ManagedType::kManagedObject, connection, path);
ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
objKey, managedStore::MockManagedStoreTest::CreateValueType(
managedObj))
.ok());
// Mock chassis subtree paths containing only OtherChassis (not InvalidChassis)
std::vector<std::string> chassisPaths = {
"/xyz/openbmc_project/inventory/system/chassis/OtherChassis",
};
KeyType chassisSubtreeKey(
ManagedType::kManagedSubtreePaths, "/xyz/openbmc_project/inventory", 0,
{"xyz.openbmc_project.Inventory.Item.Board",
"xyz.openbmc_project.Inventory.Item.Chassis"});
ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
chassisSubtreeKey,
managedStore::MockManagedStoreTest::CreateValueType(
chassisPaths))
.ok());
asyncPopulatePid(connection, path, "", {}, share_async_resp_);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
// When chassis is invalid, Chassis property should not be reported
EXPECT_FALSE(
json["Oem"]["OpenBmc"]["Fan"]["FanZones"][zoneName].contains("Chassis"));
}
} // namespace
} // namespace redfish