userver: /data/code/userver/libraries/protobuf/tests/json/repeated_from_json_test.cpp Source File
Loading...
Searching...
No Matches
repeated_from_json_test.cpp
1#include <gtest/gtest.h>
2
3#include <ostream>
4#include <string>
5
6#include <fmt/format.h>
7
8#include <userver/protobuf/json/convert.hpp>
9#include <userver/utest/assert_macros.hpp>
10
11#include "utils.hpp"
12
13USERVER_NAMESPACE_BEGIN
14
15namespace protobuf::json::tests {
16
17struct RepeatedFromJsonSuccessTestParam {
18 std::string input = {};
19 RepeatedMessageData expected_message = {};
20 ParseOptions options = {};
21};
22
23struct RepeatedFromJsonFailureTestParam {
24 std::string input = {};
25 ParseErrorCode expected_errc = {};
26 std::string expected_path = {};
27 ParseOptions options = {};
28
29 // Protobuf ProtoJSON legacy syntax supports some features which we want to prohibit (because
30 // we do not want our clients to use syntax that may break in the newer protobuf versions). This
31 // variable is used disable some checks that will fail for legacy syntax.
32 bool skip_native_check = false;
33};
34
35void PrintTo(const RepeatedFromJsonSuccessTestParam& param, std::ostream* os) {
36 *os << fmt::format("{{ input = '{}' }}", param.input);
37}
38
39void PrintTo(const RepeatedFromJsonFailureTestParam& param, std::ostream* os) {
40 *os << fmt::format("{{ input = '{}' }}", param.input);
41}
42
43class RepeatedFromJsonSuccessTest : public ::testing::TestWithParam<RepeatedFromJsonSuccessTestParam> {};
44class RepeatedFromJsonFailureTest : public ::testing::TestWithParam<RepeatedFromJsonFailureTestParam> {};
45
46INSTANTIATE_TEST_SUITE_P(
47 ,
48 RepeatedFromJsonSuccessTest,
49 ::testing::Values(
50 RepeatedFromJsonSuccessTestParam{R"({})", RepeatedMessageData{}},
51 RepeatedFromJsonSuccessTestParam{
52 R"({
53 "field1":[],"field2":[],"field3":[],"field4":[],"field5":[],"field6":[],"field7":[],
54 "field8":[],"field9":[],"field10":[],"field11":[],"field12":[]
55 })",
56 RepeatedMessageData{}
57 },
58 RepeatedFromJsonSuccessTestParam{
59 // Can't use "field4:null" because native legacy parser treats this as a single item array.
60 R"({
61 "field1":null,"field2":null,"field3":null,"field5":null,"field6":null,"field7":null,"field8":null,
62 "field9":null,"field10":null,"field11":null,"field12":null
63 })",
64 RepeatedMessageData{}
65 },
66 RepeatedFromJsonSuccessTestParam{
67 R"({
68 "field1":[100],
69 "field2":[{"field1":true}],
70 "field3":["123.987s"],
71 "field4":[null],
72 "field5":[1],
73 "field6":["-1"],
74 "field7":["1"],
75 "field8":["-1.5"],
76 "field9":[1.5],
77 "field10":[false],
78 "field11":["hello"],
79 "field12":["TEST_VALUE1"]
80 })",
81 RepeatedMessageData{
82 {100},
83 {{true}},
84 {{.seconds = 123, .nanos = 987'000'000}},
85 {ProtoValue{ProtoNullValue{}}},
86 {1},
87 {-1},
88 {1},
89 {-1.5},
90 {1.5},
91 {false},
92 {"hello"},
93 {proto_json::messages::RepeatedMessage::TEST_VALUE1}
94 }
95 },
96 RepeatedFromJsonSuccessTestParam{
97 R"({
98 "field1":[100,0,200],
99 "field2":[{"field1":true},{"field1":false}],
100 "field3":["123.987s","0s","-987s"]
101 })",
102 RepeatedMessageData{
103 {100, 0, 200},
104 {{true}, {false}},
105 {{.seconds = 123, .nanos = 987'000'000}, {}, {.seconds = -987}}
106 }
107 }
108 )
109);
110
111INSTANTIATE_TEST_SUITE_P(
112 ,
113 RepeatedFromJsonFailureTest,
114 ::testing::Values(
115 RepeatedFromJsonFailureTestParam{R"({"field1":{}})", ParseErrorCode::kInvalidType, "field1"},
116 RepeatedFromJsonFailureTestParam{
117 R"({"field1":1})",
119 "field1",
120 {},
121 // Legacy implementation treats single value as array of one item.
122 true
123 },
124 RepeatedFromJsonFailureTestParam{R"({"field2":true})", ParseErrorCode::kInvalidType, "field2", {}, true},
125 RepeatedFromJsonFailureTestParam{R"({"field3":"test"})", ParseErrorCode::kInvalidType, "field3", {}, true},
126 RepeatedFromJsonFailureTestParam{
127 R"({"field1":[1, null, 2]})",
129 "field1[1]",
130 {},
131 // Legacy implementation ignores null as items.
132 true
133 },
134 RepeatedFromJsonFailureTestParam{R"({"field2":[null]})", ParseErrorCode::kInvalidValue, "field2[0]", {}, true},
135 RepeatedFromJsonFailureTestParam{
136 R"({"field3":["123.100s", "-123.100s", null]})",
138 "field3[2]",
139 {},
140 // Legacy implementation ignores null as items.
141 true
142 },
143 RepeatedFromJsonFailureTestParam{
144 R"({"field1":[[1,2,3]]})",
146 "field1[0]",
147 {},
148 // Legacy implementation flattens array in this case.
149 true
150 },
151 RepeatedFromJsonFailureTestParam{
152 R"({"field2":[[{"field1":true}]]})",
154 "field2[0]",
155 {},
156 // Legacy implementation flattens array in this case.
157 true
158 },
159 RepeatedFromJsonFailureTestParam{
160 R"({"field2":[{"field1":true},"oops"]})",
162 "field2[1]"
163 },
164 RepeatedFromJsonFailureTestParam{
165 R"({"field3":["123.100s", "oops"]})",
167 "field3[1]"
168 }
169 )
170);
171
172TEST_P(RepeatedFromJsonSuccessTest, Test) {
173 using Message = proto_json::messages::RepeatedMessage;
174 const auto& param = GetParam();
175
176 Message message;
177 Message expected_message;
178 Message sample_message;
179 formats::json::Value input = PrepareJsonTestData(param.input);
180 expected_message = PrepareTestData(param.expected_message);
181
182 UASSERT_NO_THROW((message = JsonToMessage<Message>(input, param.options)));
183 UASSERT_NO_THROW(InitSampleMessage(param.input, sample_message, param.options));
184
185 CheckMessageEqual(message, sample_message);
186 CheckMessageEqual(message, expected_message);
187}
188
189TEST_P(RepeatedFromJsonFailureTest, Test) {
190 using Message = proto_json::messages::RepeatedMessage;
191 const auto& param = GetParam();
192
193 Message sample;
194 formats::json::Value input = PrepareJsonTestData(param.input);
195
196 EXPECT_PARSE_ERROR((void)JsonToMessage<Message>(input, param.options), param.expected_errc, param.expected_path);
197
198 if (!param.skip_native_check) {
199 UEXPECT_THROW(InitSampleMessage(param.input, sample, param.options), SampleError);
200 }
201}
202
203} // namespace protobuf::json::tests
204
205USERVER_NAMESPACE_END