userver: /data/code/userver/libraries/protobuf/tests/json/field_mask_from_json_test.cpp Source File
Loading...
Searching...
No Matches
field_mask_from_json_test.cpp
1#include <gtest/gtest.h>
2
3#include <memory>
4#include <ostream>
5#include <string>
6#include <unordered_set>
7
8#include <fmt/format.h>
9#include <fmt/ranges.h>
10#include <google/protobuf/dynamic_message.h>
11
12#include <userver/formats/json/serialize.hpp>
13#include <userver/protobuf/json/convert.hpp>
14#include <userver/utest/assert_macros.hpp>
15
16#include "utils.hpp"
17
18USERVER_NAMESPACE_BEGIN
19
20namespace protobuf::json::tests {
21
22struct FieldMaskFromJsonSuccessTestParam {
23 std::string input = {};
24 FieldMaskMessageData expected_message = {};
25 ParseOptions options = {};
26};
27
28struct FieldMaskFromJsonFailureTestParam {
29 std::string input = {};
30 ParseErrorCode expected_errc = {};
31 std::string expected_path = {};
32 ParseOptions options = {};
33
34 // Protobuf ProtoJSON legacy syntax supports some features, which we want to prohibit (because
35 // we do not want our clients to use syntax that may break in the newer protobuf versions).
36 // This variable is used disable some checks that will fail for legacy syntax.
37 bool skip_native_check = false;
38};
39
40void PrintTo(const FieldMaskFromJsonSuccessTestParam& param, std::ostream* os) {
41 *os << fmt::format("{{ input = '{}' }}", param.input);
42}
43
44void PrintTo(const FieldMaskFromJsonFailureTestParam& param, std::ostream* os) {
45 *os << fmt::format("{{ input = '{}' }}", param.input);
46}
47
48class FieldMaskFromJsonSuccessTest : public ::testing::TestWithParam<FieldMaskFromJsonSuccessTestParam> {};
49class FieldMaskFromJsonFailureTest : public ::testing::TestWithParam<FieldMaskFromJsonFailureTestParam> {};
50
51INSTANTIATE_TEST_SUITE_P(
52 ,
53 FieldMaskFromJsonSuccessTest,
54 ::testing::Values(
55 FieldMaskFromJsonSuccessTestParam{R"({})", FieldMaskMessageData{}},
56 FieldMaskFromJsonSuccessTestParam{R"({"field1":null})", FieldMaskMessageData{}},
57 FieldMaskFromJsonSuccessTestParam{R"({"field1":""})", FieldMaskMessageData{std::vector<std::string>{}}},
58 FieldMaskFromJsonSuccessTestParam{R"({"field1":","})", FieldMaskMessageData{std::vector<std::string>{"", ""}}},
59 FieldMaskFromJsonSuccessTestParam{
60 R"({"field1":",,,aaa,"})",
61 FieldMaskMessageData{std::vector<std::string>{"", "", "", "aaa", ""}}
62 },
63 FieldMaskFromJsonSuccessTestParam{
64 R"({"field1":"someField.anotherField"})",
65 FieldMaskMessageData{std::vector<std::string>{"some_field.another_field"}}
66 },
67 FieldMaskFromJsonSuccessTestParam{
68 R"({"field1":"someField.anotherField..oneMore,AB0C"})",
69 FieldMaskMessageData{std::vector<std::string>{"some_field.another_field..one_more", "_a_b0_c"}}
70 }
71 )
72);
73
74INSTANTIATE_TEST_SUITE_P(
75 ,
76 FieldMaskFromJsonFailureTest,
77 ::testing::Values(
78 FieldMaskFromJsonFailureTestParam{R"({"field1":[]})", ParseErrorCode::kInvalidType, "field1"},
79 FieldMaskFromJsonFailureTestParam{R"({"field1":{}})", ParseErrorCode::kInvalidType, "field1", {}, true},
80 FieldMaskFromJsonFailureTestParam{R"({"field1":true})", ParseErrorCode::kInvalidType, "field1"},
81 FieldMaskFromJsonFailureTestParam{R"({"field1":1})", ParseErrorCode::kInvalidType, "field1"},
82 FieldMaskFromJsonFailureTestParam{
83 R"({"field1":"some_field"})",
84 ParseErrorCode::kInvalidValue,
85 "field1",
86 {},
87 true
88 },
89 FieldMaskFromJsonFailureTestParam{
90 R"({"field1":"someF!ield"})",
91 ParseErrorCode::kInvalidValue,
92 "field1",
93 {},
94 true
95 }
96 )
97);
98
99TEST_P(FieldMaskFromJsonSuccessTest, Test) {
100 const auto& param = GetParam();
101
102 proto_json::messages::FieldMaskMessage message;
103 proto_json::messages::FieldMaskMessage expected_message;
104 proto_json::messages::FieldMaskMessage sample_message;
105 formats::json::Value input = PrepareJsonTestData(param.input);
106 expected_message = PrepareTestData(param.expected_message);
107
108 message.mutable_field1()->add_paths("dump");
109
110 UASSERT_NO_THROW((message = JsonToMessage<proto_json::messages::FieldMaskMessage>(input, param.options)));
111 UASSERT_NO_THROW(InitSampleMessage(param.input, sample_message, param.options));
112
113 CheckMessageEqual(message, sample_message);
114 CheckMessageEqual(message, expected_message);
115}
116
117TEST_P(FieldMaskFromJsonFailureTest, Test) {
118 const auto& param = GetParam();
119
120 proto_json::messages::FieldMaskMessage sample_message;
121 formats::json::Value input = PrepareJsonTestData(param.input);
122
124 (void)JsonToMessage<proto_json::messages::FieldMaskMessage>(input, param.options),
125 param.expected_errc,
126 param.expected_path
127 );
128
129 if (!param.skip_native_check) {
130 UEXPECT_THROW(InitSampleMessage(param.input, sample_message, param.options), SampleError);
131 }
132}
133
134TEST(FieldMaskFromJsonAdditionalTest, InlinedNonNull) {
135 using Message = ::google::protobuf::FieldMask;
136
137 const char* json_str = "\"someField.anotherField,oneMore\"";
138 const auto json = formats::json::FromString(json_str);
139 Message message;
140 Message sample;
141
142 message.add_paths("dump");
143
144 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
145 UASSERT_NO_THROW(InitSampleMessage(json_str, sample));
146
147 CheckMessageEqual(message, sample);
148}
149
150TEST(FieldMaskFromJsonAdditionalTest, InlinedNull) {
151 using Message = ::google::protobuf::FieldMask;
152
153 {
154 const auto json = formats::json::FromString("null");
155 Message message;
156
157 EXPECT_PARSE_ERROR((message = JsonToMessage<Message>(json)), ParseErrorCode::kInvalidType, "/");
158 UEXPECT_THROW(InitSampleMessage("null", message), SampleError);
159 }
160
161 {
162 const auto json = formats::json::FromString("\"\"");
163 Message message;
164 Message sample;
165
166 message.add_paths("dump");
167
168 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
169 UASSERT_NO_THROW(InitSampleMessage("{}", sample));
170
171 EXPECT_TRUE(message.paths().empty());
172 CheckMessageEqual(message, sample);
173 }
174}
175
176TEST(FieldMaskFromJsonAdditionalTest, DynamicMessage) {
177 using Message = ::google::protobuf::FieldMask;
178
179 const char* json_str = "\"someField.anotherField,oneMore\"";
180 const auto json = formats::json::FromString(json_str);
181 ::google::protobuf::DynamicMessageFactory factory;
182
183 {
184 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
185
186 UASSERT_NO_THROW(JsonToMessage(json, *message));
187
188 const auto reflection = message->GetReflection();
189 const auto paths_desc = message->GetDescriptor()->FindFieldByName("paths");
190 std::unordered_multiset<std::string> paths;
191
192 for (int i = 0; i < reflection->FieldSize(*message, paths_desc); ++i) {
193 paths.insert(reflection->GetRepeatedString(*message, paths_desc, i));
194 }
195
196 EXPECT_EQ(paths, (std::unordered_multiset<std::string>{"some_field.another_field", "one_more"}));
197 }
198}
199
200} // namespace protobuf::json::tests
201
202USERVER_NAMESPACE_END