| #include "redfish-core/lib/nvme_features.hpp" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include "absl/functional/any_invocable.h" |
| #include "boost/asio/post.hpp" // NOLINT |
| #include "http_request.hpp" |
| #include "http_response.hpp" |
| #include "dbus_utility.hpp" |
| #include "test/redfish-core/lib/snapshot_fixture.hpp" |
| #include <nlohmann/json_fwd.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.hpp" |
| #include "sdbusplus/message/native_types.hpp" |
| |
| namespace redfish { |
| namespace { |
| |
| using ::dbus::utility::DBusInteracesMap; |
| using ::dbus::utility::DBusPropertiesMap; |
| using ::dbus::utility::DbusVariantType; |
| using ::dbus::utility::ManagedObjectType; |
| using ::dbus::utility::MapperGetSubTreePathsResponse; |
| using ::dbus::utility::MapperGetSubTreeResponse; |
| using ::managedStore::KeyType; |
| using ::managedStore::ManagedType; |
| using ::managedStore::ValueType; |
| using ::testing::Contains; |
| |
| using FeatureCollectionType = |
| std::vector<std::tuple<std::string, std::string, std::string>>; |
| |
| MapperGetSubTreeResponse CreateMockStorageSubTree() { |
| return MapperGetSubTreeResponse( |
| {{"/xyz/openbmc_project/inventory/system/storage/storage0", |
| {{"xyz.openbmc_project.StorageService", |
| {"xyz.openbmc_project.Inventory.Item.Storage"}}}}}); |
| } |
| |
| MapperGetSubTreeResponse CreateMockControllerSubTree() { |
| return MapperGetSubTreeResponse( |
| {{"/xyz/openbmc_project/inventory/system/storage/storage0/" |
| "storage_controller/controller0", |
| {{"xyz.openbmc_project.NVMe.FeatureStore", |
| {"xyz.openbmc_project.Inventory.Item.StorageController", |
| "xyz.openbmc_project.NVMe.FeatureStore"}}}}}); |
| } |
| |
| MapperGetSubTreePathsResponse CreateMockStorageForControllerPaths() { |
| return MapperGetSubTreePathsResponse( |
| {"/xyz/openbmc_project/inventory/system/storage/storage0"}); |
| } |
| |
| void InjectMocks(managedStore::MockSerializedManagedObjectStore* mockStore) { |
| KeyType storageKey(ManagedType::kManagedSubtree, |
| "/xyz/openbmc_project/inventory", 0, |
| {"xyz.openbmc_project.Inventory.Item.Storage"}); |
| std::shared_ptr<ValueType> mockStorageSubTreeResponse = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| CreateMockStorageSubTree()); |
| EXPECT_TRUE(mockStore |
| ->upsertMockObjectIntoManagedStore(storageKey, |
| mockStorageSubTreeResponse) |
| .ok()); |
| |
| KeyType controllerKey( |
| ManagedType::kManagedAssociatedSubtree, |
| sdbusplus::message::object_path("/xyz/openbmc_project/inventory/system/" |
| "storage/storage0/storage_controller"), |
| sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, |
| {"xyz.openbmc_project.Inventory.Item.StorageController"}); |
| std::shared_ptr<ValueType> mockControllerSubTreeResponse = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| CreateMockControllerSubTree()); |
| EXPECT_TRUE(mockStore |
| ->upsertMockObjectIntoManagedStore( |
| controllerKey, mockControllerSubTreeResponse) |
| .ok()); |
| |
| KeyType storageForControllerKey( |
| ManagedType::kManagedAssociatedSubtreePaths, |
| sdbusplus::message::object_path( |
| "/xyz/openbmc_project/inventory/system/storage/storage0/" |
| "storage_controller/controller0/storage"), |
| sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, |
| {"xyz.openbmc_project.Inventory.Item.Storage"}); |
| std::shared_ptr<ValueType> mockStorageForControllerPathsResponse = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| CreateMockStorageForControllerPaths()); |
| EXPECT_TRUE( |
| mockStore |
| ->upsertMockObjectIntoManagedStore( |
| storageForControllerKey, mockStorageForControllerPathsResponse) |
| .ok()); |
| } |
| |
| TEST_F(SnapshotFixture, GetNvmeFeaturesCollectionReturnsCorrectResponse) { |
| auto mockStoreRaw = |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( |
| managedStore::GetManagedObjectStore()); |
| ASSERT_TRUE(mockStoreRaw != nullptr); |
| InjectMocks(mockStoreRaw); |
| |
| FeatureCollectionType col = {{"feature1", "0x01", "class1"}, |
| {"feature2", "0x02", "class2"}}; |
| |
| KeyType propKey(ManagedType::kManagedProperty, |
| "xyz.openbmc_project.NVMe.FeatureStore", |
| sdbusplus::message::object_path( |
| "/xyz/openbmc_project/inventory/system/storage/storage0/" |
| "storage_controller/controller0"), |
| "xyz.openbmc_project.NVMe.FeatureStore", "FeatureCollection"); |
| auto mockPropResp = managedStore::MockManagedStoreTest::CreateValueType(col); |
| EXPECT_TRUE( |
| mockStoreRaw->upsertMockObjectIntoManagedStore(propKey, mockPropResp) |
| .ok()); |
| |
| crow::Request req = CreateRequest(); |
| handleControllerNvmeFeaturesCollectionGet( |
| app_, req, share_async_resp_, "system", "storage0", "controller0"); |
| |
| RunIoUntilDone(); |
| |
| nlohmann::json& json = share_async_resp_->res.jsonValue; |
| EXPECT_EQ(json["@odata.id"], |
| "/redfish/v1/Systems/system/Storage/storage0/Controllers/" |
| "controller0/Oem/Google/Features"); |
| EXPECT_EQ(json["@odata.type"], |
| "#GoogleNvmeFeatureCollection.GoogleNvmeFeatureCollection"); |
| EXPECT_EQ(json["Name"], "NVMe Features Collection"); |
| EXPECT_EQ(json["Members@odata.count"], 2); |
| EXPECT_THAT(json["Members"], |
| Contains(nlohmann::json( |
| {{"@odata.id", |
| "/redfish/v1/Systems/system/Storage/storage0/Controllers/" |
| "controller0/Oem/Google/Features/feature1"}}))); |
| EXPECT_THAT(json["Members"], |
| Contains(nlohmann::json( |
| {{"@odata.id", |
| "/redfish/v1/Systems/system/Storage/storage0/Controllers/" |
| "controller0/Oem/Google/Features/feature2"}}))); |
| } |
| |
| TEST_F(SnapshotFixture, GetNvmeFeatureReturnsCorrectResponse) { |
| auto mockStoreRaw = |
| dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( |
| managedStore::GetManagedObjectStore()); |
| ASSERT_TRUE(mockStoreRaw != nullptr); |
| InjectMocks(mockStoreRaw); |
| |
| FeatureCollectionType col = {{"feature1_select1", "0x01", "class1"}}; |
| |
| std::vector<std::pair<std::string, DbusVariantType>> props; |
| props.push_back({"ProtoLibraryVersion", std::string("1.2")}); |
| props.push_back({"FeatureCollection", col}); |
| |
| KeyType allPropsKey(ManagedType::kManagedPropertyMap, |
| "xyz.openbmc_project.NVMe.FeatureStore", |
| sdbusplus::message::object_path( |
| "/xyz/openbmc_project/inventory/system/storage/" |
| "storage0/storage_controller/controller0"), |
| "xyz.openbmc_project.NVMe.FeatureStore"); |
| auto mockAllPropsResp = |
| managedStore::MockManagedStoreTest::CreateValueType(props); |
| EXPECT_TRUE( |
| mockStoreRaw |
| ->upsertMockObjectIntoManagedStore(allPropsKey, mockAllPropsResp) |
| .ok()); |
| |
| std::vector<uint8_t> payload = {1, 2, 3}; |
| EXPECT_CALL( |
| *mockStoreRaw, |
| PostDbusCallToIoContextThreadSafe( |
| testing::_, |
| testing::An<absl::AnyInvocable<void( |
| const boost::system::error_code&, const sdbusplus::message_t&, |
| const std::vector<uint8_t>&)>&&>(), |
| testing::TypedEq<const std::string&>( |
| "xyz.openbmc_project.NVMe.FeatureStore"), |
| testing::TypedEq<const std::string&>( |
| "/xyz/openbmc_project/inventory/system/storage/storage0/" |
| "storage_controller/controller0"), |
| testing::TypedEq<const std::string&>( |
| "xyz.openbmc_project.NVMe.FeatureStore"), |
| testing::TypedEq<const std::string&>("GetFeature"), |
| testing::TypedEq<const std::string&>("feature1_select1"))) |
| .WillOnce( |
| [payload]( |
| const std::shared_ptr<boost::asio::io_context::strand>&, |
| absl::AnyInvocable<void(const boost::system::error_code&, |
| const sdbusplus::message_t&, |
| const std::vector<uint8_t>&)>&& callback, |
| const std::string&, const std::string&, const std::string&, |
| const std::string&, const std::string&) { |
| boost::asio::post( |
| managedStore::MockManagedStoreTest::GetStaticIoContext(), |
| [payload, callback = std::move(callback)]() mutable { |
| sdbusplus::message_t msg; |
| callback(boost::system::errc::make_error_code( |
| boost::system::errc::success), |
| msg, payload); |
| }); |
| }); |
| |
| crow::Request req = CreateRequest(); |
| handleControllerNvmeFeatureGet(app_, req, share_async_resp_, "system", |
| "storage0", "controller0", "feature1_select1"); |
| |
| RunIoUntilDone(); |
| |
| nlohmann::json& json = share_async_resp_->res.jsonValue; |
| EXPECT_EQ(json["@odata.id"], |
| "/redfish/v1/Systems/system/Storage/storage0/Controllers/" |
| "controller0/Oem/Google/Features/feature1_select1"); |
| EXPECT_EQ(json["@odata.type"], "#GoogleNvmeFeature.v1_0_0.GoogleNvmeFeature"); |
| EXPECT_EQ(json["Id"], "feature1_select1"); |
| EXPECT_EQ(json["Name"], "feature1_select1"); |
| EXPECT_EQ(json["FeatureId"], "0x01"); |
| EXPECT_EQ(json["Select"], "select1"); |
| EXPECT_EQ(json["DataProto"]["ClassName"], "class1"); |
| EXPECT_EQ(json["DataProto"]["Revision"], "1.2"); |
| } |
| |
| TEST(ParseNvmeFeaturePropertiesTest, Success) { |
| FeatureCollectionType col = {{"feature1", "0x01", "class1"}}; |
| std::vector<std::pair<std::string, DbusVariantType>> properties = { |
| {"ProtoLibraryVersion", std::string("1.0")}, {"FeatureCollection", col}}; |
| |
| std::string revision; |
| std::string featureIdStr; |
| std::string className; |
| crow::Response res; |
| |
| bool result = parseNvmeFeatureProperties(properties, "feature1", revision, |
| featureIdStr, className, res); |
| EXPECT_TRUE(result); |
| EXPECT_EQ(revision, "1.0"); |
| EXPECT_EQ(featureIdStr, "0x01"); |
| EXPECT_EQ(className, "class1"); |
| } |
| |
| TEST(ParseNvmeFeaturePropertiesTest, MissingRevision) { |
| FeatureCollectionType col = {{"feature1", "0x01", "class1"}}; |
| std::vector<std::pair<std::string, DbusVariantType>> properties = { |
| {"FeatureCollection", col}}; |
| |
| std::string revision; |
| std::string featureIdStr; |
| std::string className; |
| crow::Response res; |
| |
| bool result = parseNvmeFeatureProperties(properties, "feature1", revision, |
| featureIdStr, className, res); |
| EXPECT_FALSE(result); |
| EXPECT_EQ(res.result(), boost::beast::http::status::internal_server_error); |
| } |
| |
| TEST(ParseNvmeFeaturePropertiesTest, MissingFeatureCollection) { |
| std::vector<std::pair<std::string, DbusVariantType>> properties = { |
| {"ProtoLibraryVersion", std::string("1.0")}}; |
| |
| std::string revision; |
| std::string featureIdStr; |
| std::string className; |
| crow::Response res; |
| |
| bool result = parseNvmeFeatureProperties(properties, "feature1", revision, |
| featureIdStr, className, res); |
| EXPECT_FALSE(result); |
| EXPECT_EQ(res.result(), boost::beast::http::status::internal_server_error); |
| } |
| |
| TEST(ParseNvmeFeaturePropertiesTest, FeatureNotFound) { |
| FeatureCollectionType col = {{"feature2", "0x01", "class1"}}; |
| std::vector<std::pair<std::string, DbusVariantType>> properties = { |
| {"ProtoLibraryVersion", std::string("1.0")}, {"FeatureCollection", col}}; |
| |
| std::string revision; |
| std::string featureIdStr; |
| std::string className; |
| crow::Response res; |
| |
| bool result = parseNvmeFeatureProperties(properties, "feature1", revision, |
| featureIdStr, className, res); |
| EXPECT_FALSE(result); |
| EXPECT_EQ(res.result(), boost::beast::http::status::not_found); |
| } |
| |
| } // namespace |
| } // namespace redfish |