1#include <gtest/gtest.h>
7#include <google/protobuf/dynamic_message.h>
9#include <userver/protobuf/json/convert.hpp>
10#include <userver/utest/assert_macros.hpp>
14USERVER_NAMESPACE_BEGIN
16namespace protobuf::json::tests {
18struct AnyToJsonSuccessTestParam {
20 std::string expected_json = {};
24 bool skip_native_check =
false;
27struct AnyToJsonFailureTestParam {
30 std::string expected_path = {};
34 bool skip_native_check =
false;
37class AnyToJsonSuccessTest :
public ::testing::TestWithParam<AnyToJsonSuccessTestParam> {};
38class AnyToJsonFailureTest :
public ::testing::TestWithParam<AnyToJsonFailureTestParam> {};
40INSTANTIATE_TEST_SUITE_P(
44 AnyToJsonSuccessTestParam{AnyMessageData{}, R"({})"},
45 AnyToJsonSuccessTestParam{AnyMessageData{RawAnyData{
"",
""}}, R"({"field1":{}})"},
46 AnyToJsonSuccessTestParam{
47 AnyMessageData{Int32MessageData{}},
49 "field1": {
50 "@type":"type.googleapis.com/proto_json.messages.Int32Message"
51 }
52 })",
57 AnyToJsonSuccessTestParam{
58 AnyMessageData{Int32MessageData{}},
60 "field1": {
61 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
62 "field1":0,"field2":0,"field3":0
63 }
64 })",
65 {.always_print_fields_with_no_presence =
true},
68 AnyToJsonSuccessTestParam{
69 AnyMessageData{Int32MessageData{10, 20, 30}},
71 "field1": {
72 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
73 "field1":10,"field2":20,"field3":30
74 }
75 })"
77 AnyToJsonSuccessTestParam{
78 AnyMessageData{DurationMessageData{123, 987}},
80 "field1": {
81 "@type":"type.googleapis.com/google.protobuf.Duration",
82 "value":"123.000000987s"
83 }
84 })"
86 AnyToJsonSuccessTestParam{
87 AnyMessageData{ValueMessageData{kProtoNullValue}},
89 "field1": {
90 "@type":"type.googleapis.com/google.protobuf.Value",
91 "value":null
92 }
93 })"
95 AnyToJsonSuccessTestParam{
96 AnyMessageData{ValueMessageData{1.5}},
98 "field1": {
99 "@type":"type.googleapis.com/google.protobuf.Value",
100 "value":1.5
101 }
102 })"
104 AnyToJsonSuccessTestParam{
105 AnyMessageData{ValueMessageData{std::vector<
double>{1.5, 1.5}}},
107 "field1": {
108 "@type":"type.googleapis.com/google.protobuf.Value",
109 "value":[1.5,1.5]
110 }
111 })"
113 AnyToJsonSuccessTestParam{
114 AnyMessageData{ValueMessageData{std::map<std::string, std::string>{{
"aaa",
"hello"}}}},
116 "field1": {
117 "@type":"type.googleapis.com/google.protobuf.Value",
118 "value":{"aaa":"hello"}
119 }
120 })"
122 AnyToJsonSuccessTestParam{
123 AnyMessageData{Int32MessageData{100, 200, 300},
true},
125 "field1": {
126 "@type":"type.googleapis.com/google.protobuf.Any",
127 "value": {
128 "@type":"type.googleapis.com/proto_json.messages.Int32Message",
129 "field1":100,"field2":200,"field3":300
130 }
131 }
132 })"
134 AnyToJsonSuccessTestParam{
135 AnyMessageData{DurationMessageData{-123, -567},
true},
137 "field1": {
138 "@type":"type.googleapis.com/google.protobuf.Any",
139 "value": {
140 "@type":"type.googleapis.com/google.protobuf.Duration",
141 "value":"-123.000000567s"
142 }
143 }
144 })"
146 AnyToJsonSuccessTestParam{
147 AnyMessageData{ValueMessageData{
true},
true},
149 "field1": {
150 "@type":"type.googleapis.com/google.protobuf.Any",
151 "value": {
152 "@type":"type.googleapis.com/google.protobuf.Value",
153 "value":true
154 }
155 }
156 })"
159 AnyToJsonSuccessTestParam{
160 AnyMessageData{Int32MessageData{1, 2, 3}},
161 R"({"field1":{"type_url":"type.googleapis.com/proto_json.messages.Int32Message","value":"CAEQBB0DAAAA"}})",
162 {.preserve_proto_field_names =
true, .nonportable_raw_any =
true},
166 AnyToJsonSuccessTestParam{
167 AnyMessageData{Int32MessageData{1, 2, 3}},
168 R"({"field1":{"typeUrl":"type.googleapis.com/proto_json.messages.Int32Message","value":"CAEQBB0DAAAA"}})",
169 {.nonportable_raw_any =
true},
173 AnyToJsonSuccessTestParam{
174 AnyMessageData{RawAnyData{
"type.googleapis.com/proto_json.messages.NonExistent",
"\x08\x01"}},
175 R"({"field1":{"type_url":"type.googleapis.com/proto_json.messages.NonExistent","value":"CAE="}})",
176 {.preserve_proto_field_names =
true, .nonportable_raw_any =
true},
180 AnyToJsonSuccessTestParam{
181 AnyMessageData{RawAnyData{
"type.googleapis.com/proto_json.messages.NonExistent",
"\x08\x01"}},
182 R"({"field1":{"typeUrl":"type.googleapis.com/proto_json.messages.NonExistent","value":"CAE="}})",
183 {.nonportable_raw_any =
true},
187 AnyToJsonSuccessTestParam{
188 AnyMessageData{RawAnyData{
"",
""}},
189 R"({"field1":{"type_url":"","value":""}})",
190 {.preserve_proto_field_names =
true, .nonportable_raw_any =
true},
194 AnyToJsonSuccessTestParam{
195 AnyMessageData{RawAnyData{
"",
""}},
196 R"({"field1":{"typeUrl":"","value":""}})",
197 {.nonportable_raw_any =
true},
204INSTANTIATE_TEST_SUITE_P(
206 AnyToJsonFailureTest,
208 AnyToJsonFailureTestParam{AnyMessageData{RawAnyData{
"",
"\x08\x01"}}, PrintErrorCode::kInvalidValue,
"field1"},
209 AnyToJsonFailureTestParam{
210 AnyMessageData{RawAnyData{
"oops",
""}},
211 PrintErrorCode::kInvalidValue,
217 AnyToJsonFailureTestParam{
218 AnyMessageData{RawAnyData{
"type.googleapis.com/proto_json.messages.NonExistent",
"\x08\x01"}},
219 PrintErrorCode::kInvalidValue,
223 AnyToJsonFailureTestParam{
224 AnyMessageData{RawAnyData{
"type.googleapis.com/proto_json.messages.Int32Message",
"\x80"}},
225 PrintErrorCode::kInvalidValue,
231 AnyToJsonFailureTestParam{AnyMessageData{DurationMessageData{-1, 1}}, PrintErrorCode::kInvalidValue,
"field1"},
232 AnyToJsonFailureTestParam{
233 AnyMessageData{ValueMessageData{std::vector<
double>{std::numeric_limits<
double>::infinity()}}},
234 PrintErrorCode::kInvalidValue,
235 "field1.list_value.values[0].number_value",
243TEST_P(AnyToJsonSuccessTest, Test) {
244 const auto& param = GetParam();
246 auto input = PrepareTestData(param.input);
252 UASSERT_NO_THROW((expected_json = PrepareJsonTestData(param.expected_json)));
254 if (!param.skip_native_check) {
256 EXPECT_EQ(json, sample_json);
259 EXPECT_EQ(json, expected_json);
262TEST_P(AnyToJsonFailureTest, Test) {
263 const auto& param = GetParam();
264 auto input = PrepareTestData(param.input);
266 EXPECT_PRINT_ERROR((
void)MessageToJson(input, param.options), param.expected_errc, param.expected_path);
268 if (!param.skip_native_check) {
273TEST(AnyToJsonAdditionalTest, InlinedNonNull) {
275 auto message = PrepareTestData(data);
282 EXPECT_EQ(json, sample);
283 EXPECT_EQ(json
["@type"].As<std::string>(),
"type.googleapis.com/proto_json.messages.Int32Message");
284 EXPECT_EQ(json
["field1"].As<std::int32_t>(), 1);
285 EXPECT_EQ(json
["field2"].As<std::int32_t>(), 2);
286 EXPECT_EQ(json
["field3"].As<std::int32_t>(), 3);
289TEST(AnyToJsonAdditionalTest, InlinedNull) {
290 proto_json::messages::AnyMessage message;
297 EXPECT_EQ(json, sample);
301TEST(AnyToJsonAdditionalTest, DynamicMessage) {
302 using Message = ::google::protobuf::Any;
303 using ProtobufStringType =
304 decltype(std::declval<::google::protobuf::Reflection>()
305 .GetString(std::declval<
const ::google::protobuf::Message&>(),
nullptr));
307 ::google::protobuf::DynamicMessageFactory factory;
308 proto_json::messages::Int32Message payload;
309 ProtobufStringType payload_str;
311 payload.set_field1(1001);
312 payload.set_field2(1002);
313 payload.set_field3(1003);
315 ASSERT_TRUE(payload.SerializeToString(&payload_str));
316 proto_json::messages::Int32Message payload2;
319 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
320 const auto reflection = message->GetReflection();
321 const auto type_url_desc = message->GetDescriptor()->FindFieldByName(
"type_url");
322 const auto value_desc = message->GetDescriptor()->FindFieldByName(
"value");
324 reflection->SetString(message.get(), type_url_desc,
"type.googleapis.com/proto_json.messages.Int32Message");
325 reflection->SetString(message.get(), value_desc, payload_str);
332 EXPECT_EQ(json
["@type"].As<std::string>(),
"type.googleapis.com/proto_json.messages.Int32Message");
333 EXPECT_EQ(json
["field1"].As<std::int32_t>(), 1001);
334 EXPECT_EQ(json
["field2"].As<std::int32_t>(), 1002);
335 EXPECT_EQ(json
["field3"].As<std::int32_t>(), 1003);