1#include <gtest/gtest.h>
8#include <google/protobuf/dynamic_message.h>
10#include <userver/formats/json/serialize.hpp>
11#include <userver/protobuf/json/convert.hpp>
12#include <userver/utest/assert_macros.hpp>
16USERVER_NAMESPACE_BEGIN
18namespace protobuf::json::tests {
20struct AnyFromJsonSuccessTestParam {
21 std::string input = {};
23 ParseOptions options = {};
26 bool skip_native_check =
false;
29struct AnyFromJsonFailureTestParam {
30 std::string input = {};
31 ParseErrorCode expected_errc = {};
32 std::string expected_path = {};
33 ParseOptions options = {};
36void PrintTo(
const AnyFromJsonSuccessTestParam& param, std::ostream* os) {
37 *os << fmt::format(
"{{ input = '{}' }}", param.input);
40void PrintTo(
const AnyFromJsonFailureTestParam& param, std::ostream* os) {
41 *os << fmt::format(
"{{ input = '{}' }}", param.input);
44class AnyFromJsonSuccessTest :
public ::testing::TestWithParam<AnyFromJsonSuccessTestParam> {};
45class AnyFromJsonFailureTest :
public ::testing::TestWithParam<AnyFromJsonFailureTestParam> {};
47INSTANTIATE_TEST_SUITE_P(
49 AnyFromJsonSuccessTest,
51 AnyFromJsonSuccessTestParam{R"({})", AnyMessageData{}},
52 AnyFromJsonSuccessTestParam{R"({"field1":null})", AnyMessageData{}},
53 AnyFromJsonSuccessTestParam{R"({"field1":{}})", AnyMessageData{RawAnyData{
"",
""}}},
54 AnyFromJsonSuccessTestParam{
56 "field1": {
57 "@type":"type.googleapis.com/proto_json.messages.Int32Message"
58 }
59 })",
60 AnyMessageData{Int32MessageData{}}
62 AnyFromJsonSuccessTestParam{
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}}
71 AnyFromJsonSuccessTestParam{
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}}
80 AnyFromJsonSuccessTestParam{
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}}
89 AnyFromJsonSuccessTestParam{
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}
99 AnyFromJsonSuccessTestParam{
101 "field1": {
102 "@type":"type.googleapis.com/google.protobuf.Duration",
103 "value":"123.000000987s"
104 }
105 })",
106 AnyMessageData{DurationMessageData{123, 987}}
108 AnyFromJsonSuccessTestParam{
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},
120 AnyFromJsonSuccessTestParam{
122 "field1": {
123 "@type":"type.googleapis.com/google.protobuf.Duration"
124 }
125 })",
126 AnyMessageData{DurationMessageData{0, 0}}
128 AnyFromJsonSuccessTestParam{
130 "field1": {
131 "@type":"type.googleapis.com/google.protobuf.Duration",
132 "value":null
133 }
134 })",
135 AnyMessageData{DurationMessageData{0, 0}}
137 AnyFromJsonSuccessTestParam{
139 "field1": {
140 "@type":"type.googleapis.com/google.protobuf.Value",
141 "value":null
142 }
143 })",
144 AnyMessageData{ValueMessageData{kProtoNullValue}}
146 AnyFromJsonSuccessTestParam{
148 "field1": {
149 "@type":"type.googleapis.com/google.protobuf.Value",
150 "value":"hello"
151 }
152 })",
153 AnyMessageData{ValueMessageData{
"hello"}}
155 AnyFromJsonSuccessTestParam{
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}}}
164 AnyFromJsonSuccessTestParam{
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"}}}}
176INSTANTIATE_TEST_SUITE_P(
178 AnyFromJsonFailureTest,
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{
186 "field1": {
187 "@type":"oops"
188 }
189 })",
190 ParseErrorCode::kInvalidValue,
193 AnyFromJsonFailureTestParam{
195 "field1": {
196 "@type":"type.googleapis.com/proto_json.messages.NonExistent"
197 }
198 })",
199 ParseErrorCode::kInvalidValue,
202 AnyFromJsonFailureTestParam{
204 "field1": {
205 "@type":true
206 }
207 })",
208 ParseErrorCode::kInvalidValue,
211 AnyFromJsonFailureTestParam{
213 "field1": {
214 "value":"123.000000987s"
215 }
216 })",
217 ParseErrorCode::kInvalidValue,
220 AnyFromJsonFailureTestParam{
222 "field1": {
223 "field1":1
224 }
225 })",
226 ParseErrorCode::kInvalidValue,
229 AnyFromJsonFailureTestParam{
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}
240 AnyFromJsonFailureTestParam{
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}
255TEST_P(AnyFromJsonSuccessTest, Test) {
256 const auto& param = GetParam();
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);
264 message.mutable_field1()->set_type_url(
"dump");
266 UASSERT_NO_THROW((message = JsonToMessage<proto_json::messages::AnyMessage>(input, param.options)));
268 if (!param.skip_native_check) {
269 UASSERT_NO_THROW(InitSampleMessage(param.input, sample_message, param.options));
270 CheckMessageEqual(message, sample_message);
273 CheckMessageEqual(message, expected_message);
276TEST_P(AnyFromJsonFailureTest, Test) {
277 const auto& param = GetParam();
279 proto_json::messages::AnyMessage sample_message;
280 formats::json::
Value input = PrepareJsonTestData(param.input);
283 (
void)JsonToMessage<proto_json::messages::AnyMessage>(input, param.options),
287 UEXPECT_THROW(InitSampleMessage(param.input, sample_message, param.options), SampleError);
290TEST(AnyFromJsonAdditionalTest, InlinedNonNull) {
291 using Message = ::google::protobuf::Any;
293 const char* json_str =
295 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
296 "field1":1,"field2":2,"field3":3
297 })";
301 proto_json::messages::Int32Message payload;
303 message.set_type_url(
"dump");
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));
310 EXPECT_EQ(payload.field1(), 1);
311 EXPECT_EQ(payload.field2(), 2);
312 EXPECT_EQ(payload.field3(), 3);
313 CheckMessageEqual(message, sample);
316TEST(AnyFromJsonAdditionalTest, InlinedNull) {
317 using Message = ::google::protobuf::Any;
323 EXPECT_PARSE_ERROR((message = JsonToMessage<Message>(json)), ParseErrorCode::kInvalidType,
"/");
324 UEXPECT_THROW(InitSampleMessage(
"null", message), SampleError);
332 message.set_type_url(
"dump");
334 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
335 UASSERT_NO_THROW(InitSampleMessage(
"{}", sample));
337 EXPECT_TRUE(message.type_url().empty());
338 EXPECT_TRUE(message.value().empty());
339 CheckMessageEqual(message, sample);
343TEST(AnyFromJsonAdditionalTest, DynamicMessage) {
344 using Message = ::google::protobuf::Any;
346 const char* json_str =
348 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
349 "field1":1,"field2":2,"field3":3
350 })";
353 proto_json::messages::Int32Message payload;
354 ::google::protobuf::DynamicMessageFactory factory;
357 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
359 UASSERT_NO_THROW(JsonToMessage(json, *message));
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");
366 reflection->GetString(*message, type_url_desc),
367 "type.googleapis.com/proto_json.messages.Int32Message"
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);