1# Copyright 2015 gRPC authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15require 'spec_helper' 16require 'grpc/generic/rpc_desc' 17 18describe GRPC::RpcDesc do 19 RpcDesc = GRPC::RpcDesc 20 Stream = RpcDesc::Stream 21 OK = GRPC::Core::StatusCodes::OK 22 INTERNAL = GRPC::Core::StatusCodes::INTERNAL 23 UNKNOWN = GRPC::Core::StatusCodes::UNKNOWN 24 CallError = GRPC::Core::CallError 25 26 before(:each) do 27 @request_response = RpcDesc.new('rr', Object.new, Object.new, 'encode', 28 'decode') 29 @client_streamer = RpcDesc.new('cs', Stream.new(Object.new), Object.new, 30 'encode', 'decode') 31 @server_streamer = RpcDesc.new('ss', Object.new, Stream.new(Object.new), 32 'encode', 'decode') 33 @bidi_streamer = RpcDesc.new('ss', Stream.new(Object.new), 34 Stream.new(Object.new), 'encode', 'decode') 35 @bs_code = INTERNAL 36 @ok_response = Object.new 37 end 38 39 shared_examples 'it handles errors' do 40 it 'sends the specified status if BadStatus is raised' do 41 expect(@call).to receive(:read_unary_request).once.and_return(Object.new) 42 expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false, 43 metadata: {}) 44 this_desc.run_server_method(@call, method(:bad_status)) 45 end 46 47 it 'sends status UNKNOWN if other StandardErrors are raised' do 48 expect(@call).to receive(:read_unary_request).once.and_return(Object.new) 49 expect(@call).to receive(:send_status).once.with(UNKNOWN, 50 arg_error_msg, 51 false, metadata: {}) 52 this_desc.run_server_method(@call, method(:other_error)) 53 end 54 55 it 'sends status UNKNOWN if NotImplementedErrors are raised' do 56 expect(@call).to receive(:read_unary_request).once.and_return(Object.new) 57 expect(@call).to receive(:send_status).once.with( 58 UNKNOWN, not_implemented_error_msg, false, metadata: {}) 59 this_desc.run_server_method(@call, method(:not_implemented)) 60 end 61 62 it 'absorbs CallError with no further action' do 63 expect(@call).to receive(:read_unary_request).once.and_raise(CallError) 64 blk = proc do 65 this_desc.run_server_method(@call, method(:fake_reqresp)) 66 end 67 expect(&blk).to_not raise_error 68 end 69 end 70 71 describe '#run_server_method' do 72 let(:fake_md) { { k1: 'v1', k2: 'v2' } } 73 describe 'for request responses' do 74 let(:this_desc) { @request_response } 75 before(:each) do 76 @call = double('active_call') 77 allow(@call).to receive(:single_req_view).and_return(@call) 78 allow(@call).to receive(:output_metadata).and_return(@call) 79 end 80 81 it_behaves_like 'it handles errors' 82 83 it 'sends a response and closes the stream if there no errors' do 84 req = Object.new 85 expect(@call).to receive(:read_unary_request).once.and_return(req) 86 expect(@call).to receive(:output_metadata).once.and_return(fake_md) 87 expect(@call).to receive(:server_unary_response).once 88 .with(@ok_response, trailing_metadata: fake_md) 89 90 this_desc.run_server_method(@call, method(:fake_reqresp)) 91 end 92 end 93 94 describe 'for client streamers' do 95 before(:each) do 96 @call = double('active_call') 97 allow(@call).to receive(:multi_req_view).and_return(@call) 98 end 99 100 it 'sends the specified status if BadStatus is raised' do 101 expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false, 102 metadata: {}) 103 @client_streamer.run_server_method(@call, method(:bad_status_alt)) 104 end 105 106 it 'sends status UNKNOWN if other StandardErrors are raised' do 107 expect(@call).to receive(:send_status).once.with(UNKNOWN, arg_error_msg, 108 false, metadata: {}) 109 @client_streamer.run_server_method(@call, method(:other_error_alt)) 110 end 111 112 it 'sends status UNKNOWN if NotImplementedErrors are raised' do 113 expect(@call).to receive(:send_status).once.with( 114 UNKNOWN, not_implemented_error_msg, false, metadata: {}) 115 @client_streamer.run_server_method(@call, method(:not_implemented_alt)) 116 end 117 118 it 'absorbs CallError with no further action' do 119 expect(@call).to receive(:server_unary_response).once.and_raise( 120 CallError) 121 allow(@call).to receive(:output_metadata).and_return({}) 122 blk = proc do 123 @client_streamer.run_server_method(@call, method(:fake_clstream)) 124 end 125 expect(&blk).to_not raise_error 126 end 127 128 it 'sends a response and closes the stream if there no errors' do 129 expect(@call).to receive(:output_metadata).and_return( 130 fake_md) 131 expect(@call).to receive(:server_unary_response).once 132 .with(@ok_response, trailing_metadata: fake_md) 133 134 @client_streamer.run_server_method(@call, method(:fake_clstream)) 135 end 136 end 137 138 describe 'for server streaming' do 139 let(:this_desc) { @request_response } 140 before(:each) do 141 @call = double('active_call') 142 allow(@call).to receive(:single_req_view).and_return(@call) 143 end 144 145 it_behaves_like 'it handles errors' 146 147 it 'sends a response and closes the stream if there no errors' do 148 req = Object.new 149 expect(@call).to receive(:read_unary_request).once.and_return(req) 150 expect(@call).to receive(:remote_send).twice.with(@ok_response) 151 expect(@call).to receive(:output_metadata).and_return(fake_md) 152 expect(@call).to receive(:send_status).once.with(OK, 'OK', true, 153 metadata: fake_md) 154 @server_streamer.run_server_method(@call, method(:fake_svstream)) 155 end 156 end 157 158 describe 'for bidi streamers' do 159 before(:each) do 160 @call = double('active_call') 161 enq_th, rwl_th = double('enqueue_th'), ('read_write_loop_th') 162 allow(enq_th).to receive(:join) 163 allow(rwl_th).to receive(:join) 164 end 165 166 it 'sends the specified status if BadStatus is raised' do 167 e = GRPC::BadStatus.new(@bs_code, 'NOK') 168 expect(@call).to receive(:run_server_bidi).and_raise(e) 169 expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false, 170 metadata: {}) 171 @bidi_streamer.run_server_method(@call, method(:bad_status_alt)) 172 end 173 174 it 'sends status UNKNOWN if other StandardErrors are raised' do 175 error_msg = arg_error_msg(StandardError.new) 176 expect(@call).to receive(:run_server_bidi).and_raise(StandardError) 177 expect(@call).to receive(:send_status).once.with(UNKNOWN, error_msg, 178 false, metadata: {}) 179 @bidi_streamer.run_server_method(@call, method(:other_error_alt)) 180 end 181 182 it 'sends status UNKNOWN if NotImplementedErrors are raised' do 183 expect(@call).to receive(:run_server_bidi).and_raise( 184 not_implemented_error) 185 expect(@call).to receive(:send_status).once.with( 186 UNKNOWN, not_implemented_error_msg, false, metadata: {}) 187 @bidi_streamer.run_server_method(@call, method(:not_implemented_alt)) 188 end 189 190 it 'closes the stream if there no errors' do 191 expect(@call).to receive(:run_server_bidi) 192 expect(@call).to receive(:output_metadata).and_return(fake_md) 193 expect(@call).to receive(:send_status).once.with(OK, 'OK', true, 194 metadata: fake_md) 195 @bidi_streamer.run_server_method(@call, method(:fake_bidistream)) 196 end 197 end 198 end 199 200 describe '#assert_arity_matches' do 201 def no_arg 202 end 203 204 def fake_clstream(_arg) 205 end 206 207 def fake_svstream(_arg1, _arg2) 208 end 209 210 def fake_three_args(_arg1, _arg2, _arg3) 211 end 212 213 it 'raises when a request_response does not have 2 args' do 214 [:fake_clstream, :no_arg].each do |mth| 215 blk = proc do 216 @request_response.assert_arity_matches(method(mth)) 217 end 218 expect(&blk).to raise_error 219 end 220 end 221 222 it 'passes when a request_response has 2 args' do 223 blk = proc do 224 @request_response.assert_arity_matches(method(:fake_svstream)) 225 end 226 expect(&blk).to_not raise_error 227 end 228 229 it 'raises when a server_streamer does not have 2 args' do 230 [:fake_clstream, :no_arg].each do |mth| 231 blk = proc do 232 @server_streamer.assert_arity_matches(method(mth)) 233 end 234 expect(&blk).to raise_error 235 end 236 end 237 238 it 'passes when a server_streamer has 2 args' do 239 blk = proc do 240 @server_streamer.assert_arity_matches(method(:fake_svstream)) 241 end 242 expect(&blk).to_not raise_error 243 end 244 245 it 'raises when a client streamer does not have 1 arg' do 246 [:fake_svstream, :no_arg].each do |mth| 247 blk = proc do 248 @client_streamer.assert_arity_matches(method(mth)) 249 end 250 expect(&blk).to raise_error 251 end 252 end 253 254 it 'passes when a client_streamer has 1 arg' do 255 blk = proc do 256 @client_streamer.assert_arity_matches(method(:fake_clstream)) 257 end 258 expect(&blk).to_not raise_error 259 end 260 261 it 'raises when a bidi streamer does not have 1 or 2 args' do 262 [:fake_three_args, :no_arg].each do |mth| 263 blk = proc do 264 @bidi_streamer.assert_arity_matches(method(mth)) 265 end 266 expect(&blk).to raise_error 267 end 268 end 269 270 it 'passes when a bidi streamer has 1 arg' do 271 blk = proc do 272 @bidi_streamer.assert_arity_matches(method(:fake_clstream)) 273 end 274 expect(&blk).to_not raise_error 275 end 276 277 it 'passes when a bidi streamer has 2 args' do 278 blk = proc do 279 @bidi_streamer.assert_arity_matches(method(:fake_svstream)) 280 end 281 expect(&blk).to_not raise_error 282 end 283 end 284 285 describe '#request_response?' do 286 it 'is true only input and output are both not Streams' do 287 expect(@request_response.request_response?).to be(true) 288 expect(@client_streamer.request_response?).to be(false) 289 expect(@bidi_streamer.request_response?).to be(false) 290 expect(@server_streamer.request_response?).to be(false) 291 end 292 end 293 294 describe '#client_streamer?' do 295 it 'is true only when input is a Stream and output is not a Stream' do 296 expect(@client_streamer.client_streamer?).to be(true) 297 expect(@request_response.client_streamer?).to be(false) 298 expect(@server_streamer.client_streamer?).to be(false) 299 expect(@bidi_streamer.client_streamer?).to be(false) 300 end 301 end 302 303 describe '#server_streamer?' do 304 it 'is true only when output is a Stream and input is not a Stream' do 305 expect(@server_streamer.server_streamer?).to be(true) 306 expect(@client_streamer.server_streamer?).to be(false) 307 expect(@request_response.server_streamer?).to be(false) 308 expect(@bidi_streamer.server_streamer?).to be(false) 309 end 310 end 311 312 describe '#bidi_streamer?' do 313 it 'is true only when output is a Stream and input is a Stream' do 314 expect(@bidi_streamer.bidi_streamer?).to be(true) 315 expect(@server_streamer.bidi_streamer?).to be(false) 316 expect(@client_streamer.bidi_streamer?).to be(false) 317 expect(@request_response.bidi_streamer?).to be(false) 318 end 319 end 320 321 def fake_reqresp(_req, _call) 322 @ok_response 323 end 324 325 def fake_clstream(_call) 326 @ok_response 327 end 328 329 def fake_svstream(_req, _call) 330 [@ok_response, @ok_response] 331 end 332 333 def fake_bidistream(an_array) 334 an_array 335 end 336 337 def bad_status(_req, _call) 338 fail GRPC::BadStatus.new(@bs_code, 'NOK') 339 end 340 341 def other_error(_req, _call) 342 fail(ArgumentError, 'other error') 343 end 344 345 def bad_status_alt(_call) 346 fail GRPC::BadStatus.new(@bs_code, 'NOK') 347 end 348 349 def other_error_alt(_call) 350 fail(ArgumentError, 'other error') 351 end 352 353 def not_implemented(_req, _call) 354 fail not_implemented_error 355 end 356 357 def not_implemented_alt(_call) 358 fail not_implemented_error 359 end 360 361 def arg_error_msg(error = nil) 362 error ||= ArgumentError.new('other error') 363 "#{error.class}: #{error.message}" 364 end 365 366 def not_implemented_error 367 NotImplementedError.new('some OS feature not implemented') 368 end 369 370 def not_implemented_error_msg(error = nil) 371 error ||= not_implemented_error 372 "#{error.class}: #{error.message}" 373 end 374end 375