1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
6#include <google/protobuf/util/time_util.h>
8#include <userver/proto-structs/convert.hpp>
10#include "messages.pb.h"
13USERVER_NAMESPACE_BEGIN
15namespace proto_structs::tests {
17TEST(StructToMessage, Empty) {
21 ASSERT_NO_THROW(StructToMessage(obj, msg));
22 ASSERT_NO_THROW((msg = StructToMessage(obj)));
23 ASSERT_NO_THROW((msg = StructToMessage(structs::
Empty{obj})));
26TEST(StructToMessage, Scalar) {
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),
38 .f10 = structs::TestEnum::kValue1,
41 auto msg = StructToMessage(obj);
42 CheckScalarEqual(obj, msg);
44 msg = StructToMessage(structs::
Scalar{obj});
45 CheckScalarEqual(obj, msg);
57 ASSERT_NO_THROW(StructToMessage(obj, msg));
58 CheckScalarEqual(obj, msg);
64 ASSERT_NO_THROW(StructToMessage(structs::
Scalar{obj}, msg));
65 CheckScalarEqual(obj, msg);
68 if (std::numeric_limits<std::size_t>::max() > std::numeric_limits<std::int32_t>::max()) {
70 obj.f11 = std::numeric_limits<std::size_t>::max();
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'"));
78 FAIL() <<
"unexpected exception type";
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}}};
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}}};
97 auto msg = StructToMessage(obj);
99 CheckWellKnownStdEqual(obj, msg);
103 CheckWellKnownStdEqual(obj, msg);
108 messages::WellKnownStd msg;
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);
114 ASSERT_NO_THROW(StructToMessage(obj, msg));
115 CheckWellKnownStdEqual(obj, msg);
117 obj.f2 = std::chrono::milliseconds{-987'654'321};
118 *msg.mutable_f2() = ::google::protobuf::util::TimeUtil::NanosecondsToDuration(123'456'789'987'654'321LL);
120 ASSERT_NO_THROW(StructToMessage(structs::
WellKnownStd{obj}, msg));
121 CheckWellKnownStdEqual(obj, msg);
124 constexpr auto kMaxSecondsInStdTimePoint =
125 (TimePoint::duration::max().count() / TimePoint::duration::period::den) * TimePoint::duration::period::num;
127 if (kMaxSecondsInStdTimePoint > ::google::protobuf::util::TimeUtil::kTimestampMaxSeconds) {
129 messages::WellKnownStd msg;
131 obj.f1 = TimePoint{std::chrono::seconds{kMaxSecondsInStdTimePoint}};
134 [&obj]() {
static_cast<
void>(StructToMessage(obj)); },
135 ::testing::ThrowsMessage<WriteError>(::testing::HasSubstr(
"'messages.WellKnownStd.f1'"))
139 if ((std::chrono::milliseconds::max().count() / 1000) > ::google::protobuf::util::TimeUtil::kDurationMaxSeconds) {
141 messages::WellKnownStd msg;
143 obj.f2 = std::chrono::milliseconds::max();
146 [&obj]() {
static_cast<
void>(StructToMessage(obj)); },
147 ::testing::ThrowsMessage<WriteError>(::testing::HasSubstr(
"'messages.WellKnownStd.f2'"))
153 obj.f3 = {std::chrono::year{2025}, std::chrono::month{2}, std::chrono::day{29}};
156 [&obj]() {
static_cast<
void>(StructToMessage(obj)); },
157 ::testing::ThrowsMessage<WriteError>(::testing::HasSubstr(
"'messages.WellKnownStd.f3'"))
163 obj.f4 = std::chrono::hh_mm_ss<std::chrono::microseconds>{std::chrono::hours{25}};
166 [&obj]() {
static_cast<
void>(StructToMessage(obj)); },
167 ::testing::ThrowsMessage<WriteError>(::testing::HasSubstr(
"'messages.WellKnownStd.f4'"))
172TEST(StructToMessage, WellKnownUsrv) {
173 using TimePoint = Timestamp::TimePoint;
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}},
182 std::chrono::hh_mm_ss<std::chrono::microseconds>{
183 std::chrono::minutes(65) + std::chrono::seconds{13} + std::chrono::microseconds{123'456}},
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;
191 auto msg = StructToMessage(obj);
193 CheckWellKnownUsrvEqual(obj, msg);
194 ASSERT_TRUE(msg.f1().UnpackTo(&any_payload));
195 CheckSimpleEqual(obj.f1.Unpack<structs::
Simple>(), any_payload);
199 CheckWellKnownUsrvEqual(obj, msg);
200 ASSERT_TRUE(msg.f1().UnpackTo(&any_payload));
201 CheckSimpleEqual(obj.f1.Unpack<structs::
Simple>(), any_payload);
206 messages::WellKnownUsrv msg;
208 obj.f2 = TimePoint{std::chrono::milliseconds{-987'654'321}};
209 obj.f7 = decimal64::Decimal<3>(
"1001.001");
211 messages::Scalar any_payload;
212 any_payload.set_f10(messages::TestEnum::TEST_ENUM_VALUE1);
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);
218 ASSERT_NO_THROW(StructToMessage(obj, msg));
219 CheckWellKnownUsrvEqual(obj, msg);
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);
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);
233TEST(StructToMessage, Optional) {
236 .f1 = 1001, .f2 =
"test", .f3 = structs::TestEnum::kValue1, .f4 = structs::
Simple{.f1 = 10}};
238 auto msg = StructToMessage(obj);
239 CheckOptionalEqual(obj, msg);
241 msg = StructToMessage(structs::
Optional{obj});
242 CheckOptionalEqual(obj, msg);
247 messages::Optional msg;
252 msg.set_f3(messages::TestEnum::TEST_ENUM_VALUE2);
253 msg.mutable_f4()->set_f1(5);
255 ASSERT_NO_THROW(StructToMessage(obj, msg));
256 CheckOptionalEqual(obj, msg);
260 ASSERT_NO_THROW(StructToMessage(structs::
Optional{obj}, msg));
261 CheckOptionalEqual(obj, msg);
265TEST(StructToMessage, Repeated) {
268 obj.f1 = {10, 11, 12};
269 obj.f2 = {
"test1",
"test2"};
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}};
277 auto msg = StructToMessage(obj);
278 CheckRepeatedEqual(obj, msg);
280 msg = StructToMessage(structs::
Repeated{obj});
281 CheckRepeatedEqual(obj, msg);
286 messages::Repeated msg;
289 obj.f4 = {{.f1 = 13}};
294 msg.mutable_f4()->Add()->set_f1(5);
296 ASSERT_NO_THROW(StructToMessage(obj, msg));
297 CheckRepeatedEqual(obj, msg);
299 obj.f2.push_back(
"hello");
301 ASSERT_NO_THROW(StructToMessage(obj, msg));
302 CheckRepeatedEqual(obj, msg);
306TEST(StructToMessage, Map) {
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}}};
314 auto msg = StructToMessage(obj);
315 CheckMapEqual(obj, msg);
317 msg = StructToMessage(structs::
Map{obj});
318 CheckMapEqual(obj, msg);
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);
333 ASSERT_NO_THROW(StructToMessage(obj, msg));
334 CheckMapEqual(obj, msg);
336 obj.f2.emplace(
"key2",
"value2");
338 ASSERT_NO_THROW(StructToMessage(obj, msg));
339 CheckMapEqual(obj, msg);
343TEST(StructToMessage, Oneof) {
346 obj.test_oneof.Set<0>(1001);
348 auto msg = StructToMessage(obj);
349 CheckOneofEqual(obj, msg);
351 msg = StructToMessage(structs::
Oneof{obj});
352 CheckOneofEqual(obj, msg);
359 obj.test_oneof.Set<1>(
"test");
362 ASSERT_NO_THROW(StructToMessage(obj, msg));
363 CheckOneofEqual(obj, msg);
365 obj.test_oneof.Set<2>(structs::TestEnum::kUnspecified);
367 ASSERT_NO_THROW(StructToMessage(obj, msg));
368 CheckOneofEqual(obj, msg);
370 obj.test_oneof.Set<3>({.f1 = 1001});
372 ASSERT_NO_THROW(StructToMessage(obj, msg));
373 CheckOneofEqual(obj, msg);
377TEST(StructToMessage, Indirect) {
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});
386 obj.f8 = {structs::TestEnum::kValue1, structs::TestEnum::kValue2};
387 obj.f9 = {{
"hello", structs::
Simple{.f1 = 9}}, {
"", structs::
Simple{.f1 = 10}}};
389 auto msg = StructToMessage(obj);
390 CheckIndirectEqual(obj, msg);
392 msg = StructToMessage(structs::
Indirect{obj});
393 CheckIndirectEqual(obj, msg);
398 messages::Indirect msg;
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);
407 ASSERT_NO_THROW(StructToMessage(obj, msg));
408 CheckIndirectEqual(obj, msg);
410 obj.test_oneof.Set<0>(structs::
Simple{.f1 = 11});
411 msg.add_f8(messages::TEST_ENUM_VALUE1);
413 ASSERT_NO_THROW(StructToMessage(obj, msg));
414 CheckIndirectEqual(obj, msg);
418TEST(StructToMessage, Strong) {
421 obj.f1 = structs::Strong::F1Strong{1};
422 obj.f2 = structs::Strong::F2Strong{
"hello"};
424 structs::Strong::F3Strong{structs::TestEnum::kValue1},
425 structs::Strong::F3Strong{structs::TestEnum::kValue2}};
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}});
431 auto msg = StructToMessage(obj);
432 CheckStrongEqual(obj, msg);
434 msg = StructToMessage(structs::
Strong{obj});
435 CheckStrongEqual(obj, msg);
440 messages::Strong msg;
442 obj.f1 = structs::Strong::F1Strong{1};
444 {100, structs::Strong::F4Strong(structs::Simple{.f1 = 2})},
445 {200, structs::Strong::F4Strong(structs::Simple{.f1 = 3})}};
447 msg.add_f3(messages::TestEnum::TEST_ENUM_VALUE2);
449 ASSERT_NO_THROW(StructToMessage(obj, msg));
450 CheckStrongEqual(obj, msg);
452 obj.test_oneof.Set<0>(structs::Strong::F5Strong{std::chrono::nanoseconds{123'456'789'987'654'321}});
454 ASSERT_NO_THROW(StructToMessage(obj, msg));
455 CheckStrongEqual(obj, msg);
459TEST(StructToMessage, Erroneous) {
462 messages::Erroneous msg;
464 EXPECT_NO_THROW(msg = StructToMessage(obj));
469 obj.f1 = {.error_type = structs::ConversionFailureType::kException};
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"));
478 FAIL() <<
"unexpected exception type";
484 obj.f2 = {{.error_type = structs::ConversionFailureType::kError}};
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"));
493 FAIL() <<
"unexpected exception type";
499 obj.f3 = {{10, {.error_type = structs::ConversionFailureType::kErrorWithUnknownField}}};
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"));
508 FAIL() <<
"unexpected exception type";
514 obj.test_oneof.Set<0>({.error_type = structs::ConversionFailureType::kErrorWithUnknownField});
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"));
523 FAIL() <<
"unexpected exception type";