userver: /data/code/userver/libraries/protobuf/tests/json/wrapper_from_json_test.cpp Source File
Loading...
Searching...
No Matches
wrapper_from_json_test.cpp
1#include <gtest/gtest.h>
2
3#include <memory>
4#include <ostream>
5#include <stdexcept>
6#include <string>
7
8#include <fmt/format.h>
9#include <google/protobuf/dynamic_message.h>
10
11#include <userver/formats/json/serialize.hpp>
12#include <userver/protobuf/json/convert.hpp>
13#include <userver/utest/assert_macros.hpp>
14
15#include "utils.hpp"
16
17USERVER_NAMESPACE_BEGIN
18
19namespace protobuf::json::tests {
20
21template <typename T>
22T GetWrappedValue(const ::google::protobuf::Message& msg) {
23 const auto reflection = msg.GetReflection();
24 const auto value_desc = msg.GetDescriptor()->FindFieldByName("value");
25
26 if (!value_desc) {
27 throw std::runtime_error("Field 'value' is not found in wrapper message");
28 }
29
30 if constexpr (std::is_same_v<T, double>) {
31 return reflection->GetDouble(msg, value_desc);
32 } else if constexpr (std::is_same_v<T, float>) {
33 return reflection->GetFloat(msg, value_desc);
34 } else if constexpr (std::is_same_v<T, std::int64_t>) {
35 return reflection->GetInt64(msg, value_desc);
36 } else if constexpr (std::is_same_v<T, std::uint64_t>) {
37 return reflection->GetUInt64(msg, value_desc);
38 } else if constexpr (std::is_same_v<T, std::int32_t>) {
39 return reflection->GetInt32(msg, value_desc);
40 } else if constexpr (std::is_same_v<T, std::uint32_t>) {
41 return reflection->GetUInt32(msg, value_desc);
42 } else if constexpr (std::is_same_v<T, bool>) {
43 return reflection->GetBool(msg, value_desc);
44 } else if constexpr (std::is_same_v<T, std::string>) {
45 return reflection->GetString(msg, value_desc);
46 } else {
47 static_assert(sizeof(T) && false, "unexpected type");
48 }
49}
50
51struct WrapperFromJsonSuccessTestParam {
52 std::string input = {};
53 WrapperMessageData expected_message = {};
54 ParseOptions options = {};
55};
56
57struct WrapperFromJsonFailureTestParam {
58 std::string input = {};
59 ParseErrorCode expected_errc = {};
60 std::string expected_path = {};
61 ParseOptions options = {};
62
63 // Protobuf ProtoJSON legacy syntax supports out-of-spec object value for wrappers, which we
64 // want to prohibit (because we do not want our clients to use syntax that may break in the
65 // newer protobuf versions). This variable is used disable some checks that will fail for
66 // legacy syntax.
67 bool skip_native_check = false;
68};
69
70void PrintTo(const WrapperFromJsonFailureTestParam& param, std::ostream* os) {
71 *os << fmt::format("{{ input = '{}' }}", param.input);
72}
73
74class WrapperFromJsonSuccessTest : public ::testing::TestWithParam<WrapperFromJsonSuccessTestParam> {};
75class WrapperFromJsonFailureTest : public ::testing::TestWithParam<WrapperFromJsonFailureTestParam> {};
76
77INSTANTIATE_TEST_SUITE_P(
78 ,
79 WrapperFromJsonSuccessTest,
80 ::testing::Values(
81 WrapperFromJsonSuccessTestParam{R"({})", WrapperMessageData{}},
82 WrapperFromJsonSuccessTestParam{
83 R"({
84 "field1":null,
85 "field2":null,
86 "field3":null,
87 "field4":null,
88 "field5":null,
89 "field6":null,
90 "field7":null,
91 "field8":null,
92 "field9":null
93 })",
94 WrapperMessageData{},
95 },
96 WrapperFromJsonSuccessTestParam{
97 R"({
98 "field1":0,
99 "field2":0,
100 "field3":"0",
101 "field4":"0",
102 "field5":0,
103 "field6":0,
104 "field7":false,
105 "field8":"",
106 "field9":""
107 })",
108 WrapperMessageData{0, 0, 0, 0, 0, 0, false, "", ""},
109 },
110 WrapperFromJsonSuccessTestParam{
111 // using 1.5 for doubles because it is represented without precision loss in IEEE 754
112 R"({
113 "field1":1.5,
114 "field2":1.5,
115 "field3":"-123",
116 "field4":"123",
117 "field5":-321,
118 "field6":321,
119 "field7":true,
120 "field8":"hello",
121 "field9":"d29ybGQ="
122 })",
123 WrapperMessageData{1.5, 1.5, -123, 123, -321, 321, true, "hello", "world"},
124 },
125 WrapperFromJsonSuccessTestParam{
126 // using 1.5 for doubles because it is represented without precision loss in IEEE 754
127 R"({
128 "field3":"-123",
129 "field5":-321,
130 "field7":true,
131 "field9":"d29ybGQ="
132 })",
133 WrapperMessageData{
134 std::nullopt,
135 std::nullopt,
136 -123,
137 std::nullopt,
138 -321,
139 std::nullopt,
140 true,
141 std::nullopt,
142 "world"
143 }
144 }
145 )
146);
147
148INSTANTIATE_TEST_SUITE_P(
149 ,
150 WrapperFromJsonFailureTest,
151 ::testing::Values(
152 WrapperFromJsonFailureTestParam{R"({"field1":[]})", ParseErrorCode::kInvalidType, "field1"},
153 WrapperFromJsonFailureTestParam{R"({"field2":{}})", ParseErrorCode::kInvalidType, "field2", {}, true},
154 WrapperFromJsonFailureTestParam{R"({"field3":true})", ParseErrorCode::kInvalidType, "field3"},
155 WrapperFromJsonFailureTestParam{R"({"field4":"hello"})", ParseErrorCode::kInvalidValue, "field4"},
156 WrapperFromJsonFailureTestParam{R"({"field5":1.5})", ParseErrorCode::kInvalidValue, "field5"}
157 )
158);
159
160TEST_P(WrapperFromJsonSuccessTest, Test) {
161 const auto& param = GetParam();
162
163 proto_json::messages::WrapperMessage message;
164 proto_json::messages::WrapperMessage expected_message;
165 proto_json::messages::WrapperMessage sample_message;
166 formats::json::Value input = PrepareJsonTestData(param.input);
167 expected_message = PrepareTestData(param.expected_message);
168
169 message.mutable_field1()->set_value(100001);
170 message.mutable_field2()->set_value(200002);
171 message.mutable_field3()->set_value(300003);
172 message.mutable_field4()->set_value(400004);
173 message.mutable_field5()->set_value(500005);
174 message.mutable_field6()->set_value(600006);
175 message.mutable_field7()->set_value(true);
176 message.mutable_field8()->set_value("dump1");
177 message.mutable_field9()->set_value("dump2");
178
179 UASSERT_NO_THROW((message = JsonToMessage<proto_json::messages::WrapperMessage>(input, param.options)));
180 UASSERT_NO_THROW(InitSampleMessage(param.input, sample_message, param.options));
181
182 CheckMessageEqual(message, sample_message);
183 CheckMessageEqual(message, expected_message);
184}
185
186TEST_P(WrapperFromJsonFailureTest, Test) {
187 const auto& param = GetParam();
188
189 proto_json::messages::WrapperMessage sample_message;
190 formats::json::Value input = PrepareJsonTestData(param.input);
191
193 (void)JsonToMessage<proto_json::messages::WrapperMessage>(input, param.options),
194 param.expected_errc,
195 param.expected_path
196 );
197
198 if (!param.skip_native_check) {
199 UEXPECT_THROW(InitSampleMessage(param.input, sample_message, param.options), SampleError);
200 }
201}
202
203template <typename T>
204class WrapperFromJsonAdditionalTest : public ::testing::Test {
205public:
206 using Param = T;
207};
208
209using WrapperTypes = ::testing::Types<
210 DoubleValue,
211 FloatValue,
212 Int64Value,
213 UInt64Value,
214 Int32Value,
215 UInt32Value,
216 BoolValue,
217 StringValue,
218 BytesValue>;
219
220TYPED_TEST_SUITE(WrapperFromJsonAdditionalTest, WrapperTypes);
221
222TYPED_TEST(WrapperFromJsonAdditionalTest, InlinedNonNull) {
223 using Param = typename TestFixture::Param;
224 using Message = typename Param::Message;
225
226 const auto json = formats::json::FromString(Param::kJson);
227 Message message;
228 Message sample;
229
230 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
231 UASSERT_NO_THROW(InitSampleMessage(Param::kJson, sample));
232 EXPECT_EQ(message.value(), sample.value());
233 EXPECT_EQ(message.value(), Param::kValue);
234}
235
236TYPED_TEST(WrapperFromJsonAdditionalTest, InlinedNull) {
237 using Param = typename TestFixture::Param;
238 using Message = typename Param::Message;
239 using Value = std::remove_const_t<decltype(Param::kValue)>;
240
241 const auto json = formats::json::FromString("null");
242 Message message;
243 Message sample;
244
245 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
246 UASSERT_NO_THROW(InitSampleMessage("null", sample));
247 EXPECT_EQ(message.value(), sample.value());
248 EXPECT_EQ(message.value(), Value{});
249}
250
251TYPED_TEST(WrapperFromJsonAdditionalTest, DynamicMessage) {
252 using Param = typename TestFixture::Param;
253 using Message = typename Param::Message;
254
255 const auto json = formats::json::FromString(Param::kJson);
256 ::google::protobuf::DynamicMessageFactory factory;
257
258 {
259 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
260
261 UASSERT_NO_THROW(JsonToMessage(json, *message));
262 EXPECT_EQ(GetWrappedValue<WrappedType<Param>>(*message), Param::kValue);
263 }
264}
265
266} // namespace protobuf::json::tests
267
268USERVER_NAMESPACE_END