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 ReadOptions 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 ReadErrorCode expected_errc = {};
32 std::string expected_path = {};
33 ReadOptions 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":[]})", ReadErrorCode::kInvalidType, "field1"},
181 AnyFromJsonFailureTestParam{R"({"field1":true})", ReadErrorCode::kInvalidType, "field1"},
182 AnyFromJsonFailureTestParam{R"({"field1":1})", ReadErrorCode::kInvalidType, "field1"},
183 AnyFromJsonFailureTestParam{R"({"field1":"hello"})", ReadErrorCode::kInvalidType, "field1"},
184 AnyFromJsonFailureTestParam{
185 R"({
186 "field1": {
187 "@type":"oops"
188 }
189 })",
190 ReadErrorCode::kInvalidValue,
191 "field1"
192 },
193 AnyFromJsonFailureTestParam{
194 R"({
195 "field1": {
196 "@type":"type.googleapis.com/proto_json.messages.NonExistent"
197 }
198 })",
199 ReadErrorCode::kInvalidValue,
200 "field1"
201 },
202 AnyFromJsonFailureTestParam{
203 R"({
204 "field1": {
205 "@type":true
206 }
207 })",
208 ReadErrorCode::kInvalidValue,
209 "field1"
210 },
211 AnyFromJsonFailureTestParam{
212 R"({
213 "field1": {
214 "value":"123.000000987s"
215 }
216 })",
217 ReadErrorCode::kInvalidValue,
218 "field1"
219 },
220 AnyFromJsonFailureTestParam{
221 R"({
222 "field1": {
223 "field1":1
224 }
225 })",
226 ReadErrorCode::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 ReadErrorCode::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 ReadErrorCode::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, expected_message, sample_message;
259 formats::json::Value input = PrepareJsonTestData(param.input);
260 expected_message = PrepareTestData(param.expected_message);
261
262 message.mutable_field1()->set_type_url("dump");
263
264 UASSERT_NO_THROW((message = JsonToMessage<proto_json::messages::AnyMessage>(input, param.options)));
265
266 if (!param.skip_native_check) {
267 UASSERT_NO_THROW(InitSampleMessage(param.input, param.options, sample_message));
268 CheckMessageEqual(message, sample_message);
269 }
270
271 CheckMessageEqual(message, expected_message);
272}
273
274TEST_P(AnyFromJsonFailureTest, Test) {
275 const auto& param = GetParam();
276
277 proto_json::messages::AnyMessage sample_message;
278 formats::json::Value input = PrepareJsonTestData(param.input);
279
280 EXPECT_READ_ERROR(
281 (void)JsonToMessage<proto_json::messages::AnyMessage>(input, param.options),
282 param.expected_errc,
283 param.expected_path
284 );
285 UEXPECT_THROW(InitSampleMessage(param.input, param.options, sample_message), SampleError);
286}
287
288TEST(AnyFromJsonAdditionalTest, InlinedNonNull) {
289 using Message = ::google::protobuf::Any;
290
291 const char* json_str =
292 R"({
293 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
294 "field1":1,"field2":2,"field3":3
295 })";
296 const auto json = formats::json::FromString(json_str);
297 Message message, sample;
298 proto_json::messages::Int32Message payload;
299
300 message.set_type_url("dump");
301
302 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
303 UASSERT_NO_THROW(InitSampleMessage(json_str, {}, sample));
304 ASSERT_TRUE(message.Is<proto_json::messages::Int32Message>());
305 ASSERT_TRUE(message.UnpackTo(&payload));
306
307 EXPECT_EQ(payload.field1(), 1);
308 EXPECT_EQ(payload.field2(), 2);
309 EXPECT_EQ(payload.field3(), 3);
310 CheckMessageEqual(message, sample);
311}
312
313TEST(AnyFromJsonAdditionalTest, InlinedNull) {
314 using Message = ::google::protobuf::Any;
315
316 {
317 const auto json = formats::json::FromString("null");
318 Message message;
319
320 EXPECT_READ_ERROR((message = JsonToMessage<Message>(json)), ReadErrorCode::kInvalidType, "/");
321 UEXPECT_THROW(InitSampleMessage("null", {}, message), SampleError);
322 }
323
324 {
325 const auto json = formats::json::FromString("{}");
326 Message message, sample;
327
328 message.set_type_url("dump");
329
330 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
331 UASSERT_NO_THROW(InitSampleMessage("{}", {}, sample));
332
333 EXPECT_TRUE(message.type_url().empty());
334 EXPECT_TRUE(message.value().empty());
335 CheckMessageEqual(message, sample);
336 }
337}
338
339TEST(AnyFromJsonAdditionalTest, DynamicMessage) {
340 using Message = ::google::protobuf::Any;
341
342 const char* json_str =
343 R"({
344 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
345 "field1":1,"field2":2,"field3":3
346 })";
347 const auto json = formats::json::FromString(json_str);
348 Message message;
349 proto_json::messages::Int32Message payload;
350 ::google::protobuf::DynamicMessageFactory factory;
351
352 {
353 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
354
355 UASSERT_NO_THROW(JsonToMessage(json, {}, *message));
356
357 const auto reflection = message->GetReflection();
358 const auto type_url_desc = message->GetDescriptor()->FindFieldByName("type_url");
359 const auto value_desc = message->GetDescriptor()->FindFieldByName("value");
360
361 ASSERT_EQ(
362 reflection->GetString(*message, type_url_desc),
363 "type.googleapis.com/proto_json.messages.Int32Message"
364 );
365 ASSERT_TRUE(payload.ParseFromString(reflection->GetString(*message, value_desc)));
366 EXPECT_EQ(payload.field1(), 1);
367 EXPECT_EQ(payload.field2(), 2);
368 EXPECT_EQ(payload.field3(), 3);
369 }
370}
371
372} // namespace protobuf::json::tests
373
374USERVER_NAMESPACE_END