grpc_mock 0.3.0 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64d71ca90aa6a226e3adabaaeb57706fca0c0f64d2a1067ce6e43096325309dd
4
- data.tar.gz: 31d13e31248b47d0fbef1e2b6a684c14e09849b9f122788154701f3df44c4acb
3
+ metadata.gz: 1d9ce22e13c7dae6452813ff348fab4e0b894f21b33fdff005600334df3f77d8
4
+ data.tar.gz: 4dfd66d56c40ac155d0d2d67c88f7f97dbb7931a34e93d1df0ce37a2e069cd18
5
5
  SHA512:
6
- metadata.gz: 1da4555681fd8e75403c2cad300793c3a7732671258a09ee57426e4cf5b3df5ce68d8d1e8bf5db7ca8122e52997030dbc81de74984f2ff011dc053842de4cd90
7
- data.tar.gz: c40cfd23df2b2106d13e752c882e1dfe809aed80f9bdc41723d632a7b77f9390bf9e20ebd524c7d65857bd5e7bfe527f4cf5eae7c0e04afc92fc28c429a6827b
6
+ metadata.gz: 4bb6164d3b3afa78b6bd8e85bb0c7864aea6b52c78038c3f1bc810faf944adfe60901d832df4b6bd6ce3cdad3a98b890974dc9a620e2ba81ecdbe82d58b05b71
7
+ data.tar.gz: 8b3385763458fe785ad159f4ca5601ff24fd8773f7c8ae3607429c388aef745e2504adcf966ae359af117001f894111f92f1f92a84dc43c4448c3e5f1ea78bfa
data/README.md CHANGED
@@ -30,35 +30,69 @@ require 'grpc_mock/rspec'
30
30
 
31
31
  See definition of protocol buffers and gRPC generated code in [spec/exmaples/hello](https://github.com/ganmacs/grpc_mock/tree/master/spec/examples/hello)
32
32
 
33
- ##### Stubbed request based on path and with the default response
33
+ ### Stubbed request based on path and with the default response
34
34
 
35
35
  ```ruby
36
36
  GrpcMock.stub_request("/hello.hello/Hello").to_return(Hello::HelloResponse.new(msg: 'test'))
37
37
 
38
38
  client = Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure)
39
- client = client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Hello::HelloResponse.new(msg: 'test')
39
+ client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Hello::HelloResponse.new(msg: 'test')
40
40
  ```
41
41
 
42
- ##### Stubbing requests based on path and request
42
+ ### Stubbing requests based on path and request
43
43
 
44
44
  ```ruby
45
45
  GrpcMock.stub_request("/hello.hello/Hello").with(Hello::HelloRequest.new(msg: 'hi')).to_return(Hello::HelloResponse.new(msg: 'test'))
46
46
 
47
47
  client = Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure)
48
- client = client.hello(Hello::HelloRequest.new(msg: 'hello')) # => send a request to server
49
- client = client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Hello::HelloResponse.new(msg: 'test') (without any requests to server)
48
+ client.hello(Hello::HelloRequest.new(msg: 'hello')) # => send a request to server
49
+ client client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Hello::HelloResponse.new(msg: 'test') (without any requests to server)
50
50
  ```
51
51
 
52
- ##### Real requests to network can be allowed or disabled
52
+ ### Responding dynamically to the stubbed requests
53
+
54
+ ```ruby
55
+ GrpcMock.stub_request("/hello.hello/Hello").to_return do |req, call|
56
+ Hello::HelloResponse.new(msg: "#{req.msg} too")
57
+ end
58
+
59
+ client = Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure)
60
+ client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Hello::HelloResponse.new(msg: 'hi too')
61
+ ```
62
+
63
+ ### Real requests to network can be allowed or disabled
53
64
 
54
65
  ```ruby
55
66
  client = Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure)
56
67
 
57
68
  GrpcMock.disable_net_connect!
58
- client = client.hello(Hello::HelloRequest.new(msg: 'hello')) # => Raise NetConnectNotAllowedError error
69
+ client.hello(Hello::HelloRequest.new(msg: 'hello')) # => Raise NetConnectNotAllowedError error
59
70
 
60
71
  GrpcMock.allow_net_connect!
61
- client = Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure) # => send a request to server
72
+ Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure) # => send a request to server
73
+ ```
74
+
75
+ ### Raising errors
76
+
77
+ **Exception declared by class**
78
+
79
+ ```ruby
80
+ GrpcMock.stub_request("/hello.hello/Hello").to_raise(StandardError)
81
+
82
+ client = Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure)
83
+ client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Raise StandardError
84
+ ```
85
+
86
+ **or by exception instance**
87
+
88
+ ```ruby
89
+ GrpcMock.stub_request("/hello.hello/Hello").to_raise(StandardError.new("Some error"))
90
+ ```
91
+
92
+ **or by string**
93
+
94
+ ```ruby
95
+ GrpcMock.stub_request("/hello.hello/Hello").to_raise("Some error")
62
96
  ```
63
97
 
64
98
  ## Contributing
@@ -22,12 +22,12 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ['lib']
24
24
 
25
- spec.add_dependency 'grpc', '>= 1.12.0', '< 1.21.0'
25
+ spec.add_dependency 'grpc', '>= 1.12.0', '< 2'
26
26
 
27
27
  spec.add_development_dependency 'bundler'
28
28
  spec.add_development_dependency 'grpc-tools'
29
29
  spec.add_development_dependency 'pry-byebug'
30
- spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'rake', '>= 12.3.3'
31
31
  spec.add_development_dependency 'rspec', '~> 3.0'
32
32
  spec.add_development_dependency 'rubocop'
33
33
  end
@@ -2,20 +2,22 @@
2
2
 
3
3
  require 'grpc'
4
4
  require 'grpc_mock/errors'
5
+ require 'grpc_mock/mocked_call'
5
6
 
6
7
  module GrpcMock
7
8
  class GrpcStubAdapter
8
9
  # To make hook point for GRPC::ClientStub
9
10
  # https://github.com/grpc/grpc/blob/bec3b5ada2c5e5d782dff0b7b5018df646b65cb0/src/ruby/lib/grpc/generic/service.rb#L150-L186
10
11
  class AdapterClass < GRPC::ClientStub
11
- def request_response(method, request, *args)
12
+ def request_response(method, request, *args, metadata: {}, **kwargs)
12
13
  unless GrpcMock::GrpcStubAdapter.enabled?
13
14
  return super
14
15
  end
15
16
 
16
17
  mock = GrpcMock.stub_registry.response_for_request(method, request)
17
18
  if mock
18
- mock
19
+ call = GrpcMock::MockedCall.new(metadata: metadata)
20
+ mock.evaluate(request, call.single_req_view)
19
21
  elsif GrpcMock.config.allow_net_connect
20
22
  super
21
23
  else
@@ -24,7 +26,7 @@ module GrpcMock
24
26
  end
25
27
 
26
28
  # TODO
27
- def client_streamer(method, requests, *args)
29
+ def client_streamer(method, requests, *args, metadata: {}, **kwargs)
28
30
  unless GrpcMock::GrpcStubAdapter.enabled?
29
31
  return super
30
32
  end
@@ -32,7 +34,8 @@ module GrpcMock
32
34
  r = requests.to_a # FIXME: this may not work
33
35
  mock = GrpcMock.stub_registry.response_for_request(method, r)
34
36
  if mock
35
- mock
37
+ call = GrpcMock::MockedCall.new(metadata: metadata)
38
+ mock.evaluate(r, call.multi_req_view)
36
39
  elsif GrpcMock.config.allow_net_connect
37
40
  super
38
41
  else
@@ -40,14 +43,15 @@ module GrpcMock
40
43
  end
41
44
  end
42
45
 
43
- def server_streamer(method, request, *args)
46
+ def server_streamer(method, request, *args, metadata: {}, **kwargs)
44
47
  unless GrpcMock::GrpcStubAdapter.enabled?
45
48
  return super
46
49
  end
47
50
 
48
51
  mock = GrpcMock.stub_registry.response_for_request(method, request)
49
52
  if mock
50
- mock
53
+ call = GrpcMock::MockedCall.new(metadata: metadata)
54
+ mock.evaluate(request, call.single_req_view)
51
55
  elsif GrpcMock.config.allow_net_connect
52
56
  super
53
57
  else
@@ -55,7 +59,7 @@ module GrpcMock
55
59
  end
56
60
  end
57
61
 
58
- def bidi_streamer(method, requests, *args)
62
+ def bidi_streamer(method, requests, *args, metadata: {}, **kwargs)
59
63
  unless GrpcMock::GrpcStubAdapter.enabled?
60
64
  return super
61
65
  end
@@ -63,7 +67,7 @@ module GrpcMock
63
67
  r = requests.to_a # FIXME: this may not work
64
68
  mock = GrpcMock.stub_registry.response_for_request(method, r)
65
69
  if mock
66
- mock
70
+ mock.evaluate(r, nil) # FIXME: provide BidiCall equivalent
67
71
  elsif GrpcMock.config.allow_net_connect
68
72
  super
69
73
  else
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc'
4
+
5
+ module GrpcMock
6
+ class MockedCall
7
+ attr_reader :deadline, :metadata
8
+
9
+ def initialize(metadata: {}, deadline: nil)
10
+ @metadata = sanitize_metadata(metadata)
11
+ @deadline = deadline
12
+ end
13
+
14
+ def multi_req_view
15
+ GRPC::ActiveCall::MultiReqView.new(self)
16
+ end
17
+
18
+ def single_req_view
19
+ GRPC::ActiveCall::SingleReqView.new(self)
20
+ end
21
+
22
+ private
23
+
24
+ def sanitize_metadata(metadata)
25
+ # Largely based on
26
+ # - grpc_rb_md_ary_fill_hash_cb https://github.com/grpc/grpc/blob/v1.29.1/src/ruby/ext/grpc/rb_call.c#L390-L465
27
+ # - grpc_rb_md_ary_convert https://github.com/grpc/grpc/blob/v1.29.1/src/ruby/ext/grpc/rb_call.c#L490-L511
28
+ # - grpc_rb_md_ary_to_h https://github.com/grpc/grpc/blob/v1.29.1/src/ruby/ext/grpc/rb_call.c#L513-L541
29
+ # See also https://github.com/grpc/grpc/blob/v1.29.1/doc/PROTOCOL-HTTP2.md for specification
30
+
31
+ raise TypeError, "got <#{metadata.class}>, want <Hash>" unless metadata.is_a?(Hash)
32
+
33
+ headers = []
34
+ metadata.each do |key, value|
35
+ raise TypeError, "bad type for key parameter" unless key.is_a?(String) || key.is_a?(Symbol)
36
+
37
+ key = key.to_s
38
+ # https://github.com/grpc/grpc/blob/v1.29.1/src/core/lib/surface/validate_metadata.cc#L61-L79
39
+ raise ArgumentError, "'#{key}' is an invalid header key" unless key.match?(/\A[a-z0-9\-_.]+\z/) && key != ''
40
+ raise ArgumentError, "Header values must be of type string or array" unless value.is_a?(String) || value.is_a?(Array)
41
+
42
+ Array(value).each do |elem|
43
+ raise TypeError, "Header value must be of type string" unless elem.is_a?(String)
44
+
45
+ unless key.end_with?('-bin')
46
+ # Non-binary metadata are translated as plain HTTP2 headers, thus this requirement.
47
+ # https://github.com/grpc/grpc/blob/v1.29.1/src/core/lib/surface/validate_metadata.cc#L85-L92
48
+ raise ArgumentError, "Header value '#{elem}' has invalid characters" unless elem.match(/\A[ -~]+\z/)
49
+
50
+ # "ASCII-Value should not have leading or trailing whitespace. If it contains leading or trailing whitespace, it may be stripped."
51
+ # https://github.com/grpc/grpc/blob/v1.29.1/doc/PROTOCOL-HTTP2.md
52
+ elem = elem.strip
53
+ end
54
+ headers << [key, elem]
55
+ end
56
+ end
57
+
58
+ metadata = {}
59
+ headers.each do |key, elem|
60
+ if metadata[key].nil?
61
+ metadata[key] = elem
62
+ elsif metadata[key].is_a?(Array)
63
+ metadata[key] << elem
64
+ else
65
+ metadata[key] = [metadata[key], elem]
66
+ end
67
+ end
68
+
69
+ metadata
70
+ end
71
+ end
72
+ end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'grpc_mock/request_pattern'
4
- require 'grpc_mock/resopnse_sequence'
4
+ require 'grpc_mock/response'
5
+ require 'grpc_mock/response_sequence'
5
6
  require 'grpc_mock/errors'
6
7
 
7
8
  module GrpcMock
@@ -17,8 +18,16 @@ module GrpcMock
17
18
  self
18
19
  end
19
20
 
20
- def to_return(*resp)
21
- @response_sequence << GrpcMock::ResponsesSequence.new(resp)
21
+ def to_return(*values, &block)
22
+ responses = [*values].flatten.map { |v| Response::Value.new(v) }
23
+ responses << Response::BlockValue.new(block) if block
24
+ @response_sequence << GrpcMock::ResponsesSequence.new(responses)
25
+ self
26
+ end
27
+
28
+ def to_raise(*exceptions)
29
+ responses = [*exceptions].flatten.map { |e| Response::ExceptionValue.new(e) }
30
+ @response_sequence << GrpcMock::ResponsesSequence.new(responses)
22
31
  self
23
32
  end
24
33
 
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrpcMock
4
+ module Response
5
+ class ExceptionValue
6
+ def initialize(exception)
7
+ @exception = case exception
8
+ when String
9
+ StandardError.new(exception)
10
+ when Class
11
+ exception.new('Exception from GrpcMock')
12
+ when Exception
13
+ exception
14
+ else
15
+ raise ArgumentError.new(message: "Invalid exception class: #{exception.class}")
16
+ end
17
+ end
18
+
19
+ def evaluate(_request = nil, _call = nil)
20
+ raise @exception.dup
21
+ end
22
+ end
23
+
24
+ class Value
25
+ def initialize(value)
26
+ @value = value
27
+ end
28
+
29
+ def evaluate(_request = nil, _call = nil)
30
+ @value.dup
31
+ end
32
+ end
33
+
34
+ class BlockValue
35
+ def initialize(block)
36
+ @block = block
37
+ end
38
+
39
+ def evaluate(request, call = nil)
40
+ @block.call(request, call)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -24,7 +24,7 @@ module GrpcMock
24
24
  end
25
25
 
26
26
  if rstub
27
- rstub.response.dup
27
+ rstub.response
28
28
  end
29
29
  end
30
30
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrpcMock
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.3'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grpc_mock
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuta Iwama
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-23 00:00:00.000000000 Z
11
+ date: 2020-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: grpc
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 1.12.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: 1.21.0
22
+ version: '2'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: 1.12.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: 1.21.0
32
+ version: '2'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: bundler
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -76,16 +76,16 @@ dependencies:
76
76
  name: rake
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - "~>"
79
+ - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: '10.0'
81
+ version: 12.3.3
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
- - - "~>"
86
+ - - ">="
87
87
  - !ruby/object:Gem::Version
88
- version: '10.0'
88
+ version: 12.3.3
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: rspec
91
91
  requirement: !ruby/object:Gem::Requirement
@@ -140,9 +140,11 @@ files:
140
140
  - lib/grpc_mock/grpc_stub_adapter.rb
141
141
  - lib/grpc_mock/matchers/hash_argument_matcher.rb
142
142
  - lib/grpc_mock/matchers/request_including_matcher.rb
143
+ - lib/grpc_mock/mocked_call.rb
143
144
  - lib/grpc_mock/request_pattern.rb
144
145
  - lib/grpc_mock/request_stub.rb
145
- - lib/grpc_mock/resopnse_sequence.rb
146
+ - lib/grpc_mock/response.rb
147
+ - lib/grpc_mock/response_sequence.rb
146
148
  - lib/grpc_mock/rspec.rb
147
149
  - lib/grpc_mock/stub_registry.rb
148
150
  - lib/grpc_mock/version.rb