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