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, expected_message, sample_message;
103 formats::json::Value input = PrepareJsonTestData(param.input);
104 expected_message = PrepareTestData(param.expected_message);
105
106 message.mutable_field1()->add_paths("dump");
107
108 UASSERT_NO_THROW((message = JsonToMessage<proto_json::messages::FieldMaskMessage>(input, param.options)));
109 UASSERT_NO_THROW(InitSampleMessage(param.input, sample_message, param.options));
110
111 CheckMessageEqual(message, sample_message);
112 CheckMessageEqual(message, expected_message);
113}
114
115TEST_P(FieldMaskFromJsonFailureTest, Test) {
116 const auto& param = GetParam();
117
118 proto_json::messages::FieldMaskMessage sample_message;
119 formats::json::Value input = PrepareJsonTestData(param.input);
120
122 (void)JsonToMessage<proto_json::messages::FieldMaskMessage>(input, param.options),
123 param.expected_errc,
124 param.expected_path
125 );
126
127 if (!param.skip_native_check) {
128 UEXPECT_THROW(InitSampleMessage(param.input, sample_message, param.options), SampleError);
129 }
130}
131
132TEST(FieldMaskFromJsonAdditionalTest, InlinedNonNull) {
133 using Message = ::google::protobuf::FieldMask;
134
135 const char* json_str = "\"someField.anotherField,oneMore\"";
136 const auto json = formats::json::FromString(json_str);
137 Message message, sample;
138
139 message.add_paths("dump");
140
141 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
142 UASSERT_NO_THROW(InitSampleMessage(json_str, sample));
143
144 CheckMessageEqual(message, sample);
145}
146
147TEST(FieldMaskFromJsonAdditionalTest, InlinedNull) {
148 using Message = ::google::protobuf::FieldMask;
149
150 {
151 const auto json = formats::json::FromString("null");
152 Message message;
153
154 EXPECT_PARSE_ERROR((message = JsonToMessage<Message>(json)), ParseErrorCode::kInvalidType, "/");
155 UEXPECT_THROW(InitSampleMessage("null", message), SampleError);
156 }
157
158 {
159 const auto json = formats::json::FromString("\"\"");
160 Message message, sample;
161
162 message.add_paths("dump");
163
164 UASSERT_NO_THROW((message = JsonToMessage<Message>(json)));
165 UASSERT_NO_THROW(InitSampleMessage("{}", sample));
166
167 EXPECT_TRUE(message.paths().empty());
168 CheckMessageEqual(message, sample);
169 }
170}
171
172TEST(FieldMaskFromJsonAdditionalTest, DynamicMessage) {
173 using Message = ::google::protobuf::FieldMask;
174
175 const char* json_str = "\"someField.anotherField,oneMore\"";
176 const auto json = formats::json::FromString(json_str);
177 ::google::protobuf::DynamicMessageFactory factory;
178
179 {
180 std::unique_ptr<::google::protobuf::Message> message(factory.GetPrototype(Message::descriptor())->New());
181
182 UASSERT_NO_THROW(JsonToMessage(json, *message));
183
184 const auto reflection = message->GetReflection();
185 const auto paths_desc = message->GetDescriptor()->FindFieldByName("paths");
186 std::unordered_multiset<std::string> paths;
187
188 for (int i = 0; i < reflection->FieldSize(*message, paths_desc); ++i) {
189 paths.insert(reflection->GetRepeatedString(*message, paths_desc, i));
190 }
191
192 EXPECT_EQ(paths, (std::unordered_multiset<std::string>{"some_field.another_field", "one_more"}));
193 }
194}
195
196} // namespace protobuf::json::tests
197
198USERVER_NAMESPACE_END