userver: /data/code/userver/libraries/protobuf/tests/json/timestamp_from_json_test.cpp Source File
Loading...
Searching...
No Matches
timestamp_from_json_test.cpp
1#include <gtest/gtest.h>
2
3#include <chrono>
4#include <memory>
5#include <ostream>
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/datetime.hpp>
13#include <userver/protobuf/json/convert.hpp>
14#include <userver/utest/assert_macros.hpp>
15
16#include "utils.hpp"
17
18USERVER_NAMESPACE_BEGIN
19
20namespace protobuf::json::tests {
21
22using namespace std::chrono_literals;
23
24constexpr auto kTestSeconds = 1s + 1min + 1h + 24h + (31 * 24h) + (365 * 24h);
25
26struct TimestampFromJsonSuccessTestParam {
27 std::string input = {};
28 TimestampMessageData expected_message = {};
29 ParseOptions options = {};
30};
31
32struct TimestampFromJsonFailureTestParam {
33 std::string input = {};
34 ParseErrorCode expected_errc = {};
35 std::string expected_path = {};
36 ParseOptions options = {};
37
38 // Protobuf ProtoJSON legacy syntax supports some features, which we want to prohibit (because
39 // we do not want our clients to use syntax that may break in the newer protobuf versions).
40 // This variable is used disable some checks that will fail for legacy syntax.
41 bool skip_native_check = false;
42};
43
44void PrintTo(const TimestampFromJsonSuccessTestParam& param, std::ostream* os) {
45 *os << fmt::format("{{ input = '{}' }}", param.input);
46}
47
48void PrintTo(const TimestampFromJsonFailureTestParam& param, std::ostream* os) {
49 *os << fmt::format("{{ input = '{}' }}", param.input);
50}
51
52class TimestampFromJsonSuccessTest : public ::testing::TestWithParam<TimestampFromJsonSuccessTestParam> {};
53class TimestampFromJsonFailureTest : public ::testing::TestWithParam<TimestampFromJsonFailureTestParam> {};
54
55INSTANTIATE_TEST_SUITE_P(
56 ,
57 TimestampFromJsonSuccessTest,
58 ::testing::Values(
59 TimestampFromJsonSuccessTestParam{R"({})", TimestampMessageData{0, 0, true}},
60 TimestampFromJsonSuccessTestParam{R"({"field1":null})", TimestampMessageData{0, 0, true}},
61 TimestampFromJsonSuccessTestParam{R"({"field1":"1970-01-01T00:00:00Z"})", TimestampMessageData{0, 0}},
62 TimestampFromJsonSuccessTestParam{
63 R"({"field1":"1971-02-02T01:01:01.123456789Z"})",
64 TimestampMessageData{kTestSeconds.count(), 123456789}
65 },
66 TimestampFromJsonSuccessTestParam{
67 R"({"field1":"1968-11-29T22:58:59.987654321Z"})",
68 TimestampMessageData{-kTestSeconds.count(), 987654321}
69 },
70 TimestampFromJsonSuccessTestParam{
71 R"({"field1":"9999-12-31T23:59:59.999999999Z"})",
72 TimestampMessageData{kMaxTimestampSeconds, kMaxTimestampNanos}
73 },
74 TimestampFromJsonSuccessTestParam{
75 R"({"field1":"0001-01-01T00:00:00Z"})",
76 TimestampMessageData{kMinTimestampSeconds, kMinTimestampNanos}
77 },
78 TimestampFromJsonSuccessTestParam{
79 R"({"field1":"1970-01-01T00:00:00.123456780Z"})",
80 TimestampMessageData{0, 123456780}
81 },
82 TimestampFromJsonSuccessTestParam{
83 R"({"field1":"1970-01-01T00:00:00.123456Z"})",
84 TimestampMessageData{0, 123456000}
85 },
86 TimestampFromJsonSuccessTestParam{
87 R"({"field1":"1970-01-01T00:00:00.123400Z"})",
88 TimestampMessageData{0, 123400000}
89 },
90 TimestampFromJsonSuccessTestParam{
91 R"({"field1":"1970-01-01T00:00:00.100Z"})",
92 TimestampMessageData{0, 100000000}
93 },
94 TimestampFromJsonSuccessTestParam{R"({"field1":"1970-01-01T00:00:00.000000009Z"})", TimestampMessageData{0, 9}},
95 TimestampFromJsonSuccessTestParam{
96 R"({"field1":"1970-01-01T00:01:13.123+02:00"})",
97 TimestampMessageData{-(2 * 60 * 60) + 60 + 13, 123000000}
98 },
99 TimestampFromJsonSuccessTestParam{
100 R"({"field1":"1970-01-01T00:01:13.123-02:00"})",
101 TimestampMessageData{(2 * 60 * 60) + 60 + 13, 123000000}
102 },
103 TimestampFromJsonSuccessTestParam{
104 R"({"field1":"1970-01-01T00:01:13.123+02:10"})",
105 TimestampMessageData{-(2 * 60 * 60 + 10 * 60) + 60 + 13, 123000000}
106 },
107 TimestampFromJsonSuccessTestParam{
108 R"({"field1":"1970-01-01T00:01:13.123-02:10"})",
109 TimestampMessageData{(2 * 60 * 60 + 10 * 60) + 60 + 13, 123000000}
110 },
111 TimestampFromJsonSuccessTestParam{
112 R"({"field1":"1970-01-01T00:01:13.123+00:00"})",
113 TimestampMessageData{60 + 13, 123000000}
114 },
115 TimestampFromJsonSuccessTestParam{
116 R"({"field1":"1970-01-01T00:01:13.123-00:00"})",
117 TimestampMessageData{60 + 13, 123000000}
118 }
119 )
120);
121
122INSTANTIATE_TEST_SUITE_P(
123 ,
124 TimestampFromJsonFailureTest,
125 ::testing::Values(
126 TimestampFromJsonFailureTestParam{R"({"field1":[]})", ParseErrorCode::kInvalidType, "field1"},
127 TimestampFromJsonFailureTestParam{R"({"field1":{}})", ParseErrorCode::kInvalidType, "field1", {}, true},
128 TimestampFromJsonFailureTestParam{R"({"field1":true})", ParseErrorCode::kInvalidType, "field1"},
129 TimestampFromJsonFailureTestParam{R"({"field1":10})", ParseErrorCode::kInvalidType, "field1"},
130 TimestampFromJsonFailureTestParam{R"({"field1":""})", ParseErrorCode::kInvalidValue, "field1"},
131 TimestampFromJsonFailureTestParam{R"({"field1":"abc"})", ParseErrorCode::kInvalidValue, "field1"},
132 TimestampFromJsonFailureTestParam{R"({"field1":"-"})", ParseErrorCode::kInvalidValue, "field1"},
133 TimestampFromJsonFailureTestParam{R"({"field1":"2010"})", ParseErrorCode::kInvalidValue, "field1"},
134 TimestampFromJsonFailureTestParam{R"({"field1":"2010-"})", ParseErrorCode::kInvalidValue, "field1"},
135 TimestampFromJsonFailureTestParam{R"({"field1":"2010-10"})", ParseErrorCode::kInvalidValue, "field1"},
136 TimestampFromJsonFailureTestParam{R"({"field1":"2010-10-"})", ParseErrorCode::kInvalidValue, "field1"},
137 TimestampFromJsonFailureTestParam{R"({"field1":"2010-10-10"})", ParseErrorCode::kInvalidValue, "field1"},
138 TimestampFromJsonFailureTestParam{R"({"field1":"2010-10-10T"})", ParseErrorCode::kInvalidValue, "field1"},
139 TimestampFromJsonFailureTestParam{R"({"field1":"2010-10-10T10"})", ParseErrorCode::kInvalidValue, "field1"},
140 TimestampFromJsonFailureTestParam{R"({"field1":"2010-10-10T10:10"})", ParseErrorCode::kInvalidValue, "field1"},
141 TimestampFromJsonFailureTestParam{
142 R"({"field1":"2010-10-10T10:10:10"})",
144 "field1"
145 },
146 TimestampFromJsonFailureTestParam{
147 R"({"field1":"2010-10-10T10:10:10z"})",
149 "field1"
150 },
151 TimestampFromJsonFailureTestParam{
152 R"({"field1":"2010-10-10T10:10:10ZZ"})",
154 "field1"
155 },
156 TimestampFromJsonFailureTestParam{
157 R"({"field1":"2010-10-10T10:10:10+"})",
159 "field1"
160 },
161 TimestampFromJsonFailureTestParam{
162 R"({"field1":"2010-10-10T10:10:10+02"})",
164 "field1"
165 },
166 TimestampFromJsonFailureTestParam{
167 R"({"field1":"2010-10-10T10:10:10-02:"})",
169 "field1"
170 },
171 TimestampFromJsonFailureTestParam{
172 R"({"field1":"2010-10-10T10:10:10+02:00Z"})",
174 "field1"
175 },
176 TimestampFromJsonFailureTestParam{
177 R"({"field1":"2010-10-10T10:10:10+02:0"})",
179 "field1",
180 {},
181 !kIsModernProtoJson
182 },
183 TimestampFromJsonFailureTestParam{
184 R"({"field1":"2010-10-10T10:10:10+2:00"})",
186 "field1",
187 {},
188 !kIsModernProtoJson
189 },
190 TimestampFromJsonFailureTestParam{
191 R"({"field1":"2010-10-10T10:10:10Z+02:00"})",
193 "field1"
194 },
195 TimestampFromJsonFailureTestParam{
196 R"({"field1":"2010-10-10T10:10:1Z"})",
198 "field1",
199 {},
200 !kIsModernProtoJson
201 },
202 TimestampFromJsonFailureTestParam{
203 R"({"field1":"2010-10-10T10:1:10Z"})",
205 "field1",
206 {},
207 !kIsModernProtoJson
208 },
209 TimestampFromJsonFailureTestParam{
210 R"({"field1":"2010-10-10T1:10:10Z"})",
212 "field1",
213 {},
214 !kIsModernProtoJson
215 },
216 TimestampFromJsonFailureTestParam{
217 R"({"field1":"2010-10-10T1:10:10Z"})",
219 "field1",
220 {},
221 !kIsModernProtoJson
222 },
223 TimestampFromJsonFailureTestParam{
224 R"({"field1":"2010-10-10t10:10:10Z"})",
226 "field1"
227 },
228 TimestampFromJsonFailureTestParam{
229 R"({"field1":"2010-10-10TT10:10:10Z"})",
231 "field1"
232 },
233 TimestampFromJsonFailureTestParam{
234 R"({"field1":"2010-10-1T10:10:10Z"})",
236 "field1",
237 {},
238 !kIsModernProtoJson
239 },
240 TimestampFromJsonFailureTestParam{
241 R"({"field1":"2010-1-10T10:10:10Z"})",
243 "field1",
244 {},
245 !kIsModernProtoJson
246 },
247 TimestampFromJsonFailureTestParam{
248 R"({"field1":"201-10-10T10:10:10Z"})",
250 "field1",
251 {},
252 !kIsModernProtoJson
253 },
254 TimestampFromJsonFailureTestParam{
255 R"({"field1":"201A-10-10T10:10:10Z"})",
257 "field1"
258 },
259 TimestampFromJsonFailureTestParam{
260 R"({"field1":"2010--10-10T10:10:10Z"})",
262 "field1"
263 },
264 TimestampFromJsonFailureTestParam{
265 R"({"field1":"2010-1A-10T10:10:10Z"})",
267 "field1"
268 },
269 TimestampFromJsonFailureTestParam{
270 R"({"field1":"2010-10--10T10:10:10Z"})",
272 "field1"
273 },
274 TimestampFromJsonFailureTestParam{
275 R"({"field1":"2010-10-1AT10:10:10Z"})",
277 "field1"
278 },
279 TimestampFromJsonFailureTestParam{
280 R"({"field1":"2010-10-10T1A:10:10Z"})",
282 "field1"
283 },
284 TimestampFromJsonFailureTestParam{
285 R"({"field1":"2010-10-10T10-10:10Z"})",
287 "field1"
288 },
289 TimestampFromJsonFailureTestParam{
290 R"({"field1":"2010-10-10T10:1A:10Z"})",
292 "field1"
293 },
294 TimestampFromJsonFailureTestParam{
295 R"({"field1":"2010-10-10T10:10::10Z"})",
297 "field1"
298 },
299 TimestampFromJsonFailureTestParam{
300 R"({"field1":"2010-10-10T10:10:1AZ"})",
302 "field1"
303 },
304 TimestampFromJsonFailureTestParam{
305 R"({"field1":"2010-10-10T10:10:10+-02:00"})",
307 "field1"
308 }
309 )
310);
311
312TEST_P(TimestampFromJsonSuccessTest, Test) {
313 const auto& param = GetParam();
314
315 proto_json::messages::TimestampMessage message;
316 proto_json::messages::TimestampMessage expected_message;
317 proto_json::messages::TimestampMessage sample_message;
318 formats::json::Value input = PrepareJsonTestData(param.input);
319 expected_message = PrepareTestData(param.expected_message);
320
321 message.mutable_field1()->set_seconds(100001);
322
323 UASSERT_NO_THROW((message = JsonToMessage<proto_json::messages::TimestampMessage>(input, param.options)));
324 UASSERT_NO_THROW(InitSampleMessage(param.input, sample_message, param.options));
325
326 CheckMessageEqual(message, sample_message);
327 CheckMessageEqual(message, expected_message);
328}
329
330TEST_P(TimestampFromJsonFailureTest, Test) {
331 const auto& param = GetParam();
332
333 proto_json::messages::TimestampMessage sample_message;
334 formats::json::Value input = PrepareJsonTestData(param.input);
335
337 (void)JsonToMessage<proto_json::messages::TimestampMessage>(input, param.options),
338 param.expected_errc,
339 param.expected_path
340 );
341
342 if (!param.skip_native_check) {
343 UEXPECT_THROW(InitSampleMessage(param.input, sample_message, param.options), SampleError);
344 }
345}
346
347TEST(TimestampFromJsonAdditionalTest, InlinedNonNull) {
348 using Message = ::google::protobuf::Timestamp;
349
350 const char* json_str = "\"1970-01-01T00:02:03.000000321Z\"";
351 const auto json = formats::json::FromString(json_str);
352 Message message;
353 Message sample;
354
355 message.set_seconds(100001);
356
357 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
358 UASSERT_NO_THROW(InitSampleMessage(json_str, sample));
359
360 EXPECT_EQ(message.seconds(), 123);
361 EXPECT_EQ(message.nanos(), 321);
362 CheckMessageEqual(message, sample);
363}
364
365TEST(TimestampFromJsonAdditionalTest, InlinedNull) {
366 using Message = ::google::protobuf::Timestamp;
367
368 const auto json = formats::json::FromString("null");
369 Message message;
370 Message sample;
371
372 message.set_seconds(100001);
373
374 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
375 UASSERT_NO_THROW(InitSampleMessage("null", sample));
376
377 EXPECT_EQ(message.seconds(), 0);
378 EXPECT_EQ(message.nanos(), 0);
379 CheckMessageEqual(message, sample);
380}
381
382TEST(TimestampFromJsonAdditionalTest, DynamicMessage) {
383 using Message = ::google::protobuf::Timestamp;
384
385 const char* json_str = "\"2016-02-29T00:00:00.123450Z\"";
386 const auto json = formats::json::FromString(json_str);
387 ::google::protobuf::DynamicMessageFactory factory;
388
389 {
390 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
391
392 UASSERT_NO_THROW(JsonToMessage(json, *message));
393
394 const auto reflection = message->GetReflection();
395 const auto seconds_desc = message->GetDescriptor()->FindFieldByName("seconds");
396 const auto nanos_desc = message->GetDescriptor()->FindFieldByName("nanos");
397
398 EXPECT_EQ(reflection->GetInt64(*message, seconds_desc), 1456704000);
399 EXPECT_EQ(reflection->GetInt32(*message, nanos_desc), 123450000);
400 }
401}
402
403} // namespace protobuf::json::tests
404
405USERVER_NAMESPACE_END