proto_pharm 0.6.0

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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/rspec.yml +35 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +3 -0
  6. data/.travis.yml +7 -0
  7. data/CHANGELOG.md +12 -0
  8. data/Gemfile +8 -0
  9. data/Gemfile.lock +113 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +238 -0
  12. data/Rakefile +8 -0
  13. data/bin/console +11 -0
  14. data/bin/regen_examples +7 -0
  15. data/bin/release +16 -0
  16. data/bin/setup +8 -0
  17. data/lib/proto_pharm.rb +47 -0
  18. data/lib/proto_pharm/action_stub.rb +57 -0
  19. data/lib/proto_pharm/adapter.rb +13 -0
  20. data/lib/proto_pharm/api.rb +31 -0
  21. data/lib/proto_pharm/configuration.rb +11 -0
  22. data/lib/proto_pharm/errors.rb +21 -0
  23. data/lib/proto_pharm/grpc_stub_adapter.rb +24 -0
  24. data/lib/proto_pharm/grpc_stub_adapter/mock_stub.rb +74 -0
  25. data/lib/proto_pharm/introspection.rb +18 -0
  26. data/lib/proto_pharm/introspection/rpc_inspector.rb +58 -0
  27. data/lib/proto_pharm/introspection/service_resolver.rb +24 -0
  28. data/lib/proto_pharm/matchers/hash_argument_matcher.rb +43 -0
  29. data/lib/proto_pharm/matchers/request_including_matcher.rb +39 -0
  30. data/lib/proto_pharm/operation_stub.rb +30 -0
  31. data/lib/proto_pharm/request_pattern.rb +29 -0
  32. data/lib/proto_pharm/request_stub.rb +67 -0
  33. data/lib/proto_pharm/response.rb +36 -0
  34. data/lib/proto_pharm/response_sequence.rb +40 -0
  35. data/lib/proto_pharm/rspec.rb +28 -0
  36. data/lib/proto_pharm/rspec/action_stub_builder.rb +37 -0
  37. data/lib/proto_pharm/rspec/action_stub_proxy.rb +58 -0
  38. data/lib/proto_pharm/rspec/dsl.rb +15 -0
  39. data/lib/proto_pharm/rspec/matchers/have_received_rpc.rb +72 -0
  40. data/lib/proto_pharm/stub_components/failure_response.rb +30 -0
  41. data/lib/proto_pharm/stub_registry.rb +36 -0
  42. data/lib/proto_pharm/version.rb +5 -0
  43. data/proto_pharm.gemspec +38 -0
  44. data/rakelib/regen_examples.rake +11 -0
  45. metadata +248 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 404f9289692becf1d216f4c8a3dbdb574394035f4fe80849565931b61ecba9ba
4
+ data.tar.gz: 6bb014799feda5517ed91d065a92c3feb5955c253779d5bf312fd6abc81f7882
5
+ SHA512:
6
+ metadata.gz: 84fc28f8e009deb5dee062d15b8eff4091219bb3af1b9ad1d83dd90399c171e1c19b435eb271258fdfb449e60d7210ef4a57a6d61ab5a9d23c22282ef9df476e
7
+ data.tar.gz: ed36363f230916dfcab696c78f517b78256e7d3e954e02665485d7a0faf8aa0eb518be92daba1f30c58d4f85b92b5d1556cade1ac1e986e4441433f288d74cb0
@@ -0,0 +1,35 @@
1
+ name: RSpec
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby:
11
+ - "2.6.x"
12
+ - "2.7.x"
13
+
14
+ steps:
15
+ - uses: actions/checkout@v1
16
+ - uses: actions/cache@v1
17
+ with:
18
+ path: vendor/bundle
19
+ key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ hashFiles('**/Gemfile.lock') }}
20
+ restore-keys: |
21
+ ${{ runner.os }}-gems-${{ matrix.ruby }}
22
+ - name: Set up Ruby ${{ matrix.ruby }}
23
+ uses: actions/setup-ruby@v1
24
+ with:
25
+ ruby-version: ${{ matrix.ruby }}
26
+ - name: Set up dependencies
27
+ env:
28
+ BUNDLE_GEM__FURY__IO: ${{ secrets.GEMFURY_DEPLOY_TOKEN }}
29
+ run: |
30
+ gem install bundler
31
+ bundle config path vendor/bundle
32
+ bundle install --jobs 4 --retry 3
33
+ - name: Run RSpec
34
+ run: |
35
+ bundle exec rspec
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,3 @@
1
+ inherit_gem:
2
+ spicerack-styleguide:
3
+ - rubocop.yml
@@ -0,0 +1,7 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.6.3
5
+ - 2.5.3
6
+ - 2.4.5
7
+ before_install: gem install bundler -v 1.16.1
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ # 0.6.0
4
+ *Release Date: 2020-12-07*
5
+
6
+ - Rename gem to `proto_pharm`
7
+ - Add support for `return_op` option([#1](https://github.com/Freshly/proto_pharm_old/pull/1), [#2](https://github.com/Freshly/proto_pharm_old/pull/2))
8
+ - Fix load order bug ([#3](https://github.com/Freshly/proto_pharm_old/pull/3))
9
+ - Add `stub_grpc_action` convenience method ([#9](https://github.com/Freshly/proto_pharm_old/pull/9), [#12](https://github.com/Freshly/proto_pharm_old/pull/12))
10
+ - Add `to_fail_with` stub method ([#14](https://github.com/Freshly/proto_pharm_old/pull/14), [#18](https://github.com/Freshly/proto_pharm_old/pull/18))
11
+ - Add `have_received_rpc` matcher ([#19](https://github.com/Freshly/proto_pharm_old/pull/19))
12
+ - Add RSpec DSL ([#10](https://github.com/Freshly/proto_pharm_old/pull/10), [#26](https://github.com/Freshly/proto_pharm_old/pull/26))
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in proto_pharm.gemspec
8
+ gemspec
@@ -0,0 +1,113 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ proto_pharm (0.6.0)
5
+ activesupport (>= 5.2.0)
6
+ grpc (>= 1.12.0, < 2)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (6.0.3.4)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 0.7, < 2)
14
+ minitest (~> 5.1)
15
+ tzinfo (~> 1.1)
16
+ zeitwerk (~> 2.2, >= 2.2.2)
17
+ ast (2.4.1)
18
+ byebug (11.1.1)
19
+ coderay (1.1.2)
20
+ concurrent-ruby (1.1.7)
21
+ diff-lcs (1.4.4)
22
+ faker (2.14.0)
23
+ i18n (>= 1.6, < 2)
24
+ google-protobuf (3.14.0)
25
+ googleapis-common-protos-types (1.0.5)
26
+ google-protobuf (~> 3.11)
27
+ grpc (1.34.0)
28
+ google-protobuf (~> 3.13)
29
+ googleapis-common-protos-types (~> 1.0)
30
+ grpc-tools (1.32.0)
31
+ i18n (1.8.5)
32
+ concurrent-ruby (~> 1.0)
33
+ method_source (0.9.2)
34
+ minitest (5.14.2)
35
+ parallel (1.20.1)
36
+ parser (2.7.2.0)
37
+ ast (~> 2.4.1)
38
+ pry (0.12.2)
39
+ coderay (~> 1.1.0)
40
+ method_source (~> 0.9.0)
41
+ pry-byebug (3.7.0)
42
+ byebug (~> 11.0)
43
+ pry (~> 0.10)
44
+ rack (2.2.3)
45
+ rainbow (3.0.0)
46
+ rake (13.0.1)
47
+ regexp_parser (1.8.2)
48
+ rexml (3.2.4)
49
+ rspec (3.9.0)
50
+ rspec-core (~> 3.9.0)
51
+ rspec-expectations (~> 3.9.0)
52
+ rspec-mocks (~> 3.9.0)
53
+ rspec-core (3.9.3)
54
+ rspec-support (~> 3.9.3)
55
+ rspec-expectations (3.9.4)
56
+ diff-lcs (>= 1.2.0, < 2.0)
57
+ rspec-support (~> 3.9.0)
58
+ rspec-mocks (3.9.1)
59
+ diff-lcs (>= 1.2.0, < 2.0)
60
+ rspec-support (~> 3.9.0)
61
+ rspec-support (3.9.4)
62
+ rspice (0.25.2)
63
+ faker (>= 1.8, < 3.0)
64
+ rspec (~> 3.0)
65
+ rubocop (0.92.0)
66
+ parallel (~> 1.10)
67
+ parser (>= 2.7.1.5)
68
+ rainbow (>= 2.2.2, < 4.0)
69
+ regexp_parser (>= 1.7)
70
+ rexml
71
+ rubocop-ast (>= 0.5.0)
72
+ ruby-progressbar (~> 1.7)
73
+ unicode-display_width (>= 1.4.0, < 2.0)
74
+ rubocop-ast (1.1.1)
75
+ parser (>= 2.7.1.5)
76
+ rubocop-performance (1.8.1)
77
+ rubocop (>= 0.87.0)
78
+ rubocop-ast (>= 0.4.0)
79
+ rubocop-rails (2.8.1)
80
+ activesupport (>= 4.2.0)
81
+ rack (>= 1.1)
82
+ rubocop (>= 0.87.0)
83
+ rubocop-rspec (1.43.2)
84
+ rubocop (~> 0.87)
85
+ ruby-progressbar (1.10.1)
86
+ spicerack-styleguide (0.25.2)
87
+ rubocop (~> 0.92.0)
88
+ rubocop-performance (~> 1.8.0)
89
+ rubocop-rails (~> 2.8.0)
90
+ rubocop-rspec (~> 1.43.0)
91
+ thread_safe (0.3.6)
92
+ tzinfo (1.2.8)
93
+ thread_safe (~> 0.1)
94
+ unicode-display_width (1.7.0)
95
+ zeitwerk (2.4.2)
96
+
97
+ PLATFORMS
98
+ ruby
99
+
100
+ DEPENDENCIES
101
+ bundler
102
+ faker
103
+ grpc-tools
104
+ proto_pharm!
105
+ pry-byebug
106
+ rake (>= 12.3.3)
107
+ rspec (~> 3.0)
108
+ rspice
109
+ rubocop
110
+ spicerack-styleguide
111
+
112
+ BUNDLED WITH
113
+ 2.1.4
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Yuta Iwama
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,238 @@
1
+ # ProtoPharm
2
+
3
+ Stub your gRPCs with lab-grown proto objects. Life is better on the pharm.
4
+
5
+ Built on a great foundation by @ganmacs at [ganmacs/grpc_mock](https://github.com/ganmacs/grpc_mock).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile in the development/test group:
10
+
11
+ ```ruby
12
+ gem 'proto_pharm'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install proto_pharm
22
+
23
+ ## Let's go Pharming!
24
+
25
+ Before we dive into the code - all examples below will refer to a gRPC service called `hello.hello` with (among others) an rpc endpoint `Hello` which receives the proto `hello.HelloRequest` and responds with the proto `hello.HelloResponse`.
26
+
27
+ The local variable `client` is defined as follows:
28
+ ```ruby
29
+ client = Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure)
30
+ ```
31
+
32
+ See full definition of protocol buffers and gRPC generated code in [spec/examples/hello](https://github.com/Freshly/proto_pharm/tree/master/spec/examples/hello).
33
+
34
+ ## RSpec Usage
35
+
36
+ To take full advantage of the helpers listed here, make sure to add the following to your `spec_helper.rb` or `rails_helper.rb`:
37
+ ```ruby
38
+ require 'proto_pharm/rspec'
39
+ ```
40
+
41
+ ### Stubbing service responses
42
+
43
+ For the simplest use case, stub a response value for a given rpc endpoint like so:
44
+ ```ruby
45
+ allow_grpc_service(Hello::Hello)
46
+ .to receive_rpc(:hello)
47
+ .and_return(msg: 'Hello!')
48
+
49
+ client.hello(Hello::HelloRequest.new(msg: 'Hello?')) # => <Hello::HelloResponse: msg: "Hello!">
50
+ ```
51
+
52
+ To stub a response for a specific request received:
53
+ ```ruby
54
+ allow_grpc_service(Hello::Hello)
55
+ .to receive_rpc(:hello)
56
+ .with(msg: 'Hola?')
57
+ .and_return(msg: 'Bienvenidos!')
58
+
59
+ client.hello(Hello::HelloRequest.new(msg: 'Hello?')) # => Sent to network
60
+ client.hello(Hello::HelloRequest.new(msg: 'Hola?')) # => <Hello::HelloResponse: msg: "Bienvenidos!">
61
+ ```
62
+
63
+ Stub a failure response:
64
+ ```ruby
65
+ allow_grpc_service(Hello::Hello)
66
+ .to receive_rpc(:hello)
67
+ .and_fail_with(:not_found, "No one's here...")
68
+
69
+ client.hello(Hello::HelloRequest.new(msg: 'Hello?')) # => <GRPC::NotFound: 5:No one's here...>
70
+ ```
71
+
72
+ Stub failure metadata:
73
+ ```ruby
74
+ allow_grpc_service(Hello::Hello)
75
+ .to receive_rpc(:hello)
76
+ .and_fail_with(:not_found, metadata: { people_here: :none })
77
+
78
+ begin
79
+ client.hello(Hello::HelloRequest.new(msg: 'Hello?'))
80
+ rescue => e
81
+ e # => <GRPC::NotFound: 5:>
82
+ e.metadata # => {:people_here=>"none"}
83
+ end
84
+ ```
85
+ Note here that the `"none"` value is a string - all metadata values will be cast as strings on response to emulate actual gRPC behavior.
86
+
87
+ Or, if you just want the call to fail and don't care about the failure type, it defaults to `:invalid_argument`:
88
+ ```ruby
89
+ allow_grpc_service(Hello::Hello).to receive_rpc(:hello).and_fail
90
+ client.hello(Hello::HelloRequest.new(msg: 'Hello?')) # => <GRPC::InvalidArgument: 3:>
91
+
92
+ # Or with some metadata...
93
+
94
+ allow_grpc_service(Hello::Hello).to receive_rpc(:hello).and_fail_with(metadata: { some: :meta_here })
95
+ client.hello(Hello::HelloRequest.new(msg: 'Hello?')) # => You get the picture
96
+ ```
97
+
98
+ #### Asserting RPC reception
99
+
100
+ ProtoPharm also adds a matcher to assert rpc reception. For example:
101
+ ```ruby
102
+ allow_grpc_service(Hello::Hello)
103
+ .to receive_rpc(:hello)
104
+ .and_return(msg: 'Hello!')
105
+
106
+ client.hello(Hello::HelloRequest.new(msg: 'Hello?'))
107
+ expect(Hello::Hello).to have_received_rpc(:hello)
108
+ ```
109
+
110
+ You can also assert the arguments received:
111
+ ```ruby
112
+ expect(Hello::Hello).to have_received_rpc(:hello).with(msg: 'Hello?')
113
+ ```
114
+
115
+ ### Argument Flexibility
116
+ You may have noticed that the above examples stub proto objects without specifying the proto type (for example, `.and_return(msg: 'Hello!')`). No `Hello::HelloRequest.new`s in sight! Both `with` and `and_return` will happily accept protos, hashes or keyword args. If you pass an invalid key to a stub method, you'll get an error:
117
+ ```ruby
118
+ allow_grpc_service(Hello::Hello).to receive_rpc(:hello).and_return(message: "Is this thing on?")
119
+ # => ArgumentError: Unknown field name 'message' in initialization map entry.
120
+ ```
121
+
122
+ Happy stubbing!
123
+
124
+ ## Usage for Minitest etc.
125
+
126
+ ### Stubbed request based on path and with the default response
127
+
128
+ ```ruby
129
+ ProtoPharm.stub_request("/hello.hello/Hello").to_return(Hello::HelloResponse.new(msg: 'test'))
130
+
131
+ client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Hello::HelloResponse.new(msg: 'test')
132
+ ```
133
+
134
+ ### Stubbing requests based on path and request
135
+
136
+ ```ruby
137
+ ProtoPharm.stub_request("/hello.hello/Hello").with(Hello::HelloRequest.new(msg: 'hi')).to_return(Hello::HelloResponse.new(msg: 'test'))
138
+
139
+ client.hello(Hello::HelloRequest.new(msg: 'hello')) # => send a request to server
140
+ client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Hello::HelloResponse.new(msg: 'test') (without any requests to server)
141
+ ```
142
+
143
+ ### Stubbing per-action requests based on parametrized request
144
+
145
+ ```ruby
146
+ ProtoPharm.stub_grpc_action(Hello::Hello::Service, :Hello).with(msg: 'hi').to_return(msg: 'test')
147
+
148
+ client.hello(Hello::HelloRequest.new(msg: 'hello')) # => send a request to server
149
+ client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Hello::HelloResponse.new(msg: 'test') (without any requests to server)
150
+
151
+ ```
152
+
153
+ ### You can user either proto objects or hash for stubbing requests
154
+
155
+ ```ruby
156
+ ProtoPharm.stub_grpc_action(Hello::Hello::Service, :Hello).with(Hello::HelloRequest.new(msg: 'hi')).to_return(msg: 'test')
157
+ # or
158
+ ProtoPharm.stub_grpc_action(Hello::Hello::Service, :Hello).with(msg: 'hi').to_return(Hello::HelloResponse.new(msg: 'test'))
159
+
160
+ client.hello(Hello::HelloRequest.new(msg: 'hello')) # => send a request to server
161
+ client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Hello::HelloResponse.new(msg: 'test') (without any requests to server)
162
+ ```
163
+
164
+ ### Real requests to network can be allowed or disabled
165
+
166
+ ```ruby
167
+ client = Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure)
168
+
169
+ ProtoPharm.disable_net_connect!
170
+ client.hello(Hello::HelloRequest.new(msg: 'hello')) # => Raise NetConnectNotAllowedError error
171
+
172
+ ProtoPharm.allow_net_connect!
173
+ Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure) # => send a request to server
174
+ ```
175
+
176
+ ### Stubbing Failures
177
+
178
+ Specific gRPC failure codes can be stubbed with metadata
179
+ ```ruby
180
+ ProtoPharm.
181
+ stub_grpc_action(Hello::Hello::Service, :Hello).
182
+ with(Hello::HelloRequest.new(msg: 'hi')).
183
+ to_fail_with(:invalid_argument, "This message is optional", metadata: { put: :your, metadata: :here })
184
+
185
+ begin
186
+ client.hello(Hello::HelloRequest.new(msg: 'hi'))
187
+ rescue => e
188
+ e # => #<GRPC::InvalidArgument: 3:This message is optional>
189
+ e.metadata # => { :put => :your, :metadata => here }
190
+ ```
191
+
192
+ By default, The failure code is `invalid_argument` and the message is optional - so if the code under test doesn't rely on those for any downstream behavior, you can simplify the stubbing by passing only metadata:
193
+ ```ruby
194
+ stub_grpc_action(Hello::Hello::Service, :Hello).
195
+ to_fail_with(metadata: { important_things: [:in, :here] })
196
+ client.hello(Hello::HelloRequest.new(msg: 'hi'))
197
+ # => #<GRPC::InvalidArgument: 3:>
198
+ exception.metadata
199
+ # => { :important_things => [:in, :here] }
200
+
201
+ ```
202
+ ...or by passing nothing at all:
203
+ ```ruby
204
+ stub_grpc_action(Hello::Hello::Service, :Hello).to_fail
205
+ client.hello(Hello::HelloRequest.new(msg: 'hi'))
206
+ # => #<GRPC::InvalidArgument: 3:>
207
+ ```
208
+
209
+ ### Raising errors
210
+
211
+ **Exception declared by class**
212
+
213
+ ```ruby
214
+ ProtoPharm.stub_request("/hello.hello/Hello").to_raise(StandardError)
215
+
216
+ client = Hello::Hello::Stub.new('localhost:8000', :this_channel_is_insecure)
217
+ client.hello(Hello::HelloRequest.new(msg: 'hi')) # => Raise StandardError
218
+ ```
219
+
220
+ **or by exception instance**
221
+
222
+ ```ruby
223
+ ProtoPharm.stub_request("/hello.hello/Hello").to_raise(StandardError.new("Some error"))
224
+ ```
225
+
226
+ **or by string**
227
+
228
+ ```ruby
229
+ ProtoPharm.stub_request("/hello.hello/Hello").to_raise("Some error")
230
+ ```
231
+
232
+ ## Contributing
233
+
234
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Freshly/proto_pharm. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
235
+
236
+ ## License
237
+
238
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).