1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
6#include <google/protobuf/util/time_util.h>
8#include <userver/formats/json/value_builder.hpp>
9#include <userver/proto-structs/convert.hpp>
10#include <userver/utest/assert_macros.hpp>
12#include "messages.pb.h"
15USERVER_NAMESPACE_BEGIN
17namespace proto_structs::tests {
19TEST(StructToMessage, Empty) {
23 UASSERT_NO_THROW(StructToMessage(obj, msg));
24 UASSERT_NO_THROW((msg = StructToMessage(obj)));
25 UASSERT_NO_THROW((msg = StructToMessage(structs::
Empty{obj})));
28TEST(StructToMessage, Scalar) {
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),
40 .f10 = structs::TestEnum::kValue1,
44 auto msg = StructToMessage(obj);
45 CheckScalarEqual(obj, msg);
47 msg = StructToMessage(structs::
Scalar{obj});
48 CheckScalarEqual(obj, msg);
60 UASSERT_NO_THROW(StructToMessage(obj, msg));
61 CheckScalarEqual(obj, msg);
67 UASSERT_NO_THROW(StructToMessage(structs::
Scalar{obj}, msg));
68 CheckScalarEqual(obj, msg);
71 if (std::numeric_limits<std::size_t>::max() > std::numeric_limits<std::int32_t>::max()) {
73 obj.f11 = std::numeric_limits<std::size_t>::max();
75 UEXPECT_THROW_MSG(
static_cast<
void>(StructToMessage(obj)), WriteError,
"'messages.Scalar.f11'");
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}}};
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}},
91 std::chrono::hh_mm_ss<std::chrono::microseconds>{
92 std::chrono::minutes(65) + std::chrono::seconds{13} + std::chrono::microseconds{123'456}
96 auto msg = StructToMessage(obj);
98 CheckWellKnownStdEqual(obj, msg);
102 CheckWellKnownStdEqual(obj, msg);
107 messages::WellKnownStd msg;
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);
113 UASSERT_NO_THROW(StructToMessage(obj, msg));
114 CheckWellKnownStdEqual(obj, msg);
116 obj.f2 = std::chrono::milliseconds{-987'654'321};
117 *msg.mutable_f2() = ::google::protobuf::util::TimeUtil::NanosecondsToDuration(123'456'789'987'654'321LL);
119 UASSERT_NO_THROW(StructToMessage(structs::
WellKnownStd{obj}, msg));
120 CheckWellKnownStdEqual(obj, msg);
123 constexpr auto kMaxSecondsInStdTimePoint =
124 (TimePoint::duration::max().count() / TimePoint::duration::period::den) * TimePoint::duration::period::num;
126 if (kMaxSecondsInStdTimePoint > ::google::protobuf::util::TimeUtil::kTimestampMaxSeconds) {
128 messages::WellKnownStd msg;
130 obj.f1 = TimePoint{std::chrono::seconds{kMaxSecondsInStdTimePoint}};
132 UEXPECT_THROW_MSG(
static_cast<
void>(StructToMessage(obj)), WriteError,
"'messages.WellKnownStd.f1'");
135 if ((std::chrono::milliseconds::max().count() / 1000) > ::google::protobuf::util::TimeUtil::kDurationMaxSeconds) {
137 messages::WellKnownStd msg;
139 obj.f2 = std::chrono::milliseconds::max();
141 UEXPECT_THROW_MSG(
static_cast<
void>(StructToMessage(obj)), WriteError,
"'messages.WellKnownStd.f2'");
146 obj.f3 = {std::chrono::year{2025}, std::chrono::month{2}, std::chrono::day{29}};
148 UEXPECT_THROW_MSG(
static_cast<
void>(StructToMessage(obj)), WriteError,
"'messages.WellKnownStd.f3'");
153 obj.f4 = std::chrono::hh_mm_ss<std::chrono::microseconds>{std::chrono::hours{25}};
155 UEXPECT_THROW_MSG(
static_cast<
void>(StructToMessage(obj)), WriteError,
"'messages.WellKnownStd.f4'");
159TEST(StructToMessage, WellKnownUsrv) {
160 using TimePoint = Timestamp::TimePoint;
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}},
169 std::chrono::hh_mm_ss<std::chrono::microseconds>{
170 std::chrono::minutes(65) + std::chrono::seconds{13} + std::chrono::microseconds{123'456}
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}
177 .f7 = decimal64::Decimal<3>{
"123.456"}
179 messages::Simple any_payload;
181 auto msg = StructToMessage(obj);
183 CheckWellKnownUsrvEqual(obj, msg);
184 ASSERT_TRUE(msg.f1().UnpackTo(&any_payload));
185 CheckSimpleEqual(obj.f1.Unpack<structs::
Simple>(), any_payload);
189 CheckWellKnownUsrvEqual(obj, msg);
190 ASSERT_TRUE(msg.f1().UnpackTo(&any_payload));
191 CheckSimpleEqual(obj.f1.Unpack<structs::
Simple>(), any_payload);
196 messages::WellKnownUsrv msg;
198 obj.f2 = TimePoint{std::chrono::milliseconds{-987'654'321}};
199 obj.f7 = decimal64::Decimal<3>(
"1001.001");
201 messages::Scalar any_payload;
202 any_payload.set_f10(messages::TestEnum::TEST_ENUM_VALUE1);
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);
208 UASSERT_NO_THROW(StructToMessage(obj, msg));
209 CheckWellKnownUsrvEqual(obj, msg);
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);
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);
223TEST(StructToMessage, WellKnownJson) {
224 const auto create_array = [](
double num,
const std::string& str,
bool b) {
230 return std::move(builder).ExtractValue();
233 const auto create_object = [](
double num,
const std::string& str,
bool b) {
235 builder[
"key0"] = formats::json::ValueBuilder{formats::common::Type::kNull};
236 builder[
"key1"] = num;
237 builder[
"key2"] = str;
239 return std::move(builder).ExtractValue();
244 auto msg = StructToMessage(obj);
246 CheckWellKnownJsonEqual(obj, msg);
251 auto msg = StructToMessage(obj);
253 CheckWellKnownJsonEqual(obj, msg);
257 structs::
WellKnownJson obj = {.f2 = formats::json::Array{create_array(100,
"test",
false)}};
258 auto msg = StructToMessage(obj);
260 CheckWellKnownJsonEqual(obj, msg);
264 structs::
WellKnownJson obj = {.f3 = formats::json::Object{create_object(200,
"oops",
true)}};
265 auto msg = StructToMessage(obj);
267 CheckWellKnownJsonEqual(obj, msg);
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)}
276 auto msg = StructToMessage(std::move(obj));
278 CheckWellKnownJsonEqual(obj, msg);
285 auto msg = StructToMessage(std::move(obj));
287 CheckWellKnownJsonEqual(obj, msg);
292 builder[
"top"] = create_object(6,
"bonjour",
false);
294 auto msg = StructToMessage(std::move(obj));
296 CheckWellKnownJsonEqual(obj, msg);
300TEST(StructToMessage, Optional) {
303 {.f1 = 1001, .f2 =
"test", .f3 = structs::TestEnum::kValue1, .f4 = structs::
Simple{.f1 = 10}};
305 auto msg = StructToMessage(obj);
306 CheckOptionalEqual(obj, msg);
308 msg = StructToMessage(structs::
Optional{obj});
309 CheckOptionalEqual(obj, msg);
314 messages::Optional msg;
319 msg.set_f3(messages::TestEnum::TEST_ENUM_VALUE2);
320 msg.mutable_f4()->set_f1(5);
322 UASSERT_NO_THROW(StructToMessage(obj, msg));
323 CheckOptionalEqual(obj, msg);
327 UASSERT_NO_THROW(StructToMessage(structs::
Optional{obj}, msg));
328 CheckOptionalEqual(obj, msg);
332TEST(StructToMessage, Repeated) {
335 obj.f1 = {10, 11, 12};
336 obj.f2 = {
"test1",
"test2"};
338 structs::TestEnum::kValue1,
339 structs::TestEnum::kUnspecified,
340 structs::TestEnum::kValue2,
341 static_cast<structs::TestEnum>(1001)
343 obj.f4 = {{.f1 = 1000}, {.f1 = 1001}};
345 auto msg = StructToMessage(obj);
346 CheckRepeatedEqual(obj, msg);
348 msg = StructToMessage(structs::
Repeated{obj});
349 CheckRepeatedEqual(obj, msg);
354 messages::Repeated msg;
357 obj.f4 = {{.f1 = 13}};
362 msg.mutable_f4()->Add()->set_f1(5);
364 UASSERT_NO_THROW(StructToMessage(obj, msg));
365 CheckRepeatedEqual(obj, msg);
367 obj.f2.push_back(
"hello");
369 UASSERT_NO_THROW(StructToMessage(obj, msg));
370 CheckRepeatedEqual(obj, msg);
374TEST(StructToMessage, Map) {
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}}};
382 auto msg = StructToMessage(obj);
383 CheckMapEqual(obj, msg);
385 msg = StructToMessage(structs::
Map{obj});
386 CheckMapEqual(obj, msg);
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);
401 UASSERT_NO_THROW(StructToMessage(obj, msg));
402 CheckMapEqual(obj, msg);
404 obj.f2.emplace(
"key2",
"value2");
406 UASSERT_NO_THROW(StructToMessage(obj, msg));
407 CheckMapEqual(obj, msg);
411TEST(StructToMessage, Oneof) {
414 obj.test_oneof.Set<0>(1001);
416 auto msg = StructToMessage(obj);
417 CheckOneofEqual(obj, msg);
419 msg = StructToMessage(structs::
Oneof{obj});
420 CheckOneofEqual(obj, msg);
427 obj.test_oneof.Set<1>(
"test");
430 UASSERT_NO_THROW(StructToMessage(obj, msg));
431 CheckOneofEqual(obj, msg);
433 obj.test_oneof.Set<2>(structs::TestEnum::kUnspecified);
435 UASSERT_NO_THROW(StructToMessage(obj, msg));
436 CheckOneofEqual(obj, msg);
438 obj.test_oneof.Set<3>({.f1 = 1001});
440 UASSERT_NO_THROW(StructToMessage(obj, msg));
441 CheckOneofEqual(obj, msg);
445TEST(StructToMessage, Indirect) {
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});
454 obj.f8 = {structs::TestEnum::kValue1, structs::TestEnum::kValue2};
455 obj.f9 = {{
"hello", structs::
Simple{.f1 = 9}}, {
"", structs::
Simple{.f1 = 10}}};
457 auto msg = StructToMessage(obj);
458 CheckIndirectEqual(obj, msg);
460 msg = StructToMessage(structs::
Indirect{obj});
461 CheckIndirectEqual(obj, msg);
466 messages::Indirect msg;
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);
475 UASSERT_NO_THROW(StructToMessage(obj, msg));
476 CheckIndirectEqual(obj, msg);
478 obj.test_oneof.Set<0>(structs::
Simple{.f1 = 11});
479 msg.add_f8(messages::TEST_ENUM_VALUE1);
481 UASSERT_NO_THROW(StructToMessage(obj, msg));
482 CheckIndirectEqual(obj, msg);
486TEST(StructToMessage, Strong) {
489 obj.f1 = structs::Strong::F1Strong{1};
490 obj.f2 = structs::Strong::F2Strong{
"hello"};
492 structs::Strong::F3Strong{structs::TestEnum::kValue1},
493 structs::Strong::F3Strong{structs::TestEnum::kValue2}
496 {1, structs::Strong::F4Strong(structs::Simple{.f1 = 3})},
497 {2, structs::Strong::F4Strong(structs::Simple{.f1 = 4})}
499 obj.test_oneof.Set<0>(structs::Strong::F5Strong{std::chrono::nanoseconds{-123'456'789'987'654'321}});
501 auto msg = StructToMessage(obj);
502 CheckStrongEqual(obj, msg);
504 msg = StructToMessage(structs::
Strong{obj});
505 CheckStrongEqual(obj, msg);
510 messages::Strong msg;
512 obj.f1 = structs::Strong::F1Strong{1};
514 {100, structs::Strong::F4Strong(structs::Simple{.f1 = 2})},
515 {200, structs::Strong::F4Strong(structs::Simple{.f1 = 3})}
518 msg.add_f3(messages::TestEnum::TEST_ENUM_VALUE2);
520 UASSERT_NO_THROW(StructToMessage(obj, msg));
521 CheckStrongEqual(obj, msg);
523 obj.test_oneof.Set<0>(structs::Strong::F5Strong{std::chrono::nanoseconds{123'456'789'987'654'321}});
525 UASSERT_NO_THROW(StructToMessage(obj, msg));
526 CheckStrongEqual(obj, msg);
530TEST(StructToMessage, Erroneous) {
533 messages::Erroneous msg;
535 EXPECT_NO_THROW(msg = StructToMessage(obj));
540 obj.f1 = {.error_type = structs::ConversionFailureType::kException};
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"));
549 FAIL() <<
"unexpected exception type";
555 obj.f2 = {{.error_type = structs::ConversionFailureType::kError}};
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"));
564 FAIL() <<
"unexpected exception type";
570 obj.f3 = {{10, {.error_type = structs::ConversionFailureType::kErrorWithUnknownField}}};
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"));
579 FAIL() <<
"unexpected exception type";
585 obj.test_oneof.Set<0>({.error_type = structs::ConversionFailureType::kErrorWithUnknownField});
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"));
594 FAIL() <<
"unexpected exception type";