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