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 ReadOptions options = {};
55};
56
57struct WrapperFromJsonFailureTestParam {
58 std::string input = {};
59 ReadErrorCode expected_errc = {};
60 std::string expected_path = {};
61 ReadOptions 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":[]})", ReadErrorCode::kInvalidType, "field1"},
153 WrapperFromJsonFailureTestParam{R"({"field2":{}})", ReadErrorCode::kInvalidType, "field2", {}, true},
154 WrapperFromJsonFailureTestParam{R"({"field3":true})", ReadErrorCode::kInvalidType, "field3"},
155 WrapperFromJsonFailureTestParam{R"({"field4":"hello"})", ReadErrorCode::kInvalidValue, "field4"},
156 WrapperFromJsonFailureTestParam{R"({"field5":1.5})", ReadErrorCode::kInvalidValue, "field5"}
157 )
158);
159
160TEST_P(WrapperFromJsonSuccessTest, Test) {
161 const auto& param = GetParam();
162
163 proto_json::messages::WrapperMessage message, expected_message, sample_message;
164 formats::json::Value input = PrepareJsonTestData(param.input);
165 expected_message = PrepareTestData(param.expected_message);
166
167 message.mutable_field1()->set_value(100001);
168 message.mutable_field2()->set_value(200002);
169 message.mutable_field3()->set_value(300003);
170 message.mutable_field4()->set_value(400004);
171 message.mutable_field5()->set_value(500005);
172 message.mutable_field6()->set_value(600006);
173 message.mutable_field7()->set_value(true);
174 message.mutable_field8()->set_value("dump1");
175 message.mutable_field9()->set_value("dump2");
176
177 UASSERT_NO_THROW((message = JsonToMessage<proto_json::messages::WrapperMessage>(input, param.options)));
178 UASSERT_NO_THROW(InitSampleMessage(param.input, param.options, sample_message));
179
180 CheckMessageEqual(message, sample_message);
181 CheckMessageEqual(message, expected_message);
182}
183
184TEST_P(WrapperFromJsonFailureTest, Test) {
185 const auto& param = GetParam();
186
187 proto_json::messages::WrapperMessage sample_message;
188 formats::json::Value input = PrepareJsonTestData(param.input);
189
190 EXPECT_READ_ERROR(
191 (void)JsonToMessage<proto_json::messages::WrapperMessage>(input, param.options),
192 param.expected_errc,
193 param.expected_path
194 );
195
196 if (!param.skip_native_check) {
197 UEXPECT_THROW(InitSampleMessage(param.input, param.options, sample_message), SampleError);
198 }
199}
200
201template <typename T>
202class WrapperFromJsonAdditionalTest : public ::testing::Test {
203public:
204 using Param = T;
205};
206
207using WrapperTypes = ::testing::Types<
208 DoubleValue,
209 FloatValue,
210 Int64Value,
211 UInt64Value,
212 Int32Value,
213 UInt32Value,
214 BoolValue,
215 StringValue,
216 BytesValue>;
217
218TYPED_TEST_SUITE(WrapperFromJsonAdditionalTest, WrapperTypes);
219
220TYPED_TEST(WrapperFromJsonAdditionalTest, InlinedNonNull) {
221 using Param = typename TestFixture::Param;
222 using Message = typename Param::Message;
223
224 const auto json = formats::json::FromString(Param::kJson);
225 Message message, sample;
226
227 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
228 UASSERT_NO_THROW(InitSampleMessage(Param::kJson, {}, sample));
229 EXPECT_EQ(message.value(), sample.value());
230 EXPECT_EQ(message.value(), Param::kValue);
231}
232
233TYPED_TEST(WrapperFromJsonAdditionalTest, InlinedNull) {
234 using Param = typename TestFixture::Param;
235 using Message = typename Param::Message;
236 using Value = std::remove_const_t<decltype(Param::kValue)>;
237
238 const auto json = formats::json::FromString("null");
239 Message message, sample;
240
241 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
242 UASSERT_NO_THROW(InitSampleMessage("null", {}, sample));
243 EXPECT_EQ(message.value(), sample.value());
244 EXPECT_EQ(message.value(), Value{});
245}
246
247TYPED_TEST(WrapperFromJsonAdditionalTest, DynamicMessage) {
248 using Param = typename TestFixture::Param;
249 using Message = typename Param::Message;
250
251 const auto json = formats::json::FromString(Param::kJson);
252 ::google::protobuf::DynamicMessageFactory factory;
253
254 {
255 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
256
257 UASSERT_NO_THROW(JsonToMessage(json, {}, *message));
258 EXPECT_EQ(GetWrappedValue<WrappedType<Param>>(*message), Param::kValue);
259 }
260}
261
262} // namespace protobuf::json::tests
263
264USERVER_NAMESPACE_END