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 true // we want 'ignore_unknown_options' to work in all cases
119 },
120 AnyFromJsonSuccessTestParam{
121 R"({
122 "field1": {
123 "@type":"type.googleapis.com/google.protobuf.Duration"
124 }
125 })",
126 AnyMessageData{DurationMessageData{0, 0}}
127 },
128 AnyFromJsonSuccessTestParam{
129 R"({
130 "field1": {
131 "@type":"type.googleapis.com/google.protobuf.Duration",
132 "value":null
133 }
134 })",
135 AnyMessageData{DurationMessageData{0, 0}}
136 },
137 AnyFromJsonSuccessTestParam{
138 R"({
139 "field1": {
140 "@type":"type.googleapis.com/google.protobuf.Value",
141 "value":null
142 }
143 })",
144 AnyMessageData{ValueMessageData{kProtoNullValue}}
145 },
146 AnyFromJsonSuccessTestParam{
147 R"({
148 "field1": {
149 "@type":"type.googleapis.com/google.protobuf.Value",
150 "value":"hello"
151 }
152 })",
153 AnyMessageData{ValueMessageData{"hello"}}
154 },
155 AnyFromJsonSuccessTestParam{
156 R"({
157 "field1": {
158 "@type":"type.googleapis.com/google.protobuf.Value",
159 "value":[1.5,1.5]
160 }
161 })",
162 AnyMessageData{ValueMessageData{std::vector<double>{1.5, 1.5}}}
163 },
164 AnyFromJsonSuccessTestParam{
165 R"({
166 "field1": {
167 "@type":"type.googleapis.com/google.protobuf.Value",
168 "value":{"aaa":"hello","bbb":"world"}
169 }
170 })",
171 AnyMessageData{ValueMessageData{std::map<std::string, std::string>{{"aaa", "hello"}, {"bbb", "world"}}}}
172 }
173 )
174);
175
176INSTANTIATE_TEST_SUITE_P(
177 ,
178 AnyFromJsonFailureTest,
179 ::testing::Values(
180 AnyFromJsonFailureTestParam{R"({"field1":[]})", ParseErrorCode::kInvalidType, "field1"},
181 AnyFromJsonFailureTestParam{R"({"field1":true})", ParseErrorCode::kInvalidType, "field1"},
182 AnyFromJsonFailureTestParam{R"({"field1":1})", ParseErrorCode::kInvalidType, "field1"},
183 AnyFromJsonFailureTestParam{R"({"field1":"hello"})", ParseErrorCode::kInvalidType, "field1"},
184 AnyFromJsonFailureTestParam{
185 R"({
186 "field1": {
187 "@type":"oops"
188 }
189 })",
190 ParseErrorCode::kInvalidValue,
191 "field1"
192 },
193 AnyFromJsonFailureTestParam{
194 R"({
195 "field1": {
196 "@type":"type.googleapis.com/proto_json.messages.NonExistent"
197 }
198 })",
199 ParseErrorCode::kInvalidValue,
200 "field1"
201 },
202 AnyFromJsonFailureTestParam{
203 R"({
204 "field1": {
205 "@type":true
206 }
207 })",
208 ParseErrorCode::kInvalidValue,
209 "field1"
210 },
211 AnyFromJsonFailureTestParam{
212 R"({
213 "field1": {
214 "value":"123.000000987s"
215 }
216 })",
217 ParseErrorCode::kInvalidValue,
218 "field1"
219 },
220 AnyFromJsonFailureTestParam{
221 R"({
222 "field1": {
223 "field1":1
224 }
225 })",
226 ParseErrorCode::kInvalidValue,
227 "field1"
228 },
229 AnyFromJsonFailureTestParam{
230 R"({
231 "field1": {
232 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
233 "field1":10,"field2":20,"field3":30,"unknown_field":true
234 }
235 })",
236 ParseErrorCode::kUnknownField,
237 "field1.unknown_field",
238 {.ignore_unknown_fields = false}
239 },
240 AnyFromJsonFailureTestParam{
241 R"({
242 "field1": {
243 "@type":"type.googleapis.com/google.protobuf.Duration",
244 "value":"123.000000987s",
245 "unknown_field":true
246 }
247 })",
248 ParseErrorCode::kUnknownField,
249 "field1.unknown_field",
250 {.ignore_unknown_fields = false}
251 }
252 )
253);
254
255TEST_P(AnyFromJsonSuccessTest, Test) {
256 const auto& param = GetParam();
257
258 proto_json::messages::AnyMessage message;
259 proto_json::messages::AnyMessage expected_message;
260 proto_json::messages::AnyMessage sample_message;
261 formats::json::Value input = PrepareJsonTestData(param.input);
262 expected_message = PrepareTestData(param.expected_message);
263
264 message.mutable_field1()->set_type_url("dump");
265
266 UASSERT_NO_THROW((message = JsonToMessage<proto_json::messages::AnyMessage>(input, param.options)));
267
268 if (!param.skip_native_check) {
269 UASSERT_NO_THROW(InitSampleMessage(param.input, sample_message, param.options));
270 CheckMessageEqual(message, sample_message);
271 }
272
273 CheckMessageEqual(message, expected_message);
274}
275
276TEST_P(AnyFromJsonFailureTest, Test) {
277 const auto& param = GetParam();
278
279 proto_json::messages::AnyMessage sample_message;
280 formats::json::Value input = PrepareJsonTestData(param.input);
281
283 (void)JsonToMessage<proto_json::messages::AnyMessage>(input, param.options),
284 param.expected_errc,
285 param.expected_path
286 );
287 UEXPECT_THROW(InitSampleMessage(param.input, sample_message, param.options), SampleError);
288}
289
290TEST(AnyFromJsonAdditionalTest, InlinedNonNull) {
291 using Message = ::google::protobuf::Any;
292
293 const char* json_str =
294 R"({
295 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
296 "field1":1,"field2":2,"field3":3
297 })";
298 const auto json = formats::json::FromString(json_str);
299 Message message;
300 Message sample;
301 proto_json::messages::Int32Message payload;
302
303 message.set_type_url("dump");
304
305 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
306 UASSERT_NO_THROW(InitSampleMessage(json_str, sample));
307 ASSERT_TRUE(message.Is<proto_json::messages::Int32Message>());
308 ASSERT_TRUE(message.UnpackTo(&payload));
309
310 EXPECT_EQ(payload.field1(), 1);
311 EXPECT_EQ(payload.field2(), 2);
312 EXPECT_EQ(payload.field3(), 3);
313 CheckMessageEqual(message, sample);
314}
315
316TEST(AnyFromJsonAdditionalTest, InlinedNull) {
317 using Message = ::google::protobuf::Any;
318
319 {
320 const auto json = formats::json::FromString("null");
321 Message message;
322
323 EXPECT_PARSE_ERROR((message = JsonToMessage<Message>(json)), ParseErrorCode::kInvalidType, "/");
324 UEXPECT_THROW(InitSampleMessage("null", message), SampleError);
325 }
326
327 {
328 const auto json = formats::json::FromString("{}");
329 Message message;
330 Message sample;
331
332 message.set_type_url("dump");
333
334 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
335 UASSERT_NO_THROW(InitSampleMessage("{}", sample));
336
337 EXPECT_TRUE(message.type_url().empty());
338 EXPECT_TRUE(message.value().empty());
339 CheckMessageEqual(message, sample);
340 }
341}
342
343TEST(AnyFromJsonAdditionalTest, DynamicMessage) {
344 using Message = ::google::protobuf::Any;
345
346 const char* json_str =
347 R"({
348 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
349 "field1":1,"field2":2,"field3":3
350 })";
351 const auto json = formats::json::FromString(json_str);
352 Message message;
353 proto_json::messages::Int32Message payload;
354 ::google::protobuf::DynamicMessageFactory factory;
355
356 {
357 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
358
359 UASSERT_NO_THROW(JsonToMessage(json, *message));
360
361 const auto reflection = message->GetReflection();
362 const auto type_url_desc = message->GetDescriptor()->FindFieldByName("type_url");
363 const auto value_desc = message->GetDescriptor()->FindFieldByName("value");
364
365 ASSERT_EQ(
366 reflection->GetString(*message, type_url_desc),
367 "type.googleapis.com/proto_json.messages.Int32Message"
368 );
369 ASSERT_TRUE(payload.ParseFromString(reflection->GetString(*message, value_desc)));
370 EXPECT_EQ(payload.field1(), 1);
371 EXPECT_EQ(payload.field2(), 2);
372 EXPECT_EQ(payload.field3(), 3);
373 }
374}
375
376} // namespace protobuf::json::tests
377
378USERVER_NAMESPACE_END