• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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