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) {
226 builder.PushBack(formats::json::ValueBuilder{formats::
common::
Type::kNull});
227 builder.PushBack(num);
228 builder.PushBack(str);
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);
250 structs::
WellKnownJson obj = {.f1 = formats::json::ValueBuilder{1001}.ExtractValue()};
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 const auto expected = obj;
277 auto msg = StructToMessage(std::move(obj));
279 CheckWellKnownJsonEqual(expected, msg);
284 builder.PushBack(create_array(5,
"hola",
true));
286 const auto expected = obj;
287 auto msg = StructToMessage(std::move(obj));
289 CheckWellKnownJsonEqual(expected, msg);
294 builder[
"top"] = create_object(6,
"bonjour",
false);
296 const auto expected = obj;
297 auto msg = StructToMessage(std::move(obj));
299 CheckWellKnownJsonEqual(expected, msg);
303TEST(StructToMessage, Optional) {
306 {.f1 = 1001, .f2 =
"test", .f3 = structs::TestEnum::kValue1, .f4 = structs::
Simple{.f1 = 10}};
308 auto msg = StructToMessage(obj);
309 CheckOptionalEqual(obj, msg);
311 msg = StructToMessage(structs::
Optional{obj});
312 CheckOptionalEqual(obj, msg);
317 messages::Optional msg;
322 msg.set_f3(messages::TestEnum::TEST_ENUM_VALUE2);
323 msg.mutable_f4()->set_f1(5);
325 UASSERT_NO_THROW(StructToMessage(obj, msg));
326 CheckOptionalEqual(obj, msg);
330 UASSERT_NO_THROW(StructToMessage(structs::
Optional{obj}, msg));
331 CheckOptionalEqual(obj, msg);
335TEST(StructToMessage, Repeated) {
338 obj.f1 = {10, 11, 12};
339 obj.f2 = {
"test1",
"test2"};
341 structs::TestEnum::kValue1,
342 structs::TestEnum::kUnspecified,
343 structs::TestEnum::kValue2,
344 static_cast<structs::TestEnum>(1001)
346 obj.f4 = {{.f1 = 1000}, {.f1 = 1001}};
348 auto msg = StructToMessage(obj);
349 CheckRepeatedEqual(obj, msg);
351 msg = StructToMessage(structs::
Repeated{obj});
352 CheckRepeatedEqual(obj, msg);
357 messages::Repeated msg;
360 obj.f4 = {{.f1 = 13}};
365 msg.mutable_f4()->Add()->set_f1(5);
367 UASSERT_NO_THROW(StructToMessage(obj, msg));
368 CheckRepeatedEqual(obj, msg);
370 obj.f2.push_back(
"hello");
372 UASSERT_NO_THROW(StructToMessage(obj, msg));
373 CheckRepeatedEqual(obj, msg);
377TEST(StructToMessage, Map) {
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}}};
385 auto msg = StructToMessage(obj);
386 CheckMapEqual(obj, msg);
388 msg = StructToMessage(structs::
Map{obj});
389 CheckMapEqual(obj, msg);
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);
404 UASSERT_NO_THROW(StructToMessage(obj, msg));
405 CheckMapEqual(obj, msg);
407 obj.f2.emplace(
"key2",
"value2");
409 UASSERT_NO_THROW(StructToMessage(obj, msg));
410 CheckMapEqual(obj, msg);
414TEST(StructToMessage, Oneof) {
417 obj.test_oneof.Set<0>(1001);
419 auto msg = StructToMessage(obj);
420 CheckOneofEqual(obj, msg);
422 msg = StructToMessage(structs::
Oneof{obj});
423 CheckOneofEqual(obj, msg);
430 obj.test_oneof.Set<1>(
"test");
433 UASSERT_NO_THROW(StructToMessage(obj, msg));
434 CheckOneofEqual(obj, msg);
436 obj.test_oneof.Set<2>(structs::TestEnum::kUnspecified);
438 UASSERT_NO_THROW(StructToMessage(obj, msg));
439 CheckOneofEqual(obj, msg);
441 obj.test_oneof.Set<3>({.f1 = 1001});
443 UASSERT_NO_THROW(StructToMessage(obj, msg));
444 CheckOneofEqual(obj, msg);
448TEST(StructToMessage, Indirect) {
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});
457 obj.f8 = {structs::TestEnum::kValue1, structs::TestEnum::kValue2};
458 obj.f9 = {{
"hello", structs::
Simple{.f1 = 9}}, {
"", structs::
Simple{.f1 = 10}}};
460 auto msg = StructToMessage(obj);
461 CheckIndirectEqual(obj, msg);
463 msg = StructToMessage(structs::
Indirect{obj});
464 CheckIndirectEqual(obj, msg);
469 messages::Indirect msg;
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);
478 UASSERT_NO_THROW(StructToMessage(obj, msg));
479 CheckIndirectEqual(obj, msg);
481 obj.test_oneof.Set<0>(structs::
Simple{.f1 = 11});
482 msg.add_f8(messages::TEST_ENUM_VALUE1);
484 UASSERT_NO_THROW(StructToMessage(obj, msg));
485 CheckIndirectEqual(obj, msg);
489TEST(StructToMessage, Strong) {
492 obj.f1 = structs::Strong::F1Strong{1};
493 obj.f2 = structs::Strong::F2Strong{
"hello"};
495 structs::Strong::F3Strong{structs::TestEnum::kValue1},
496 structs::Strong::F3Strong{structs::TestEnum::kValue2}
499 {1, structs::Strong::F4Strong(structs::Simple{.f1 = 3})},
500 {2, structs::Strong::F4Strong(structs::Simple{.f1 = 4})}
502 obj.test_oneof.Set<0>(structs::Strong::F5Strong{std::chrono::nanoseconds{-123'456'789'987'654'321}});
504 auto msg = StructToMessage(obj);
505 CheckStrongEqual(obj, msg);
507 msg = StructToMessage(structs::
Strong{obj});
508 CheckStrongEqual(obj, msg);
513 messages::Strong msg;
515 obj.f1 = structs::Strong::F1Strong{1};
517 {100, structs::Strong::F4Strong(structs::Simple{.f1 = 2})},
518 {200, structs::Strong::F4Strong(structs::Simple{.f1 = 3})}
521 msg.add_f3(messages::TestEnum::TEST_ENUM_VALUE2);
523 UASSERT_NO_THROW(StructToMessage(obj, msg));
524 CheckStrongEqual(obj, msg);
526 obj.test_oneof.Set<0>(structs::Strong::F5Strong{std::chrono::nanoseconds{123'456'789'987'654'321}});
528 UASSERT_NO_THROW(StructToMessage(obj, msg));
529 CheckStrongEqual(obj, msg);
533TEST(StructToMessage, Erroneous) {
536 messages::Erroneous msg;
538 EXPECT_NO_THROW(msg = StructToMessage(obj));
543 obj.f1 = {.error_type = structs::ConversionFailureType::kException};
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"));
552 FAIL() <<
"unexpected exception type";
558 obj.f2 = {{.error_type = structs::ConversionFailureType::kError}};
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"));
567 FAIL() <<
"unexpected exception type";
573 obj.f3 = {{10, {.error_type = structs::ConversionFailureType::kErrorWithUnknownField}}};
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"));
582 FAIL() <<
"unexpected exception type";
588 obj.test_oneof.Set<0>({.error_type = structs::ConversionFailureType::kErrorWithUnknownField});
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"));
597 FAIL() <<
"unexpected exception type";