userver: /data/code/userver/libraries/protobuf/tests/json/value_to_json_test.cpp Source File
Loading...
Searching...
No Matches
value_to_json_test.cpp
1#include <gtest/gtest.h>
2
3#include <limits>
4#include <memory>
5#include <string>
6
7#include <fmt/format.h>
8#include <google/protobuf/dynamic_message.h>
9
10#include <userver/protobuf/json/convert.hpp>
11#include <userver/utest/assert_macros.hpp>
12
13#include "utils.hpp"
14
15USERVER_NAMESPACE_BEGIN
16
17namespace protobuf::json::tests {
18
19struct ValueToJsonSuccessTestParam {
20 ValueMessageData input = {};
21 std::string expected_json = {};
22 WriteOptions options = {};
23};
24
25struct ValueToJsonFailureTestParam {
26 ValueMessageData input = {};
27 WriteErrorCode expected_errc = {};
28 std::string expected_path = {};
29 WriteOptions options = {};
30
31 // We want to skip some native checks because userver implementation has stricter requirements.
32 bool skip_native_check = false;
33};
34
35class ValueToJsonSuccessTest : public ::testing::TestWithParam<ValueToJsonSuccessTestParam> {};
36class ValueToJsonFailureTest : public ::testing::TestWithParam<ValueToJsonFailureTestParam> {};
37
38INSTANTIATE_TEST_SUITE_P(
39 ,
40 ValueToJsonSuccessTest,
41 ::
42 testing::
43 Values(
44 ValueToJsonSuccessTestParam{ValueMessageData{}, R"({})"},
45 ValueToJsonSuccessTestParam{ValueMessageData{ProtoValue{kProtoNullValue}}, R"({"field1":null})"},
46 ValueToJsonSuccessTestParam{ValueMessageData{ProtoValue{1.5}}, R"({"field1":1.5})"},
47 ValueToJsonSuccessTestParam{ValueMessageData{ProtoValue{"hello"}}, R"({"field1":"hello"})"},
48 ValueToJsonSuccessTestParam{ValueMessageData{ProtoValue{true}}, R"({"field1":true})"},
49 ValueToJsonSuccessTestParam{ValueMessageData{ProtoValue{std::vector<double>{}}}, R"({"field1":[]})"},
50 ValueToJsonSuccessTestParam{
51 ValueMessageData{ProtoValue{std::vector<double>{1.5, 1.5}}},
52 R"({"field1":[1.5, 1.5]})"
53 },
54 ValueToJsonSuccessTestParam{
55 ValueMessageData{ProtoValue{std::map<std::string, std::string>{}}},
56 R"({"field1":{}})"
57 },
58 ValueToJsonSuccessTestParam{
59 ValueMessageData{ProtoValue{std::map<std::string, std::string>{{"aaa", "hello"}, {"bbb", "world"}}}
60 },
61 R"({"field1":{"aaa":"hello","bbb":"world"}})"
62 },
63 ValueToJsonSuccessTestParam{
64 ValueMessageData{
65 std::vector<ProtoValue>{},
66 },
67 R"({"field1":[]})"
68 },
69 ValueToJsonSuccessTestParam{
70 ValueMessageData{
71 std::vector<ProtoValue>{std::vector<double>{}},
72 },
73 R"({"field1":[[]]})"
74 },
75 ValueToJsonSuccessTestParam{
76 ValueMessageData{
77 std::vector<ProtoValue>{std::vector<double>{1.5}, std::vector<double>{0, 1.5}},
78 },
79 R"({"field1":[[1.5],[0,1.5]]})"
80 },
81 ValueToJsonSuccessTestParam{
82 ValueMessageData{
83 std::vector<ProtoValue>{
84 kProtoNullValue,
85 1.5,
86 "hello",
87 true,
88 std::vector<double>{1.5, 1.5},
89 std::map<std::string, std::string>{{"aaa", "hello"}, {"bbb", "world"}}
90 },
91 },
92 R"({"field1":[null,1.5,"hello",true,[1.5,1.5],{"aaa":"hello","bbb":"world"}]})"
93 },
94 ValueToJsonSuccessTestParam{
95 ValueMessageData{
96 std::map<std::string, ProtoValue>{},
97 },
98 R"({"field1":{}})"
99 },
100 ValueToJsonSuccessTestParam{
101 ValueMessageData{
102 std::map<std::string, ProtoValue>{{"aaa", ProtoValue{std::map<std::string, std::string>{}}}},
103 },
104 R"({"field1":{"aaa":{}}})"
105 },
106 ValueToJsonSuccessTestParam{
107 ValueMessageData{
108 std::map<std::string, ProtoValue>{
109 {"aaa", kProtoNullValue},
110 {"bbb", 1.5},
111 {"ccc", "hello"},
112 {"ddd", true},
113 {"eee", std::vector<double>{1.5, 1.5}},
114 {"", std::map<std::string, std::string>{{"", "hello"}, {"bbb", "world"}}}
115 },
116 },
117 R"({"field1":{"aaa":null,"bbb":1.5,"ccc":"hello","ddd":true,"eee":[1.5,1.5],"":{"":"hello","bbb":"world"}}})"
118 }
119 )
120);
121
122INSTANTIATE_TEST_SUITE_P(
123 ,
124 ValueToJsonFailureTest,
125 ::testing::Values(
126 ValueToJsonFailureTestParam{
127 ValueMessageData{ProtoValue{std::monostate{}}},
128 WriteErrorCode::kInvalidValue,
129 "field1",
130 {},
131 true // native implementation silently discards 'Value' without any alternative set which we find bug-prone
132 },
133 ValueToJsonFailureTestParam{
134 ValueMessageData{ProtoValue{std::numeric_limits<double>::quiet_NaN()}},
135 WriteErrorCode::kInvalidValue,
136 "field1.number_value"
137 },
138 ValueToJsonFailureTestParam{
139 ValueMessageData{ProtoValue{std::numeric_limits<double>::signaling_NaN()}},
140 WriteErrorCode::kInvalidValue,
141 "field1.number_value"
142 },
143 ValueToJsonFailureTestParam{
144 ValueMessageData{ProtoValue{std::numeric_limits<double>::infinity()}},
145 WriteErrorCode::kInvalidValue,
146 "field1.number_value"
147 },
148 ValueToJsonFailureTestParam{
149 ValueMessageData{ProtoValue{-std::numeric_limits<double>::infinity()}},
150 WriteErrorCode::kInvalidValue,
151 "field1.number_value"
152 },
153 ValueToJsonFailureTestParam{
154 ValueMessageData{std::vector<ProtoValue>{ProtoValue{1.5}, ProtoValue{std::monostate{}}, ProtoValue{true}}},
155 WriteErrorCode::kInvalidValue,
156 "field1.list_value.values[1]",
157 {},
158 true
159 },
160 ValueToJsonFailureTestParam{
161 ValueMessageData{std::vector<ProtoValue>{
162 ProtoValue{1.5},
163 ProtoValue{std::vector<double>{1.5, std::numeric_limits<double>::infinity()}},
164 ProtoValue{true}
165 }},
166 WriteErrorCode::kInvalidValue,
167 "field1.list_value.values[1].list_value.values[1].number_value"
168 },
169 ValueToJsonFailureTestParam{
170 ValueMessageData{std::map<
171 std::string,
172 ProtoValue>{{"aaa", ProtoValue{1.5}}, {"bbb", ProtoValue{std::monostate{}}}, {"ccc", ProtoValue{true}}}
173 },
174 WriteErrorCode::kInvalidValue,
175 "field1.struct_value.fields['bbb']",
176 {},
177 true
178 }
179 )
180);
181
182TEST_P(ValueToJsonSuccessTest, Test) {
183 const auto& param = GetParam();
184
185 auto input = PrepareTestData(param.input);
186 formats::json::Value json, expected_json, sample_json;
187
188 UASSERT_NO_THROW((json = MessageToJson(input, param.options)));
189 UASSERT_NO_THROW((expected_json = PrepareJsonTestData(param.expected_json)));
190 UASSERT_NO_THROW((sample_json = CreateSampleJson(input, param.options)));
191
192 EXPECT_EQ(json, expected_json);
193 EXPECT_EQ(expected_json, sample_json);
194}
195
196TEST_P(ValueToJsonFailureTest, Test) {
197 const auto& param = GetParam();
198 auto input = PrepareTestData(param.input);
199
200 EXPECT_WRITE_ERROR((void)MessageToJson(input, param.options), param.expected_errc, param.expected_path);
201
202 if (!param.skip_native_check) {
203 UEXPECT_THROW((void)CreateSampleJson(input, param.options), SampleError);
204 }
205}
206
207TEST(ValueToJsonAdditionalTest, InlinedNonNull) {
208 {
209 ValueMessageData data{kProtoNullValue};
210 auto message = PrepareTestData(data);
211 formats::json::Value json, sample;
212
213 UASSERT_NO_THROW((json = MessageToJson(message.field1())));
214 UASSERT_NO_THROW((sample = CreateSampleJson(message.field1())));
215 EXPECT_EQ(json, sample);
216 EXPECT_TRUE(json.IsNull());
217 }
218
219 {
220 ValueMessageData data{1.5};
221 auto message = PrepareTestData(data);
222 formats::json::Value json, sample;
223
224 UASSERT_NO_THROW((json = MessageToJson(message.field1())));
225 UASSERT_NO_THROW((sample = CreateSampleJson(message.field1())));
226 EXPECT_EQ(json, sample);
227 ASSERT_TRUE(json.IsNumber());
228 EXPECT_EQ(json.As<double>(), 1.5);
229 }
230
231 {
232 ValueMessageData data{"hello"};
233 auto message = PrepareTestData(data);
234 formats::json::Value json, sample;
235
236 UASSERT_NO_THROW((json = MessageToJson(message.field1())));
237 UASSERT_NO_THROW((sample = CreateSampleJson(message.field1())));
238 EXPECT_EQ(json, sample);
239 EXPECT_TRUE(json.IsString());
240 EXPECT_EQ(json.As<std::string>(), "hello");
241 }
242
243 {
244 ValueMessageData data{true};
245 auto message = PrepareTestData(data);
246 formats::json::Value json, sample;
247
248 UASSERT_NO_THROW((json = MessageToJson(message.field1())));
249 UASSERT_NO_THROW((sample = CreateSampleJson(message.field1())));
250 EXPECT_EQ(json, sample);
251 EXPECT_TRUE(json.IsBool());
252 EXPECT_TRUE(json.As<bool>());
253 }
254
255 {
256 ValueMessageData data{std::vector<double>{}};
257 auto message = PrepareTestData(data);
258 formats::json::Value json, sample;
259
260 UASSERT_NO_THROW((json = MessageToJson(message.field1())));
261 UASSERT_NO_THROW((sample = CreateSampleJson(message.field1())));
262 EXPECT_EQ(json, sample);
263 EXPECT_TRUE(json.IsArray());
264 ASSERT_EQ(json.GetSize(), std::size_t{0});
265 }
266
267 {
268 ValueMessageData data{std::vector<double>{1.5, 0}};
269 auto message = PrepareTestData(data);
270 formats::json::Value json, sample;
271
272 UASSERT_NO_THROW((json = MessageToJson(message.field1())));
273 UASSERT_NO_THROW((sample = CreateSampleJson(message.field1())));
274 EXPECT_EQ(json, sample);
275 EXPECT_TRUE(json.IsArray());
276 ASSERT_EQ(json.GetSize(), std::size_t{2});
277 EXPECT_EQ(json[0].As<double>(), 1.5);
278 EXPECT_EQ(json[1].As<double>(), 0);
279 }
280
281 {
282 ValueMessageData data{std::map<std::string, std::string>{}};
283 auto message = PrepareTestData(data);
284 formats::json::Value json, sample;
285
286 UASSERT_NO_THROW((json = MessageToJson(message.field1())));
287 UASSERT_NO_THROW((sample = CreateSampleJson(message.field1())));
288 EXPECT_EQ(json, sample);
289 EXPECT_TRUE(json.IsObject());
290 ASSERT_EQ(json.GetSize(), std::size_t{0});
291 }
292
293 {
294 ValueMessageData data{std::map<std::string, std::string>{{"aaa", "hello"}, {"bbb", "world"}}};
295 auto message = PrepareTestData(data);
296 formats::json::Value json, sample;
297
298 UASSERT_NO_THROW((json = MessageToJson(message.field1())));
299 UASSERT_NO_THROW((sample = CreateSampleJson(message.field1())));
300 EXPECT_EQ(json, sample);
301 EXPECT_TRUE(json.IsObject());
302 ASSERT_EQ(json.GetSize(), std::size_t{2});
303 ASSERT_TRUE(json.HasMember("aaa"));
304 ASSERT_TRUE(json.HasMember("bbb"));
305 EXPECT_EQ(json["aaa"].As<std::string>(), "hello");
306 EXPECT_EQ(json["bbb"].As<std::string>(), "world");
307 }
308}
309
310TEST(ValueToJsonAdditionalTest, InlinedNull) {
311 ValueMessageData data{std::monostate{}};
312 auto message = PrepareTestData(data);
313
314 EXPECT_WRITE_ERROR((void)MessageToJson(message.field1()), WriteErrorCode::kInvalidValue, "/");
315}
316
317TEST(ValueToJsonAdditionalTest, DynamicMessage) {
318 using Message = ::google::protobuf::Value;
319 ::google::protobuf::DynamicMessageFactory factory;
320
321 {
322 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
323 const auto reflection = message->GetReflection();
324 const auto null_value_desc = message->GetDescriptor()->FindFieldByName("null_value");
325
326 reflection->SetEnumValue(message.get(), null_value_desc, 0);
327
328 formats::json::Value json;
329
330 UASSERT_NO_THROW((json = MessageToJson(*message)));
331 ASSERT_TRUE(json.IsNull());
332 }
333
334 {
335 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
336 const auto reflection = message->GetReflection();
337 const auto number_value_desc = message->GetDescriptor()->FindFieldByName("number_value");
338
339 reflection->SetDouble(message.get(), number_value_desc, 1.5);
340
341 formats::json::Value json;
342
343 UASSERT_NO_THROW((json = MessageToJson(*message)));
344 ASSERT_TRUE(json.IsNumber());
345 EXPECT_EQ(json.As<double>(), 1.5);
346 }
347
348 {
349 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
350 const auto reflection = message->GetReflection();
351 const auto number_value_desc = message->GetDescriptor()->FindFieldByName("string_value");
352
353 reflection->SetString(message.get(), number_value_desc, "hello");
354
355 formats::json::Value json;
356
357 UASSERT_NO_THROW((json = MessageToJson(*message)));
358 ASSERT_TRUE(json.IsString());
359 EXPECT_EQ(json.As<std::string>(), "hello");
360 }
361
362 {
363 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
364 const auto reflection = message->GetReflection();
365 const auto bool_value_desc = message->GetDescriptor()->FindFieldByName("bool_value");
366
367 reflection->SetBool(message.get(), bool_value_desc, true);
368
369 formats::json::Value json;
370
371 UASSERT_NO_THROW((json = MessageToJson(*message)));
372 ASSERT_TRUE(json.IsBool());
373 EXPECT_EQ(json.As<bool>(), true);
374 }
375
376 {
377 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
378 const auto reflection = message->GetReflection();
379 const auto list_value_desc = message->GetDescriptor()->FindFieldByName("list_value");
380 const auto list_value_message = reflection->MutableMessage(message.get(), list_value_desc, &factory);
381 const auto list_value_reflection = list_value_message->GetReflection();
382 const auto values_desc = list_value_message->GetDescriptor()->FindFieldByName("values");
383 const auto item_message = list_value_reflection->AddMessage(list_value_message, values_desc, &factory);
384 const auto item_reflection = item_message->GetReflection();
385 const auto number_value_desc = item_message->GetDescriptor()->FindFieldByName("number_value");
386
387 item_reflection->SetDouble(item_message, number_value_desc, 1.5);
388
389 formats::json::Value json;
390
391 UASSERT_NO_THROW((json = MessageToJson(*message)));
392 ASSERT_TRUE(json.IsArray());
393 ASSERT_EQ(json.GetSize(), std::size_t{1});
394 EXPECT_EQ(json[0].As<double>(), 1.5);
395 }
396
397 {
398 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
399 const auto reflection = message->GetReflection();
400 const auto struct_value_desc = message->GetDescriptor()->FindFieldByName("struct_value");
401 const auto struct_value_message = reflection->MutableMessage(message.get(), struct_value_desc, &factory);
402 const auto struct_value_reflection = struct_value_message->GetReflection();
403 const auto fields_desc = struct_value_message->GetDescriptor()->FindFieldByName("fields");
404 const auto item_message = struct_value_reflection->AddMessage(struct_value_message, fields_desc, &factory);
405 const auto item_reflection = item_message->GetReflection();
406 const auto map_key_desc = item_message->GetDescriptor()->map_key();
407 const auto map_value_desc = item_message->GetDescriptor()->map_value();
408 const auto map_value_message = item_reflection->MutableMessage(item_message, map_value_desc, &factory);
409 const auto map_value_reflection = map_value_message->GetReflection();
410 const auto number_value_desc = map_value_message->GetDescriptor()->FindFieldByName("number_value");
411
412 item_reflection->SetString(item_message, map_key_desc, "aaa");
413 map_value_reflection->SetDouble(map_value_message, number_value_desc, 1.5);
414
415 formats::json::Value json;
416
417 UASSERT_NO_THROW((json = MessageToJson(*message)));
418 ASSERT_TRUE(json.IsObject());
419 ASSERT_EQ(json.GetSize(), std::size_t{1});
420 ASSERT_TRUE(json.HasMember("aaa"));
421 EXPECT_EQ(json["aaa"].As<double>(), 1.5);
422 }
423}
424
425} // namespace protobuf::json::tests
426
427USERVER_NAMESPACE_END