1#include <userver/utest/utest.hpp>
3#include <buf/validate/validate.pb.h>
5#include <userver/engine/async.hpp>
6#include <userver/ugrpc/client/exceptions.hpp>
7#include <userver/ugrpc/tests/service_fixtures.hpp>
9#include <types/unit_test_client.usrv.pb.hpp>
10#include <types/unit_test_service.usrv.pb.hpp>
12#include <grpc-protovalidate/server/middleware.hpp>
15USERVER_NAMESPACE_BEGIN
19class UnitTestServiceValidator
final :
public types::UnitTestServiceBase {
21 CheckConstraintsUnaryResult CheckConstraintsUnary(CallContext&, types::ConstrainedRequest&& request)
override {
22 types::ConstrainedResponse response;
23 response.set_field(request.field() + 1);
27 CheckConstraintsStreamingResult CheckConstraintsStreaming(
29 CheckConstraintsStreamingReaderWriter& stream
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));
37 return grpc::Status::OK;
40 CheckInvalidRequestConstraintsResult CheckInvalidRequestConstraints(CallContext&, types::InvalidConstraints&&)
42 return google::protobuf::Empty{};
46class GrpcServerValidatorTest
47 :
public ugrpc::tests::ServiceWithClientFixture<UnitTestServiceValidator, types::UnitTestServiceClient>,
48 public testing::WithParamInterface<grpc_protovalidate::server::Settings> {
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{}
61INSTANTIATE_UTEST_SUITE_P(
63 GrpcServerValidatorTest,
65 grpc_protovalidate::server::Settings{
67 {{
"types.UnitTestService/CheckConstraintsUnary",
68 {.fail_fast =
false, .send_violations =
true, .validate_responses =
true}},
69 {
"/UnknownMethod", {.fail_fast =
true, .send_violations =
false}}}
71 grpc_protovalidate::server::Settings{
72 .global = {.fail_fast =
false, .send_violations =
true},
74 {{
"types.UnitTestService/CheckConstraintsUnary",
75 {.fail_fast =
false, .send_violations =
true, .validate_responses =
true}},
76 {
"/UnknownMethod", {.fail_fast =
true, .send_violations =
false}}}
78 grpc_protovalidate::server::Settings{
79 .global = {.fail_fast =
true, .send_violations =
true},
81 {{
"types.UnitTestService/CheckConstraintsUnary",
82 {.fail_fast =
false, .send_violations =
true, .validate_responses =
true}},
83 {
"/UnknownMethod", {.fail_fast =
true, .send_violations =
false}}}
88UTEST_P_MT(GrpcServerValidatorTest, AllValid, 2) {
89 constexpr std::size_t kRequestCount = 3;
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));
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());
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);
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);
120 return stream.WritesDone();
123 std::vector<types::ConstrainedResponse> responses;
124 while (stream.Read(response)) {
125 responses.push_back(std::move(response));
128 ASSERT_TRUE(write_task.Get());
129 ASSERT_EQ(responses.size(), kRequestCount);
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());
137UTEST_P_MT(GrpcServerValidatorTest, AllInvalid, 2) {
138 constexpr std::size_t kRequestCount = 3;
140 types::ConstrainedRequest request;
141 request.set_field(1);
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();
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;
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);
163 ADD_FAILURE() <<
"'InvalidArgumentError' exception expected";
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);
176 return stream.WritesDone();
179 types::ConstrainedResponse response;
181 ASSERT_TRUE(stream.Read(response));
182 EXPECT_EQ(response.field(), 1);
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);
191 if (streaming_settings.send_violations) {
192 ASSERT_TRUE(violations);
194 if (streaming_settings.fail_fast) {
195 EXPECT_EQ(violations->violations().size(), 1);
197 EXPECT_EQ(violations->violations().size(), 20);
200 EXPECT_FALSE(violations);
203 ADD_FAILURE() <<
"'InvalidArgumentError' exception expected";
209UTEST_P(GrpcServerValidatorTest, InvalidConstraints) {
210 types::InvalidConstraints request;
211 request.set_field(1);
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'"
222UTEST_P(GrpcServerValidatorTest, InvalidResponse) {
223 types::ConstrainedRequest request;
224 request.set_field(10);
227 GetClient().CheckConstraintsUnary(std::move(request)),
228 ugrpc::client::DataLossError,
229 "Message 'types.ConstrainedResponse' validation error: 1 constraint(s) violated"