userver: /data/code/userver/libraries/protobuf/tests/json/any_from_json_test.cpp Source File
Loading...
Searching...
No Matches
any_from_json_test.cpp
1#include <gtest/gtest.h>
2
3#include <memory>
4#include <ostream>
5#include <string>
6
7#include <fmt/format.h>
8#include <google/protobuf/dynamic_message.h>
9
10#include <userver/formats/json/serialize.hpp>
11#include <userver/protobuf/json/convert.hpp>
12#include <userver/utest/assert_macros.hpp>
13
14#include "utils.hpp"
15
16USERVER_NAMESPACE_BEGIN
17
18namespace protobuf::json::tests {
19
20struct AnyFromJsonSuccessTestParam {
21 std::string input = {};
22 AnyMessageData expected_message = {};
23 ParseOptions options = {};
24
25 // This variable is used to disable some checks that fail in the native protobuf implementation.
26 bool skip_native_check = false;
27};
28
29struct AnyFromJsonFailureTestParam {
30 std::string input = {};
31 ParseErrorCode expected_errc = {};
32 std::string expected_path = {};
33 ParseOptions options = {};
34};
35
36void PrintTo(const AnyFromJsonSuccessTestParam& param, std::ostream* os) {
37 *os << fmt::format("{{ input = '{}' }}", param.input);
38}
39
40void PrintTo(const AnyFromJsonFailureTestParam& param, std::ostream* os) {
41 *os << fmt::format("{{ input = '{}' }}", param.input);
42}
43
44class AnyFromJsonSuccessTest : public ::testing::TestWithParam<AnyFromJsonSuccessTestParam> {};
45class AnyFromJsonFailureTest : public ::testing::TestWithParam<AnyFromJsonFailureTestParam> {};
46
47INSTANTIATE_TEST_SUITE_P(
48 ,
49 AnyFromJsonSuccessTest,
50 ::testing::Values(
51 AnyFromJsonSuccessTestParam{R"({})", AnyMessageData{}},
52 AnyFromJsonSuccessTestParam{R"({"field1":null})", AnyMessageData{}},
53 AnyFromJsonSuccessTestParam{R"({"field1":{}})", AnyMessageData{RawAnyData{"", ""}}},
54 AnyFromJsonSuccessTestParam{
55 R"({
56 "field1": {
57 "@type":"type.googleapis.com/proto_json.messages.Int32Message"
58 }
59 })",
60 AnyMessageData{Int32MessageData{}}
61 },
62 AnyFromJsonSuccessTestParam{
63 R"({
64 "field1": {
65 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
66 "field1":0,"field2":0,"field3":0
67 }
68 })",
69 AnyMessageData{Int32MessageData{0, 0, 0}}
70 },
71 AnyFromJsonSuccessTestParam{
72 R"({
73 "field1": {
74 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
75 "field1":null,"field2":null,"field3":1
76 }
77 })",
78 AnyMessageData{Int32MessageData{0, 0, 1}}
79 },
80 AnyFromJsonSuccessTestParam{
81 R"({
82 "field1": {
83 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
84 "field1":10,"field2":20,"field3":30
85 }
86 })",
87 AnyMessageData{Int32MessageData{10, 20, 30}}
88 },
89 AnyFromJsonSuccessTestParam{
90 R"({
91 "field1": {
92 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
93 "field1":10,"field2":20,"field3":30,"unknown_field":true
94 }
95 })",
96 AnyMessageData{Int32MessageData{10, 20, 30}},
97 {.ignore_unknown_fields = true}
98 },
99 AnyFromJsonSuccessTestParam{
100 R"({
101 "field1": {
102 "@type":"type.googleapis.com/google.protobuf.Duration",
103 "value":"123.000000987s"
104 }
105 })",
106 AnyMessageData{DurationMessageData{123, 987}}
107 },
108 AnyFromJsonSuccessTestParam{
109 R"({
110 "field1": {
111 "@type":"type.googleapis.com/google.protobuf.Duration",
112 "value":"123.000000987s",
113 "unknown_field":true
114 }
115 })",
116 AnyMessageData{DurationMessageData{123, 987}},
117 {.ignore_unknown_fields = true},
118 // We want 'ignore_unknown_options' to work in all cases.
119 true
120 },
121 AnyFromJsonSuccessTestParam{
122 R"({
123 "field1": {
124 "@type":"type.googleapis.com/google.protobuf.Duration"
125 }
126 })",
127 AnyMessageData{DurationMessageData{0, 0}}
128 },
129 AnyFromJsonSuccessTestParam{
130 R"({
131 "field1": {
132 "@type":"type.googleapis.com/google.protobuf.Duration",
133 "value":null
134 }
135 })",
136 AnyMessageData{DurationMessageData{0, 0}}
137 },
138 AnyFromJsonSuccessTestParam{
139 R"({
140 "field1": {
141 "@type":"type.googleapis.com/google.protobuf.Value",
142 "value":null
143 }
144 })",
145 AnyMessageData{ValueMessageData{kProtoNullValue}}
146 },
147 AnyFromJsonSuccessTestParam{
148 R"({
149 "field1": {
150 "@type":"type.googleapis.com/google.protobuf.Value",
151 "value":"hello"
152 }
153 })",
154 AnyMessageData{ValueMessageData{"hello"}}
155 },
156 AnyFromJsonSuccessTestParam{
157 R"({
158 "field1": {
159 "@type":"type.googleapis.com/google.protobuf.Value",
160 "value":[1.5,1.5]
161 }
162 })",
163 AnyMessageData{ValueMessageData{std::vector<double>{1.5, 1.5}}}
164 },
165 AnyFromJsonSuccessTestParam{
166 R"({
167 "field1": {
168 "@type":"type.googleapis.com/google.protobuf.Value",
169 "value":{"aaa":"hello","bbb":"world"}
170 }
171 })",
172 AnyMessageData{ValueMessageData{std::map<std::string, std::string>{{"aaa", "hello"}, {"bbb", "world"}}}}
173 }
174 )
175);
176
177INSTANTIATE_TEST_SUITE_P(
178 ,
179 AnyFromJsonFailureTest,
180 ::testing::Values(
181 AnyFromJsonFailureTestParam{R"({"field1":[]})", ParseErrorCode::kInvalidType, "field1"},
182 AnyFromJsonFailureTestParam{R"({"field1":true})", ParseErrorCode::kInvalidType, "field1"},
183 AnyFromJsonFailureTestParam{R"({"field1":1})", ParseErrorCode::kInvalidType, "field1"},
184 AnyFromJsonFailureTestParam{R"({"field1":"hello"})", ParseErrorCode::kInvalidType, "field1"},
185 AnyFromJsonFailureTestParam{
186 R"({
187 "field1": {
188 "@type":"oops"
189 }
190 })",
192 "field1"
193 },
194 AnyFromJsonFailureTestParam{
195 R"({
196 "field1": {
197 "@type":"type.googleapis.com/proto_json.messages.NonExistent"
198 }
199 })",
201 "field1"
202 },
203 AnyFromJsonFailureTestParam{
204 R"({
205 "field1": {
206 "@type":true
207 }
208 })",
210 "field1"
211 },
212 AnyFromJsonFailureTestParam{
213 R"({
214 "field1": {
215 "value":"123.000000987s"
216 }
217 })",
219 "field1"
220 },
221 AnyFromJsonFailureTestParam{
222 R"({
223 "field1": {
224 "field1":1
225 }
226 })",
228 "field1"
229 },
230 AnyFromJsonFailureTestParam{
231 R"({
232 "field1": {
233 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
234 "field1":10,"field2":20,"field3":30,"unknown_field":true
235 }
236 })",
238 "field1.unknown_field",
239 {.ignore_unknown_fields = false}
240 },
241 AnyFromJsonFailureTestParam{
242 R"({
243 "field1": {
244 "@type":"type.googleapis.com/google.protobuf.Duration",
245 "value":"123.000000987s",
246 "unknown_field":true
247 }
248 })",
250 "field1.unknown_field",
251 {.ignore_unknown_fields = false}
252 }
253 )
254);
255
256TEST_P(AnyFromJsonSuccessTest, Test) {
257 const auto& param = GetParam();
258
259 proto_json::messages::AnyMessage message;
260 proto_json::messages::AnyMessage expected_message;
261 proto_json::messages::AnyMessage sample_message;
262 formats::json::Value input = PrepareJsonTestData(param.input);
263 expected_message = PrepareTestData(param.expected_message);
264
265 message.mutable_field1()->set_type_url("dump");
266
267 UASSERT_NO_THROW((message = JsonToMessage<proto_json::messages::AnyMessage>(input, param.options)));
268
269 if (!param.skip_native_check) {
270 UASSERT_NO_THROW(InitSampleMessage(param.input, sample_message, param.options));
271 CheckMessageEqual(message, sample_message);
272 }
273
274 CheckMessageEqual(message, expected_message);
275}
276
277TEST_P(AnyFromJsonFailureTest, Test) {
278 const auto& param = GetParam();
279
280 proto_json::messages::AnyMessage sample_message;
281 formats::json::Value input = PrepareJsonTestData(param.input);
282
284 (void)JsonToMessage<proto_json::messages::AnyMessage>(input, param.options),
285 param.expected_errc,
286 param.expected_path
287 );
288 UEXPECT_THROW(InitSampleMessage(param.input, sample_message, param.options), SampleError);
289}
290
291TEST(AnyFromJsonAdditionalTest, InlinedNonNull) {
292 using Message = ::google::protobuf::Any;
293
294 const char* json_str =
295 R"({
296 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
297 "field1":1,"field2":2,"field3":3
298 })";
299 const auto json = formats::json::FromString(json_str);
300 Message message;
301 Message sample;
302 proto_json::messages::Int32Message payload;
303
304 message.set_type_url("dump");
305
306 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
307 UASSERT_NO_THROW(InitSampleMessage(json_str, sample));
308 ASSERT_TRUE(message.Is<proto_json::messages::Int32Message>());
309 ASSERT_TRUE(message.UnpackTo(&payload));
310
311 EXPECT_EQ(payload.field1(), 1);
312 EXPECT_EQ(payload.field2(), 2);
313 EXPECT_EQ(payload.field3(), 3);
314 CheckMessageEqual(message, sample);
315}
316
317TEST(AnyFromJsonAdditionalTest, InlinedNull) {
318 using Message = ::google::protobuf::Any;
319
320 {
321 const auto json = formats::json::FromString("null");
322 Message message;
323
324 EXPECT_PARSE_ERROR((message = JsonToMessage<Message>(json)), ParseErrorCode::kInvalidType, "/");
325 UEXPECT_THROW(InitSampleMessage("null", message), SampleError);
326 }
327
328 {
329 const auto json = formats::json::FromString("{}");
330 Message message;
331 Message sample;
332
333 message.set_type_url("dump");
334
335 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
336 UASSERT_NO_THROW(InitSampleMessage("{}", sample));
337
338 EXPECT_TRUE(message.type_url().empty());
339 EXPECT_TRUE(message.value().empty());
340 CheckMessageEqual(message, sample);
341 }
342}
343
344TEST(AnyFromJsonAdditionalTest, DynamicMessage) {
345 using Message = ::google::protobuf::Any;
346
347 const char* json_str =
348 R"({
349 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
350 "field1":1,"field2":2,"field3":3
351 })";
352 const auto json = formats::json::FromString(json_str);
353 Message message;
354 proto_json::messages::Int32Message payload;
355 ::google::protobuf::DynamicMessageFactory factory;
356
357 {
358 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
359
360 UASSERT_NO_THROW(JsonToMessage(json, *message));
361
362 const auto reflection = message->GetReflection();
363 const auto type_url_desc = message->GetDescriptor()->FindFieldByName("type_url");
364 const auto value_desc = message->GetDescriptor()->FindFieldByName("value");
365
366 ASSERT_EQ(
367 reflection->GetString(*message, type_url_desc),
368 "type.googleapis.com/proto_json.messages.Int32Message"
369 );
370 ASSERT_TRUE(payload.ParseFromString(reflection->GetString(*message, value_desc)));
371 EXPECT_EQ(payload.field1(), 1);
372 EXPECT_EQ(payload.field2(), 2);
373 EXPECT_EQ(payload.field3(), 3);
374 }
375}
376
377} // namespace protobuf::json::tests
378
379USERVER_NAMESPACE_END