blob: 306f5e0ae5f49e63ad064bfc48626f2e9af4a721 [file] [log] [blame]
#include "absl/cleanup/cleanup.h"
#include "absl/log/log.h"
#include "bmcweb_config.h"
#include "app.hpp"
#include "async_resp.hpp"
#include "bios.hpp"
#include "http_response.hpp"
#include "nlohmann/json.hpp"
#include "snapshot_fixture.hpp"
#include <boost/system/error_code.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <optional>
#include <vector>
#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 <gtest/gtest-matchers.h>
namespace redfish
{
namespace
{
using ::dbus::utility::DbusVariantType;
using ::managedStore::KeyType;
using ::managedStore::ManagedObjectStoreContext;
using ::managedStore::ManagedType;
using ::managedStore::SimulateFailedAsyncPostDbusCallThreadSafeAction;
using ::managedStore::SimulateSuccessfulAsyncPostDbusCallThreadSafeAction;
using ::managedStore::ValueType;
using ::testing::_;
using ::testing::An;
TEST(ResourceIdFromNameString, ResourceIdFromNameString)
{
EXPECT_EQ(resourceIdFromNameString("system10"), "10");
}
TEST_F(SnapshotFixture,
SingleHostPlatformHandleBiosServiceGetTestReturnsCorrectResponse)
{
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system");
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["@odata.id"], "/redfish/v1/Systems/system/Bios");
EXPECT_EQ(json["@odata.type"], "#Bios.v1_1_0.Bios");
EXPECT_EQ(json["Actions"]["#Bios.ResetBios"]["target"],
"/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios");
EXPECT_EQ(json["Description"], "BIOS Configuration Service");
EXPECT_EQ(json["Id"], "BIOS");
EXPECT_EQ(json["Links"]["ActiveSoftwareImage"]["@odata.id"],
"/redfish/v1/UpdateService/FirmwareInventory/bios_active");
EXPECT_TRUE(json["Links"]["SoftwareImages"].is_array());
EXPECT_EQ(json["Links"]["SoftwareImages"].size(), 1);
EXPECT_EQ(json["Links"]["SoftwareImages"][0]["@odata.id"],
"/redfish/v1/UpdateService/FirmwareInventory/bios_active");
EXPECT_EQ(json["Links"]["SoftwareImages@odata.count"], 1);
EXPECT_EQ(json["Name"], "BIOS Configuration");
EXPECT_EQ(json["Oem"]["Google"]["MemTestBlob"], "");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture,
SingleHostPlatformHandleBiosServiceGetTestReturnsNotFound)
{
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system1");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(SnapshotFixture,
SingleHostPlatformHandleBiosServiceGetSystemSubTreePathsResponseError)
{
// Looking for
// kManagedSubtreePaths|/|0|xyz.openbmc_project.Inventory.Item.System
KeyType key(ManagedType::kManagedSubtreePaths, "/", 0,
{"xyz.openbmc_project.Inventory.Item.System"});
absl::StatusOr<std::shared_ptr<ValueType>> systemObjectsPathsStatusOr =
managedStore::GetManagedObjectStore()->getMockObjectFromManagedStore(
key);
ASSERT_TRUE(systemObjectsPathsStatusOr.ok());
// TODO(b/416746677): Uncomment after the bug is fixed
// ASSERT_EQ(systemObjectsPathsStatusOr.value()->managedType,
// ManagedType::kManagedSubtreePaths);
std::optional<dbus::utility::MapperGetSubTreePathsResponse>
originalSystemObjectsPaths =
systemObjectsPathsStatusOr.value()->managedSubtreePaths;
ASSERT_TRUE(originalSystemObjectsPaths.has_value());
ASSERT_TRUE(
managedStore::GetManagedObjectStore()
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateErrorValueType(
originalSystemObjectsPaths.value(),
boost::system::errc::make_error_code(
boost::system::errc::io_error)))
.ok());
absl::Cleanup mockManagedStoreResetter = [key,
originalSystemObjectsPaths]() {
ASSERT_TRUE(
managedStore::GetManagedObjectStore()
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(
originalSystemObjectsPaths.value()))
.ok());
};
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
class SingleHostBiosBlobPatchTest : public SnapshotFixture
{
protected:
static void createEmptyBlobFile()
{
if (std::filesystem::exists(biosSettingFilePath))
{
// remove file if already exists
std::error_code ec;
std::filesystem::remove(biosSettingFilePath, ec);
if (ec)
{
LOG(ERROR) << "BIOS setting blob file " << biosSettingFilePath
<< " exists, failed to remove: " << ec.message();
FAIL();
}
}
// create a emtpy file to start
std::ofstream out(biosSettingFilePath);
out.close();
}
static void SetUpTestSuite()
{
SnapshotFixture::SetUpTestSuite();
biosSettingFilepathFromConfig = biosSettingFilePath;
}
static void TearDownTestSuite()
{
SnapshotFixture::TearDownTestSuite();
std::error_code ec;
std::filesystem::remove(biosSettingFilePath, ec);
if (ec)
{
LOG(WARNING) << "Failed to remove BIOS setting blob file "
<< biosSettingFilePath
<< " in TearDownTestSuite():" << ec.message();
}
}
void TearDown() override
{
std::error_code ec;
std::filesystem::remove(biosSettingFilePath, ec);
if (ec)
{
LOG(WARNING) << "Failed to remove BIOS setting blob file "
<< biosSettingFilePath
<< " in TearDown():" << ec.message();
}
}
static inline const std::string biosSettingFilePath =
"/run/oem_bios_setting";
};
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsPeerNotAuthenticated)
{
// no BIOS setting file if createEmptyBlobFile() is not called
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// peer_authenticated to false
req.peer_authenticated = false;
patchBiosSettings(app_, req, share_async_resp_, "system");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::forbidden);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsRequestNoProperArgument)
{
// no BIOS setting file if createEmptyBlobFile() is not called
crow::Request req = CreateRequest("{\"Whatever\": {\"AAEC\"}}}");
// peer is authenticated
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, "system");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsToNonExistSystem)
{
// no BIOS setting file if createEmptyBlobFile() is not called
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// peer is authenticated
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, "system1");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsBlobFileNotExist)
{
biosSettingFilepathFromConfig = "/non_exist/oem_bios_setting";
absl::Cleanup bioSettingFilePathResetter = []() {
biosSettingFilepathFromConfig = biosSettingFilePath;
};
// no BIOS setting file if createEmptyBlobFile() is not called
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// peer is authenticated
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, "system");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsWithNullBlob)
{
// no BIOS setting file if createEmptyBlobFile() is not called
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"\"}}}");
// peer is authenticated
req.peer_authenticated = true;
handleBiosSettingPatch(share_async_resp_, biosSettingFilepathFromConfig,
false, std::nullopt);
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsWithInvalidBlob)
{
// no BIOS setting file if createEmptyBlobFile() is not called
// ABCDE= is a invalid base64 encoded string
crow::Request req = CreateRequest(
"{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"ABCDE=\"}}}");
// peer is authenticated
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, "system");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(SingleHostBiosBlobPatchTest,
PatchBiosSettingsGetSystemSubTreePathsResponseError)
{
// prepare the BIOS setting file
createEmptyBlobFile();
// Looking for
// kManagedSubtreePaths|/|0|xyz.openbmc_project.Inventory.Item.System
KeyType key(ManagedType::kManagedSubtreePaths, "/", 0,
{"xyz.openbmc_project.Inventory.Item.System"});
absl::StatusOr<std::shared_ptr<ValueType>> systemObjectsPathsStatusOr =
managedStore::GetManagedObjectStore()->getMockObjectFromManagedStore(
key);
ASSERT_TRUE(systemObjectsPathsStatusOr.ok());
// TODO(b/416746677): Uncomment after the bug is fixed
// ASSERT_EQ(systemObjectsPathsStatusOr.value()->managedType,
// ManagedType::kManagedSubtreePaths);
std::optional<dbus::utility::MapperGetSubTreePathsResponse>
originalSystemObjectsPaths =
systemObjectsPathsStatusOr.value()->managedSubtreePaths;
ASSERT_TRUE(originalSystemObjectsPaths.has_value());
ASSERT_TRUE(
managedStore::GetManagedObjectStore()
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateErrorValueType(
originalSystemObjectsPaths.value(),
boost::system::errc::make_error_code(
boost::system::errc::io_error)))
.ok());
absl::Cleanup mockManagedStoreResetter = [key,
originalSystemObjectsPaths]() {
ASSERT_TRUE(
managedStore::GetManagedObjectStore()
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(
originalSystemObjectsPaths.value()))
.ok());
};
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// set authenticated to true
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, "system");
RunIoUntilDone();
// PATCH request use no_content as convention to indicate a success
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsSuccessfully)
{
crow::Logger::setLogLevel(crow::LogLevel::Error);
// prepare the BIOS setting file
createEmptyBlobFile();
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system");
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["Oem"]["Google"]["MemTestBlob"], "");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
if (std::filesystem::exists(biosSettingFilePath))
{
LOG(INFO) << biosSettingFilePath << " exists in test body";
}
share_async_resp_->res.clear();
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// set authenticated to true
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, "system");
RunIoUntilDone();
// PATCH request use no_content as convention to indicate a success
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::no_content);
share_async_resp_->res.clear();
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system");
RunIoUntilDone();
json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["Oem"]["Google"]["MemTestBlob"], "AAEC");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
} // namespace
} // namespace redfish