1 #include "http_request.hpp"
2 #include "http_response.hpp"
3 #include "utils/json_utils.hpp"
4 
5 #include <boost/beast/http/field.hpp>
6 #include <boost/beast/http/status.hpp>
7 #include <nlohmann/json.hpp>
8 
9 #include <cstddef>
10 #include <cstdint>
11 #include <optional>
12 #include <string>
13 #include <system_error>
14 #include <variant>
15 #include <vector>
16 
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 
20 namespace redfish::json_util
21 {
22 namespace
23 {
24 
25 using ::testing::ElementsAre;
26 using ::testing::IsEmpty;
27 using ::testing::Not;
28 
TEST(ReadJson,ValidElementsReturnsTrueResponseOkValuesUnpackedCorrectly)29 TEST(ReadJson, ValidElementsReturnsTrueResponseOkValuesUnpackedCorrectly)
30 {
31     crow::Response res;
32     nlohmann::json jsonRequest = {{"integer", 1},
33                                   {"string", "hello"},
34                                   {"vector", std::vector<uint64_t>{1, 2, 3}}};
35 
36     int64_t integer = 0;
37     std::string str;
38     std::vector<uint64_t> vec;
39     ASSERT_TRUE(readJson(jsonRequest, res, "integer", integer, "string", str,
40                          "vector", vec));
41     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
42     EXPECT_THAT(res.jsonValue, IsEmpty());
43 
44     EXPECT_EQ(integer, 1);
45     EXPECT_EQ(str, "hello");
46     EXPECT_THAT(vec, ElementsAre(1, 2, 3));
47 }
48 
TEST(ReadJson,ValidObjectElementsReturnsTrueResponseOkValuesUnpackedCorrectly)49 TEST(ReadJson, ValidObjectElementsReturnsTrueResponseOkValuesUnpackedCorrectly)
50 {
51     crow::Response res;
52     nlohmann::json::object_t jsonRequest;
53     jsonRequest["integer"] = 1;
54     jsonRequest["string"] = "hello";
55     jsonRequest["vector"] = std::vector<uint64_t>{1, 2, 3};
56 
57     int64_t integer = 0;
58     std::string str;
59     std::vector<uint64_t> vec;
60     ASSERT_TRUE(readJsonObject(jsonRequest, res, "integer", integer, "string",
61                                str, "vector", vec));
62     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
63     EXPECT_THAT(res.jsonValue, IsEmpty());
64 
65     EXPECT_EQ(integer, 1);
66     EXPECT_EQ(str, "hello");
67     EXPECT_THAT(vec, ElementsAre(1, 2, 3));
68 }
69 
TEST(ReadJson,VariantValueUnpackedNull)70 TEST(ReadJson, VariantValueUnpackedNull)
71 {
72     crow::Response res;
73     nlohmann::json jsonRequest = {{"nullval", nullptr}};
74 
75     std::variant<std::string, std::nullptr_t> str;
76 
77     ASSERT_TRUE(readJson(jsonRequest, res, "nullval", str));
78     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
79 
80     EXPECT_TRUE(std::holds_alternative<std::nullptr_t>(str));
81 }
82 
TEST(ReadJson,VariantValueUnpackedValue)83 TEST(ReadJson, VariantValueUnpackedValue)
84 {
85     crow::Response res;
86     nlohmann::json jsonRequest = {{"stringval", "mystring"}};
87 
88     std::variant<std::string, std::nullptr_t> str;
89 
90     ASSERT_TRUE(readJson(jsonRequest, res, "stringval", str));
91     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
92 
93     ASSERT_TRUE(std::holds_alternative<std::string>(str));
94     EXPECT_EQ(std::get<std::string>(str), "mystring");
95 }
96 
TEST(readJson,ExtraElementsReturnsFalseReponseIsBadRequest)97 TEST(readJson, ExtraElementsReturnsFalseReponseIsBadRequest)
98 {
99     crow::Response res;
100     nlohmann::json jsonRequest = {{"integer", 1}, {"string", "hello"}};
101 
102     std::optional<int> integer;
103     std::optional<std::string> str;
104 
105     ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer));
106     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
107     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
108     EXPECT_EQ(integer, 1);
109 
110     ASSERT_FALSE(readJson(jsonRequest, res, "string", str));
111     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
112     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
113     EXPECT_EQ(str, "hello");
114 }
115 
TEST(ReadJson,WrongElementTypeReturnsFalseReponseIsBadRequest)116 TEST(ReadJson, WrongElementTypeReturnsFalseReponseIsBadRequest)
117 {
118     crow::Response res;
119     nlohmann::json jsonRequest = {{"integer", 1}, {"string0", "hello"}};
120 
121     int64_t integer = 0;
122     std::string str0;
123     ASSERT_FALSE(readJson(jsonRequest, res, "integer", str0));
124     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
125     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
126 
127     ASSERT_FALSE(readJson(jsonRequest, res, "string0", integer));
128     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
129     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
130 
131     ASSERT_FALSE(
132         readJson(jsonRequest, res, "integer", str0, "string0", integer));
133     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
134     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
135 }
136 
TEST(ReadJson,MissingElementReturnsFalseReponseIsBadRequest)137 TEST(ReadJson, MissingElementReturnsFalseReponseIsBadRequest)
138 {
139     crow::Response res;
140     nlohmann::json jsonRequest = {{"integer", 1}, {"string0", "hello"}};
141 
142     int64_t integer = 0;
143     std::string str0;
144     std::string str1;
145     std::vector<uint8_t> vec;
146     ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer, "string0", str0,
147                           "vector", vec));
148     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
149     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
150 
151     ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer, "string0", str0,
152                           "string1", str1));
153     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
154     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
155 }
156 
TEST(ReadJson,JsonArrayAreUnpackedCorrectly)157 TEST(ReadJson, JsonArrayAreUnpackedCorrectly)
158 {
159     crow::Response res;
160     nlohmann::json jsonRequest = R"(
161         {
162             "TestJson": [{"hello": "yes"}, [{"there": "no"}, "nice"]]
163         }
164     )"_json;
165 
166     std::vector<nlohmann::json> jsonVec;
167     ASSERT_TRUE(readJson(jsonRequest, res, "TestJson", jsonVec));
168     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
169     EXPECT_THAT(res.jsonValue, IsEmpty());
170     EXPECT_THAT(jsonVec, ElementsAre(R"({"hello": "yes"})"_json,
171                                      R"([{"there": "no"}, "nice"])"_json));
172 }
173 
TEST(ReadJson,JsonSubElementValueAreUnpackedCorrectly)174 TEST(ReadJson, JsonSubElementValueAreUnpackedCorrectly)
175 {
176     crow::Response res;
177     nlohmann::json jsonRequest = R"(
178         {
179             "json": {"integer": 42}
180         }
181     )"_json;
182 
183     int integer = 0;
184     ASSERT_TRUE(readJson(jsonRequest, res, "json/integer", integer));
185     EXPECT_EQ(integer, 42);
186     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
187     EXPECT_THAT(res.jsonValue, IsEmpty());
188 }
189 
TEST(ReadJson,JsonDeeperSubElementValueAreUnpackedCorrectly)190 TEST(ReadJson, JsonDeeperSubElementValueAreUnpackedCorrectly)
191 {
192     crow::Response res;
193     nlohmann::json jsonRequest = R"(
194         {
195             "json": {
196                 "json2": {"string": "foobar"}
197             }
198         }
199     )"_json;
200 
201     std::string foobar;
202     ASSERT_TRUE(readJson(jsonRequest, res, "json/json2/string", foobar));
203     EXPECT_EQ(foobar, "foobar");
204     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
205     EXPECT_THAT(res.jsonValue, IsEmpty());
206 }
207 
TEST(ReadJson,MultipleJsonSubElementValueAreUnpackedCorrectly)208 TEST(ReadJson, MultipleJsonSubElementValueAreUnpackedCorrectly)
209 {
210     crow::Response res;
211     nlohmann::json jsonRequest = R"(
212         {
213             "json": {
214                 "integer": 42,
215                 "string": "foobar"
216             },
217             "string": "bazbar"
218         }
219     )"_json;
220 
221     int integer = 0;
222     std::string foobar;
223     std::string bazbar;
224     ASSERT_TRUE(readJson(jsonRequest, res, "json/integer", integer,
225                          "json/string", foobar, "string", bazbar));
226     EXPECT_EQ(integer, 42);
227     EXPECT_EQ(foobar, "foobar");
228     EXPECT_EQ(bazbar, "bazbar");
229     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
230     EXPECT_THAT(res.jsonValue, IsEmpty());
231 }
232 
TEST(ReadJson,ExtraElement)233 TEST(ReadJson, ExtraElement)
234 {
235     crow::Response res;
236     nlohmann::json jsonRequest = {{"integer", 1}, {"string", "hello"}};
237 
238     std::optional<int> integer;
239     std::optional<std::string> str;
240 
241     EXPECT_FALSE(readJson(jsonRequest, res, "integer", integer));
242     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
243     EXPECT_FALSE(res.jsonValue.empty());
244     EXPECT_EQ(integer, 1);
245 
246     EXPECT_FALSE(readJson(jsonRequest, res, "string", str));
247     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
248     EXPECT_FALSE(res.jsonValue.empty());
249     EXPECT_EQ(str, "hello");
250 }
251 
TEST(ReadJson,ValidMissingElementReturnsTrue)252 TEST(ReadJson, ValidMissingElementReturnsTrue)
253 {
254     crow::Response res;
255     nlohmann::json jsonRequest = {{"integer", 1}};
256 
257     std::optional<int> integer;
258     int requiredInteger = 0;
259     std::optional<std::string> str0;
260     std::optional<std::string> str1;
261     std::optional<std::vector<uint8_t>> vec;
262     ASSERT_TRUE(readJson(jsonRequest, res, "missing_integer", integer,
263                          "integer", requiredInteger));
264     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
265     EXPECT_TRUE(res.jsonValue.empty());
266     EXPECT_EQ(integer, std::nullopt);
267 
268     ASSERT_TRUE(readJson(jsonRequest, res, "missing_string", str0, "integer",
269                          requiredInteger));
270     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
271     EXPECT_THAT(res.jsonValue, IsEmpty());
272     EXPECT_EQ(str0, std::nullopt);
273 
274     ASSERT_TRUE(readJson(jsonRequest, res, "integer", integer, "string", str0,
275                          "vector", vec));
276     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
277     EXPECT_THAT(res.jsonValue, IsEmpty());
278     EXPECT_EQ(integer, 1);
279     EXPECT_EQ(str0, std::nullopt);
280     EXPECT_EQ(vec, std::nullopt);
281 
282     ASSERT_TRUE(readJson(jsonRequest, res, "integer", integer, "string0", str0,
283                          "missing_string", str1));
284     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
285     EXPECT_THAT(res.jsonValue, IsEmpty());
286     EXPECT_EQ(str1, std::nullopt);
287 }
288 
TEST(ReadJson,InvalidMissingElementReturnsFalse)289 TEST(ReadJson, InvalidMissingElementReturnsFalse)
290 {
291     crow::Response res;
292     nlohmann::json jsonRequest = {{"integer", 1}, {"string", "hello"}};
293 
294     int integer = 0;
295     std::string str0;
296     std::string str1;
297     std::vector<uint8_t> vec;
298     ASSERT_FALSE(readJson(jsonRequest, res, "missing_integer", integer));
299     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
300     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
301 
302     ASSERT_FALSE(readJson(jsonRequest, res, "missing_string", str0));
303     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
304     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
305 
306     ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer, "string", str0,
307                           "vector", vec));
308     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
309     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
310 
311     ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer, "string0", str0,
312                           "missing_string", str1));
313     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
314     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
315 }
316 
TEST(ReadJsonPatch,ValidElementsReturnsTrueResponseOkValuesUnpackedCorrectly)317 TEST(ReadJsonPatch, ValidElementsReturnsTrueResponseOkValuesUnpackedCorrectly)
318 {
319     crow::Response res;
320     std::error_code ec;
321     crow::Request req("{\"integer\": 1}", ec);
322 
323     // Ignore errors intentionally
324     req.addHeader(boost::beast::http::field::content_type, "application/json");
325 
326     int64_t integer = 0;
327     ASSERT_TRUE(readJsonPatch(req, res, "integer", integer));
328     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
329     EXPECT_THAT(res.jsonValue, IsEmpty());
330     EXPECT_EQ(integer, 1);
331 }
332 
TEST(ReadJsonPatch,EmptyObjectReturnsFalseResponseBadRequest)333 TEST(ReadJsonPatch, EmptyObjectReturnsFalseResponseBadRequest)
334 {
335     crow::Response res;
336     std::error_code ec;
337     crow::Request req("{}", ec);
338     // Ignore errors intentionally
339 
340     std::optional<int64_t> integer = 0;
341     ASSERT_FALSE(readJsonPatch(req, res, "integer", integer));
342     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
343     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
344 }
345 
TEST(ReadJsonPatch,OdataIgnored)346 TEST(ReadJsonPatch, OdataIgnored)
347 {
348     crow::Response res;
349     std::error_code ec;
350     crow::Request req(R"({"@odata.etag": "etag", "integer": 1})", ec);
351     req.addHeader(boost::beast::http::field::content_type, "application/json");
352     // Ignore errors intentionally
353 
354     std::optional<int64_t> integer = 0;
355     ASSERT_TRUE(readJsonPatch(req, res, "integer", integer));
356     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
357     EXPECT_THAT(res.jsonValue, IsEmpty());
358     EXPECT_EQ(integer, 1);
359 }
360 
TEST(ReadJsonPatch,OnlyOdataGivesNoOperation)361 TEST(ReadJsonPatch, OnlyOdataGivesNoOperation)
362 {
363     crow::Response res;
364     std::error_code ec;
365     crow::Request req(R"({"@odata.etag": "etag"})", ec);
366     // Ignore errors intentionally
367 
368     std::optional<int64_t> integer = 0;
369     ASSERT_FALSE(readJsonPatch(req, res, "integer", integer));
370     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
371     EXPECT_THAT(res.jsonValue, Not(IsEmpty()));
372 }
373 
TEST(ReadJsonPatch,VerifyReadJsonPatchIntegerReturnsOutOfRange)374 TEST(ReadJsonPatch, VerifyReadJsonPatchIntegerReturnsOutOfRange)
375 {
376     crow::Response res;
377     std::error_code ec;
378 
379     // 4294967296 is an out-of-range value for uint32_t
380     crow::Request req(R"({"@odata.etag": "etag", "integer": 4294967296})", ec);
381     req.addHeader(boost::beast::http::field::content_type, "application/json");
382 
383     uint32_t integer = 0;
384     ASSERT_FALSE(readJsonPatch(req, res, "integer", integer));
385     EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
386 
387     const nlohmann::json& resExtInfo =
388         res.jsonValue["error"]["@Message.ExtendedInfo"];
389     EXPECT_THAT(resExtInfo[0]["@odata.type"], "#Message.v1_1_1.Message");
390     EXPECT_THAT(resExtInfo[0]["MessageId"],
391                 "Base.1.19.0.PropertyValueOutOfRange");
392     EXPECT_THAT(resExtInfo[0]["MessageSeverity"], "Warning");
393 }
394 
TEST(ReadJsonAction,ValidElementsReturnsTrueResponseOkValuesUnpackedCorrectly)395 TEST(ReadJsonAction, ValidElementsReturnsTrueResponseOkValuesUnpackedCorrectly)
396 {
397     crow::Response res;
398     std::error_code ec;
399     crow::Request req("{\"integer\": 1}", ec);
400     req.addHeader(boost::beast::http::field::content_type, "application/json");
401     // Ignore errors intentionally
402 
403     int64_t integer = 0;
404     ASSERT_TRUE(readJsonAction(req, res, "integer", integer));
405     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
406     EXPECT_THAT(res.jsonValue, IsEmpty());
407     EXPECT_EQ(integer, 1);
408 }
409 
TEST(ReadJsonAction,EmptyObjectReturnsTrueResponseOk)410 TEST(ReadJsonAction, EmptyObjectReturnsTrueResponseOk)
411 {
412     crow::Response res;
413     std::error_code ec;
414     crow::Request req({"{}"}, ec);
415     req.addHeader(boost::beast::http::field::content_type, "application/json");
416     // Ignore errors intentionally
417 
418     std::optional<int64_t> integer = 0;
419     ASSERT_TRUE(readJsonAction(req, res, "integer", integer));
420     EXPECT_EQ(res.result(), boost::beast::http::status::ok);
421     EXPECT_THAT(res.jsonValue, IsEmpty());
422 }
423 
TEST(odataObjectCmp,PositiveCases)424 TEST(odataObjectCmp, PositiveCases)
425 {
426     EXPECT_EQ(0, odataObjectCmp(R"({"@odata.id": "/redfish/v1/1"})"_json,
427                                 R"({"@odata.id": "/redfish/v1/1"})"_json));
428     EXPECT_EQ(0, odataObjectCmp(R"({"@odata.id": ""})"_json,
429                                 R"({"@odata.id": ""})"_json));
430     EXPECT_EQ(0, odataObjectCmp(R"({"@odata.id": 42})"_json,
431                                 R"({"@odata.id": 0})"_json));
432     EXPECT_EQ(0, odataObjectCmp(R"({})"_json, R"({})"_json));
433 
434     EXPECT_GT(0, odataObjectCmp(R"({"@odata.id": "/redfish/v1"})"_json,
435                                 R"({"@odata.id": "/redfish/v1/1"})"_json));
436     EXPECT_LT(0, odataObjectCmp(R"({"@odata.id": "/redfish/v1/1"})"_json,
437                                 R"({"@odata.id": "/redfish/v1"})"_json));
438 
439     EXPECT_LT(0, odataObjectCmp(R"({"@odata.id": "/10"})"_json,
440                                 R"({"@odata.id": "/1"})"_json));
441     EXPECT_GT(0, odataObjectCmp(R"({"@odata.id": "/1"})"_json,
442                                 R"({"@odata.id": "/10"})"_json));
443 
444     EXPECT_GT(0, odataObjectCmp(R"({})"_json, R"({"@odata.id": "/1"})"_json));
445     EXPECT_LT(0, odataObjectCmp(R"({"@odata.id": "/1"})"_json, R"({})"_json));
446 
447     EXPECT_GT(0, odataObjectCmp(R"({"@odata.id": 4})"_json,
448                                 R"({"@odata.id": "/1"})"_json));
449     EXPECT_LT(0, odataObjectCmp(R"({"@odata.id": "/1"})"_json,
450                                 R"({"@odata.id": 4})"_json));
451 }
452 
TEST(SortJsonArrayByOData,ElementMissingKeyReturnsFalseArrayIsPartlySorted)453 TEST(SortJsonArrayByOData, ElementMissingKeyReturnsFalseArrayIsPartlySorted)
454 {
455     nlohmann::json::array_t array =
456         R"([{"@odata.id" : "/redfish/v1/100"}, {"@odata.id": "/redfish/v1/1"}, {"@odata.id" : "/redfish/v1/20"}])"_json;
457     sortJsonArrayByOData(array);
458     // Objects with other keys are always larger than those with the specified
459     // key.
460     EXPECT_THAT(array,
461                 ElementsAre(R"({"@odata.id": "/redfish/v1/1"})"_json,
462                             R"({"@odata.id" : "/redfish/v1/20"})"_json,
463                             R"({"@odata.id" : "/redfish/v1/100"})"_json));
464 }
465 
TEST(SortJsonArrayByOData,SortedByStringValueOnSuccessArrayIsSorted)466 TEST(SortJsonArrayByOData, SortedByStringValueOnSuccessArrayIsSorted)
467 {
468     nlohmann::json::array_t array =
469         R"([{"@odata.id": "/redfish/v1/20"}, {"@odata.id" : "/redfish/v1"}, {"@odata.id" : "/redfish/v1/100"}])"_json;
470     sortJsonArrayByOData(array);
471     EXPECT_THAT(array,
472                 ElementsAre(R"({"@odata.id": "/redfish/v1"})"_json,
473                             R"({"@odata.id" : "/redfish/v1/20"})"_json,
474                             R"({"@odata.id" : "/redfish/v1/100"})"_json));
475 }
476 
TEST(objectKeyCmp,PositiveCases)477 TEST(objectKeyCmp, PositiveCases)
478 {
479     EXPECT_EQ(
480         0, objectKeyCmp("@odata.id",
481                         R"({"@odata.id": "/redfish/v1/1", "Name": "a"})"_json,
482                         R"({"@odata.id": "/redfish/v1/1", "Name": "b"})"_json));
483     EXPECT_GT(
484         0, objectKeyCmp("Name",
485                         R"({"@odata.id": "/redfish/v1/1", "Name": "a"})"_json,
486                         R"({"@odata.id": "/redfish/v1/1", "Name": "b"})"_json));
487 
488     EXPECT_GT(
489         0, objectKeyCmp("@odata.id",
490                         R"({"@odata.id": "/redfish/v1/1", "Name": "b"})"_json,
491                         R"({"@odata.id": "/redfish/v1/2", "Name": "b"})"_json));
492     EXPECT_EQ(
493         0, objectKeyCmp("Name",
494                         R"({"@odata.id": "/redfish/v1/1", "Name": "b"})"_json,
495                         R"({"@odata.id": "/redfish/v1/2", "Name": "b"})"_json));
496 
497     EXPECT_LT(0,
498               objectKeyCmp(
499                   "@odata.id",
500                   R"({"@odata.id": "/redfish/v1/p10/", "Name": "a1"})"_json,
501                   R"({"@odata.id": "/redfish/v1/p1/", "Name": "a10"})"_json));
502     EXPECT_GT(0,
503               objectKeyCmp(
504                   "Name",
505                   R"({"@odata.id": "/redfish/v1/p10/", "Name": "a1"})"_json,
506                   R"({"@odata.id": "/redfish/v1/p1/", "Name": "a10"})"_json));
507 
508     nlohmann::json leftRequest =
509         R"({"Name": "fan1", "@odata.id": "/redfish/v1/Chassis/chassis2"})"_json;
510     nlohmann::json rightRequest =
511         R"({"Name": "fan2", "@odata.id": "/redfish/v1/Chassis/chassis1"})"_json;
512 
513     EXPECT_GT(0, objectKeyCmp("Name", leftRequest, rightRequest));
514     EXPECT_LT(0, objectKeyCmp("@odata.id", leftRequest, rightRequest));
515     EXPECT_EQ(0, objectKeyCmp("DataSourceUri", leftRequest, rightRequest));
516 }
517 
TEST(SortJsonArrayByKey,ElementMissingKeyReturnsFalseArrayIsPartlySorted)518 TEST(SortJsonArrayByKey, ElementMissingKeyReturnsFalseArrayIsPartlySorted)
519 {
520     nlohmann::json::array_t array =
521         R"([{"@odata.id" : "/redfish/v1/100"}, {"Name" : "/redfish/v1/5"}, {"@odata.id": "/redfish/v1/1"}, {"@odata.id" : "/redfish/v1/20"}])"_json;
522     sortJsonArrayByKey(array, "@odata.id");
523     // Objects with other keys are always smaller than those with the specified
524     // key.
525     EXPECT_THAT(array,
526                 ElementsAre(R"({"Name" : "/redfish/v1/5"})"_json,
527                             R"({"@odata.id": "/redfish/v1/1"})"_json,
528                             R"({"@odata.id" : "/redfish/v1/20"})"_json,
529                             R"({"@odata.id" : "/redfish/v1/100"})"_json));
530 }
531 
TEST(SortJsonArrayByKey,SortedByStringValueOnSuccessArrayIsSorted)532 TEST(SortJsonArrayByKey, SortedByStringValueOnSuccessArrayIsSorted)
533 {
534     nlohmann::json::array_t array =
535         R"([{"@odata.id": "/redfish/v1/20", "Name": "a"}, {"@odata.id" : "/redfish/v1", "Name": "c"}, {"@odata.id" : "/redfish/v1/100", "Name": "b"}])"_json;
536 
537     sortJsonArrayByKey(array, "@odata.id");
538     EXPECT_THAT(
539         array,
540         ElementsAre(R"({"@odata.id": "/redfish/v1", "Name": "c"})"_json,
541                     R"({"@odata.id": "/redfish/v1/20", "Name": "a"})"_json,
542                     R"({"@odata.id": "/redfish/v1/100", "Name": "b"})"_json));
543 
544     sortJsonArrayByKey(array, "Name");
545     EXPECT_THAT(
546         array,
547         ElementsAre(R"({"@odata.id": "/redfish/v1/20", "Name": "a"})"_json,
548                     R"({"@odata.id": "/redfish/v1/100", "Name": "b"})"_json,
549                     R"({"@odata.id": "/redfish/v1", "Name": "c"})"_json));
550 }
551 
TEST(GetEstimatedJsonSize,NumberIs8Bytpes)552 TEST(GetEstimatedJsonSize, NumberIs8Bytpes)
553 {
554     EXPECT_EQ(getEstimatedJsonSize(nlohmann::json(123)), 8);
555     EXPECT_EQ(getEstimatedJsonSize(nlohmann::json(77777777777)), 8);
556     EXPECT_EQ(getEstimatedJsonSize(nlohmann::json(3.14)), 8);
557 }
558 
TEST(GetEstimatedJsonSize,BooleanIs5Byte)559 TEST(GetEstimatedJsonSize, BooleanIs5Byte)
560 {
561     EXPECT_EQ(getEstimatedJsonSize(nlohmann::json(true)), 5);
562     EXPECT_EQ(getEstimatedJsonSize(nlohmann::json(false)), 5);
563 }
564 
TEST(GetEstimatedJsonSize,NullIs4Byte)565 TEST(GetEstimatedJsonSize, NullIs4Byte)
566 {
567     EXPECT_EQ(getEstimatedJsonSize(nlohmann::json()), 4);
568 }
569 
TEST(GetEstimatedJsonSize,StringAndBytesReturnsLengthAndQuote)570 TEST(GetEstimatedJsonSize, StringAndBytesReturnsLengthAndQuote)
571 {
572     EXPECT_EQ(getEstimatedJsonSize(nlohmann::json("1234")), 6);
573     EXPECT_EQ(getEstimatedJsonSize(nlohmann::json::binary({1, 2, 3, 4})), 4);
574 }
575 
TEST(GetEstimatedJsonSize,ArrayReturnsSum)576 TEST(GetEstimatedJsonSize, ArrayReturnsSum)
577 {
578     nlohmann::json arr = {1, 3.14, "123", nlohmann::json::binary({1, 2, 3, 4})};
579     EXPECT_EQ(getEstimatedJsonSize(arr), 8 + 8 + 5 + 4);
580 }
581 
TEST(GetEstimatedJsonSize,ObjectsReturnsSumWithKeyAndValue)582 TEST(GetEstimatedJsonSize, ObjectsReturnsSumWithKeyAndValue)
583 {
584     nlohmann::json obj = R"(
585 {
586   "key0": 123,
587   "key1": "123",
588   "key2": [1, 2, 3],
589   "key3": {"key4": "123"}
590 }
591 )"_json;
592 
593     uint64_t expected = 0;
594     // 5 keys of length 4
595     expected += uint64_t(5) * 4;
596     // 5 colons, 5 quote pairs, and 5 spaces for 5 keys
597     expected += uint64_t(5) * (1 + 2 + 1);
598     // 2 string values of length 3
599     expected += uint64_t(2) * (3 + 2);
600     // 1 number value
601     expected += 8;
602     // 1 array value of 3 numbers
603     expected += uint64_t(3) * 8;
604     EXPECT_EQ(getEstimatedJsonSize(obj), expected);
605 }
606 
607 } // namespace
608 } // namespace redfish::json_util
609