1 #include "async_resp.hpp" 2 #include "redfish_aggregator.hpp" 3 4 #include <nlohmann/json.hpp> 5 6 #include <gtest/gtest.h> // IWYU pragma: keep 7 8 namespace redfish 9 { 10 namespace 11 { 12 13 TEST(IsPropertyUri, SupportedPropertyReturnsTrue) 14 { 15 EXPECT_TRUE(isPropertyUri("@Redfish.ActionInfo")); 16 EXPECT_TRUE(isPropertyUri("@odata.id")); 17 EXPECT_TRUE(isPropertyUri("Image")); 18 EXPECT_TRUE(isPropertyUri("MetricProperty")); 19 EXPECT_TRUE(isPropertyUri("TaskMonitor")); 20 EXPECT_TRUE(isPropertyUri("target")); 21 } 22 23 TEST(IsPropertyUri, CaseInsensitiveURIReturnsTrue) 24 { 25 EXPECT_TRUE(isPropertyUri("AdditionalDataURI")); 26 EXPECT_TRUE(isPropertyUri("DataSourceUri")); 27 EXPECT_TRUE(isPropertyUri("uri")); 28 EXPECT_TRUE(isPropertyUri("URI")); 29 } 30 31 TEST(IsPropertyUri, SpeificallyIgnoredPropertyReturnsFalse) 32 { 33 EXPECT_FALSE(isPropertyUri("@odata.context")); 34 EXPECT_FALSE(isPropertyUri("Destination")); 35 EXPECT_FALSE(isPropertyUri("HostName")); 36 EXPECT_FALSE(isPropertyUri("OriginOfCondition")); 37 } 38 39 TEST(IsPropertyUri, UnsupportedPropertyReturnsFalse) 40 { 41 EXPECT_FALSE(isPropertyUri("Name")); 42 EXPECT_FALSE(isPropertyUri("Health")); 43 EXPECT_FALSE(isPropertyUri("Id")); 44 } 45 46 TEST(addPrefixToItem, ValidURIs) 47 { 48 nlohmann::json jsonRequest; 49 constexpr std::array validRoots{"Cables", 50 "Chassis", 51 "Fabrics", 52 "PowerEquipment/FloorPDUs", 53 "Systems", 54 "TaskService/Tasks", 55 "TelemetryService/LogService/Entries", 56 "UpdateService/SoftwareInventory"}; 57 58 // We're only testing prefix fixing so it's alright that some of the 59 // resulting URIs will not actually be possible as defined by the schema 60 constexpr std::array validIDs{"1", 61 "1/", 62 "Test", 63 "Test/", 64 "Extra_Test", 65 "Extra_Test/", 66 "Extra_Test/Sensors", 67 "Extra_Test/Sensors/", 68 "Extra_Test/Sensors/power_sensor", 69 "Extra_Test/Sensors/power_sensor/"}; 70 71 // Construct URIs which should have prefix fixing applied 72 for (const auto& root : validRoots) 73 { 74 for (const auto& id : validIDs) 75 { 76 std::string initial("/redfish/v1/" + std::string(root) + "/"); 77 std::string correct(initial + "asdfjkl_" + std::string(id)); 78 initial += id; 79 jsonRequest["@odata.id"] = initial; 80 addPrefixToItem(jsonRequest["@odata.id"], "asdfjkl"); 81 EXPECT_EQ(jsonRequest["@odata.id"], correct); 82 } 83 } 84 } 85 86 TEST(addPrefixToItem, UnsupportedURIs) 87 { 88 nlohmann::json jsonRequest; 89 constexpr std::array invalidRoots{ 90 "FakeCollection", "JsonSchemas", 91 "PowerEquipment", "TaskService", 92 "TelemetryService/Entries", "UpdateService"}; 93 94 constexpr std::array validIDs{"1", 95 "1/", 96 "Test", 97 "Test/", 98 "Extra_Test", 99 "Extra_Test/", 100 "Extra_Test/Sensors", 101 "Extra_Test/Sensors/", 102 "Extra_Test/Sensors/power_sensor", 103 "Extra_Test/Sensors/power_sensor/"}; 104 105 // Construct URIs which should NOT have prefix fixing applied 106 for (const auto& root : invalidRoots) 107 { 108 for (const auto& id : validIDs) 109 { 110 std::string initial("/redfish/v1/" + std::string(root) + "/"); 111 std::string correct(initial + "asdfjkl_" + std::string(id)); 112 initial += id; 113 jsonRequest["@odata.id"] = initial; 114 addPrefixToItem(jsonRequest["@odata.id"], "asdfjkl"); 115 EXPECT_EQ(jsonRequest["@odata.id"], initial); 116 } 117 } 118 } 119 120 TEST(addPrefixToItem, TopLevelCollections) 121 { 122 nlohmann::json jsonRequest; 123 constexpr std::array validRoots{"Cables", 124 "Chassis/", 125 "Fabrics", 126 "JsonSchemas", 127 "PowerEquipment/FloorPDUs", 128 "Systems", 129 "TaskService/Tasks", 130 "TelemetryService/LogService/Entries", 131 "TelemetryService/LogService/Entries/", 132 "UpdateService/SoftwareInventory/"}; 133 134 // Construct URIs for top level collections. Prefixes should NOT be 135 // applied to any of the URIs 136 for (const auto& root : validRoots) 137 { 138 std::string initial("/redfish/v1/" + std::string(root)); 139 jsonRequest["@odata.id"] = initial; 140 addPrefixToItem(jsonRequest["@odata.id"], "perfix"); 141 EXPECT_EQ(jsonRequest["@odata.id"], initial); 142 } 143 } 144 145 TEST(addPrefixes, ParseJsonObject) 146 { 147 nlohmann::json parameter; 148 parameter["Name"] = "/redfish/v1/Chassis/fakeName"; 149 parameter["@odata.id"] = "/redfish/v1/Chassis/fakeChassis"; 150 151 addPrefixes(parameter, "abcd"); 152 EXPECT_EQ(parameter["Name"], "/redfish/v1/Chassis/fakeName"); 153 EXPECT_EQ(parameter["@odata.id"], "/redfish/v1/Chassis/abcd_fakeChassis"); 154 } 155 156 TEST(addPrefixes, ParseJsonArray) 157 { 158 nlohmann::json array = nlohmann::json::parse(R"( 159 { 160 "Conditions": [ 161 { 162 "Message": "This is a test", 163 "@odata.id": "/redfish/v1/Chassis/TestChassis" 164 }, 165 { 166 "Message": "This is also a test", 167 "@odata.id": "/redfish/v1/Chassis/TestChassis2" 168 } 169 ] 170 } 171 )", 172 nullptr, false); 173 174 addPrefixes(array, "5B42"); 175 EXPECT_EQ(array["Conditions"][0]["@odata.id"], 176 "/redfish/v1/Chassis/5B42_TestChassis"); 177 EXPECT_EQ(array["Conditions"][1]["@odata.id"], 178 "/redfish/v1/Chassis/5B42_TestChassis2"); 179 } 180 181 TEST(addPrefixes, ParseJsonObjectNestedArray) 182 { 183 nlohmann::json objWithArray = nlohmann::json::parse(R"( 184 { 185 "Status": { 186 "Conditions": [ 187 { 188 "Message": "This is a test", 189 "MessageId": "Test", 190 "OriginOfCondition": { 191 "@odata.id": "/redfish/v1/Chassis/TestChassis" 192 }, 193 "Severity": "Critical" 194 } 195 ], 196 "Health": "Critical", 197 "State": "Enabled" 198 } 199 } 200 )", 201 nullptr, false); 202 203 addPrefixes(objWithArray, "5B42"); 204 nlohmann::json& array = objWithArray["Status"]["Conditions"]; 205 EXPECT_EQ(array[0]["OriginOfCondition"]["@odata.id"], 206 "/redfish/v1/Chassis/5B42_TestChassis"); 207 } 208 209 TEST(addPrefixes, FixHttpHeadersInResponseBody) 210 { 211 nlohmann::json taskResp = nlohmann::json::parse(R"( 212 { 213 "@odata.id": "/redfish/v1/TaskService/Tasks/0", 214 "Name": "Task 0", 215 "Payload": { 216 "HttpHeaders": [ 217 "User-Agent: curl/7.87.0", 218 "Accept: */*", 219 "Host: 127.127.12.7", 220 "Content-Length: 33", 221 "Location: /redfish/v1/Managers/bmc/LogServices/Dump/Entries/0" 222 ] 223 }, 224 "PercentComplete": 100, 225 "TaskMonitor": "/redfish/v1/TaskService/Tasks/0/Monitor", 226 "TaskState": "Completed", 227 "TaskStatus": "OK" 228 } 229 )", 230 nullptr, false); 231 232 addPrefixes(taskResp, "5B247A"); 233 EXPECT_EQ(taskResp["@odata.id"], "/redfish/v1/TaskService/Tasks/5B247A_0"); 234 EXPECT_EQ(taskResp["TaskMonitor"], 235 "/redfish/v1/TaskService/Tasks/5B247A_0/Monitor"); 236 nlohmann::json& httpHeaders = taskResp["Payload"]["HttpHeaders"]; 237 EXPECT_EQ( 238 httpHeaders[4], 239 "Location: /redfish/v1/Managers/5B247A_bmc/LogServices/Dump/Entries/0"); 240 } 241 242 // Attempts to perform prefix fixing on a response with response code "result". 243 // Fixing should always occur 244 void assertProcessResponse(unsigned result) 245 { 246 nlohmann::json jsonResp; 247 jsonResp["@odata.id"] = "/redfish/v1/Chassis/TestChassis"; 248 jsonResp["Name"] = "Test"; 249 250 crow::Response resp; 251 resp.body() = jsonResp.dump(2, ' ', true, 252 nlohmann::json::error_handler_t::replace); 253 resp.addHeader("Content-Type", "application/json"); 254 resp.addHeader("Allow", "GET"); 255 resp.addHeader("Location", "/redfish/v1/Chassis/TestChassis"); 256 resp.addHeader("Link", "</redfish/v1/Test.json>; rel=describedby"); 257 resp.addHeader("Retry-After", "120"); 258 resp.result(result); 259 260 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 261 RedfishAggregator::processResponse("prefix", asyncResp, resp); 262 263 EXPECT_EQ(asyncResp->res.getHeaderValue("Content-Type"), 264 "application/json"); 265 EXPECT_EQ(asyncResp->res.getHeaderValue("Allow"), "GET"); 266 EXPECT_EQ(asyncResp->res.getHeaderValue("Location"), 267 "/redfish/v1/Chassis/prefix_TestChassis"); 268 EXPECT_EQ(asyncResp->res.getHeaderValue("Link"), ""); 269 EXPECT_EQ(asyncResp->res.getHeaderValue("Retry-After"), "120"); 270 271 EXPECT_EQ(asyncResp->res.jsonValue["Name"], "Test"); 272 EXPECT_EQ(asyncResp->res.jsonValue["@odata.id"], 273 "/redfish/v1/Chassis/prefix_TestChassis"); 274 EXPECT_EQ(asyncResp->res.resultInt(), result); 275 } 276 277 TEST(processResponse, validResponseCodes) 278 { 279 assertProcessResponse(100); 280 assertProcessResponse(200); 281 assertProcessResponse(204); 282 assertProcessResponse(300); 283 assertProcessResponse(404); 284 assertProcessResponse(405); 285 assertProcessResponse(500); 286 assertProcessResponse(507); 287 } 288 289 TEST(processResponse, preserveHeaders) 290 { 291 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 292 asyncResp->res.addHeader("OData-Version", "4.0"); 293 asyncResp->res.result(boost::beast::http::status::ok); 294 295 crow::Response resp; 296 resp.addHeader("OData-Version", "3.0"); 297 resp.addHeader(boost::beast::http::field::location, 298 "/redfish/v1/Chassis/Test"); 299 resp.result(boost::beast::http::status::too_many_requests); // 429 300 301 RedfishAggregator::processResponse("prefix", asyncResp, resp); 302 EXPECT_EQ(asyncResp->res.resultInt(), 429); 303 EXPECT_EQ(asyncResp->res.getHeaderValue("OData-Version"), "4.0"); 304 EXPECT_EQ(asyncResp->res.getHeaderValue("Location"), ""); 305 306 asyncResp->res.result(boost::beast::http::status::ok); 307 resp.result(boost::beast::http::status::bad_gateway); // 502 308 309 RedfishAggregator::processResponse("prefix", asyncResp, resp); 310 EXPECT_EQ(asyncResp->res.resultInt(), 502); 311 EXPECT_EQ(asyncResp->res.getHeaderValue("OData-Version"), "4.0"); 312 EXPECT_EQ(asyncResp->res.getHeaderValue("Location"), ""); 313 } 314 315 // Helper function to correctly populate a ComputerSystem collection response 316 void populateCollectionResponse(crow::Response& resp) 317 { 318 nlohmann::json jsonResp = nlohmann::json::parse(R"( 319 { 320 "@odata.id": "/redfish/v1/Systems", 321 "@odata.type": "#ComputerSystemCollection.ComputerSystemCollection", 322 "Members": [ 323 { 324 "@odata.id": "/redfish/v1/Systems/system" 325 } 326 ], 327 "Members@odata.count": 1, 328 "Name": "Computer System Collection" 329 } 330 )", 331 nullptr, false); 332 333 resp.clear(); 334 // resp.body() = 335 // jsonResp.dump(2, ' ', true, 336 // nlohmann::json::error_handler_t::replace); 337 resp.jsonValue = std::move(jsonResp); 338 resp.addHeader("OData-Version", "4.0"); 339 resp.addHeader("Content-Type", "application/json"); 340 resp.result(boost::beast::http::status::ok); 341 } 342 343 void populateCollectionNotFound(crow::Response& resp) 344 { 345 resp.clear(); 346 resp.addHeader("OData-Version", "4.0"); 347 resp.result(boost::beast::http::status::not_found); 348 } 349 350 // Used with the above functions to convert the response to appear like it's 351 // from a satellite which will not have a json component 352 void convertToSat(crow::Response& resp) 353 { 354 resp.body() = resp.jsonValue.dump(2, ' ', true, 355 nlohmann::json::error_handler_t::replace); 356 resp.jsonValue.clear(); 357 } 358 359 TEST(processCollectionResponse, localOnly) 360 { 361 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 362 crow::Response resp; 363 populateCollectionResponse(asyncResp->res); 364 populateCollectionNotFound(resp); 365 366 RedfishAggregator::processCollectionResponse("prefix", asyncResp, resp); 367 EXPECT_EQ(asyncResp->res.getHeaderValue("OData-Version"), "4.0"); 368 EXPECT_EQ(asyncResp->res.resultInt(), 200); 369 EXPECT_EQ(asyncResp->res.getHeaderValue("Content-Type"), 370 "application/json"); 371 EXPECT_EQ(asyncResp->res.jsonValue["Members@odata.count"], 1); 372 for (auto& member : asyncResp->res.jsonValue["Members"]) 373 { 374 // There should only be one member 375 EXPECT_EQ(member["@odata.id"], "/redfish/v1/Systems/system"); 376 } 377 } 378 379 TEST(processCollectionResponse, satelliteOnly) 380 { 381 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 382 crow::Response resp; 383 populateCollectionNotFound(asyncResp->res); 384 populateCollectionResponse(resp); 385 convertToSat(resp); 386 387 RedfishAggregator::processCollectionResponse("prefix", asyncResp, resp); 388 EXPECT_EQ(asyncResp->res.getHeaderValue("OData-Version"), "4.0"); 389 EXPECT_EQ(asyncResp->res.resultInt(), 200); 390 EXPECT_EQ(asyncResp->res.getHeaderValue("Content-Type"), 391 "application/json"); 392 EXPECT_EQ(asyncResp->res.jsonValue["Members@odata.count"], 1); 393 for (auto& member : asyncResp->res.jsonValue["Members"]) 394 { 395 // There should only be one member 396 EXPECT_EQ(member["@odata.id"], "/redfish/v1/Systems/prefix_system"); 397 } 398 } 399 400 TEST(processCollectionResponse, bothExist) 401 { 402 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 403 crow::Response resp; 404 populateCollectionResponse(asyncResp->res); 405 populateCollectionResponse(resp); 406 convertToSat(resp); 407 408 RedfishAggregator::processCollectionResponse("prefix", asyncResp, resp); 409 EXPECT_EQ(asyncResp->res.getHeaderValue("OData-Version"), "4.0"); 410 EXPECT_EQ(asyncResp->res.resultInt(), 200); 411 EXPECT_EQ(asyncResp->res.getHeaderValue("Content-Type"), 412 "application/json"); 413 EXPECT_EQ(asyncResp->res.jsonValue["Members@odata.count"], 2); 414 415 bool foundLocal = false; 416 bool foundSat = false; 417 for (const auto& member : asyncResp->res.jsonValue["Members"]) 418 { 419 if (member["@odata.id"] == "/redfish/v1/Systems/system") 420 { 421 foundLocal = true; 422 } 423 else if (member["@odata.id"] == "/redfish/v1/Systems/prefix_system") 424 { 425 foundSat = true; 426 } 427 } 428 EXPECT_TRUE(foundLocal); 429 EXPECT_TRUE(foundSat); 430 } 431 432 TEST(processCollectionResponse, satelliteWrongContentHeader) 433 { 434 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 435 crow::Response resp; 436 populateCollectionResponse(asyncResp->res); 437 populateCollectionResponse(resp); 438 convertToSat(resp); 439 440 // Ignore the satellite even though otherwise valid 441 resp.addHeader("Content-Type", ""); 442 443 RedfishAggregator::processCollectionResponse("prefix", asyncResp, resp); 444 EXPECT_EQ(asyncResp->res.getHeaderValue("OData-Version"), "4.0"); 445 EXPECT_EQ(asyncResp->res.resultInt(), 200); 446 EXPECT_EQ(asyncResp->res.getHeaderValue("Content-Type"), 447 "application/json"); 448 EXPECT_EQ(asyncResp->res.jsonValue["Members@odata.count"], 1); 449 for (auto& member : asyncResp->res.jsonValue["Members"]) 450 { 451 EXPECT_EQ(member["@odata.id"], "/redfish/v1/Systems/system"); 452 } 453 } 454 455 TEST(processCollectionResponse, neitherExist) 456 { 457 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 458 crow::Response resp; 459 populateCollectionNotFound(asyncResp->res); 460 populateCollectionNotFound(resp); 461 convertToSat(resp); 462 463 RedfishAggregator::processCollectionResponse("prefix", asyncResp, resp); 464 EXPECT_EQ(asyncResp->res.getHeaderValue("OData-Version"), "4.0"); 465 EXPECT_EQ(asyncResp->res.resultInt(), 404); 466 EXPECT_EQ(asyncResp->res.getHeaderValue("Content-Type"), ""); 467 } 468 469 TEST(processCollectionResponse, preserveHeaders) 470 { 471 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 472 crow::Response resp; 473 populateCollectionNotFound(asyncResp->res); 474 populateCollectionResponse(resp); 475 convertToSat(resp); 476 477 resp.addHeader("OData-Version", "3.0"); 478 resp.addHeader(boost::beast::http::field::location, 479 "/redfish/v1/Chassis/Test"); 480 481 // We skip processing collection responses that have a 429 or 502 code 482 resp.result(boost::beast::http::status::too_many_requests); // 429 483 RedfishAggregator::processCollectionResponse("prefix", asyncResp, resp); 484 EXPECT_EQ(asyncResp->res.resultInt(), 404); 485 EXPECT_EQ(asyncResp->res.getHeaderValue("OData-Version"), "4.0"); 486 EXPECT_EQ(asyncResp->res.getHeaderValue("Location"), ""); 487 488 resp.result(boost::beast::http::status::bad_gateway); // 502 489 RedfishAggregator::processCollectionResponse("prefix", asyncResp, resp); 490 EXPECT_EQ(asyncResp->res.resultInt(), 404); 491 EXPECT_EQ(asyncResp->res.getHeaderValue("OData-Version"), "4.0"); 492 EXPECT_EQ(asyncResp->res.getHeaderValue("Location"), ""); 493 } 494 void assertProcessResponseContentType(std::string_view contentType) 495 { 496 crow::Response resp; 497 resp.body() = "responseBody"; 498 resp.addHeader("Content-Type", contentType); 499 resp.addHeader("Location", "/redfish/v1/Chassis/TestChassis"); 500 resp.addHeader("Link", "metadataLink"); 501 resp.addHeader("Retry-After", "120"); 502 503 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 504 RedfishAggregator::processResponse("prefix", asyncResp, resp); 505 EXPECT_EQ(asyncResp->res.getHeaderValue("Content-Type"), contentType); 506 EXPECT_EQ(asyncResp->res.getHeaderValue("Location"), 507 "/redfish/v1/Chassis/prefix_TestChassis"); 508 EXPECT_EQ(asyncResp->res.getHeaderValue("Link"), ""); 509 EXPECT_EQ(asyncResp->res.getHeaderValue("Retry-After"), "120"); 510 EXPECT_EQ(asyncResp->res.body(), "responseBody"); 511 } 512 513 TEST(processResponse, DifferentContentType) 514 { 515 assertProcessResponseContentType("application/xml"); 516 assertProcessResponseContentType("application/yaml"); 517 assertProcessResponseContentType("text/event-stream"); 518 assertProcessResponseContentType(";charset=utf-8"); 519 } 520 521 bool containsSubordinateCollection(const std::string_view uri) 522 { 523 return searchCollectionsArray(uri, SearchType::ContainsSubordinate); 524 } 525 526 bool containsCollection(const std::string_view uri) 527 { 528 return searchCollectionsArray(uri, SearchType::Collection); 529 } 530 531 bool isCollOrCon(const std::string_view uri) 532 { 533 return searchCollectionsArray(uri, SearchType::CollOrCon); 534 } 535 536 TEST(searchCollectionsArray, containsSubordinateValidURIs) 537 { 538 EXPECT_TRUE(containsSubordinateCollection("/redfish/v1")); 539 EXPECT_TRUE(containsSubordinateCollection("/redfish/v1/")); 540 EXPECT_TRUE( 541 containsSubordinateCollection("/redfish/v1/AggregationService")); 542 EXPECT_TRUE( 543 containsSubordinateCollection("/redfish/v1/CompositionService/")); 544 EXPECT_TRUE(containsSubordinateCollection("/redfish/v1/JobService")); 545 EXPECT_TRUE(containsSubordinateCollection("/redfish/v1/JobService/Log")); 546 EXPECT_TRUE(containsSubordinateCollection("/redfish/v1/KeyService")); 547 EXPECT_TRUE(containsSubordinateCollection("/redfish/v1/LicenseService/")); 548 EXPECT_TRUE(containsSubordinateCollection("/redfish/v1/PowerEquipment")); 549 EXPECT_TRUE(containsSubordinateCollection("/redfish/v1/TaskService")); 550 EXPECT_TRUE(containsSubordinateCollection("/redfish/v1/TelemetryService")); 551 EXPECT_TRUE(containsSubordinateCollection( 552 "/redfish/v1/TelemetryService/LogService/")); 553 EXPECT_TRUE(containsSubordinateCollection("/redfish/v1/UpdateService")); 554 } 555 556 TEST(searchCollectionsArray, containsSubordinateInvalidURIs) 557 { 558 EXPECT_FALSE(containsSubordinateCollection("")); 559 EXPECT_FALSE(containsSubordinateCollection("http://")); 560 EXPECT_FALSE(containsSubordinateCollection("/redfish")); 561 EXPECT_FALSE(containsSubordinateCollection("/redfish/")); 562 EXPECT_FALSE(containsSubordinateCollection("/redfish//")); 563 EXPECT_FALSE(containsSubordinateCollection("/redfish/v1//")); 564 EXPECT_FALSE(containsSubordinateCollection("/redfish/v11")); 565 EXPECT_FALSE(containsSubordinateCollection("/redfish/v11/")); 566 EXPECT_FALSE(containsSubordinateCollection("www.test.com/redfish/v1")); 567 EXPECT_FALSE(containsSubordinateCollection("/fail")); 568 EXPECT_FALSE(containsSubordinateCollection( 569 "/redfish/v1/AggregationService/Aggregates")); 570 EXPECT_FALSE(containsSubordinateCollection( 571 "/redfish/v1/AggregationService/AggregationSources/")); 572 EXPECT_FALSE(containsSubordinateCollection("/redfish/v1/Cables/")); 573 EXPECT_FALSE( 574 containsSubordinateCollection("/redfish/v1/Chassis/chassisId")); 575 EXPECT_FALSE(containsSubordinateCollection("/redfish/v1/Fake")); 576 EXPECT_FALSE( 577 containsSubordinateCollection("/redfish/v1/TelemetryService//")); 578 EXPECT_FALSE(containsSubordinateCollection( 579 "/redfish/v1/TelemetryService/LogService/Entries")); 580 EXPECT_FALSE(containsSubordinateCollection( 581 "/redfish/v1/UpdateService/SoftwareInventory/")); 582 EXPECT_FALSE(containsSubordinateCollection( 583 "/redfish/v1/UpdateService/SoftwareInventory/Te")); 584 EXPECT_FALSE(containsSubordinateCollection( 585 "/redfish/v1/UpdateService/SoftwareInventory2")); 586 } 587 588 TEST(searchCollectionsArray, collectionURIs) 589 { 590 EXPECT_TRUE(containsCollection("/redfish/v1/Chassis")); 591 EXPECT_TRUE(containsCollection("/redfish/v1/Chassis/")); 592 EXPECT_TRUE(containsCollection("/redfish/v1/Managers")); 593 EXPECT_TRUE(containsCollection("/redfish/v1/Systems")); 594 EXPECT_TRUE( 595 containsCollection("/redfish/v1/TelemetryService/LogService/Entries")); 596 EXPECT_TRUE( 597 containsCollection("/redfish/v1/TelemetryService/LogService/Entries/")); 598 EXPECT_TRUE( 599 containsCollection("/redfish/v1/UpdateService/FirmwareInventory")); 600 EXPECT_TRUE( 601 containsCollection("/redfish/v1/UpdateService/FirmwareInventory/")); 602 603 EXPECT_FALSE(containsCollection("http://")); 604 EXPECT_FALSE(containsCollection("/redfish/v11/Chassis")); 605 EXPECT_FALSE(containsCollection("/redfish/v11/Chassis/")); 606 EXPECT_FALSE(containsCollection("/redfish/v1")); 607 EXPECT_FALSE(containsCollection("/redfish/v1/")); 608 EXPECT_FALSE(containsCollection("/redfish/v1//")); 609 EXPECT_FALSE(containsCollection("/redfish/v1/Chassis//")); 610 EXPECT_FALSE(containsCollection("/redfish/v1/Chassis/Test")); 611 EXPECT_FALSE(containsCollection("/redfish/v1/TelemetryService")); 612 EXPECT_FALSE(containsCollection("/redfish/v1/TelemetryService/")); 613 EXPECT_FALSE(containsCollection("/redfish/v1/UpdateService")); 614 EXPECT_FALSE( 615 containsCollection("/redfish/v1/UpdateService/FirmwareInventory/Test")); 616 EXPECT_FALSE( 617 containsCollection("/redfish/v1/UpdateService/FirmwareInventory/Tes/")); 618 EXPECT_FALSE( 619 containsCollection("/redfish/v1/UpdateService/SoftwareInventory/Te")); 620 EXPECT_FALSE( 621 containsCollection("/redfish/v1/UpdateService/SoftwareInventory2")); 622 EXPECT_FALSE(containsCollection("/redfish/v11")); 623 EXPECT_FALSE(containsCollection("/redfish/v11/")); 624 } 625 626 TEST(searchCollectionsArray, collectionOrContainsURIs) 627 { 628 // Resources that are a top level collection or are uptree of one 629 EXPECT_TRUE(isCollOrCon("/redfish/v1/")); 630 EXPECT_TRUE(isCollOrCon("/redfish/v1/AggregationService")); 631 EXPECT_TRUE(isCollOrCon("/redfish/v1/CompositionService/")); 632 EXPECT_TRUE(isCollOrCon("/redfish/v1/Chassis")); 633 EXPECT_TRUE(isCollOrCon("/redfish/v1/Cables/")); 634 EXPECT_TRUE(isCollOrCon("/redfish/v1/Fabrics")); 635 EXPECT_TRUE(isCollOrCon("/redfish/v1/Managers")); 636 EXPECT_TRUE(isCollOrCon("/redfish/v1/UpdateService/FirmwareInventory")); 637 EXPECT_TRUE(isCollOrCon("/redfish/v1/UpdateService/FirmwareInventory/")); 638 639 EXPECT_FALSE(isCollOrCon("http://")); 640 EXPECT_FALSE(isCollOrCon("/redfish/v11")); 641 EXPECT_FALSE(isCollOrCon("/redfish/v11/")); 642 EXPECT_FALSE(isCollOrCon("/redfish/v1/Chassis/Test")); 643 EXPECT_FALSE(isCollOrCon("/redfish/v1/Managers/Test/")); 644 EXPECT_FALSE(isCollOrCon("/redfish/v1/TaskService/Tasks/0")); 645 EXPECT_FALSE(isCollOrCon("/redfish/v1/UpdateService/FirmwareInventory/Te")); 646 EXPECT_FALSE(isCollOrCon("/redfish/v1/UpdateService/SoftwareInventory/Te")); 647 EXPECT_FALSE(isCollOrCon("/redfish/v1/UpdateService/SoftwareInventory2")); 648 } 649 650 TEST(processContainsSubordinateResponse, addLinks) 651 { 652 crow::Response resp; 653 resp.result(200); 654 nlohmann::json jsonValue; 655 resp.addHeader("Content-Type", "application/json"); 656 jsonValue["@odata.id"] = "/redfish/v1"; 657 jsonValue["Fabrics"]["@odata.id"] = "/redfish/v1/Fabrics"; 658 jsonValue["Test"]["@odata.id"] = "/redfish/v1/Test"; 659 jsonValue["TelemetryService"]["@odata.id"] = "/redfish/v1/TelemetryService"; 660 jsonValue["UpdateService"]["@odata.id"] = "/redfish/v1/UpdateService"; 661 resp.body() = jsonValue.dump(2, ' ', true, 662 nlohmann::json::error_handler_t::replace); 663 664 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 665 asyncResp->res.result(200); 666 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1"; 667 asyncResp->res.jsonValue["Chassis"]["@odata.id"] = "/redfish/v1/Chassis"; 668 669 RedfishAggregator::processContainsSubordinateResponse("prefix", asyncResp, 670 resp); 671 EXPECT_EQ(asyncResp->res.jsonValue["Chassis"]["@odata.id"], 672 "/redfish/v1/Chassis"); 673 EXPECT_EQ(asyncResp->res.jsonValue["Fabrics"]["@odata.id"], 674 "/redfish/v1/Fabrics"); 675 EXPECT_EQ(asyncResp->res.jsonValue["TelemetryService"]["@odata.id"], 676 "/redfish/v1/TelemetryService"); 677 EXPECT_EQ(asyncResp->res.jsonValue["UpdateService"]["@odata.id"], 678 "/redfish/v1/UpdateService"); 679 EXPECT_FALSE(asyncResp->res.jsonValue.contains("Test")); 680 } 681 682 TEST(processContainsSubordinateResponse, localNotOK) 683 { 684 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 685 asyncResp->res.addHeader("Content-Type", "application/json"); 686 messages::resourceNotFound(asyncResp->res, "", ""); 687 688 // This field was added by resourceNotFound() 689 // Sanity test to make sure it gets removed later 690 EXPECT_TRUE(asyncResp->res.jsonValue.contains("error")); 691 692 crow::Response resp; 693 resp.result(200); 694 nlohmann::json jsonValue; 695 resp.addHeader("Content-Type", "application/json"); 696 jsonValue["@odata.id"] = "/redfish/v1"; 697 jsonValue["@odata.type"] = "#ServiceRoot.v1_11_0.ServiceRoot"; 698 jsonValue["Id"] = "RootService"; 699 jsonValue["Name"] = "Root Service"; 700 jsonValue["Fabrics"]["@odata.id"] = "/redfish/v1/Fabrics"; 701 jsonValue["Test"]["@odata.id"] = "/redfish/v1/Test"; 702 jsonValue["TelemetryService"]["@odata.id"] = "/redfish/v1/TelemetryService"; 703 jsonValue["UpdateService"]["@odata.id"] = "/redfish/v1/UpdateService"; 704 resp.body() = jsonValue.dump(2, ' ', true, 705 nlohmann::json::error_handler_t::replace); 706 707 RedfishAggregator::processContainsSubordinateResponse("prefix", asyncResp, 708 resp); 709 710 // Most of the response should get copied over since asyncResp is a 404 711 EXPECT_EQ(asyncResp->res.resultInt(), 200); 712 EXPECT_EQ(asyncResp->res.jsonValue["@odata.id"], "/redfish/v1"); 713 EXPECT_EQ(asyncResp->res.jsonValue["@odata.type"], 714 "#ServiceRoot.v1_11_0.ServiceRoot"); 715 EXPECT_EQ(asyncResp->res.jsonValue["Id"], "RootService"); 716 EXPECT_EQ(asyncResp->res.jsonValue["Name"], "Root Service"); 717 718 EXPECT_EQ(asyncResp->res.jsonValue["Fabrics"]["@odata.id"], 719 "/redfish/v1/Fabrics"); 720 EXPECT_EQ(asyncResp->res.jsonValue["TelemetryService"]["@odata.id"], 721 "/redfish/v1/TelemetryService"); 722 EXPECT_EQ(asyncResp->res.jsonValue["UpdateService"]["@odata.id"], 723 "/redfish/v1/UpdateService"); 724 EXPECT_FALSE(asyncResp->res.jsonValue.contains("Test")); 725 EXPECT_FALSE(asyncResp->res.jsonValue.contains("error")); 726 727 // Test for local response being partially populated before throwing error 728 asyncResp = std::make_shared<bmcweb::AsyncResp>(); 729 asyncResp->res.addHeader("Content-Type", "application/json"); 730 asyncResp->res.jsonValue["Chassis"]["@odata.id"] = "/redfish/v1/Chassis"; 731 asyncResp->res.jsonValue["Fake"]["@odata.id"] = "/redfish/v1/Fake"; 732 messages::internalError(asyncResp->res); 733 734 RedfishAggregator::processContainsSubordinateResponse("prefix", asyncResp, 735 resp); 736 737 // These should also be copied over since asyncResp is a 500 738 EXPECT_EQ(asyncResp->res.resultInt(), 200); 739 EXPECT_EQ(asyncResp->res.jsonValue["@odata.id"], "/redfish/v1"); 740 EXPECT_EQ(asyncResp->res.jsonValue["@odata.type"], 741 "#ServiceRoot.v1_11_0.ServiceRoot"); 742 EXPECT_EQ(asyncResp->res.jsonValue["Id"], "RootService"); 743 EXPECT_EQ(asyncResp->res.jsonValue["Name"], "Root Service"); 744 745 EXPECT_EQ(asyncResp->res.jsonValue["Fabrics"]["@odata.id"], 746 "/redfish/v1/Fabrics"); 747 EXPECT_EQ(asyncResp->res.jsonValue["TelemetryService"]["@odata.id"], 748 "/redfish/v1/TelemetryService"); 749 EXPECT_EQ(asyncResp->res.jsonValue["UpdateService"]["@odata.id"], 750 "/redfish/v1/UpdateService"); 751 EXPECT_FALSE(asyncResp->res.jsonValue.contains("Test")); 752 EXPECT_FALSE(asyncResp->res.jsonValue.contains("error")); 753 754 // These fields should still be present 755 EXPECT_EQ(asyncResp->res.jsonValue["Chassis"]["@odata.id"], 756 "/redfish/v1/Chassis"); 757 EXPECT_EQ(asyncResp->res.jsonValue["Fake"]["@odata.id"], 758 "/redfish/v1/Fake"); 759 } 760 761 TEST(processContainsSubordinateResponse, noValidLinks) 762 { 763 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 764 asyncResp->res.result(500); 765 asyncResp->res.jsonValue["Chassis"]["@odata.id"] = "/redfish/v1/Chassis"; 766 767 crow::Response resp; 768 resp.result(200); 769 nlohmann::json jsonValue; 770 resp.addHeader("Content-Type", "application/json"); 771 jsonValue["@odata.id"] = "/redfish/v1"; 772 resp.body() = jsonValue.dump(2, ' ', true, 773 nlohmann::json::error_handler_t::replace); 774 775 RedfishAggregator::processContainsSubordinateResponse("prefix", asyncResp, 776 resp); 777 778 // We won't add any links from response so asyncResp shouldn't change 779 EXPECT_EQ(asyncResp->res.resultInt(), 500); 780 EXPECT_EQ(asyncResp->res.jsonValue["Chassis"]["@odata.id"], 781 "/redfish/v1/Chassis"); 782 EXPECT_FALSE(asyncResp->res.jsonValue.contains("@odata.id")); 783 784 // Sat response is non-500 so it shouldn't get copied over 785 asyncResp->res.result(200); 786 resp.result(500); 787 jsonValue["Fabrics"]["@odata.id"] = "/redfish/v1/Fabrics"; 788 jsonValue["Test"]["@odata.id"] = "/redfish/v1/Test"; 789 jsonValue["TelemetryService"]["@odata.id"] = "/redfish/v1/TelemetryService"; 790 jsonValue["UpdateService"]["@odata.id"] = "/redfish/v1/UpdateService"; 791 resp.body() = jsonValue.dump(2, ' ', true, 792 nlohmann::json::error_handler_t::replace); 793 794 RedfishAggregator::processContainsSubordinateResponse("prefix", asyncResp, 795 resp); 796 797 EXPECT_EQ(asyncResp->res.resultInt(), 200); 798 EXPECT_EQ(asyncResp->res.jsonValue["Chassis"]["@odata.id"], 799 "/redfish/v1/Chassis"); 800 EXPECT_FALSE(asyncResp->res.jsonValue.contains("@odata.id")); 801 EXPECT_FALSE(asyncResp->res.jsonValue.contains("Fabrics")); 802 EXPECT_FALSE(asyncResp->res.jsonValue.contains("Test")); 803 EXPECT_FALSE(asyncResp->res.jsonValue.contains("TelemetryService")); 804 EXPECT_FALSE(asyncResp->res.jsonValue.contains("UpdateService")); 805 } 806 807 } // namespace 808 } // namespace redfish 809