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