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 .server_middlewares = {std::make_shared<grpc_protovalidate::server::Middleware>(GetParam())},
53 })
54 {}
55};
56
57} // namespace
58
60 /*no prefix*/,
61 GrpcServerValidatorTest,
62 testing::Values(
63 grpc_protovalidate::server::Settings{
64 .per_method =
65 {{"types.UnitTestService/CheckConstraintsUnary",
66 {.fail_fast = false, .send_violations = true, .validate_responses = true}},
67 {"/UnknownMethod", {.fail_fast = true, .send_violations = false}}}
68 },
69 grpc_protovalidate::server::Settings{
70 .global = {.fail_fast = false, .send_violations = true},
71 .per_method =
72 {{"types.UnitTestService/CheckConstraintsUnary",
73 {.fail_fast = false, .send_violations = true, .validate_responses = true}},
74 {"/UnknownMethod", {.fail_fast = true, .send_violations = false}}}
75 },
76 grpc_protovalidate::server::Settings{
77 .global = {.fail_fast = true, .send_violations = true},
78 .per_method =
79 {{"types.UnitTestService/CheckConstraintsUnary",
80 {.fail_fast = false, .send_violations = true, .validate_responses = true}},
81 {"/UnknownMethod", {.fail_fast = true, .send_violations = false}}}
82 }
83 )
84);
85
86UTEST_P_MT(GrpcServerValidatorTest, AllValid, 2) {
87 constexpr std::size_t kRequestCount = 3;
88
89 std::vector<types::ConstrainedMessage> messages;
90 types::ConstrainedMessage msg;
91 msg.set_required_rule(1);
92 messages.push_back(std::move(msg));
93 messages.push_back(tests::CreateValidMessage(2));
94 messages.push_back(tests::CreateValidMessage(3));
95
96 std::vector<types::ConstrainedRequest> requests(kRequestCount);
97 for (std::size_t i = 0; i < kRequestCount; ++i) {
98 requests[i].set_field(static_cast<int32_t>(i));
99 requests[i].mutable_messages()->Add(messages.begin(), messages.end());
100 }
101
102 // check unary method
103 types::ConstrainedResponse response;
104 UASSERT_NO_THROW(response = GetClient().CheckConstraintsUnary(requests[0]));
105 EXPECT_TRUE(response.has_field());
106 EXPECT_EQ(response.field(), requests[0].field() + 1);
107
108 // check streaming method
109 auto stream = GetClient().CheckConstraintsStreaming();
110 auto write_task = engine::AsyncNoTracing([&stream, &requests] {
111 for (const auto& request : requests) {
112 const bool success = stream.Write(request);
113 if (!success) {
114 return false;
115 }
116 }
117
118 return stream.WritesDone();
119 });
120
121 std::vector<types::ConstrainedResponse> responses;
122 while (stream.Read(response)) {
123 responses.push_back(std::move(response));
124 }
125
126 ASSERT_TRUE(write_task.Get());
127 ASSERT_EQ(responses.size(), kRequestCount);
128
129 for (std::size_t i = 0; i < kRequestCount; ++i) {
130 EXPECT_TRUE(responses[i].has_field());
131 EXPECT_EQ(responses[i].field(), requests[i].field());
132 }
133}
134
135UTEST_P_MT(GrpcServerValidatorTest, AllInvalid, 2) {
136 constexpr std::size_t kRequestCount = 3;
137
138 types::ConstrainedRequest request;
139 request.set_field(1);
140
141 types::ConstrainedRequest invalid_request;
142 invalid_request.set_field(100);
143 *invalid_request.add_messages() = types::ConstrainedMessage{};
144 *invalid_request.add_messages() = tests::CreateInvalidMessage();
145
146 std::vector<types::ConstrainedRequest> requests(kRequestCount);
147 requests[0] = request;
148 requests[1] = invalid_request;
149 request.set_field(3);
150 requests[2] = request;
151
152 // check unary method
153 try {
154 [[maybe_unused]] auto response = GetClient().CheckConstraintsUnary(requests[1]);
155 ADD_FAILURE() << "Call must fail";
156 } catch (const ugrpc::client::InvalidArgumentError& err) {
157 auto violations = tests::GetViolations(err);
158 ASSERT_TRUE(violations);
159 EXPECT_EQ(violations->violations().size(), 20);
160 } catch (...) {
161 ADD_FAILURE() << "'InvalidArgumentError' exception expected";
162 }
163
164 // check streaming method
165 auto stream = GetClient().CheckConstraintsStreaming();
166 auto write_task = engine::AsyncNoTracing([&stream, &requests] {
167 for (const auto& request : requests) {
168 const bool success = stream.Write(request);
169 if (!success) {
170 return false;
171 }
172 }
173
174 return stream.WritesDone();
175 });
176
177 types::ConstrainedResponse response;
178
179 ASSERT_TRUE(stream.Read(response));
180 EXPECT_EQ(response.field(), 1);
181
182 try {
183 [[maybe_unused]] const bool result = stream.Read(response);
184 ADD_FAILURE() << "Call must fail";
185 } catch (const ugrpc::client::InvalidArgumentError& err) {
186 const auto& streaming_settings = GetParam().Get("types.UnitTestService/CheckConstraintsStreaming");
187 auto violations = tests::GetViolations(err);
188
189 if (streaming_settings.send_violations) {
190 ASSERT_TRUE(violations);
191
192 if (streaming_settings.fail_fast) {
193 EXPECT_EQ(violations->violations().size(), 1);
194 } else {
195 EXPECT_EQ(violations->violations().size(), 20);
196 }
197 } else {
198 EXPECT_FALSE(violations);
199 }
200 } catch (...) {
201 ADD_FAILURE() << "'InvalidArgumentError' exception expected";
202 }
203
204 write_task.Get();
205}
206
207UTEST_P(GrpcServerValidatorTest, InvalidConstraints) {
208 types::InvalidConstraints request;
209 request.set_field(1);
210
212 GetClient().CheckInvalidRequestConstraints(std::move(request)),
213 ugrpc::client::InternalError,
214 "'types.UnitTestService/CheckInvalidRequestConstraints' failed: code=INTERNAL, message='Message "
215 "'types.InvalidConstraints' validation error: internal protovalidate error (check constraints syntax in the "
216 "proto file) - INVALID_ARGUMENT: no_such_field : non_existent_field'"
217 );
218}
219
220UTEST_P(GrpcServerValidatorTest, InvalidResponse) {
221 types::ConstrainedRequest request;
222 request.set_field(10);
223
225 GetClient().CheckConstraintsUnary(std::move(request)),
226 ugrpc::client::DataLossError,
227 "Message 'types.ConstrainedResponse' validation error: 1 constraint(s) violated"
228 );
229}
230
231USERVER_NAMESPACE_END