userver: /data/code/userver/libraries/grpc-protovalidate/tests/validator_service_test.cpp Source File
Loading...
Searching...
No Matches
validator_service_test.cpp
1#include <userver/utest/utest.hpp>
2
3#include <buf/validate/validate.pb.h>
4
5#include <userver/engine/async.hpp>
6#include <userver/ugrpc/client/exceptions.hpp>
7#include <userver/ugrpc/tests/service_fixtures.hpp>
8
9#include <types/unit_test_client.usrv.pb.hpp>
10#include <types/unit_test_service.usrv.pb.hpp>
11
12#include <grpc-protovalidate/server/middleware.hpp>
13#include "utils.hpp"
14
15USERVER_NAMESPACE_BEGIN
16
17namespace {
18
19class UnitTestServiceValidator final : public types::UnitTestServiceBase {
20public:
21 CheckConstraintsUnaryResult CheckConstraintsUnary(CallContext&, types::ConstrainedRequest&& request) override {
22 types::ConstrainedResponse response;
23 response.set_field(request.field() + 1);
24 return response;
25 }
26
27 CheckConstraintsStreamingResult CheckConstraintsStreaming(
28 CallContext&,
29 CheckConstraintsStreamingReaderWriter& stream
30 ) override {
31 types::ConstrainedRequest request;
32 while (stream.Read(request)) {
33 types::ConstrainedResponse response{};
34 response.set_field(request.field());
35 stream.Write(std::move(response));
36 }
37 return grpc::Status::OK;
38 }
39
40 CheckInvalidRequestConstraintsResult CheckInvalidRequestConstraints(CallContext&, types::InvalidConstraints&&)
41 override {
42 return google::protobuf::Empty{};
43 }
44};
45
46class GrpcServerValidatorTest
47 : public ugrpc::tests::ServiceWithClientFixture<UnitTestServiceValidator, types::UnitTestServiceClient>,
48 public testing::WithParamInterface<grpc_protovalidate::server::Settings> {
49public:
50 GrpcServerValidatorTest()
51 : ugrpc::tests::ServiceWithClientFixture<UnitTestServiceValidator, types::UnitTestServiceClient>(
52 ugrpc::server::ServerConfig{},
53 ugrpc::server::Middlewares{std::make_shared<grpc_protovalidate::server::Middleware>(GetParam())},
54 ugrpc::client::Middlewares{}
55 )
56 {}
57};
58
59} // namespace
60
61INSTANTIATE_UTEST_SUITE_P(
62 /*no prefix*/,
63 GrpcServerValidatorTest,
64 testing::Values(
65 grpc_protovalidate::server::Settings{
66 .per_method =
67 {{"types.UnitTestService/CheckConstraintsUnary",
68 {.fail_fast = false, .send_violations = true, .validate_responses = true}},
69 {"/UnknownMethod", {.fail_fast = true, .send_violations = false}}}
70 },
71 grpc_protovalidate::server::Settings{
72 .global = {.fail_fast = false, .send_violations = true},
73 .per_method =
74 {{"types.UnitTestService/CheckConstraintsUnary",
75 {.fail_fast = false, .send_violations = true, .validate_responses = true}},
76 {"/UnknownMethod", {.fail_fast = true, .send_violations = false}}}
77 },
78 grpc_protovalidate::server::Settings{
79 .global = {.fail_fast = true, .send_violations = true},
80 .per_method =
81 {{"types.UnitTestService/CheckConstraintsUnary",
82 {.fail_fast = false, .send_violations = true, .validate_responses = true}},
83 {"/UnknownMethod", {.fail_fast = true, .send_violations = false}}}
84 }
85 )
86);
87
88UTEST_P_MT(GrpcServerValidatorTest, AllValid, 2) {
89 constexpr std::size_t kRequestCount = 3;
90
91 std::vector<types::ConstrainedMessage> messages;
92 types::ConstrainedMessage msg;
93 msg.set_required_rule(1);
94 messages.push_back(std::move(msg));
95 messages.push_back(tests::CreateValidMessage(2));
96 messages.push_back(tests::CreateValidMessage(3));
97
98 std::vector<types::ConstrainedRequest> requests(kRequestCount);
99 for (std::size_t i = 0; i < kRequestCount; ++i) {
100 requests[i].set_field(static_cast<int32_t>(i));
101 requests[i].mutable_messages()->Add(messages.begin(), messages.end());
102 }
103
104 // check unary method
105 types::ConstrainedResponse response;
106 UASSERT_NO_THROW(response = GetClient().CheckConstraintsUnary(requests[0]));
107 EXPECT_TRUE(response.has_field());
108 EXPECT_EQ(response.field(), requests[0].field() + 1);
109
110 // check streaming method
111 auto stream = GetClient().CheckConstraintsStreaming();
112 auto write_task = engine::AsyncNoSpan([&stream, &requests] {
113 for (const auto& request : requests) {
114 const bool success = stream.Write(request);
115 if (!success) {
116 return false;
117 }
118 }
119
120 return stream.WritesDone();
121 });
122
123 std::vector<types::ConstrainedResponse> responses;
124 while (stream.Read(response)) {
125 responses.push_back(std::move(response));
126 }
127
128 ASSERT_TRUE(write_task.Get());
129 ASSERT_EQ(responses.size(), kRequestCount);
130
131 for (std::size_t i = 0; i < kRequestCount; ++i) {
132 EXPECT_TRUE(responses[i].has_field());
133 EXPECT_EQ(responses[i].field(), requests[i].field());
134 }
135}
136
137UTEST_P_MT(GrpcServerValidatorTest, AllInvalid, 2) {
138 constexpr std::size_t kRequestCount = 3;
139
140 types::ConstrainedRequest request;
141 request.set_field(1);
142
143 types::ConstrainedRequest invalid_request;
144 invalid_request.set_field(100);
145 *invalid_request.add_messages() = types::ConstrainedMessage{};
146 *invalid_request.add_messages() = tests::CreateInvalidMessage();
147
148 std::vector<types::ConstrainedRequest> requests(kRequestCount);
149 requests[0] = request;
150 requests[1] = invalid_request;
151 request.set_field(3);
152 requests[2] = request;
153
154 // check unary method
155 try {
156 [[maybe_unused]] auto response = GetClient().CheckConstraintsUnary(requests[1]);
157 ADD_FAILURE() << "Call must fail";
158 } catch (const ugrpc::client::InvalidArgumentError& err) {
159 auto violations = tests::GetViolations(err);
160 ASSERT_TRUE(violations);
161 EXPECT_EQ(violations->violations().size(), 20);
162 } catch (...) {
163 ADD_FAILURE() << "'InvalidArgumentError' exception expected";
164 }
165
166 // check streaming method
167 auto stream = GetClient().CheckConstraintsStreaming();
168 auto write_task = engine::AsyncNoSpan([&stream, &requests] {
169 for (const auto& request : requests) {
170 const bool success = stream.Write(request);
171 if (!success) {
172 return false;
173 }
174 }
175
176 return stream.WritesDone();
177 });
178
179 types::ConstrainedResponse response;
180
181 ASSERT_TRUE(stream.Read(response));
182 EXPECT_EQ(response.field(), 1);
183
184 try {
185 [[maybe_unused]] const bool result = stream.Read(response);
186 ADD_FAILURE() << "Call must fail";
187 } catch (const ugrpc::client::InvalidArgumentError& err) {
188 const auto& streaming_settings = GetParam().Get("types.UnitTestService/CheckConstraintsStreaming");
189 auto violations = tests::GetViolations(err);
190
191 if (streaming_settings.send_violations) {
192 ASSERT_TRUE(violations);
193
194 if (streaming_settings.fail_fast) {
195 EXPECT_EQ(violations->violations().size(), 1);
196 } else {
197 EXPECT_EQ(violations->violations().size(), 20);
198 }
199 } else {
200 EXPECT_FALSE(violations);
201 }
202 } catch (...) {
203 ADD_FAILURE() << "'InvalidArgumentError' exception expected";
204 }
205
206 write_task.Get();
207}
208
209UTEST_P(GrpcServerValidatorTest, InvalidConstraints) {
210 types::InvalidConstraints request;
211 request.set_field(1);
212
213 UEXPECT_THROW_MSG(
214 GetClient().CheckInvalidRequestConstraints(std::move(request)),
215 ugrpc::client::InternalError,
216 "'types.UnitTestService/CheckInvalidRequestConstraints' failed: code=INTERNAL, message='Message "
217 "'types.InvalidConstraints' validation error: internal protovalidate error (check constraints syntax in the "
218 "proto file) - INVALID_ARGUMENT: no_such_field : non_existent_field'"
219 );
220}
221
222UTEST_P(GrpcServerValidatorTest, InvalidResponse) {
223 types::ConstrainedRequest request;
224 request.set_field(10);
225
226 UEXPECT_THROW_MSG(
227 GetClient().CheckConstraintsUnary(std::move(request)),
228 ugrpc::client::DataLossError,
229 "Message 'types.ConstrainedResponse' validation error: 1 constraint(s) violated"
230 );
231}
232
233USERVER_NAMESPACE_END