aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ruby/spec
diff options
context:
space:
mode:
Diffstat (limited to 'src/ruby/spec')
-rw-r--r--src/ruby/spec/channel_connection_spec.rb38
-rw-r--r--src/ruby/spec/client_auth_spec.rb2
-rw-r--r--src/ruby/spec/client_server_spec.rb4
-rw-r--r--src/ruby/spec/generic/active_call_spec.rb21
-rw-r--r--src/ruby/spec/generic/client_interceptors_spec.rb153
-rw-r--r--src/ruby/spec/generic/client_stub_spec.rb8
-rw-r--r--src/ruby/spec/generic/interceptor_registry_spec.rb65
-rw-r--r--src/ruby/spec/generic/rpc_server_spec.rb59
-rw-r--r--src/ruby/spec/generic/server_interceptors_spec.rb218
-rw-r--r--src/ruby/spec/google_rpc_status_utils_spec.rb80
-rw-r--r--src/ruby/spec/pb/health/checker_spec.rb31
-rw-r--r--src/ruby/spec/server_spec.rb18
-rw-r--r--src/ruby/spec/spec_helper.rb4
-rw-r--r--src/ruby/spec/support/helpers.rb107
-rw-r--r--src/ruby/spec/support/services.rb147
15 files changed, 848 insertions, 107 deletions
diff --git a/src/ruby/spec/channel_connection_spec.rb b/src/ruby/spec/channel_connection_spec.rb
index c76056606b..5c31f41065 100644
--- a/src/ruby/spec/channel_connection_spec.rb
+++ b/src/ruby/spec/channel_connection_spec.rb
@@ -11,47 +11,15 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-require 'grpc'
+require 'spec_helper'
require 'timeout'
include Timeout
include GRPC::Core
-
-# A test message
-class EchoMsg
- def self.marshal(_o)
- ''
- end
-
- def self.unmarshal(_o)
- EchoMsg.new
- end
-end
-
-# A test service with an echo implementation.
-class EchoService
- include GRPC::GenericService
- rpc :an_rpc, EchoMsg, EchoMsg
- attr_reader :received_md
-
- def initialize(**kw)
- @trailing_metadata = kw
- @received_md = []
- end
-
- def an_rpc(req, call)
- GRPC.logger.info('echo service received a request')
- call.output_metadata.update(@trailing_metadata)
- @received_md << call.metadata unless call.metadata.nil?
- req
- end
-end
-
-EchoStub = EchoService.rpc_stub_class
+include GRPC::Spec::Helpers
def start_server(port = 0)
- @srv = GRPC::RpcServer.new(pool_size: 1)
+ @srv = new_rpc_server_for_testing(pool_size: 1)
server_port = @srv.add_http2_port("localhost:#{port}", :this_port_is_insecure)
@srv.handle(EchoService)
@server_thd = Thread.new { @srv.run }
diff --git a/src/ruby/spec/client_auth_spec.rb b/src/ruby/spec/client_auth_spec.rb
index 79c9192aa5..b955ad231e 100644
--- a/src/ruby/spec/client_auth_spec.rb
+++ b/src/ruby/spec/client_auth_spec.rb
@@ -95,7 +95,7 @@ describe 'client-server auth' do
server_opts = {
poll_period: 1
}
- @srv = RpcServer.new(**server_opts)
+ @srv = new_rpc_server_for_testing(**server_opts)
port = @srv.add_http2_port('0.0.0.0:0', create_server_creds)
@srv.handle(SslTestService)
@srv_thd = Thread.new { @srv.run }
diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb
index adab8c9d14..14ad369ac8 100644
--- a/src/ruby/spec/client_server_spec.rb
+++ b/src/ruby/spec/client_server_spec.rb
@@ -542,7 +542,7 @@ end
describe 'the http client/server' do
before(:example) do
server_host = '0.0.0.0:0'
- @server = GRPC::Core::Server.new(nil)
+ @server = new_core_server_for_testing(nil)
server_port = @server.add_http2_port(server_host, :this_port_is_insecure)
@server.start
@ch = Channel.new("0.0.0.0:#{server_port}", nil, :this_channel_is_insecure)
@@ -574,7 +574,7 @@ describe 'the secure http client/server' do
server_host = '0.0.0.0:0'
server_creds = GRPC::Core::ServerCredentials.new(
nil, [{ private_key: certs[1], cert_chain: certs[2] }], false)
- @server = GRPC::Core::Server.new(nil)
+ @server = new_core_server_for_testing(nil)
server_port = @server.add_http2_port(server_host, server_creds)
@server.start
args = { Channel::SSL_TARGET => 'foo.test.google.fr' }
diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb
index a00df9236d..135d1f28bf 100644
--- a/src/ruby/spec/generic/active_call_spec.rb
+++ b/src/ruby/spec/generic/active_call_spec.rb
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require 'grpc'
+require 'spec_helper'
include GRPC::Core::StatusCodes
@@ -40,7 +40,7 @@ describe GRPC::ActiveCall do
before(:each) do
@pass_through = proc { |x| x }
host = '0.0.0.0:0'
- @server = GRPC::Core::Server.new(nil)
+ @server = new_core_server_for_testing(nil)
server_port = @server.add_http2_port(host, :this_port_is_insecure)
@server.start
@ch = GRPC::Core::Channel.new("0.0.0.0:#{server_port}", nil,
@@ -82,6 +82,16 @@ describe GRPC::ActiveCall do
end
end
end
+
+ describe '#interceptable' do
+ it 'exposes a fixed subset of the ActiveCall.methods' do
+ want = %w(deadline)
+ v = @client_call.interceptable
+ want.each do |w|
+ expect(v.methods.include?(w))
+ end
+ end
+ end
end
describe '#remote_send' do
@@ -609,9 +619,11 @@ describe GRPC::ActiveCall do
msgs
end
+ int_ctx = GRPC::InterceptionContext.new
+
@server_thread = Thread.new do
@server_call.run_server_bidi(
- fake_gen_each_reply_with_no_call_param)
+ fake_gen_each_reply_with_no_call_param, int_ctx)
@server_call.send_status(@server_status)
end
end
@@ -624,10 +636,11 @@ describe GRPC::ActiveCall do
call_param.send_initial_metadata
msgs
end
+ int_ctx = GRPC::InterceptionContext.new
@server_thread = Thread.new do
@server_call.run_server_bidi(
- fake_gen_each_reply_with_call_param)
+ fake_gen_each_reply_with_call_param, int_ctx)
@server_call.send_status(@server_status)
end
end
diff --git a/src/ruby/spec/generic/client_interceptors_spec.rb b/src/ruby/spec/generic/client_interceptors_spec.rb
new file mode 100644
index 0000000000..f292715e4d
--- /dev/null
+++ b/src/ruby/spec/generic/client_interceptors_spec.rb
@@ -0,0 +1,153 @@
+# Copyright 2017 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+require 'spec_helper'
+
+describe 'Client Interceptors' do
+ let(:interceptor) { TestClientInterceptor.new }
+ let(:interceptors_opts) { { interceptors: [interceptor] } }
+ let(:request) { EchoMsg.new }
+ let(:service) { EchoService }
+
+ before(:each) do
+ build_rpc_server
+ end
+
+ context 'when a client interceptor is added' do
+ context 'with a request/response call' do
+ it 'should be called', server: true do
+ expect(interceptor).to receive(:request_response)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+ expect_any_instance_of(GRPC::ActiveCall).to receive(:request_response)
+ .once.and_call_original
+ expect(stub.an_rpc(request)).to be_a(EchoMsg)
+ end
+ end
+
+ it 'can modify outgoing metadata', server: true do
+ expect(interceptor).to receive(:request_response)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+ expect_any_instance_of(GRPC::ActiveCall).to receive(:request_response)
+ .with(request, metadata: { 'foo' => 'bar_from_request_response' })
+ .once.and_call_original
+ expect(stub.an_rpc(request)).to be_a(EchoMsg)
+ end
+ end
+ end
+
+ context 'with a client streaming call' do
+ it 'should be called', server: true do
+ expect(interceptor).to receive(:client_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+ expect_any_instance_of(GRPC::ActiveCall).to receive(:client_streamer)
+ .once.and_call_original
+ requests = [EchoMsg.new, EchoMsg.new]
+ expect(stub.a_client_streaming_rpc(requests)).to be_a(EchoMsg)
+ end
+ end
+
+ it 'can modify outgoing metadata', server: true do
+ expect(interceptor).to receive(:client_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+ requests = [EchoMsg.new, EchoMsg.new]
+ expect_any_instance_of(GRPC::ActiveCall).to receive(:client_streamer)
+ .with(requests, metadata: { 'foo' => 'bar_from_client_streamer' })
+ .once.and_call_original
+ expect(stub.a_client_streaming_rpc(requests)).to be_a(EchoMsg)
+ end
+ end
+ end
+
+ context 'with a server streaming call' do
+ it 'should be called', server: true do
+ expect(interceptor).to receive(:server_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+ request = EchoMsg.new
+ expect_any_instance_of(GRPC::ActiveCall).to receive(:server_streamer)
+ .once.and_call_original
+ responses = stub.a_server_streaming_rpc(request)
+ responses.each do |r|
+ expect(r).to be_a(EchoMsg)
+ end
+ end
+ end
+
+ it 'can modify outgoing metadata', server: true do
+ expect(interceptor).to receive(:server_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+ request = EchoMsg.new
+ expect_any_instance_of(GRPC::ActiveCall).to receive(:server_streamer)
+ .with(request, metadata: { 'foo' => 'bar_from_server_streamer' })
+ .once.and_call_original
+ responses = stub.a_server_streaming_rpc(request)
+ responses.each do |r|
+ expect(r).to be_a(EchoMsg)
+ end
+ end
+ end
+ end
+
+ context 'with a bidi call' do
+ it 'should be called', server: true do
+ expect(interceptor).to receive(:bidi_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+ expect_any_instance_of(GRPC::ActiveCall).to receive(:bidi_streamer)
+ .once.and_call_original
+ requests = [EchoMsg.new, EchoMsg.new]
+ responses = stub.a_bidi_rpc(requests)
+ responses.each do |r|
+ expect(r).to be_a(EchoMsg)
+ end
+ end
+ end
+
+ it 'can modify outgoing metadata', server: true do
+ expect(interceptor).to receive(:bidi_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+ requests = [EchoMsg.new, EchoMsg.new]
+ expect_any_instance_of(GRPC::ActiveCall).to receive(:bidi_streamer)
+ .with(requests, metadata: { 'foo' => 'bar_from_bidi_streamer' })
+ .once.and_call_original
+ responses = stub.a_bidi_rpc(requests)
+ responses.each do |r|
+ expect(r).to be_a(EchoMsg)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb
index 9539e56c0f..5353b534f4 100644
--- a/src/ruby/spec/generic/client_stub_spec.rb
+++ b/src/ruby/spec/generic/client_stub_spec.rb
@@ -228,7 +228,7 @@ describe 'ClientStub' do
th.join
end
- it 'should receive UNAUTHENTICATED if call credentials plugin fails' do
+ it 'should receive UNAVAILABLE if call credentials plugin fails' do
server_port = create_secure_test_server
th = run_request_response(@sent_msg, @resp, @pass)
@@ -252,7 +252,7 @@ describe 'ClientStub' do
unauth_error_occured = false
begin
get_response(stub, credentials: creds)
- rescue GRPC::Unauthenticated => e
+ rescue GRPC::Unavailable => e
unauth_error_occured = true
expect(e.details.include?(error_message)).to be true
end
@@ -888,12 +888,12 @@ describe 'ClientStub' do
secure_credentials = GRPC::Core::ServerCredentials.new(
nil, [{ private_key: certs[1], cert_chain: certs[2] }], false)
- @server = GRPC::Core::Server.new(nil)
+ @server = new_core_server_for_testing(nil)
@server.add_http2_port('0.0.0.0:0', secure_credentials)
end
def create_test_server
- @server = GRPC::Core::Server.new(nil)
+ @server = new_core_server_for_testing(nil)
@server.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
end
diff --git a/src/ruby/spec/generic/interceptor_registry_spec.rb b/src/ruby/spec/generic/interceptor_registry_spec.rb
new file mode 100644
index 0000000000..eb75d1e0b2
--- /dev/null
+++ b/src/ruby/spec/generic/interceptor_registry_spec.rb
@@ -0,0 +1,65 @@
+# Copyright 2017 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+require 'spec_helper'
+
+describe GRPC::InterceptorRegistry do
+ let(:server) { new_rpc_server_for_testing }
+ let(:interceptor) { TestServerInterceptor.new }
+ let(:interceptors) { [interceptor] }
+ let(:registry) { described_class.new(interceptors) }
+
+ describe 'initialization' do
+ subject { registry }
+
+ context 'with an interceptor extending GRPC::ServerInterceptor' do
+ it 'should add the interceptor to the registry' do
+ subject
+ is = registry.instance_variable_get('@interceptors')
+ expect(is.count).to eq 1
+ expect(is.first).to eq interceptor
+ end
+ end
+
+ context 'with multiple interceptors' do
+ let(:interceptor2) { TestServerInterceptor.new }
+ let(:interceptor3) { TestServerInterceptor.new }
+ let(:interceptors) { [interceptor, interceptor2, interceptor3] }
+
+ it 'should maintain order of insertion when iterated against' do
+ subject
+ is = registry.instance_variable_get('@interceptors')
+ expect(is.count).to eq 3
+ is.each_with_index do |i, idx|
+ case idx
+ when 0
+ expect(i).to eq interceptor
+ when 1
+ expect(i).to eq interceptor2
+ when 2
+ expect(i).to eq interceptor3
+ end
+ end
+ end
+ end
+
+ context 'with an interceptor not extending GRPC::ServerInterceptor' do
+ let(:interceptor) { Class }
+ let(:err) { GRPC::InterceptorRegistry::DescendantError }
+
+ it 'should raise an InvalidArgument exception' do
+ expect { subject }.to raise_error(err)
+ end
+ end
+ end
+end
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index b887eaaf4e..e072d0c45f 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -11,8 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-require 'grpc'
+require 'spec_helper'
def load_test_certs
test_root = File.join(File.dirname(File.dirname(__FILE__)), 'testdata')
@@ -28,17 +27,6 @@ def check_md(wanted_md, received_md)
end
end
-# A test message
-class EchoMsg
- def self.marshal(_o)
- ''
- end
-
- def self.unmarshal(_o)
- EchoMsg.new
- end
-end
-
# A test service with no methods.
class EmptyService
include GRPC::GenericService
@@ -50,27 +38,6 @@ class NoRpcImplementation
rpc :an_rpc, EchoMsg, EchoMsg
end
-# A test service with an echo implementation.
-class EchoService
- include GRPC::GenericService
- rpc :an_rpc, EchoMsg, EchoMsg
- attr_reader :received_md
-
- def initialize(**kw)
- @trailing_metadata = kw
- @received_md = []
- end
-
- def an_rpc(req, call)
- GRPC.logger.info('echo service received a request')
- call.output_metadata.update(@trailing_metadata)
- @received_md << call.metadata unless call.metadata.nil?
- req
- end
-end
-
-EchoStub = EchoService.rpc_stub_class
-
# A test service with an implementation that fails with BadStatus
class FailingService
include GRPC::GenericService
@@ -205,7 +172,7 @@ describe GRPC::RpcServer do
it 'can be created with just some args' do
opts = { server_args: { a_channel_arg: 'an_arg' } }
blk = proc do
- RpcServer.new(**opts)
+ new_rpc_server_for_testing(**opts)
end
expect(&blk).not_to raise_error
end
@@ -216,7 +183,7 @@ describe GRPC::RpcServer do
server_args: { a_channel_arg: 'an_arg' },
creds: Object.new
}
- RpcServer.new(**opts)
+ new_rpc_server_for_testing(**opts)
end
expect(&blk).to raise_error
end
@@ -225,7 +192,7 @@ describe GRPC::RpcServer do
describe '#stopped?' do
before(:each) do
opts = { server_args: { a_channel_arg: 'an_arg' }, poll_period: 1.5 }
- @srv = RpcServer.new(**opts)
+ @srv = new_rpc_server_for_testing(**opts)
@srv.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
end
@@ -257,7 +224,7 @@ describe GRPC::RpcServer do
opts = {
server_args: { a_channel_arg: 'an_arg' }
}
- r = RpcServer.new(**opts)
+ r = new_rpc_server_for_testing(**opts)
expect(r.running?).to be(false)
end
@@ -266,7 +233,7 @@ describe GRPC::RpcServer do
server_args: { a_channel_arg: 'an_arg' },
poll_period: 2
}
- r = RpcServer.new(**opts)
+ r = new_rpc_server_for_testing(**opts)
r.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
expect { r.run }.to raise_error(RuntimeError)
end
@@ -276,7 +243,7 @@ describe GRPC::RpcServer do
server_args: { a_channel_arg: 'an_arg' },
poll_period: 2.5
}
- r = RpcServer.new(**opts)
+ r = new_rpc_server_for_testing(**opts)
r.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
r.handle(EchoService)
t = Thread.new { r.run }
@@ -290,7 +257,7 @@ describe GRPC::RpcServer do
describe '#handle' do
before(:each) do
@opts = { server_args: { a_channel_arg: 'an_arg' }, poll_period: 1 }
- @srv = RpcServer.new(**@opts)
+ @srv = new_rpc_server_for_testing(**@opts)
@srv.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
end
@@ -336,7 +303,7 @@ describe GRPC::RpcServer do
server_opts = {
poll_period: 1
}
- @srv = RpcServer.new(**server_opts)
+ @srv = new_rpc_server_for_testing(**server_opts)
server_port = @srv.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
@host = "localhost:#{server_port}"
@ch = GRPC::Core::Channel.new(@host, nil, :this_channel_is_insecure)
@@ -507,7 +474,7 @@ describe GRPC::RpcServer do
poll_period: 1,
max_waiting_requests: 1
}
- alt_srv = RpcServer.new(**opts)
+ alt_srv = new_rpc_server_for_testing(**opts)
alt_srv.handle(SlowService)
alt_port = alt_srv.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
alt_host = "0.0.0.0:#{alt_port}"
@@ -571,7 +538,7 @@ describe GRPC::RpcServer do
poll_period: 1,
connect_md_proc: test_md_proc
}
- @srv = RpcServer.new(**server_opts)
+ @srv = new_rpc_server_for_testing(**server_opts)
alt_port = @srv.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
@alt_host = "0.0.0.0:#{alt_port}"
end
@@ -606,7 +573,7 @@ describe GRPC::RpcServer do
server_opts = {
poll_period: 1
}
- @srv = RpcServer.new(**server_opts)
+ @srv = new_rpc_server_for_testing(**server_opts)
alt_port = @srv.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
@alt_host = "0.0.0.0:#{alt_port}"
end
@@ -657,7 +624,7 @@ describe GRPC::RpcServer do
server_opts = {
poll_period: 1
}
- @srv = RpcServer.new(**server_opts)
+ @srv = new_rpc_server_for_testing(**server_opts)
alt_port = @srv.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
@alt_host = "0.0.0.0:#{alt_port}"
diff --git a/src/ruby/spec/generic/server_interceptors_spec.rb b/src/ruby/spec/generic/server_interceptors_spec.rb
new file mode 100644
index 0000000000..eb86686084
--- /dev/null
+++ b/src/ruby/spec/generic/server_interceptors_spec.rb
@@ -0,0 +1,218 @@
+# Copyright 2017 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+require 'spec_helper'
+
+describe 'Server Interceptors' do
+ let(:interceptor) { TestServerInterceptor.new }
+ let(:request) { EchoMsg.new }
+ let(:trailing_metadata) { {} }
+ let(:service) { EchoService.new(trailing_metadata) }
+ let(:interceptors) { [] }
+
+ before(:each) do
+ build_rpc_server(server_opts: { interceptors: interceptors })
+ end
+
+ context 'when a server interceptor is added' do
+ let(:interceptors) { [interceptor] }
+ let(:client_metadata) { { client_md: 'test' } }
+ let(:client_call_opts) { { metadata: client_metadata, return_op: true } }
+
+ context 'with a request/response call' do
+ let(:trailing_metadata) { { server_om: 'from_request_response' } }
+
+ it 'should be called', server: true do
+ expect(interceptor).to receive(:request_response)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub)
+ expect(stub.an_rpc(request)).to be_a(EchoMsg)
+ end
+ end
+
+ it 'can modify trailing metadata', server: true do
+ expect(interceptor).to receive(:request_response)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub)
+ expect_any_instance_of(GRPC::ActiveCall).to(
+ receive(:request_response).with(request, metadata: client_metadata)
+ .once.and_call_original
+ )
+ op = stub.an_rpc(request, client_call_opts)
+ msg = op.execute
+ expect(op.trailing_metadata).to eq(
+ 'interc' => 'from_request_response',
+ 'server_om' => 'from_request_response'
+ )
+ expect(msg).to be_a(EchoMsg)
+ end
+ end
+ end
+
+ context 'with a client streaming call' do
+ let(:trailing_metadata) { { server_om: 'from_client_streamer' } }
+ let(:requests) { [EchoMsg.new, EchoMsg.new] }
+
+ it 'should be called', server: true do
+ expect(interceptor).to receive(:client_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub)
+ expect(stub.a_client_streaming_rpc(requests)).to be_a(EchoMsg)
+ end
+ end
+
+ it 'can modify trailing metadata', server: true do
+ expect(interceptor).to receive(:client_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub)
+ expect_any_instance_of(GRPC::ActiveCall).to(
+ receive(:client_streamer).with(requests)
+ .once.and_call_original
+ )
+ op = stub.a_client_streaming_rpc(requests, client_call_opts)
+ msg = op.execute
+ expect(op.trailing_metadata).to eq(
+ 'interc' => 'from_client_streamer',
+ 'server_om' => 'from_client_streamer'
+ )
+ expect(msg).to be_a(EchoMsg)
+ end
+ end
+ end
+
+ context 'with a server streaming call' do
+ let(:trailing_metadata) { { server_om: 'from_server_streamer' } }
+ let(:request) { EchoMsg.new }
+
+ it 'should be called', server: true do
+ expect(interceptor).to receive(:server_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub)
+ responses = stub.a_server_streaming_rpc(request)
+ responses.each do |r|
+ expect(r).to be_a(EchoMsg)
+ end
+ end
+ end
+
+ it 'can modify trailing metadata', server: true do
+ expect(interceptor).to receive(:server_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub)
+ expect_any_instance_of(GRPC::ActiveCall).to(
+ receive(:server_streamer).with(request)
+ .once.and_call_original
+ )
+ op = stub.a_server_streaming_rpc(request, client_call_opts)
+ responses = op.execute
+ responses.each do |r|
+ expect(r).to be_a(EchoMsg)
+ end
+ expect(op.trailing_metadata).to eq(
+ 'interc' => 'from_server_streamer',
+ 'server_om' => 'from_server_streamer'
+ )
+ end
+ end
+ end
+
+ context 'with a bidi call' do
+ let(:trailing_metadata) { { server_om: 'from_bidi_streamer' } }
+ let(:requests) { [EchoMsg.new, EchoMsg.new] }
+
+ it 'should be called', server: true do
+ expect(interceptor).to receive(:bidi_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub)
+ responses = stub.a_bidi_rpc(requests)
+ responses.each do |r|
+ expect(r).to be_a(EchoMsg)
+ end
+ end
+ end
+
+ it 'can modify trailing metadata', server: true do
+ expect(interceptor).to receive(:bidi_streamer)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub)
+ expect_any_instance_of(GRPC::ActiveCall).to(
+ receive(:bidi_streamer).with(requests)
+ .once.and_call_original
+ )
+ op = stub.a_bidi_rpc(requests, client_call_opts)
+ responses = op.execute
+ responses.each do |r|
+ expect(r).to be_a(EchoMsg)
+ end
+ expect(op.trailing_metadata).to eq(
+ 'interc' => 'from_bidi_streamer',
+ 'server_om' => 'from_bidi_streamer'
+ )
+ end
+ end
+ end
+ end
+
+ context 'when multiple interceptors are added' do
+ let(:interceptor2) { TestServerInterceptor.new }
+ let(:interceptor3) { TestServerInterceptor.new }
+ let(:interceptors) do
+ [
+ interceptor,
+ interceptor2,
+ interceptor3
+ ]
+ end
+
+ it 'each should be called', server: true do
+ expect(interceptor).to receive(:request_response)
+ .once.and_call_original
+ expect(interceptor2).to receive(:request_response)
+ .once.and_call_original
+ expect(interceptor3).to receive(:request_response)
+ .once.and_call_original
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub)
+ expect(stub.an_rpc(request)).to be_a(EchoMsg)
+ end
+ end
+ end
+
+ context 'when an interceptor is not added' do
+ it 'should not be called', server: true do
+ expect(interceptor).to_not receive(:call)
+
+ run_services_on_server(@server, services: [service]) do
+ stub = build_insecure_stub(EchoStub)
+ expect(stub.an_rpc(request)).to be_a(EchoMsg)
+ end
+ end
+ end
+end
diff --git a/src/ruby/spec/google_rpc_status_utils_spec.rb b/src/ruby/spec/google_rpc_status_utils_spec.rb
index fe221c30dd..3263589b6a 100644
--- a/src/ruby/spec/google_rpc_status_utils_spec.rb
+++ b/src/ruby/spec/google_rpc_status_utils_spec.rb
@@ -19,6 +19,7 @@ require_relative '../pb/src/proto/grpc/testing/messages_pb'
require 'google/protobuf/well_known_types'
include GRPC::Core
+include GRPC::Spec::Helpers
describe 'conversion from a status struct to a google protobuf status' do
it 'fails if the input is not a status struct' do
@@ -31,12 +32,11 @@ describe 'conversion from a status struct to a google protobuf status' do
expect(exception.message.include?('bad type')).to be true
end
- it 'fails with some error if the header key is missing' do
+ it 'returns nil if the header key is missing' do
status = Struct::Status.new(1, 'details', key: 'val')
expect(status.metadata.nil?).to be false
- expect do
- GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(status)
- end.to raise_error(StandardError)
+ expect(GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(
+ status)).to be(nil)
end
it 'fails with some error if the header key fails to deserialize' do
@@ -151,7 +151,7 @@ GoogleRpcStatusTestStub = GoogleRpcStatusTestService.rpc_stub_class
describe 'receving a google rpc status from a remote endpoint' do
def start_server(encoded_rpc_status)
- @srv = GRPC::RpcServer.new(pool_size: 1)
+ @srv = new_rpc_server_for_testing(pool_size: 1)
@server_port = @srv.add_http2_port('localhost:0',
:this_port_is_insecure)
@srv.handle(GoogleRpcStatusTestService.new(encoded_rpc_status))
@@ -221,3 +221,73 @@ describe 'receving a google rpc status from a remote endpoint' do
status_from_exception)).to eq(rpc_status)
end
end
+
+# A test service that fails without explicitly setting the
+# grpc-status-details-bin trailer. Tests assumptions about value
+# of grpc-status-details-bin on the client side when the trailer wasn't
+# set explicitly.
+class NoStatusDetailsBinTestService
+ include GRPC::GenericService
+ rpc :an_rpc, EchoMsg, EchoMsg
+
+ def an_rpc(_, _)
+ fail GRPC::Unknown
+ end
+end
+
+NoStatusDetailsBinTestServiceStub = NoStatusDetailsBinTestService.rpc_stub_class
+
+describe 'when the endpoint doesnt send grpc-status-details-bin' do
+ def start_server
+ @srv = new_rpc_server_for_testing(pool_size: 1)
+ @server_port = @srv.add_http2_port('localhost:0',
+ :this_port_is_insecure)
+ @srv.handle(NoStatusDetailsBinTestService)
+ @server_thd = Thread.new { @srv.run }
+ @srv.wait_till_running
+ end
+
+ def stop_server
+ expect(@srv.stopped?).to be(false)
+ @srv.stop
+ @server_thd.join
+ expect(@srv.stopped?).to be(true)
+ end
+
+ before(:each) do
+ start_server
+ end
+
+ after(:each) do
+ stop_server
+ end
+
+ it 'should receive nil when we extract try to extract a google '\
+ 'rpc status from a BadStatus exception that didnt have it' do
+ stub = NoStatusDetailsBinTestServiceStub.new("localhost:#{@server_port}",
+ :this_channel_is_insecure)
+ begin
+ stub.an_rpc(EchoMsg.new)
+ rescue GRPC::Unknown => e
+ rpc_status = GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(
+ e.to_status)
+ end
+ expect(rpc_status).to be(nil)
+ end
+
+ it 'should receive nil when we extract try to extract a google '\
+ 'rpc status from an op views status object that didnt have it' do
+ stub = NoStatusDetailsBinTestServiceStub.new("localhost:#{@server_port}",
+ :this_channel_is_insecure)
+ op = stub.an_rpc(EchoMsg.new, return_op: true)
+ begin
+ op.execute
+ rescue GRPC::Unknown => e
+ status_from_exception = e.to_status
+ end
+ expect(GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(
+ status_from_exception)).to be(nil)
+ expect(GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(
+ op.status)).to be nil
+ end
+end
diff --git a/src/ruby/spec/pb/health/checker_spec.rb b/src/ruby/spec/pb/health/checker_spec.rb
index 6c9e206c3f..58a602327c 100644
--- a/src/ruby/spec/pb/health/checker_spec.rb
+++ b/src/ruby/spec/pb/health/checker_spec.rb
@@ -99,6 +99,35 @@ describe Grpc::Health::Checker do
end
end
+ context 'method `add_statuses`' do
+ it 'should add status to each service' do
+ checker = Grpc::Health::Checker.new
+ checker.add_statuses(
+ 'service1' => ServingStatus::SERVING,
+ 'service2' => ServingStatus::NOT_SERVING
+ )
+ service1_health = checker.check(HCReq.new(service: 'service1'), nil)
+ service2_health = checker.check(HCReq.new(service: 'service2'), nil)
+ expect(service1_health).to eq(HCResp.new(status: ServingStatus::SERVING))
+ expect(service2_health).to eq(HCResp.new(status: ServingStatus::NOT_SERVING))
+ end
+ end
+
+ context 'method `set_status_for_services`' do
+ it 'should add given status to all given services' do
+ checker = Grpc::Health::Checker.new
+ checker.set_status_for_services(
+ ServingStatus::SERVING,
+ 'service1',
+ 'service2'
+ )
+ service1_health = checker.check(HCReq.new(service: 'service1'), nil)
+ service2_health = checker.check(HCReq.new(service: 'service2'), nil)
+ expect(service1_health).to eq(HCResp.new(status: ServingStatus::SERVING))
+ expect(service2_health).to eq(HCResp.new(status: ServingStatus::SERVING))
+ end
+ end
+
context 'method `check`' do
success_tests.each do |t|
it "should fail with NOT_FOUND when #{t[:desc]}" do
@@ -163,7 +192,7 @@ describe Grpc::Health::Checker do
server_opts = {
poll_period: 1
}
- @srv = RpcServer.new(**server_opts)
+ @srv = new_rpc_server_for_testing(**server_opts)
server_port = @srv.add_http2_port(server_host, :this_port_is_insecure)
@host = "localhost:#{server_port}"
@ch = GRPC::Core::Channel.new(@host, nil, :this_channel_is_insecure)
diff --git a/src/ruby/spec/server_spec.rb b/src/ruby/spec/server_spec.rb
index c0a59572b1..a0d27b66f5 100644
--- a/src/ruby/spec/server_spec.rb
+++ b/src/ruby/spec/server_spec.rb
@@ -30,12 +30,12 @@ describe Server do
describe '#start' do
it 'runs without failing' do
- blk = proc { Server.new(nil).start }
+ blk = proc { new_core_server_for_testing(nil).start }
expect(&blk).to_not raise_error
end
it 'fails if the server is closed' do
- s = Server.new(nil)
+ s = new_core_server_for_testing(nil)
s.close
expect { s.start }.to raise_error(RuntimeError)
end
@@ -85,7 +85,7 @@ describe Server do
describe 'for insecure servers' do
it 'runs without failing' do
blk = proc do
- s = Server.new(nil)
+ s = new_core_server_for_testing(nil)
s.add_http2_port('localhost:0', :this_port_is_insecure)
s.close
end
@@ -93,7 +93,7 @@ describe Server do
end
it 'fails if the server is closed' do
- s = Server.new(nil)
+ s = new_core_server_for_testing(nil)
s.close
blk = proc do
s.add_http2_port('localhost:0', :this_port_is_insecure)
@@ -106,7 +106,7 @@ describe Server do
let(:cert) { create_test_cert }
it 'runs without failing' do
blk = proc do
- s = Server.new(nil)
+ s = new_core_server_for_testing(nil)
s.add_http2_port('localhost:0', cert)
s.close
end
@@ -114,7 +114,7 @@ describe Server do
end
it 'fails if the server is closed' do
- s = Server.new(nil)
+ s = new_core_server_for_testing(nil)
s.close
blk = proc { s.add_http2_port('localhost:0', cert) }
expect(&blk).to raise_error(RuntimeError)
@@ -124,7 +124,7 @@ describe Server do
shared_examples '#new' do
it 'takes nil channel args' do
- expect { Server.new(nil) }.to_not raise_error
+ expect { new_core_server_for_testing(nil) }.to_not raise_error
end
it 'does not take a hash with bad keys as channel args' do
@@ -175,14 +175,14 @@ describe Server do
describe '#new with an insecure channel' do
def construct_with_args(a)
- proc { Server.new(a) }
+ proc { new_core_server_for_testing(a) }
end
it_behaves_like '#new'
end
def start_a_server
- s = Server.new(nil)
+ s = new_core_server_for_testing(nil)
s.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
s.start
s
diff --git a/src/ruby/spec/spec_helper.rb b/src/ruby/spec/spec_helper.rb
index 6e1eba1945..8fe9e6e808 100644
--- a/src/ruby/spec/spec_helper.rb
+++ b/src/ruby/spec/spec_helper.rb
@@ -32,6 +32,9 @@ require 'rspec'
require 'logging'
require 'rspec/logging_helper'
+require_relative 'support/services'
+require_relative 'support/helpers'
+
# GRPC is the general RPC module
#
# Configure its logging for fine-grained log control during test runs
@@ -49,6 +52,7 @@ Logging.logger['GRPC::BidiCall'].level = :info
RSpec.configure do |config|
include RSpec::LoggingHelper
config.capture_log_messages # comment this out to see logs during test runs
+ include GRPC::Spec::Helpers
end
RSpec::Expectations.configuration.warn_about_potential_false_positives = false
diff --git a/src/ruby/spec/support/helpers.rb b/src/ruby/spec/support/helpers.rb
new file mode 100644
index 0000000000..29028df8b7
--- /dev/null
+++ b/src/ruby/spec/support/helpers.rb
@@ -0,0 +1,107 @@
+# Copyright 2017 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# GRPC contains the General RPC module.
+module GRPC
+ ##
+ # GRPC RSpec base module
+ #
+ module Spec
+ ##
+ # A module that is used for providing generic helpers across the
+ # GRPC test suite
+ #
+ module Helpers
+ # Shortcut syntax for a GRPC RPC Server
+ RpcServer = GRPC::RpcServer
+
+ ##
+ # Build an RPC server used for testing
+ #
+ def build_rpc_server(server_opts: {},
+ client_opts: {})
+ @server = new_rpc_server_for_testing({ poll_period: 1 }.merge(server_opts))
+ @port = @server.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
+ @host = "0.0.0.0:#{@port}"
+ @client_opts = client_opts
+ @server
+ end
+
+ ##
+ # Run services on an RPC server, yielding to allow testing within
+ #
+ # @param [RpcServer] server
+ # @param [Array<Class>] services
+ #
+ def run_services_on_server(server, services: [])
+ services.each do |s|
+ server.handle(s)
+ end
+ t = Thread.new { server.run }
+ server.wait_till_running
+
+ yield
+
+ server.stop
+ t.join
+ end
+
+ ##
+ # Build an insecure stub from a given stub class
+ #
+ # @param [Class] klass
+ # @param [String] host
+ #
+ def build_insecure_stub(klass, host: nil, opts: nil)
+ host ||= @host
+ opts ||= @client_opts
+ klass.new(host, :this_channel_is_insecure, **opts)
+ end
+
+ ##
+ # Build an RPCServer for use in tests. Adds args
+ # that are useful for all tests.
+ #
+ # @param [Hash] server_opts
+ #
+ def new_rpc_server_for_testing(server_opts = {})
+ server_opts[:server_args] ||= {}
+ update_server_args_hash(server_opts[:server_args])
+ RpcServer.new(**server_opts)
+ end
+
+ ##
+ # Build an GRPC::Core::Server for use in tests. Adds args
+ # that are useful for all tests.
+ #
+ # @param [Hash] server_args
+ #
+ def new_core_server_for_testing(server_args)
+ server_args.nil? && server_args = {}
+ update_server_args_hash(server_args)
+ GRPC::Core::Server.new(server_args)
+ end
+
+ def update_server_args_hash(server_args)
+ so_reuseport_arg = 'grpc.so_reuseport'
+ unless server_args[so_reuseport_arg].nil?
+ fail 'Unexpected. grpc.so_reuseport already set.'
+ end
+ # Run tests without so_reuseport to eliminate the chance of
+ # cross-talk.
+ server_args[so_reuseport_arg] = 0
+ end
+ end
+ end
+end
diff --git a/src/ruby/spec/support/services.rb b/src/ruby/spec/support/services.rb
new file mode 100644
index 0000000000..27cc8e61ac
--- /dev/null
+++ b/src/ruby/spec/support/services.rb
@@ -0,0 +1,147 @@
+# Copyright 2017 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Test stubs for various scenarios
+require 'grpc'
+
+# A test message
+class EchoMsg
+ def self.marshal(_o)
+ ''
+ end
+
+ def self.unmarshal(_o)
+ EchoMsg.new
+ end
+end
+
+# A test service with an echo implementation.
+class EchoService
+ include GRPC::GenericService
+ rpc :an_rpc, EchoMsg, EchoMsg
+ rpc :a_client_streaming_rpc, stream(EchoMsg), EchoMsg
+ rpc :a_server_streaming_rpc, EchoMsg, stream(EchoMsg)
+ rpc :a_bidi_rpc, stream(EchoMsg), stream(EchoMsg)
+ attr_reader :received_md
+
+ def initialize(**kw)
+ @trailing_metadata = kw
+ @received_md = []
+ end
+
+ def an_rpc(req, call)
+ GRPC.logger.info('echo service received a request')
+ call.output_metadata.update(@trailing_metadata)
+ @received_md << call.metadata unless call.metadata.nil?
+ req
+ end
+
+ def a_client_streaming_rpc(call)
+ # iterate through requests so call can complete
+ call.output_metadata.update(@trailing_metadata)
+ call.each_remote_read.each { |r| p r }
+ EchoMsg.new
+ end
+
+ def a_server_streaming_rpc(_req, call)
+ call.output_metadata.update(@trailing_metadata)
+ [EchoMsg.new, EchoMsg.new]
+ end
+
+ def a_bidi_rpc(requests, call)
+ call.output_metadata.update(@trailing_metadata)
+ requests.each { |r| p r }
+ [EchoMsg.new, EchoMsg.new]
+ end
+end
+
+EchoStub = EchoService.rpc_stub_class
+
+# For testing server interceptors
+class TestServerInterceptor < GRPC::ServerInterceptor
+ def request_response(request:, call:, method:)
+ p "Received request/response call at method #{method}" \
+ " with request #{request} for call #{call}"
+ call.output_metadata[:interc] = 'from_request_response'
+ p "[GRPC::Ok] (#{method.owner.name}.#{method.name})"
+ yield
+ end
+
+ def client_streamer(call:, method:)
+ call.output_metadata[:interc] = 'from_client_streamer'
+ call.each_remote_read.each do |r|
+ p "In interceptor: #{r}"
+ end
+ p "Received client streamer call at method #{method} for call #{call}"
+ yield
+ end
+
+ def server_streamer(request:, call:, method:)
+ p "Received server streamer call at method #{method} with request" \
+ " #{request} for call #{call}"
+ call.output_metadata[:interc] = 'from_server_streamer'
+ yield
+ end
+
+ def bidi_streamer(requests:, call:, method:)
+ requests.each do |r|
+ p "Bidi request: #{r}"
+ end
+ p "Received bidi streamer call at method #{method} with requests" \
+ " #{requests} for call #{call}"
+ call.output_metadata[:interc] = 'from_bidi_streamer'
+ yield
+ end
+end
+
+# For testing client interceptors
+class TestClientInterceptor < GRPC::ClientInterceptor
+ def request_response(request:, call:, method:, metadata: {})
+ p "Intercepted request/response call at method #{method}" \
+ " with request #{request} for call #{call}" \
+ " and metadata: #{metadata}"
+ metadata['foo'] = 'bar_from_request_response'
+ yield
+ end
+
+ def client_streamer(requests:, call:, method:, metadata: {})
+ p "Received client streamer call at method #{method}" \
+ " with requests #{requests} for call #{call}" \
+ " and metadata: #{metadata}"
+ requests.each do |r|
+ p "In client interceptor: #{r}"
+ end
+ metadata['foo'] = 'bar_from_client_streamer'
+ yield
+ end
+
+ def server_streamer(request:, call:, method:, metadata: {})
+ p "Received server streamer call at method #{method}" \
+ " with request #{request} for call #{call}" \
+ " and metadata: #{metadata}"
+ metadata['foo'] = 'bar_from_server_streamer'
+ yield
+ end
+
+ def bidi_streamer(requests:, call:, method:, metadata: {})
+ p "Received bidi streamer call at method #{method}" \
+ "with requests #{requests} for call #{call}" \
+ " and metadata: #{metadata}"
+ requests.each do |r|
+ p "In client interceptor: #{r}"
+ end
+ metadata['foo'] = 'bar_from_bidi_streamer'
+ yield
+ end
+end