1 #include "http_request.hpp" 2 #include "http_response.hpp" 3 #include "utils/json_utils.hpp" 4 5 #include <boost/beast/http/status.hpp> 6 #include <nlohmann/json.hpp> 7 8 #include <cstdint> 9 #include <optional> 10 #include <string> 11 #include <system_error> 12 #include <vector> 13 14 #include <gmock/gmock.h> // IWYU pragma: keep 15 #include <gtest/gtest.h> // IWYU pragma: keep 16 17 // IWYU pragma: no_include <gtest/gtest-message.h> 18 // IWYU pragma: no_include <gtest/gtest-test-part.h> 19 // IWYU pragma: no_include "gtest/gtest_pred_impl.h" 20 // IWYU pragma: no_include <boost/intrusive/detail/list_iterator.hpp> 21 22 namespace redfish::json_util 23 { 24 namespace 25 { 26 27 using ::testing::ElementsAre; 28 using ::testing::IsEmpty; 29 using ::testing::Not; 30 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 51 TEST(readJson, ExtraElementsReturnsFalseReponseIsBadRequest) 52 { 53 crow::Response res; 54 nlohmann::json jsonRequest = {{"integer", 1}, {"string", "hello"}}; 55 56 std::optional<int> integer; 57 std::optional<std::string> str; 58 59 ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer)); 60 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 61 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 62 EXPECT_EQ(integer, 1); 63 64 ASSERT_FALSE(readJson(jsonRequest, res, "string", str)); 65 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 66 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 67 EXPECT_EQ(str, "hello"); 68 } 69 70 TEST(ReadJson, WrongElementTypeReturnsFalseReponseIsBadRequest) 71 { 72 crow::Response res; 73 nlohmann::json jsonRequest = {{"integer", 1}, {"string0", "hello"}}; 74 75 int64_t integer = 0; 76 std::string str0; 77 ASSERT_FALSE(readJson(jsonRequest, res, "integer", str0)); 78 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 79 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 80 81 ASSERT_FALSE(readJson(jsonRequest, res, "string0", integer)); 82 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 83 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 84 85 ASSERT_FALSE( 86 readJson(jsonRequest, res, "integer", str0, "string0", integer)); 87 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 88 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 89 } 90 91 TEST(ReadJson, MissingElementReturnsFalseReponseIsBadRequest) 92 { 93 crow::Response res; 94 nlohmann::json jsonRequest = {{"integer", 1}, {"string0", "hello"}}; 95 96 int64_t integer = 0; 97 std::string str0; 98 std::string str1; 99 std::vector<uint8_t> vec; 100 ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer, "string0", str0, 101 "vector", vec)); 102 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 103 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 104 105 ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer, "string0", str0, 106 "string1", str1)); 107 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 108 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 109 } 110 111 TEST(ReadJson, JsonArrayAreUnpackedCorrectly) 112 { 113 crow::Response res; 114 nlohmann::json jsonRequest = R"( 115 { 116 "TestJson": [{"hello": "yes"}, [{"there": "no"}, "nice"]] 117 } 118 )"_json; 119 120 std::vector<nlohmann::json> jsonVec; 121 ASSERT_TRUE(readJson(jsonRequest, res, "TestJson", jsonVec)); 122 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 123 EXPECT_THAT(res.jsonValue, IsEmpty()); 124 EXPECT_EQ(jsonVec, R"([{"hello": "yes"}, [{"there": "no"}, "nice"]])"_json); 125 } 126 127 TEST(ReadJson, JsonSubElementValueAreUnpackedCorrectly) 128 { 129 crow::Response res; 130 nlohmann::json jsonRequest = R"( 131 { 132 "json": {"integer": 42} 133 } 134 )"_json; 135 136 int integer = 0; 137 ASSERT_TRUE(readJson(jsonRequest, res, "json/integer", integer)); 138 EXPECT_EQ(integer, 42); 139 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 140 EXPECT_THAT(res.jsonValue, IsEmpty()); 141 } 142 143 TEST(ReadJson, JsonDeeperSubElementValueAreUnpackedCorrectly) 144 { 145 crow::Response res; 146 nlohmann::json jsonRequest = R"( 147 { 148 "json": { 149 "json2": {"string": "foobar"} 150 } 151 } 152 )"_json; 153 154 std::string foobar; 155 ASSERT_TRUE(readJson(jsonRequest, res, "json/json2/string", foobar)); 156 EXPECT_EQ(foobar, "foobar"); 157 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 158 EXPECT_THAT(res.jsonValue, IsEmpty()); 159 } 160 161 TEST(ReadJson, MultipleJsonSubElementValueAreUnpackedCorrectly) 162 { 163 crow::Response res; 164 nlohmann::json jsonRequest = R"( 165 { 166 "json": { 167 "integer": 42, 168 "string": "foobar" 169 }, 170 "string": "bazbar" 171 } 172 )"_json; 173 174 int integer = 0; 175 std::string foobar; 176 std::string bazbar; 177 ASSERT_TRUE(readJson(jsonRequest, res, "json/integer", integer, 178 "json/string", foobar, "string", bazbar)); 179 EXPECT_EQ(integer, 42); 180 EXPECT_EQ(foobar, "foobar"); 181 EXPECT_EQ(bazbar, "bazbar"); 182 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 183 EXPECT_THAT(res.jsonValue, IsEmpty()); 184 } 185 186 TEST(ReadJson, ExtraElement) 187 { 188 crow::Response res; 189 nlohmann::json jsonRequest = {{"integer", 1}, {"string", "hello"}}; 190 191 std::optional<int> integer; 192 std::optional<std::string> str; 193 194 EXPECT_FALSE(readJson(jsonRequest, res, "integer", integer)); 195 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 196 EXPECT_FALSE(res.jsonValue.empty()); 197 EXPECT_EQ(integer, 1); 198 199 EXPECT_FALSE(readJson(jsonRequest, res, "string", str)); 200 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 201 EXPECT_FALSE(res.jsonValue.empty()); 202 EXPECT_EQ(str, "hello"); 203 } 204 205 TEST(ReadJson, ValidMissingElementReturnsTrue) 206 { 207 crow::Response res; 208 nlohmann::json jsonRequest = {{"integer", 1}}; 209 210 std::optional<int> integer; 211 int requiredInteger = 0; 212 std::optional<std::string> str0; 213 std::optional<std::string> str1; 214 std::optional<std::vector<uint8_t>> vec; 215 ASSERT_TRUE(readJson(jsonRequest, res, "missing_integer", integer, 216 "integer", requiredInteger)); 217 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 218 EXPECT_TRUE(res.jsonValue.empty()); 219 EXPECT_EQ(integer, std::nullopt); 220 221 ASSERT_TRUE(readJson(jsonRequest, res, "missing_string", str0, "integer", 222 requiredInteger)); 223 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 224 EXPECT_THAT(res.jsonValue, IsEmpty()); 225 EXPECT_EQ(str0, std::nullopt); 226 227 ASSERT_TRUE(readJson(jsonRequest, res, "integer", integer, "string", str0, 228 "vector", vec)); 229 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 230 EXPECT_THAT(res.jsonValue, IsEmpty()); 231 EXPECT_EQ(integer, 1); 232 EXPECT_EQ(str0, std::nullopt); 233 EXPECT_EQ(vec, std::nullopt); 234 235 ASSERT_TRUE(readJson(jsonRequest, res, "integer", integer, "string0", str0, 236 "missing_string", str1)); 237 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 238 EXPECT_THAT(res.jsonValue, IsEmpty()); 239 EXPECT_EQ(str1, std::nullopt); 240 } 241 242 TEST(ReadJson, InvalidMissingElementReturnsFalse) 243 { 244 crow::Response res; 245 nlohmann::json jsonRequest = {{"integer", 1}, {"string", "hello"}}; 246 247 int integer = 0; 248 std::string str0; 249 std::string str1; 250 std::vector<uint8_t> vec; 251 ASSERT_FALSE(readJson(jsonRequest, res, "missing_integer", integer)); 252 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 253 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 254 255 ASSERT_FALSE(readJson(jsonRequest, res, "missing_string", str0)); 256 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 257 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 258 259 ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer, "string", str0, 260 "vector", vec)); 261 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 262 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 263 264 ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer, "string0", str0, 265 "missing_string", str1)); 266 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 267 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 268 } 269 270 TEST(ReadJsonPatch, ValidElementsReturnsTrueResponseOkValuesUnpackedCorrectly) 271 { 272 crow::Response res; 273 std::error_code ec; 274 crow::Request req({}, ec); 275 // Ignore errors intentionally 276 req.body = "{\"integer\": 1}"; 277 278 int64_t integer = 0; 279 ASSERT_TRUE(readJsonPatch(req, res, "integer", integer)); 280 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 281 EXPECT_THAT(res.jsonValue, IsEmpty()); 282 EXPECT_EQ(integer, 1); 283 } 284 285 TEST(ReadJsonPatch, EmptyObjectReturnsFalseResponseBadRequest) 286 { 287 crow::Response res; 288 std::error_code ec; 289 crow::Request req({}, ec); 290 // Ignore errors intentionally 291 req.body = "{}"; 292 293 std::optional<int64_t> integer = 0; 294 ASSERT_FALSE(readJsonPatch(req, res, "integer", integer)); 295 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 296 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 297 } 298 299 TEST(ReadJsonPatch, OdataIgnored) 300 { 301 crow::Response res; 302 std::error_code ec; 303 crow::Request req({}, ec); 304 // Ignore errors intentionally 305 req.body = R"({"@odata.etag": "etag", "integer": 1})"; 306 307 std::optional<int64_t> integer = 0; 308 ASSERT_TRUE(readJsonPatch(req, res, "integer", integer)); 309 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 310 EXPECT_THAT(res.jsonValue, IsEmpty()); 311 EXPECT_EQ(integer, 1); 312 } 313 314 TEST(ReadJsonPatch, OnlyOdataGivesNoOperation) 315 { 316 crow::Response res; 317 std::error_code ec; 318 crow::Request req({}, ec); 319 // Ignore errors intentionally 320 req.body = R"({"@odata.etag": "etag"})"; 321 322 std::optional<int64_t> integer = 0; 323 ASSERT_FALSE(readJsonPatch(req, res, "integer", integer)); 324 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 325 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 326 } 327 328 TEST(ReadJsonAction, ValidElementsReturnsTrueResponseOkValuesUnpackedCorrectly) 329 { 330 crow::Response res; 331 std::error_code ec; 332 crow::Request req({}, ec); 333 // Ignore errors intentionally 334 req.body = "{\"integer\": 1}"; 335 336 int64_t integer = 0; 337 ASSERT_TRUE(readJsonAction(req, res, "integer", integer)); 338 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 339 EXPECT_THAT(res.jsonValue, IsEmpty()); 340 EXPECT_EQ(integer, 1); 341 } 342 343 TEST(ReadJsonAction, EmptyObjectReturnsTrueResponseOk) 344 { 345 crow::Response res; 346 std::error_code ec; 347 crow::Request req({}, ec); 348 // Ignore errors intentionally 349 req.body = "{}"; 350 351 std::optional<int64_t> integer = 0; 352 ASSERT_TRUE(readJsonAction(req, res, "integer", integer)); 353 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 354 EXPECT_THAT(res.jsonValue, IsEmpty()); 355 } 356 357 } // namespace 358 } // namespace redfish::json_util