summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorEd Tanous <edtanous@google.com>2023-02-07 21:02:07 +0300
committerEd Tanous <ed@tanous.net>2023-03-14 22:00:34 +0300
commitad595fa64f619124eadcdfc2af259a1975136399 (patch)
treeda02f9b36676a5e389985ae0dc8c95e0642965d6 /test
parent5315c1b149b724d0395f42ca75d4660aaecdf351 (diff)
downloadbmcweb-ad595fa64f619124eadcdfc2af259a1975136399.tar.xz
Only parse to the depth requested
There are cases in aggregation where an expand parameter might get forwarded to a client. Because our previous expand algorithm assumed that any endpoint within bmcweb would only produce "depth=1" responses, it was reasonable to assume that the pre-response could not contain expanded content. Aggregated resources can't make that assumption. This commit attempts to pass through depth through the request, to ensure that we only expand the level that the user requested, and not any level returned by the request. This is done by using the existence of the resource identifer "@odata.id" to indicate each level in an expanded response. This should be fine since the Redfish spec requires that property to exist. Added unit tests to cover aggregation scenarios. Modified existing $expand tests to comply with the resource identifier dependency. Tested: New unit tests pass Queried '/redfish/v1/Systems?$expand=.($levels=2)' on an aggregated system whose satellite BMC supported $expand. The overall response was correctly expanded for both resources on the aggregating BMC as well as on the satellite BMC. Expanding the satellite resources did not require sending multiple queries to the satellite. Signed-off-by: Ed Tanous <edtanous@google.com> Change-Id: I20ba60ee39bac11ffb3fe1768cec6299cf9ee13e Signed-off-by: Carson Labrado <clabrado@google.com>
Diffstat (limited to 'test')
-rw-r--r--test/redfish-core/include/utils/query_param_test.cpp178
1 files changed, 162 insertions, 16 deletions
diff --git a/test/redfish-core/include/utils/query_param_test.cpp b/test/redfish-core/include/utils/query_param_test.cpp
index 51f0b7101f..d049314d75 100644
--- a/test/redfish-core/include/utils/query_param_test.cpp
+++ b/test/redfish-core/include/utils/query_param_test.cpp
@@ -652,57 +652,203 @@ TEST(QueryParams, FindNavigationReferencesNonLink)
{
using nlohmann::json;
- json singleTreeNode = R"({"Foo" : {"@odata.id": "/foobar"}})"_json;
+ // Responses must include their "@odata.id" property for $expand to work
+ // correctly
+ json singleTreeNode =
+ R"({"@odata.id": "/redfish/v1",
+ "Foo" : {"@odata.id": "/foobar"}})"_json;
// Parsing as the root should net one entry
- EXPECT_THAT(findNavigationReferences(ExpandType::Both, singleTreeNode),
+ EXPECT_THAT(findNavigationReferences(ExpandType::Both, 1, singleTreeNode),
UnorderedElementsAre(
ExpandNode{json::json_pointer("/Foo"), "/foobar"}));
// Parsing in Non-hyperlinks mode should net one entry
- EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, singleTreeNode),
- UnorderedElementsAre(
- ExpandNode{json::json_pointer("/Foo"), "/foobar"}));
+ EXPECT_THAT(
+ findNavigationReferences(ExpandType::NotLinks, 1, singleTreeNode),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Foo"), "/foobar"}));
// Searching for not types should return empty set
EXPECT_TRUE(
- findNavigationReferences(ExpandType::None, singleTreeNode).empty());
+ findNavigationReferences(ExpandType::None, 1, singleTreeNode).empty());
// Searching for hyperlinks only should return empty set
EXPECT_TRUE(
- findNavigationReferences(ExpandType::Links, singleTreeNode).empty());
+ findNavigationReferences(ExpandType::Links, 1, singleTreeNode).empty());
+ // Responses must include their "@odata.id" property for $expand to work
+ // correctly
json multiTreeNodes =
- R"({"Links": {"@odata.id": "/links"}, "Foo" : {"@odata.id": "/foobar"}})"_json;
+ R"({"@odata.id": "/redfish/v1",
+ "Links": {"@odata.id": "/links"},
+ "Foo" : {"@odata.id": "/foobar"}})"_json;
+
// Should still find Foo
- EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, multiTreeNodes),
- UnorderedElementsAre(
- ExpandNode{json::json_pointer("/Foo"), "/foobar"}));
+ EXPECT_THAT(
+ findNavigationReferences(ExpandType::NotLinks, 1, multiTreeNodes),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Foo"), "/foobar"}));
}
TEST(QueryParams, FindNavigationReferencesLink)
{
using nlohmann::json;
+ // Responses must include their "@odata.id" property for $expand to work
+ // correctly
json singleLinkNode =
- R"({"Links" : {"Sessions": {"@odata.id": "/foobar"}}})"_json;
+ R"({"@odata.id": "/redfish/v1",
+ "Links" : {"Sessions": {"@odata.id": "/foobar"}}})"_json;
// Parsing as the root should net one entry
- EXPECT_THAT(findNavigationReferences(ExpandType::Both, singleLinkNode),
+ EXPECT_THAT(findNavigationReferences(ExpandType::Both, 1, singleLinkNode),
UnorderedElementsAre(ExpandNode{
json::json_pointer("/Links/Sessions"), "/foobar"}));
// Parsing in hyperlinks mode should net one entry
- EXPECT_THAT(findNavigationReferences(ExpandType::Links, singleLinkNode),
+ EXPECT_THAT(findNavigationReferences(ExpandType::Links, 1, singleLinkNode),
UnorderedElementsAre(ExpandNode{
json::json_pointer("/Links/Sessions"), "/foobar"}));
// Searching for not types should return empty set
EXPECT_TRUE(
- findNavigationReferences(ExpandType::None, singleLinkNode).empty());
+ findNavigationReferences(ExpandType::None, 1, singleLinkNode).empty());
// Searching for non-hyperlinks only should return empty set
EXPECT_TRUE(
- findNavigationReferences(ExpandType::NotLinks, singleLinkNode).empty());
+ findNavigationReferences(ExpandType::NotLinks, 1, singleLinkNode)
+ .empty());
+}
+
+TEST(QueryParams, PreviouslyExpanded)
+{
+ using nlohmann::json;
+
+ // Responses must include their "@odata.id" property for $expand to work
+ // correctly
+ json expNode = json::parse(R"(
+{
+ "@odata.id": "/redfish/v1/Chassis",
+ "@odata.type": "#ChassisCollection.ChassisCollection",
+ "Members": [
+ {
+ "@odata.id": "/redfish/v1/Chassis/5B247A_Sat1",
+ "@odata.type": "#Chassis.v1_17_0.Chassis",
+ "Sensors": {
+ "@odata.id": "/redfish/v1/Chassis/5B247A_Sat1/Sensors"
+ }
+ },
+ {
+ "@odata.id": "/redfish/v1/Chassis/5B247A_Sat2",
+ "@odata.type": "#Chassis.v1_17_0.Chassis",
+ "Sensors": {
+ "@odata.id": "/redfish/v1/Chassis/5B247A_Sat2/Sensors"
+ }
+ }
+ ],
+ "Members@odata.count": 2,
+ "Name": "Chassis Collection"
+}
+)",
+ nullptr, false);
+
+ // Expand has already occurred so we should not do anything
+ EXPECT_TRUE(
+ findNavigationReferences(ExpandType::NotLinks, 1, expNode).empty());
+
+ // Previous expand was only a single level so we should further expand
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 2, expNode),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Members/0/Sensors"),
+ "/redfish/v1/Chassis/5B247A_Sat1/Sensors"},
+ ExpandNode{json::json_pointer("/Members/1/Sensors"),
+ "/redfish/v1/Chassis/5B247A_Sat2/Sensors"}));
+
+ // Make sure we can handle when an array was expanded further down the tree
+ json expNode2 = R"({"@odata.id" : "/redfish/v1"})"_json;
+ expNode2["Chassis"] = std::move(expNode);
+ EXPECT_TRUE(
+ findNavigationReferences(ExpandType::NotLinks, 1, expNode2).empty());
+ EXPECT_TRUE(
+ findNavigationReferences(ExpandType::NotLinks, 2, expNode2).empty());
+
+ // Previous expand was two levels so we should further expand
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 3, expNode2),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Chassis/Members/0/Sensors"),
+ "/redfish/v1/Chassis/5B247A_Sat1/Sensors"},
+ ExpandNode{json::json_pointer("/Chassis/Members/1/Sensors"),
+ "/redfish/v1/Chassis/5B247A_Sat2/Sensors"}));
+}
+
+TEST(QueryParams, PartiallyPreviouslyExpanded)
+{
+ using nlohmann::json;
+
+ // Responses must include their "@odata.id" property for $expand to work
+ // correctly
+ json expNode = json::parse(R"(
+{
+ "@odata.id": "/redfish/v1/Chassis",
+ "@odata.type": "#ChassisCollection.ChassisCollection",
+ "Members": [
+ {
+ "@odata.id": "/redfish/v1/Chassis/Local"
+ },
+ {
+ "@odata.id": "/redfish/v1/Chassis/5B247A_Sat1",
+ "@odata.type": "#Chassis.v1_17_0.Chassis",
+ "Sensors": {
+ "@odata.id": "/redfish/v1/Chassis/5B247A_Sat1/Sensors"
+ }
+ }
+ ],
+ "Members@odata.count": 2,
+ "Name": "Chassis Collection"
+}
+)",
+ nullptr, false);
+
+ // The 5B247A_Sat1 Chassis was already expanded a single level so we should
+ // only want to expand the Local Chassis
+ EXPECT_THAT(
+ findNavigationReferences(ExpandType::NotLinks, 1, expNode),
+ UnorderedElementsAre(ExpandNode{json::json_pointer("/Members/0"),
+ "/redfish/v1/Chassis/Local"}));
+
+ // The 5B247A_Sat1 Chassis was already expanded a single level so we should
+ // further expand it as well as the Local Chassis
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 2, expNode),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Members/0"),
+ "/redfish/v1/Chassis/Local"},
+ ExpandNode{json::json_pointer("/Members/1/Sensors"),
+ "/redfish/v1/Chassis/5B247A_Sat1/Sensors"}));
+
+ // Now the response has paths that have been expanded 0, 1, and 2 times
+ json expNode2 = R"({"@odata.id" : "/redfish/v1",
+ "Systems": {"@odata.id": "/redfish/v1/Systems"}})"_json;
+ expNode2["Chassis"] = std::move(expNode);
+
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 1, expNode2),
+ UnorderedElementsAre(ExpandNode{json::json_pointer("/Systems"),
+ "/redfish/v1/Systems"}));
+
+ EXPECT_THAT(
+ findNavigationReferences(ExpandType::NotLinks, 2, expNode2),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Systems"), "/redfish/v1/Systems"},
+ ExpandNode{json::json_pointer("/Chassis/Members/0"),
+ "/redfish/v1/Chassis/Local"}));
+
+ EXPECT_THAT(
+ findNavigationReferences(ExpandType::NotLinks, 3, expNode2),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Systems"), "/redfish/v1/Systems"},
+ ExpandNode{json::json_pointer("/Chassis/Members/0"),
+ "/redfish/v1/Chassis/Local"},
+ ExpandNode{json::json_pointer("/Chassis/Members/1/Sensors"),
+ "/redfish/v1/Chassis/5B247A_Sat1/Sensors"}));
}
} // namespace