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 auto msg = StructToMessage(std::move(obj));
277
278 CheckWellKnownJsonEqual(obj, msg);
279 }
280
281 {
282 formats::json::ValueBuilder builder{formats::common::Type::kArray};
283 builder.PushBack(create_array(5, "hola", true));
284 structs::WellKnownJson obj = {.f1 = builder.ExtractValue()};
285 auto msg = StructToMessage(std::move(obj));
286
287 CheckWellKnownJsonEqual(obj, msg);
288 }
289
290 {
291 formats::json::ValueBuilder builder{formats::common::Type::kObject};
292 builder["top"] = create_object(6, "bonjour", false);
293 structs::WellKnownJson obj = {.f1 = builder.ExtractValue()};
294 auto msg = StructToMessage(std::move(obj));
295
296 CheckWellKnownJsonEqual(obj, msg);
297 }
298}
299
300TEST(StructToMessage, Optional) {
301 {
302 structs::Optional obj =
303 {.f1 = 1001, .f2 = "test", .f3 = structs::TestEnum::kValue1, .f4 = structs::Simple{.f1 = 10}};
304
305 auto msg = StructToMessage(obj);
306 CheckOptionalEqual(obj, msg);
307
308 msg = StructToMessage(structs::Optional{obj});
309 CheckOptionalEqual(obj, msg);
310 }
311
312 {
313 structs::Optional obj;
314 messages::Optional msg;
315
316 obj.f1 = 1001;
317 obj.f4 = {.f1 = 13};
318 msg.set_f2("test2");
319 msg.set_f3(messages::TestEnum::TEST_ENUM_VALUE2);
320 msg.mutable_f4()->set_f1(5);
321
322 UASSERT_NO_THROW(StructToMessage(obj, msg));
323 CheckOptionalEqual(obj, msg);
324
325 obj.f2 = "test3";
326
327 UASSERT_NO_THROW(StructToMessage(structs::Optional{obj}, msg));
328 CheckOptionalEqual(obj, msg);
329 }
330}
331
332TEST(StructToMessage, Repeated) {
333 {
334 structs::Repeated obj;
335 obj.f1 = {10, 11, 12};
336 obj.f2 = {"test1", "test2"};
337 obj.f3 = {
338 structs::TestEnum::kValue1,
339 structs::TestEnum::kUnspecified,
340 structs::TestEnum::kValue2,
341 static_cast<structs::TestEnum>(1001)
342 };
343 obj.f4 = {{.f1 = 1000}, {.f1 = 1001}};
344
345 auto msg = StructToMessage(obj);
346 CheckRepeatedEqual(obj, msg);
347
348 msg = StructToMessage(structs::Repeated{obj});
349 CheckRepeatedEqual(obj, msg);
350 }
351
352 {
353 structs::Repeated obj;
354 messages::Repeated msg;
355
356 obj.f1 = {1};
357 obj.f4 = {{.f1 = 13}};
358 msg.add_f1(1);
359 msg.add_f1(2);
360 msg.add_f1(3);
361 msg.add_f2("test2");
362 msg.mutable_f4()->Add()->set_f1(5);
363
364 UASSERT_NO_THROW(StructToMessage(obj, msg));
365 CheckRepeatedEqual(obj, msg);
366
367 obj.f2.push_back("hello");
368
369 UASSERT_NO_THROW(StructToMessage(obj, msg));
370 CheckRepeatedEqual(obj, msg);
371 }
372}
373
374TEST(StructToMessage, Map) {
375 {
376 structs::Map obj;
377 obj.f1 = {{5, 15}, {6, 16}, {7, 17}};
378 obj.f2 = {{"key1", "value1"}, {"key2", "value2"}};
379 obj.f3 = {{true, structs::TestEnum::kUnspecified}, {false, structs::TestEnum::kValue2}};
380 obj.f4 = {{"simple1", {.f1 = 1001}}, {"simple2", {.f1 = 1002}}};
381
382 auto msg = StructToMessage(obj);
383 CheckMapEqual(obj, msg);
384
385 msg = StructToMessage(structs::Map{obj});
386 CheckMapEqual(obj, msg);
387 }
388
389 {
390 structs::Map obj;
391 messages::Map msg;
392
393 obj.f1 = {{5, 15}};
394 obj.f4 = {{"simple1", {.f1 = 1001}}};
395 (*msg.mutable_f1())[1] = 1;
396 (*msg.mutable_f1())[2] = 2;
397 (*msg.mutable_f1())[3] = 3;
398 (*msg.mutable_f2())["key1"] = "value1";
399 (*msg.mutable_f4())["simple1"].set_f1(1001);
400
401 UASSERT_NO_THROW(StructToMessage(obj, msg));
402 CheckMapEqual(obj, msg);
403
404 obj.f2.emplace("key2", "value2");
405
406 UASSERT_NO_THROW(StructToMessage(obj, msg));
407 CheckMapEqual(obj, msg);
408 }
409}
410
411TEST(StructToMessage, Oneof) {
412 {
413 structs::Oneof obj;
414 obj.test_oneof.Set<0>(1001);
415
416 auto msg = StructToMessage(obj);
417 CheckOneofEqual(obj, msg);
418
419 msg = StructToMessage(structs::Oneof{obj});
420 CheckOneofEqual(obj, msg);
421 }
422
423 {
424 structs::Oneof obj;
425 messages::Oneof msg;
426
427 obj.test_oneof.Set<1>("test");
428 msg.set_f1(1);
429
430 UASSERT_NO_THROW(StructToMessage(obj, msg));
431 CheckOneofEqual(obj, msg);
432
433 obj.test_oneof.Set<2>(structs::TestEnum::kUnspecified);
434
435 UASSERT_NO_THROW(StructToMessage(obj, msg));
436 CheckOneofEqual(obj, msg);
437
438 obj.test_oneof.Set<3>({.f1 = 1001});
439
440 UASSERT_NO_THROW(StructToMessage(obj, msg));
441 CheckOneofEqual(obj, msg);
442 }
443}
444
445TEST(StructToMessage, Indirect) {
446 {
447 structs::Indirect obj;
448 obj.f1 = {.f1 = 1};
449 obj.f2 = std::chrono::nanoseconds{987'654'321'123'456'789LL};
450 obj.f3 = {structs::Simple{.f1 = 3}, structs::Simple{.f1 = 4}};
451 obj.f4 = {{1, structs::Simple{.f1 = 5}}, {2, structs::Simple{.f1 = 6}}};
452 obj.test_oneof.Set<0>(structs::Simple{.f1 = 7});
453 obj.f7 = 8;
454 obj.f8 = {structs::TestEnum::kValue1, structs::TestEnum::kValue2};
455 obj.f9 = {{"hello", structs::Simple{.f1 = 9}}, {"", structs::Simple{.f1 = 10}}};
456
457 auto msg = StructToMessage(obj);
458 CheckIndirectEqual(obj, msg);
459
460 msg = StructToMessage(structs::Indirect{obj});
461 CheckIndirectEqual(obj, msg);
462 }
463
464 {
465 structs::Indirect obj;
466 messages::Indirect msg;
467
468 obj.f1 = {.f1 = 1};
469 obj.f4 = {{1, structs::Simple{.f1 = 10}}};
470 obj.test_oneof.Set<1>("test");
471 msg.mutable_f1()->set_f1(1001);
472 msg.mutable_f3()->Add()->set_f1(1002);
473 msg.mutable_f5()->set_f1(1003);
474
475 UASSERT_NO_THROW(StructToMessage(obj, msg));
476 CheckIndirectEqual(obj, msg);
477
478 obj.test_oneof.Set<0>(structs::Simple{.f1 = 11});
479 msg.add_f8(messages::TEST_ENUM_VALUE1);
480
481 UASSERT_NO_THROW(StructToMessage(obj, msg));
482 CheckIndirectEqual(obj, msg);
483 }
484}
485
486TEST(StructToMessage, Strong) {
487 {
488 structs::Strong obj;
489 obj.f1 = structs::Strong::F1Strong{1};
490 obj.f2 = structs::Strong::F2Strong{"hello"};
491 obj.f3 = {
492 structs::Strong::F3Strong{structs::TestEnum::kValue1},
493 structs::Strong::F3Strong{structs::TestEnum::kValue2}
494 };
495 obj.f4 = {
496 {1, structs::Strong::F4Strong(structs::Simple{.f1 = 3})},
497 {2, structs::Strong::F4Strong(structs::Simple{.f1 = 4})}
498 };
499 obj.test_oneof.Set<0>(structs::Strong::F5Strong{std::chrono::nanoseconds{-123'456'789'987'654'321}});
500
501 auto msg = StructToMessage(obj);
502 CheckStrongEqual(obj, msg);
503
504 msg = StructToMessage(structs::Strong{obj});
505 CheckStrongEqual(obj, msg);
506 }
507
508 {
509 structs::Strong obj;
510 messages::Strong msg;
511
512 obj.f1 = structs::Strong::F1Strong{1};
513 obj.f4 = {
514 {100, structs::Strong::F4Strong(structs::Simple{.f1 = 2})},
515 {200, structs::Strong::F4Strong(structs::Simple{.f1 = 3})}
516 };
517 msg.set_f1(1001);
518 msg.add_f3(messages::TestEnum::TEST_ENUM_VALUE2);
519
520 UASSERT_NO_THROW(StructToMessage(obj, msg));
521 CheckStrongEqual(obj, msg);
522
523 obj.test_oneof.Set<0>(structs::Strong::F5Strong{std::chrono::nanoseconds{123'456'789'987'654'321}});
524
525 UASSERT_NO_THROW(StructToMessage(obj, msg));
526 CheckStrongEqual(obj, msg);
527 }
528}
529
530TEST(StructToMessage, Erroneous) {
531 {
532 structs::Erroneous obj;
533 messages::Erroneous msg;
534
535 EXPECT_NO_THROW(msg = StructToMessage(obj));
536 }
537
538 {
539 structs::Erroneous obj;
540 obj.f1 = {.error_type = structs::ConversionFailureType::kException};
541
542 try {
543 auto result = StructToMessage(obj);
544 FAIL() << "exception should be thrown";
545 } catch (const WriteError& e) {
546 EXPECT_THAT(e.what(), ::testing::HasSubstr("'messages.Erroneous.f1'"));
547 EXPECT_THAT(e.what(), ::testing::HasSubstr("conversion_failure_exception"));
548 } catch (...) {
549 FAIL() << "unexpected exception type";
550 }
551 }
552
553 {
554 structs::Erroneous obj;
555 obj.f2 = {{.error_type = structs::ConversionFailureType::kError}};
556
557 try {
558 auto result = StructToMessage(std::move(obj));
559 FAIL() << "exception should be thrown";
560 } catch (const WriteError& e) {
561 EXPECT_THAT(e.what(), ::testing::HasSubstr("'messages.Erroneous.f2.error_field'"));
562 EXPECT_THAT(e.what(), ::testing::HasSubstr("conversion_failure_error"));
563 } catch (...) {
564 FAIL() << "unexpected exception type";
565 }
566 }
567
568 {
569 structs::Erroneous obj;
570 obj.f3 = {{10, {.error_type = structs::ConversionFailureType::kErrorWithUnknownField}}};
571
572 try {
573 auto result = StructToMessage(obj);
574 FAIL() << "exception should be thrown";
575 } catch (const WriteError& e) {
576 EXPECT_THAT(e.what(), ::testing::HasSubstr("'messages.Erroneous.f3.<unknown_1001>'"));
577 EXPECT_THAT(e.what(), ::testing::HasSubstr("conversion_failure_error_with_unknown_field"));
578 } catch (...) {
579 FAIL() << "unexpected exception type";
580 }
581 }
582
583 {
584 structs::Erroneous obj;
585 obj.test_oneof.Set<0>({.error_type = structs::ConversionFailureType::kErrorWithUnknownField});
586
587 try {
588 auto result = StructToMessage(obj);
589 FAIL() << "exception should be thrown";
590 } catch (const WriteError& e) {
591 EXPECT_THAT(e.what(), ::testing::HasSubstr("'messages.Erroneous.f4.<unknown_1001>'"));
592 EXPECT_THAT(e.what(), ::testing::HasSubstr("conversion_failure_error_with_unknown_field"));
593 } catch (...) {
594 FAIL() << "unexpected exception type";
595 }
596 }
597}
598
599} // namespace proto_structs::tests
600
601USERVER_NAMESPACE_END