userver: /data/code/userver/libraries/proto-structs/tests/struct_to_message_test.cpp Source File
Loading...
Searching...
No Matches
struct_to_message_test.cpp
1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
3
4#include <limits>
5
6#include <google/protobuf/util/time_util.h>
7
8#include <userver/proto-structs/convert.hpp>
9
10#include "messages.pb.h"
11#include "structs.hpp"
12
13USERVER_NAMESPACE_BEGIN
14
15namespace proto_structs::tests {
16
17TEST(StructToMessage, Empty) {
18 structs::Empty obj;
19 messages::Empty msg;
20
21 ASSERT_NO_THROW(StructToMessage(obj, msg));
22 ASSERT_NO_THROW((msg = StructToMessage(obj)));
23 ASSERT_NO_THROW((msg = StructToMessage(structs::Empty{obj})));
24}
25
26TEST(StructToMessage, Scalar) {
27 {
28 structs::Scalar obj{
29 .f1 = true,
30 .f2 = std::numeric_limits<int32_t>::min(),
31 .f3 = std::numeric_limits<uint32_t>::max(),
32 .f4 = std::numeric_limits<int64_t>::min(),
33 .f5 = std::numeric_limits<uint64_t>::max(),
34 .f6 = static_cast<float>(1.5),
35 .f7 = -2.5,
36 .f8 = "test1",
37 .f9 = "test2",
38 .f10 = structs::TestEnum::kValue1,
39 .f11 = 987
40 };
41
42 auto msg = StructToMessage(obj);
43 CheckScalarEqual(obj, msg);
44
45 msg = StructToMessage(structs::Scalar{obj});
46 CheckScalarEqual(obj, msg);
47 }
48
49 {
50 structs::Scalar obj;
51 messages::Scalar msg;
52
53 obj.f2 = 1001;
54 msg.set_f2(1);
55 msg.set_f3(2);
56 msg.set_f8("test");
57
58 ASSERT_NO_THROW(StructToMessage(obj, msg));
59 CheckScalarEqual(obj, msg);
60
61 obj.f8 = "hello";
62 obj.f9 = "world";
63 obj.f11 = 654;
64
65 ASSERT_NO_THROW(StructToMessage(structs::Scalar{obj}, msg));
66 CheckScalarEqual(obj, msg);
67 }
68
69 if (std::numeric_limits<std::size_t>::max() > std::numeric_limits<std::int32_t>::max()) {
70 structs::Scalar obj;
71 obj.f11 = std::numeric_limits<std::size_t>::max();
72
73 try {
74 auto result = StructToMessage(obj);
75 FAIL() << "exception should be thrown";
76 } catch (const ups::WriteError& e) {
77 EXPECT_THAT(e.what(), ::testing::HasSubstr("'messages.Scalar.f11'"));
78 } catch (...) {
79 FAIL() << "unexpected exception type";
80 }
81 }
82}
83
84TEST(StructToMessage, WellKnownStd) {
85 using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
86 auto create_valid = []() {
87 return structs::WellKnownStd{.f3 = {std::chrono::year{1}, std::chrono::month{1}, std::chrono::day{1}}};
88 };
89
90 {
91 structs::WellKnownStd obj{
92 .f1 = TimePoint{std::chrono::milliseconds{123'456'789}},
93 .f2 = std::chrono::milliseconds{987'654'321},
94 .f3 = {std::chrono::year{2025}, std::chrono::month{8}, std::chrono::day{27}},
95 .f4 =
96 std::chrono::hh_mm_ss<std::chrono::microseconds>{
97 std::chrono::minutes(65) + std::chrono::seconds{13} + std::chrono::microseconds{123'456}
98 }
99 };
100
101 auto msg = StructToMessage(obj);
102
103 CheckWellKnownStdEqual(obj, msg);
104
105 msg = StructToMessage(structs::WellKnownStd{obj});
106
107 CheckWellKnownStdEqual(obj, msg);
108 }
109
110 {
111 structs::WellKnownStd obj = create_valid();
112 messages::WellKnownStd msg;
113
114 obj.f1 = TimePoint{std::chrono::milliseconds{-987'654'321}};
115 *msg.mutable_f1() = ::google::protobuf::util::TimeUtil::NanosecondsToTimestamp(123'456'789'987'654'321LL);
116 *msg.mutable_f2() = ::google::protobuf::util::TimeUtil::NanosecondsToDuration(1001);
117
118 ASSERT_NO_THROW(StructToMessage(obj, msg));
119 CheckWellKnownStdEqual(obj, msg);
120
121 obj.f2 = std::chrono::milliseconds{-987'654'321};
122 *msg.mutable_f2() = ::google::protobuf::util::TimeUtil::NanosecondsToDuration(123'456'789'987'654'321LL);
123
124 ASSERT_NO_THROW(StructToMessage(structs::WellKnownStd{obj}, msg));
125 CheckWellKnownStdEqual(obj, msg);
126 }
127
128 constexpr auto kMaxSecondsInStdTimePoint =
129 (TimePoint::duration::max().count() / TimePoint::duration::period::den) * TimePoint::duration::period::num;
130
131 if (kMaxSecondsInStdTimePoint > ::google::protobuf::util::TimeUtil::kTimestampMaxSeconds) {
132 structs::WellKnownStd obj = create_valid();
133 messages::WellKnownStd msg;
134
135 obj.f1 = TimePoint{std::chrono::seconds{kMaxSecondsInStdTimePoint}};
136
137 EXPECT_THAT(
138 [&obj]() { static_cast<void>(StructToMessage(obj)); },
139 ::testing::ThrowsMessage<WriteError>(::testing::HasSubstr("'messages.WellKnownStd.f1'"))
140 );
141 }
142
143 if ((std::chrono::milliseconds::max().count() / 1000) > ::google::protobuf::util::TimeUtil::kDurationMaxSeconds) {
144 structs::WellKnownStd obj = create_valid();
145 messages::WellKnownStd msg;
146
147 obj.f2 = std::chrono::milliseconds::max();
148
149 EXPECT_THAT(
150 [&obj]() { static_cast<void>(StructToMessage(obj)); },
151 ::testing::ThrowsMessage<WriteError>(::testing::HasSubstr("'messages.WellKnownStd.f2'"))
152 );
153 }
154
155 {
156 structs::WellKnownStd obj = create_valid();
157 obj.f3 = {std::chrono::year{2025}, std::chrono::month{2}, std::chrono::day{29}};
158
159 EXPECT_THAT(
160 [&obj]() { static_cast<void>(StructToMessage(obj)); },
161 ::testing::ThrowsMessage<WriteError>(::testing::HasSubstr("'messages.WellKnownStd.f3'"))
162 );
163 }
164
165 {
166 structs::WellKnownStd obj = create_valid();
167 obj.f4 = std::chrono::hh_mm_ss<std::chrono::microseconds>{std::chrono::hours{25}};
168
169 EXPECT_THAT(
170 [&obj]() { static_cast<void>(StructToMessage(obj)); },
171 ::testing::ThrowsMessage<WriteError>(::testing::HasSubstr("'messages.WellKnownStd.f4'"))
172 );
173 }
174}
175
176TEST(StructToMessage, WellKnownUsrv) {
177 using TimePoint = Timestamp::TimePoint;
178
179 {
180 structs::WellKnownUsrv obj{
181 .f1 = structs::Simple{.f1 = 100},
182 .f2 = TimePoint{std::chrono::milliseconds{123'456'789}},
183 .f3 = std::chrono::milliseconds{987'654'321},
184 .f4 = std::chrono::year_month_day{std::chrono::year{2025}, std::chrono::month{8}, std::chrono::day{27}},
185 .f5 =
186 std::chrono::hh_mm_ss<std::chrono::microseconds>{
187 std::chrono::minutes(65) + std::chrono::seconds{13} + std::chrono::microseconds{123'456}
188 },
189 .f6 =
190 utils::datetime::TimeOfDay<std::chrono::microseconds>{
191 std::chrono::hours{23} + std::chrono::minutes{59} + std::chrono::seconds{59} +
192 std::chrono::microseconds{999'999}
193 },
194 .f7 = decimal64::Decimal<3>{"123.456"}
195 };
196 messages::Simple any_payload;
197
198 auto msg = StructToMessage(obj);
199
200 CheckWellKnownUsrvEqual(obj, msg);
201 ASSERT_TRUE(msg.f1().UnpackTo(&any_payload));
202 CheckSimpleEqual(obj.f1.Unpack<structs::Simple>(), any_payload);
203
204 msg = StructToMessage(structs::WellKnownUsrv{obj});
205
206 CheckWellKnownUsrvEqual(obj, msg);
207 ASSERT_TRUE(msg.f1().UnpackTo(&any_payload));
208 CheckSimpleEqual(obj.f1.Unpack<structs::Simple>(), any_payload);
209 }
210
211 {
212 structs::WellKnownUsrv obj;
213 messages::WellKnownUsrv msg;
214
215 obj.f2 = TimePoint{std::chrono::milliseconds{-987'654'321}};
216 obj.f7 = decimal64::Decimal<3>("1001.001");
217
218 messages::Scalar any_payload;
219 any_payload.set_f10(messages::TestEnum::TEST_ENUM_VALUE1);
220
221 ASSERT_TRUE(msg.mutable_f1()->PackFrom(any_payload));
222 *msg.mutable_f2() = ::google::protobuf::util::TimeUtil::NanosecondsToTimestamp(123'456'789'987'654'321LL);
223 *msg.mutable_f3() = ::google::protobuf::util::TimeUtil::NanosecondsToDuration(1001);
224
225 ASSERT_NO_THROW(StructToMessage(obj, msg));
226 CheckWellKnownUsrvEqual(obj, msg);
227
228 ASSERT_NO_THROW((obj.f1 = structs::Scalar{.f2 = 5}));
229 obj.f3 = std::chrono::milliseconds{-987'654'321};
230 obj.f7 = decimal64::Decimal<3>("-1001.001");
231 *msg.mutable_f3() = ::google::protobuf::util::TimeUtil::NanosecondsToDuration(123'456'789'987'654'321LL);
232
233 ASSERT_NO_THROW(StructToMessage(structs::WellKnownUsrv{obj}, msg));
234 CheckWellKnownUsrvEqual(obj, msg);
235 ASSERT_TRUE(msg.f1().UnpackTo(&any_payload));
236 CheckScalarEqual(obj.f1.Unpack<structs::Scalar>(), any_payload);
237 }
238}
239
240TEST(StructToMessage, Optional) {
241 {
242 structs::Optional obj =
243 {.f1 = 1001, .f2 = "test", .f3 = structs::TestEnum::kValue1, .f4 = structs::Simple{.f1 = 10}};
244
245 auto msg = StructToMessage(obj);
246 CheckOptionalEqual(obj, msg);
247
248 msg = StructToMessage(structs::Optional{obj});
249 CheckOptionalEqual(obj, msg);
250 }
251
252 {
253 structs::Optional obj;
254 messages::Optional msg;
255
256 obj.f1 = 1001;
257 obj.f4 = {.f1 = 13};
258 msg.set_f2("test2");
259 msg.set_f3(messages::TestEnum::TEST_ENUM_VALUE2);
260 msg.mutable_f4()->set_f1(5);
261
262 ASSERT_NO_THROW(StructToMessage(obj, msg));
263 CheckOptionalEqual(obj, msg);
264
265 obj.f2 = "test3";
266
267 ASSERT_NO_THROW(StructToMessage(structs::Optional{obj}, msg));
268 CheckOptionalEqual(obj, msg);
269 }
270}
271
272TEST(StructToMessage, Repeated) {
273 {
274 structs::Repeated obj;
275 obj.f1 = {10, 11, 12};
276 obj.f2 = {"test1", "test2"};
277 obj.f3 = {
278 structs::TestEnum::kValue1,
279 structs::TestEnum::kUnspecified,
280 structs::TestEnum::kValue2,
281 static_cast<structs::TestEnum>(1001)
282 };
283 obj.f4 = {{.f1 = 1000}, {.f1 = 1001}};
284
285 auto msg = StructToMessage(obj);
286 CheckRepeatedEqual(obj, msg);
287
288 msg = StructToMessage(structs::Repeated{obj});
289 CheckRepeatedEqual(obj, msg);
290 }
291
292 {
293 structs::Repeated obj;
294 messages::Repeated msg;
295
296 obj.f1 = {1};
297 obj.f4 = {{.f1 = 13}};
298 msg.add_f1(1);
299 msg.add_f1(2);
300 msg.add_f1(3);
301 msg.add_f2("test2");
302 msg.mutable_f4()->Add()->set_f1(5);
303
304 ASSERT_NO_THROW(StructToMessage(obj, msg));
305 CheckRepeatedEqual(obj, msg);
306
307 obj.f2.push_back("hello");
308
309 ASSERT_NO_THROW(StructToMessage(obj, msg));
310 CheckRepeatedEqual(obj, msg);
311 }
312}
313
314TEST(StructToMessage, Map) {
315 {
316 structs::Map obj;
317 obj.f1 = {{5, 15}, {6, 16}, {7, 17}};
318 obj.f2 = {{"key1", "value1"}, {"key2", "value2"}};
319 obj.f3 = {{true, structs::TestEnum::kUnspecified}, {false, structs::TestEnum::kValue2}};
320 obj.f4 = {{"simple1", {.f1 = 1001}}, {"simple2", {.f1 = 1002}}};
321
322 auto msg = StructToMessage(obj);
323 CheckMapEqual(obj, msg);
324
325 msg = StructToMessage(structs::Map{obj});
326 CheckMapEqual(obj, msg);
327 }
328
329 {
330 structs::Map obj;
331 messages::Map msg;
332
333 obj.f1 = {{5, 15}};
334 obj.f4 = {{"simple1", {.f1 = 1001}}};
335 (*msg.mutable_f1())[1] = 1;
336 (*msg.mutable_f1())[2] = 2;
337 (*msg.mutable_f1())[3] = 3;
338 (*msg.mutable_f2())["key1"] = "value1";
339 (*msg.mutable_f4())["simple1"].set_f1(1001);
340
341 ASSERT_NO_THROW(StructToMessage(obj, msg));
342 CheckMapEqual(obj, msg);
343
344 obj.f2.emplace("key2", "value2");
345
346 ASSERT_NO_THROW(StructToMessage(obj, msg));
347 CheckMapEqual(obj, msg);
348 }
349}
350
351TEST(StructToMessage, Oneof) {
352 {
353 structs::Oneof obj;
354 obj.test_oneof.Set<0>(1001);
355
356 auto msg = StructToMessage(obj);
357 CheckOneofEqual(obj, msg);
358
359 msg = StructToMessage(structs::Oneof{obj});
360 CheckOneofEqual(obj, msg);
361 }
362
363 {
364 structs::Oneof obj;
365 messages::Oneof msg;
366
367 obj.test_oneof.Set<1>("test");
368 msg.set_f1(1);
369
370 ASSERT_NO_THROW(StructToMessage(obj, msg));
371 CheckOneofEqual(obj, msg);
372
373 obj.test_oneof.Set<2>(structs::TestEnum::kUnspecified);
374
375 ASSERT_NO_THROW(StructToMessage(obj, msg));
376 CheckOneofEqual(obj, msg);
377
378 obj.test_oneof.Set<3>({.f1 = 1001});
379
380 ASSERT_NO_THROW(StructToMessage(obj, msg));
381 CheckOneofEqual(obj, msg);
382 }
383}
384
385TEST(StructToMessage, Indirect) {
386 {
387 structs::Indirect obj;
388 obj.f1 = {.f1 = 1};
389 obj.f2 = std::chrono::nanoseconds{987'654'321'123'456'789LL};
390 obj.f3 = {structs::Simple{.f1 = 3}, structs::Simple{.f1 = 4}};
391 obj.f4 = {{1, structs::Simple{.f1 = 5}}, {2, structs::Simple{.f1 = 6}}};
392 obj.test_oneof.Set<0>(structs::Simple{.f1 = 7});
393 obj.f7 = 8;
394 obj.f8 = {structs::TestEnum::kValue1, structs::TestEnum::kValue2};
395 obj.f9 = {{"hello", structs::Simple{.f1 = 9}}, {"", structs::Simple{.f1 = 10}}};
396
397 auto msg = StructToMessage(obj);
398 CheckIndirectEqual(obj, msg);
399
400 msg = StructToMessage(structs::Indirect{obj});
401 CheckIndirectEqual(obj, msg);
402 }
403
404 {
405 structs::Indirect obj;
406 messages::Indirect msg;
407
408 obj.f1 = {.f1 = 1};
409 obj.f4 = {{1, structs::Simple{.f1 = 10}}};
410 obj.test_oneof.Set<1>("test");
411 msg.mutable_f1()->set_f1(1001);
412 msg.mutable_f3()->Add()->set_f1(1002);
413 msg.mutable_f5()->set_f1(1003);
414
415 ASSERT_NO_THROW(StructToMessage(obj, msg));
416 CheckIndirectEqual(obj, msg);
417
418 obj.test_oneof.Set<0>(structs::Simple{.f1 = 11});
419 msg.add_f8(messages::TEST_ENUM_VALUE1);
420
421 ASSERT_NO_THROW(StructToMessage(obj, msg));
422 CheckIndirectEqual(obj, msg);
423 }
424}
425
426TEST(StructToMessage, Strong) {
427 {
428 structs::Strong obj;
429 obj.f1 = structs::Strong::F1Strong{1};
430 obj.f2 = structs::Strong::F2Strong{"hello"};
431 obj.f3 = {
432 structs::Strong::F3Strong{structs::TestEnum::kValue1},
433 structs::Strong::F3Strong{structs::TestEnum::kValue2}
434 };
435 obj.f4 = {
436 {1, structs::Strong::F4Strong(structs::Simple{.f1 = 3})},
437 {2, structs::Strong::F4Strong(structs::Simple{.f1 = 4})}
438 };
439 obj.test_oneof.Set<0>(structs::Strong::F5Strong{std::chrono::nanoseconds{-123'456'789'987'654'321}});
440
441 auto msg = StructToMessage(obj);
442 CheckStrongEqual(obj, msg);
443
444 msg = StructToMessage(structs::Strong{obj});
445 CheckStrongEqual(obj, msg);
446 }
447
448 {
449 structs::Strong obj;
450 messages::Strong msg;
451
452 obj.f1 = structs::Strong::F1Strong{1};
453 obj.f4 = {
454 {100, structs::Strong::F4Strong(structs::Simple{.f1 = 2})},
455 {200, structs::Strong::F4Strong(structs::Simple{.f1 = 3})}
456 };
457 msg.set_f1(1001);
458 msg.add_f3(messages::TestEnum::TEST_ENUM_VALUE2);
459
460 ASSERT_NO_THROW(StructToMessage(obj, msg));
461 CheckStrongEqual(obj, msg);
462
463 obj.test_oneof.Set<0>(structs::Strong::F5Strong{std::chrono::nanoseconds{123'456'789'987'654'321}});
464
465 ASSERT_NO_THROW(StructToMessage(obj, msg));
466 CheckStrongEqual(obj, msg);
467 }
468}
469
470TEST(StructToMessage, Erroneous) {
471 {
472 structs::Erroneous obj;
473 messages::Erroneous msg;
474
475 EXPECT_NO_THROW(msg = StructToMessage(obj));
476 }
477
478 {
479 structs::Erroneous obj;
480 obj.f1 = {.error_type = structs::ConversionFailureType::kException};
481
482 try {
483 auto result = StructToMessage(obj);
484 FAIL() << "exception should be thrown";
485 } catch (const ups::WriteError& e) {
486 EXPECT_THAT(e.what(), ::testing::HasSubstr("'messages.Erroneous.f1'"));
487 EXPECT_THAT(e.what(), ::testing::HasSubstr("conversion_failure_exception"));
488 } catch (...) {
489 FAIL() << "unexpected exception type";
490 }
491 }
492
493 {
494 structs::Erroneous obj;
495 obj.f2 = {{.error_type = structs::ConversionFailureType::kError}};
496
497 try {
498 auto result = StructToMessage(std::move(obj));
499 FAIL() << "exception should be thrown";
500 } catch (const ups::WriteError& e) {
501 EXPECT_THAT(e.what(), ::testing::HasSubstr("'messages.Erroneous.f2.error_field'"));
502 EXPECT_THAT(e.what(), ::testing::HasSubstr("conversion_failure_error"));
503 } catch (...) {
504 FAIL() << "unexpected exception type";
505 }
506 }
507
508 {
509 structs::Erroneous obj;
510 obj.f3 = {{10, {.error_type = structs::ConversionFailureType::kErrorWithUnknownField}}};
511
512 try {
513 auto result = StructToMessage(obj);
514 FAIL() << "exception should be thrown";
515 } catch (const ups::WriteError& e) {
516 EXPECT_THAT(e.what(), ::testing::HasSubstr("'messages.Erroneous.f3.<unknown_1001>'"));
517 EXPECT_THAT(e.what(), ::testing::HasSubstr("conversion_failure_error_with_unknown_field"));
518 } catch (...) {
519 FAIL() << "unexpected exception type";
520 }
521 }
522
523 {
524 structs::Erroneous obj;
525 obj.test_oneof.Set<0>({.error_type = structs::ConversionFailureType::kErrorWithUnknownField});
526
527 try {
528 auto result = StructToMessage(obj);
529 FAIL() << "exception should be thrown";
530 } catch (const ups::WriteError& e) {
531 EXPECT_THAT(e.what(), ::testing::HasSubstr("'messages.Erroneous.f4.<unknown_1001>'"));
532 EXPECT_THAT(e.what(), ::testing::HasSubstr("conversion_failure_error_with_unknown_field"));
533 } catch (...) {
534 FAIL() << "unexpected exception type";
535 }
536 }
537}
538
539} // namespace proto_structs::tests
540
541USERVER_NAMESPACE_END