userver: /data/code/userver/libraries/protobuf/tests/json/field_mask_to_json_test.cpp Source File
Loading...
Searching...
No Matches
field_mask_to_json_test.cpp
1#include <gtest/gtest.h>
2
3#include <algorithm>
4#include <memory>
5#include <ostream>
6#include <string>
7#include <vector>
8
9#include <fmt/format.h>
10#include <fmt/ranges.h>
11#include <google/protobuf/dynamic_message.h>
12
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 FieldMaskToJsonSuccessTestParam {
23 FieldMaskMessageData input = {};
24 std::string expected_json = {};
25 PrintOptions options = {};
26};
27
28struct FieldMaskToJsonFailureTestParam {
29 FieldMaskMessageData input = {};
30 PrintErrorCode expected_errc = {};
31 std::string expected_path = {};
32 PrintOptions options = {};
33};
34
35std::vector<std::string> ParseFieldMaskStr(std::string_view paths) {
36 std::vector<std::string> result;
37 std::size_t pos = 0;
38
39 while (true) {
40 auto comma_pos = paths.find(',', pos);
41
42 if (comma_pos == std::string_view::npos) {
43 result.emplace_back(paths.substr(pos));
44 break;
45 }
46
47 result.emplace_back(paths.substr(pos, comma_pos - pos));
48 pos = comma_pos + 1;
49 }
50
51 return result;
52}
53
54void PrintTo(const FieldMaskToJsonSuccessTestParam& param, std::ostream* os) {
55 if (param.input.field1) {
56 *os << fmt::format("{{ input = {{.field1='{}'}} }}", param.input.field1.value());
57 } else {
58 *os << fmt::format("{{ input = {{.field1=nullopt}} }}");
59 }
60}
61
62void PrintTo(const FieldMaskToJsonFailureTestParam& param, std::ostream* os) {
63 if (param.input.field1) {
64 *os << fmt::format("{{ input = {{.field1='{}'}} }}", param.input.field1.value());
65 } else {
66 *os << fmt::format("{{ input = {{.field1=nullopt}} }}");
67 }
68}
69
70class FieldMaskToJsonSuccessTest : public ::testing::TestWithParam<FieldMaskToJsonSuccessTestParam> {};
71class FieldMaskToJsonFailureTest : public ::testing::TestWithParam<FieldMaskToJsonFailureTestParam> {};
72
73INSTANTIATE_TEST_SUITE_P(
74 ,
75 FieldMaskToJsonSuccessTest,
76 ::testing::Values(
77 FieldMaskToJsonSuccessTestParam{FieldMaskMessageData{}, R"({})"},
78 FieldMaskToJsonSuccessTestParam{FieldMaskMessageData{std::vector<std::string>{}}, R"({"field1":""})"},
79 FieldMaskToJsonSuccessTestParam{FieldMaskMessageData{std::vector<std::string>{""}}, R"({"field1":""})"},
80 FieldMaskToJsonSuccessTestParam{
81 FieldMaskMessageData{std::vector<std::string>{"", "", "", "aaa", ""}},
82 R"({"field1":",,,aaa,"})"
83 },
84 FieldMaskToJsonSuccessTestParam{
85 FieldMaskMessageData{std::vector<std::string>{"some_field"}},
86 R"({"field1":"someField"})",
87 {.preserve_proto_field_names = true} // does not affect field mask serialization!
88 },
89 FieldMaskToJsonSuccessTestParam{
90 FieldMaskMessageData{std::vector<std::string>{"some_field.another_field..one_more", "_a_b0_c"}},
91 R"({"field1":"someField.anotherField..oneMore,AB0C"})"
92 }
93 )
94);
95
96// Protobuf ProtoJSON in legacy mode does not return error on invalid characters, which we
97// want to prohibit (because we do not want our clients to use syntax that may break in the
98// newer protobuf versions).
99
100INSTANTIATE_TEST_SUITE_P(
101 ,
102 FieldMaskToJsonFailureTest,
103 ::testing::Values(
104 FieldMaskToJsonFailureTestParam{
105 FieldMaskMessageData{std::vector<std::string>{"Some_field"}},
106 PrintErrorCode::kInvalidValue,
107 "field1"
108 },
109 FieldMaskToJsonFailureTestParam{
110 FieldMaskMessageData{std::vector<std::string>{"some_Field"}},
111 PrintErrorCode::kInvalidValue,
112 "field1"
113 },
114 FieldMaskToJsonFailureTestParam{
115 FieldMaskMessageData{std::vector<std::string>{"some_fielD"}},
116 PrintErrorCode::kInvalidValue,
117 "field1"
118 },
119 FieldMaskToJsonFailureTestParam{
120 FieldMaskMessageData{std::vector<std::string>{"some_f!ield"}},
121 PrintErrorCode::kInvalidValue,
122 "field1"
123 },
124 FieldMaskToJsonFailureTestParam{
125 FieldMaskMessageData{std::vector<std::string>{"__some_field"}},
126 PrintErrorCode::kInvalidValue,
127 "field1"
128 },
129 FieldMaskToJsonFailureTestParam{
130 FieldMaskMessageData{std::vector<std::string>{"some__field"}},
131 PrintErrorCode::kInvalidValue,
132 "field1"
133 },
134 FieldMaskToJsonFailureTestParam{
135 FieldMaskMessageData{std::vector<std::string>{"some_field_"}},
136 PrintErrorCode::kInvalidValue,
137 "field1"
138 },
139 FieldMaskToJsonFailureTestParam{
140 FieldMaskMessageData{std::vector<std::string>{"some_0field"}},
141 PrintErrorCode::kInvalidValue,
142 "field1"
143 }
144 )
145);
146
147TEST_P(FieldMaskToJsonSuccessTest, Test) {
148 const auto& param = GetParam();
149
150 auto input = PrepareTestData(param.input);
151 formats::json::Value json;
152 formats::json::Value expected_json;
153 formats::json::Value sample_json;
154
155 UASSERT_NO_THROW((json = MessageToJson(input, param.options)));
156 UASSERT_NO_THROW((expected_json = PrepareJsonTestData(param.expected_json)));
157 UASSERT_NO_THROW((sample_json = CreateSampleJson(input, param.options)));
158
159 if (!json.HasMember("field1")) {
160 EXPECT_FALSE(expected_json.HasMember("field1"));
161 EXPECT_FALSE(sample_json.HasMember("field1"));
162 return;
163 }
164
165 ASSERT_TRUE(json["field1"].IsString());
166 ASSERT_TRUE(expected_json["field1"].IsString());
167 ASSERT_TRUE(sample_json["field1"].IsString());
168
169 auto json_paths = ParseFieldMaskStr(json["field1"].As<std::string>());
170 auto expected_json_paths = ParseFieldMaskStr(expected_json["field1"].As<std::string>());
171 auto sample_json_paths = ParseFieldMaskStr(sample_json["field1"].As<std::string>());
172
173 std::sort(json_paths.begin(), json_paths.end());
174 std::sort(expected_json_paths.begin(), expected_json_paths.end());
175 std::sort(sample_json_paths.begin(), sample_json_paths.end());
176
177 EXPECT_EQ(json_paths, expected_json_paths);
178 EXPECT_EQ(expected_json_paths, sample_json_paths);
179}
180
181TEST_P(FieldMaskToJsonFailureTest, Test) {
182 const auto& param = GetParam();
183 auto input = PrepareTestData(param.input);
184
185 EXPECT_PRINT_ERROR((void)MessageToJson(input, param.options), param.expected_errc, param.expected_path);
186}
187
188TEST(FieldMaskToJsonAdditionalTest, InlinedNonNull) {
189 FieldMaskMessageData data{std::vector<std::string>{"some_field.nested_field"}};
190 auto message = PrepareTestData(data);
191 formats::json::Value json;
192 formats::json::Value sample;
193
194 UASSERT_NO_THROW((json = MessageToJson(message.field1(), {})));
195 UASSERT_NO_THROW((sample = CreateSampleJson(message.field1())));
196 ASSERT_TRUE(json.IsString());
197 ASSERT_TRUE(sample.IsString());
198
199 auto paths = ParseFieldMaskStr(json.As<std::string>());
200 auto sample_paths = ParseFieldMaskStr(sample.As<std::string>());
201
202 std::sort(paths.begin(), paths.end());
203 std::sort(sample_paths.begin(), sample_paths.end());
204
205 EXPECT_EQ(paths, sample_paths);
206 EXPECT_EQ(paths, std::vector<std::string>{"someField.nestedField"});
207}
208
209TEST(FieldMaskToJsonAdditionalTest, InlinedNull) {
211 auto message = PrepareTestData(data);
212 formats::json::Value json;
213 formats::json::Value sample;
214
215 UASSERT_NO_THROW((json = MessageToJson(message.field1(), {})));
216 UASSERT_NO_THROW((sample = CreateSampleJson(message.field1())));
217 ASSERT_TRUE(json.IsString());
218 ASSERT_TRUE(sample.IsString());
219 EXPECT_TRUE(json.As<std::string>().empty());
220 EXPECT_TRUE(sample.As<std::string>().empty());
221}
222
223TEST(FieldMaskToJsonAdditionalTest, DynamicMessage) {
224 using Message = ::google::protobuf::FieldMask;
225 ::google::protobuf::DynamicMessageFactory factory;
226
227 {
228 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
229 const auto reflection = message->GetReflection();
230 const auto paths_desc = message->GetDescriptor()->FindFieldByName("paths");
231
232 reflection->AddString(message.get(), paths_desc, "some_field.another_field");
233 reflection->AddString(message.get(), paths_desc, "one_more");
234
235 formats::json::Value json;
236
237 UASSERT_NO_THROW((json = MessageToJson(*message, {})));
238 ASSERT_TRUE(json.IsString());
239
240 auto paths = ParseFieldMaskStr(json.As<std::string>());
241 std::sort(paths.begin(), paths.end());
242
243 ASSERT_EQ(paths.size(), std::size_t{2});
244 EXPECT_EQ(paths[0], "oneMore");
245 EXPECT_EQ(paths[1], "someField.anotherField");
246 }
247}
248
249} // namespace protobuf::json::tests
250
251USERVER_NAMESPACE_END