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