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_THAT(jsonVec, ElementsAre(R"({"hello": "yes"})"_json, 125 R"([{"there": "no"}, "nice"])"_json)); 126 } 127 128 TEST(ReadJson, JsonSubElementValueAreUnpackedCorrectly) 129 { 130 crow::Response res; 131 nlohmann::json jsonRequest = R"( 132 { 133 "json": {"integer": 42} 134 } 135 )"_json; 136 137 int integer = 0; 138 ASSERT_TRUE(readJson(jsonRequest, res, "json/integer", integer)); 139 EXPECT_EQ(integer, 42); 140 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 141 EXPECT_THAT(res.jsonValue, IsEmpty()); 142 } 143 144 TEST(ReadJson, JsonDeeperSubElementValueAreUnpackedCorrectly) 145 { 146 crow::Response res; 147 nlohmann::json jsonRequest = R"( 148 { 149 "json": { 150 "json2": {"string": "foobar"} 151 } 152 } 153 )"_json; 154 155 std::string foobar; 156 ASSERT_TRUE(readJson(jsonRequest, res, "json/json2/string", foobar)); 157 EXPECT_EQ(foobar, "foobar"); 158 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 159 EXPECT_THAT(res.jsonValue, IsEmpty()); 160 } 161 162 TEST(ReadJson, MultipleJsonSubElementValueAreUnpackedCorrectly) 163 { 164 crow::Response res; 165 nlohmann::json jsonRequest = R"( 166 { 167 "json": { 168 "integer": 42, 169 "string": "foobar" 170 }, 171 "string": "bazbar" 172 } 173 )"_json; 174 175 int integer = 0; 176 std::string foobar; 177 std::string bazbar; 178 ASSERT_TRUE(readJson(jsonRequest, res, "json/integer", integer, 179 "json/string", foobar, "string", bazbar)); 180 EXPECT_EQ(integer, 42); 181 EXPECT_EQ(foobar, "foobar"); 182 EXPECT_EQ(bazbar, "bazbar"); 183 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 184 EXPECT_THAT(res.jsonValue, IsEmpty()); 185 } 186 187 TEST(ReadJson, ExtraElement) 188 { 189 crow::Response res; 190 nlohmann::json jsonRequest = {{"integer", 1}, {"string", "hello"}}; 191 192 std::optional<int> integer; 193 std::optional<std::string> str; 194 195 EXPECT_FALSE(readJson(jsonRequest, res, "integer", integer)); 196 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 197 EXPECT_FALSE(res.jsonValue.empty()); 198 EXPECT_EQ(integer, 1); 199 200 EXPECT_FALSE(readJson(jsonRequest, res, "string", str)); 201 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 202 EXPECT_FALSE(res.jsonValue.empty()); 203 EXPECT_EQ(str, "hello"); 204 } 205 206 TEST(ReadJson, ValidMissingElementReturnsTrue) 207 { 208 crow::Response res; 209 nlohmann::json jsonRequest = {{"integer", 1}}; 210 211 std::optional<int> integer; 212 int requiredInteger = 0; 213 std::optional<std::string> str0; 214 std::optional<std::string> str1; 215 std::optional<std::vector<uint8_t>> vec; 216 ASSERT_TRUE(readJson(jsonRequest, res, "missing_integer", integer, 217 "integer", requiredInteger)); 218 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 219 EXPECT_TRUE(res.jsonValue.empty()); 220 EXPECT_EQ(integer, std::nullopt); 221 222 ASSERT_TRUE(readJson(jsonRequest, res, "missing_string", str0, "integer", 223 requiredInteger)); 224 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 225 EXPECT_THAT(res.jsonValue, IsEmpty()); 226 EXPECT_EQ(str0, std::nullopt); 227 228 ASSERT_TRUE(readJson(jsonRequest, res, "integer", integer, "string", str0, 229 "vector", vec)); 230 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 231 EXPECT_THAT(res.jsonValue, IsEmpty()); 232 EXPECT_EQ(integer, 1); 233 EXPECT_EQ(str0, std::nullopt); 234 EXPECT_EQ(vec, std::nullopt); 235 236 ASSERT_TRUE(readJson(jsonRequest, res, "integer", integer, "string0", str0, 237 "missing_string", str1)); 238 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 239 EXPECT_THAT(res.jsonValue, IsEmpty()); 240 EXPECT_EQ(str1, std::nullopt); 241 } 242 243 TEST(ReadJson, InvalidMissingElementReturnsFalse) 244 { 245 crow::Response res; 246 nlohmann::json jsonRequest = {{"integer", 1}, {"string", "hello"}}; 247 248 int integer = 0; 249 std::string str0; 250 std::string str1; 251 std::vector<uint8_t> vec; 252 ASSERT_FALSE(readJson(jsonRequest, res, "missing_integer", integer)); 253 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 254 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 255 256 ASSERT_FALSE(readJson(jsonRequest, res, "missing_string", str0)); 257 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 258 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 259 260 ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer, "string", str0, 261 "vector", vec)); 262 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 263 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 264 265 ASSERT_FALSE(readJson(jsonRequest, res, "integer", integer, "string0", str0, 266 "missing_string", str1)); 267 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 268 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 269 } 270 271 TEST(ReadJsonPatch, ValidElementsReturnsTrueResponseOkValuesUnpackedCorrectly) 272 { 273 crow::Response res; 274 std::error_code ec; 275 crow::Request req("{\"integer\": 1}", ec); 276 277 // Ignore errors intentionally 278 req.req.set(boost::beast::http::field::content_type, "application/json"); 279 280 int64_t integer = 0; 281 ASSERT_TRUE(readJsonPatch(req, res, "integer", integer)); 282 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 283 EXPECT_THAT(res.jsonValue, IsEmpty()); 284 EXPECT_EQ(integer, 1); 285 } 286 287 TEST(ReadJsonPatch, EmptyObjectReturnsFalseResponseBadRequest) 288 { 289 crow::Response res; 290 std::error_code ec; 291 crow::Request req("{}", ec); 292 // Ignore errors intentionally 293 294 std::optional<int64_t> integer = 0; 295 ASSERT_FALSE(readJsonPatch(req, res, "integer", integer)); 296 EXPECT_EQ(res.result(), boost::beast::http::status::bad_request); 297 EXPECT_THAT(res.jsonValue, Not(IsEmpty())); 298 } 299 300 TEST(ReadJsonPatch, OdataIgnored) 301 { 302 crow::Response res; 303 std::error_code ec; 304 crow::Request req(R"({"@odata.etag": "etag", "integer": 1})", ec); 305 req.req.set(boost::beast::http::field::content_type, "application/json"); 306 // Ignore errors intentionally 307 308 std::optional<int64_t> integer = 0; 309 ASSERT_TRUE(readJsonPatch(req, res, "integer", integer)); 310 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 311 EXPECT_THAT(res.jsonValue, IsEmpty()); 312 EXPECT_EQ(integer, 1); 313 } 314 315 TEST(ReadJsonPatch, OnlyOdataGivesNoOperation) 316 { 317 crow::Response res; 318 std::error_code ec; 319 crow::Request req(R"({"@odata.etag": "etag"})", ec); 320 // Ignore errors intentionally 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("{\"integer\": 1}", ec); 333 req.req.set(boost::beast::http::field::content_type, "application/json"); 334 // Ignore errors intentionally 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 req.req.set(boost::beast::http::field::content_type, "application/json"); 349 // Ignore errors intentionally 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 359