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