| # Copyright 2015, Google Inc. | |
| # All rights reserved. | |
| # | |
| # Redistribution and use in source and binary forms, with or without | |
| # modification, are permitted provided that the following conditions are | |
| # met: | |
| # | |
| # * Redistributions of source code must retain the above copyright | |
| # notice, this list of conditions and the following disclaimer. | |
| # * Redistributions in binary form must reproduce the above | |
| # copyright notice, this list of conditions and the following disclaimer | |
| # in the documentation and/or other materials provided with the | |
| # distribution. | |
| # * Neither the name of Google Inc. nor the names of its | |
| # contributors may be used to endorse or promote products derived from | |
| # this software without specific prior written permission. | |
| # | |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| require 'grpc' | |
| require 'grpc/generic/rpc_desc' | |
| require 'grpc/generic/service' | |
| # A test message that encodes/decodes using marshal/marshal. | |
| class GoodMsg | |
| def self.marshal(_o) | |
| '' | |
| end | |
| def self.unmarshal(_o) | |
| GoodMsg.new | |
| end | |
| end | |
| # A test message that encodes/decodes using encode/decode. | |
| class EncodeDecodeMsg | |
| def self.encode(_o) | |
| '' | |
| end | |
| def self.decode(_o) | |
| GoodMsg.new | |
| end | |
| end | |
| GenericService = GRPC::GenericService | |
| Dsl = GenericService::Dsl | |
| describe Dsl do | |
| it 'can be included in new classes' do | |
| blk = proc { Class.new { include Dsl } } | |
| expect(&blk).to_not raise_error | |
| end | |
| end | |
| describe GenericService do | |
| context '#underscore' do | |
| it 'should convert CamelCase to underscore separated' do | |
| expect(GenericService.underscore('AnRPC')).to eq('an_rpc') | |
| expect(GenericService.underscore('AMethod')).to eq('a_method') | |
| expect(GenericService.underscore('PrintHTML')).to eq('print_html') | |
| expect(GenericService.underscore('SeeHTMLBooks')).to eq('see_html_books') | |
| end | |
| end | |
| describe 'including it' do | |
| it 'adds a class method, rpc' do | |
| c = Class.new do | |
| include GenericService | |
| end | |
| expect(c.methods).to include(:rpc) | |
| end | |
| it 'adds rpc descs using the added class method, #rpc' do | |
| c = Class.new do | |
| include GenericService | |
| rpc :AnRpc, GoodMsg, GoodMsg | |
| end | |
| expect(c.rpc_descs).to include(:AnRpc) | |
| expect(c.rpc_descs[:AnRpc]).to be_a(GRPC::RpcDesc) | |
| end | |
| it 'give subclasses access to #rpc_descs' do | |
| base = Class.new do | |
| include GenericService | |
| rpc :AnRpc, GoodMsg, GoodMsg | |
| end | |
| c = Class.new(base) do | |
| end | |
| expect(c.rpc_descs).to include(:AnRpc) | |
| expect(c.rpc_descs[:AnRpc]).to be_a(GRPC::RpcDesc) | |
| end | |
| it 'adds a default service name' do | |
| c = Class.new do | |
| include GenericService | |
| end | |
| expect(c.service_name).to eq('GenericService') | |
| end | |
| it 'adds a default service name to subclasses' do | |
| base = Class.new do | |
| include GenericService | |
| end | |
| c = Class.new(base) do | |
| end | |
| expect(c.service_name).to eq('GenericService') | |
| end | |
| it 'adds the specified service name' do | |
| c = Class.new do | |
| include GenericService | |
| self.service_name = 'test.service.TestService' | |
| end | |
| expect(c.service_name).to eq('test.service.TestService') | |
| end | |
| it 'adds the specified service name to subclasses' do | |
| base = Class.new do | |
| include GenericService | |
| self.service_name = 'test.service.TestService' | |
| end | |
| c = Class.new(base) do | |
| end | |
| expect(c.service_name).to eq('test.service.TestService') | |
| end | |
| end | |
| describe '#include' do | |
| it 'raises if #rpc is missing an arg' do | |
| blk = proc do | |
| Class.new do | |
| include GenericService | |
| rpc :AnRpc, GoodMsg | |
| end | |
| end | |
| expect(&blk).to raise_error ArgumentError | |
| blk = proc do | |
| Class.new do | |
| include GenericService | |
| rpc :AnRpc | |
| end | |
| end | |
| expect(&blk).to raise_error ArgumentError | |
| end | |
| describe 'when #rpc args are incorrect' do | |
| it 'raises if an arg does not have the marshal or unmarshal methods' do | |
| blk = proc do | |
| Class.new do | |
| include GenericService | |
| rpc :AnRpc, GoodMsg, Object | |
| end | |
| end | |
| expect(&blk).to raise_error ArgumentError | |
| end | |
| it 'raises if a type arg only has the marshal method' do | |
| # a bad message type with only a marshal method | |
| class OnlyMarshal | |
| def marshal(o) | |
| o | |
| end | |
| end | |
| blk = proc do | |
| Class.new do | |
| include GenericService | |
| rpc :AnRpc, OnlyMarshal, GoodMsg | |
| end | |
| end | |
| expect(&blk).to raise_error ArgumentError | |
| end | |
| it 'raises if a type arg only has the unmarshal method' do | |
| # a bad message type with only an unmarshal method | |
| class OnlyUnmarshal | |
| def self.ummarshal(o) | |
| o | |
| end | |
| end | |
| blk = proc do | |
| Class.new do | |
| include GenericService | |
| rpc :AnRpc, GoodMsg, OnlyUnmarshal | |
| end | |
| end | |
| expect(&blk).to raise_error ArgumentError | |
| end | |
| end | |
| it 'is ok for services that expect the default {un,}marshal methods' do | |
| blk = proc do | |
| Class.new do | |
| include GenericService | |
| rpc :AnRpc, GoodMsg, GoodMsg | |
| end | |
| end | |
| expect(&blk).not_to raise_error | |
| end | |
| it 'is ok for services that override the default {un,}marshal methods' do | |
| blk = proc do | |
| Class.new do | |
| include GenericService | |
| self.marshal_class_method = :encode | |
| self.unmarshal_class_method = :decode | |
| rpc :AnRpc, EncodeDecodeMsg, EncodeDecodeMsg | |
| end | |
| end | |
| expect(&blk).not_to raise_error | |
| end | |
| end | |
| describe '#rpc_stub_class' do | |
| it 'generates a client class that defines any of the rpc methods' do | |
| s = Class.new do | |
| include GenericService | |
| rpc :AnRpc, GoodMsg, GoodMsg | |
| rpc :AServerStreamer, GoodMsg, stream(GoodMsg) | |
| rpc :AClientStreamer, stream(GoodMsg), GoodMsg | |
| rpc :ABidiStreamer, stream(GoodMsg), stream(GoodMsg) | |
| end | |
| client_class = s.rpc_stub_class | |
| expect(client_class.instance_methods).to include(:an_rpc) | |
| expect(client_class.instance_methods).to include(:a_server_streamer) | |
| expect(client_class.instance_methods).to include(:a_client_streamer) | |
| expect(client_class.instance_methods).to include(:a_bidi_streamer) | |
| end | |
| describe 'the generated instances' do | |
| it 'can be instanciated with just a hostname and credentials' do | |
| s = Class.new do | |
| include GenericService | |
| rpc :AnRpc, GoodMsg, GoodMsg | |
| rpc :AServerStreamer, GoodMsg, stream(GoodMsg) | |
| rpc :AClientStreamer, stream(GoodMsg), GoodMsg | |
| rpc :ABidiStreamer, stream(GoodMsg), stream(GoodMsg) | |
| end | |
| client_class = s.rpc_stub_class | |
| blk = proc do | |
| client_class.new('fakehostname', :this_channel_is_insecure) | |
| end | |
| expect(&blk).not_to raise_error | |
| end | |
| it 'has the methods defined in the service' do | |
| s = Class.new do | |
| include GenericService | |
| rpc :AnRpc, GoodMsg, GoodMsg | |
| rpc :AServerStreamer, GoodMsg, stream(GoodMsg) | |
| rpc :AClientStreamer, stream(GoodMsg), GoodMsg | |
| rpc :ABidiStreamer, stream(GoodMsg), stream(GoodMsg) | |
| end | |
| client_class = s.rpc_stub_class | |
| o = client_class.new('fakehostname', :this_channel_is_insecure) | |
| expect(o.methods).to include(:an_rpc) | |
| expect(o.methods).to include(:a_bidi_streamer) | |
| expect(o.methods).to include(:a_client_streamer) | |
| expect(o.methods).to include(:a_bidi_streamer) | |
| end | |
| end | |
| end | |
| end |