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 +4 -4
- data/README.md +42 -8
- data/grpc_mock.gemspec +2 -2
- data/lib/grpc_mock/grpc_stub_adapter.rb +12 -8
- data/lib/grpc_mock/mocked_call.rb +72 -0
- data/lib/grpc_mock/request_stub.rb +12 -3
- data/lib/grpc_mock/response.rb +44 -0
- data/lib/grpc_mock/{resopnse_sequence.rb → response_sequence.rb} +0 -0
- data/lib/grpc_mock/stub_registry.rb +1 -1
- data/lib/grpc_mock/version.rb +1 -1
- metadata +11 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d9ce22e13c7dae6452813ff348fab4e0b894f21b33fdff005600334df3f77d8
|
4
|
+
data.tar.gz: 4dfd66d56c40ac155d0d2d67c88f7f97dbb7931a34e93d1df0ce37a2e069cd18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
39
|
+
client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Hello::HelloResponse.new(msg: 'test')
|
40
40
|
```
|
41
41
|
|
42
|
-
|
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
|
49
|
-
client
|
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
|
-
|
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
|
69
|
+
client.hello(Hello::HelloRequest.new(msg: 'hello')) # => Raise NetConnectNotAllowedError error
|
59
70
|
|
60
71
|
GrpcMock.allow_net_connect!
|
61
|
-
|
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
|
data/grpc_mock.gemspec
CHANGED
@@ -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', '<
|
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', '
|
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
|
-
|
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
|
-
|
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
|
-
|
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/
|
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(*
|
21
|
-
|
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
|
File without changes
|
data/lib/grpc_mock/version.rb
CHANGED
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
|
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:
|
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:
|
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:
|
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:
|
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:
|
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/
|
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
|