webmock-twirp 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/webmock/twirp/error.rb +36 -0
- data/lib/webmock/twirp/matchers/make_twirp_request.rb +46 -0
- data/lib/webmock/twirp/matchers.rb +13 -0
- data/lib/webmock/twirp/refinements.rb +6 -0
- data/lib/webmock/twirp/request_body_diff.rb +39 -0
- data/lib/webmock/twirp/request_signature.rb +55 -0
- data/lib/webmock/twirp/request_signature_snippet.rb +40 -0
- data/lib/webmock/twirp/request_stub.rb +50 -43
- data/lib/webmock/twirp/stub_request_snippet.rb +46 -0
- data/lib/webmock/twirp/version.rb +1 -1
- data/lib/webmock/twirp.rb +22 -10
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1a34362dd929abe47a175e407335cc6f94a307c00fd902742d06285550fcd78
|
4
|
+
data.tar.gz: efefa4e116a259eeab30129f7afe2b803f682daf0e0c15785e48720c5efe5d70
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04c11a624eb11547a7d0df20fcddd5b40015e75b6af94311e764484d98bc1333ffd728b085e0b5a29b08b4b039945d209a7ae74a9312bd0b65d9bebdcf87e43c
|
7
|
+
data.tar.gz: 2179804f8df26c7c65d17781cd0a4df2c5c9806697e97c1f794f2bb008a5d5febdad89522df69b853ddfd6d2a869092b8dc572a5f5ac500fb5db3a5e30995215
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
module WebMock
|
2
|
+
module Twirp
|
3
|
+
module NetConnectNotAllowedError
|
4
|
+
def initialize(request_signature)
|
5
|
+
@request_signature = request_signature
|
6
|
+
end
|
7
|
+
|
8
|
+
def message
|
9
|
+
snippet = WebMock::RequestSignatureSnippet.new(@request_signature)
|
10
|
+
|
11
|
+
text = [
|
12
|
+
"Real Twirp connections are disabled. Unregistered request: #{@request_signature}",
|
13
|
+
snippet.stubbing_instructions,
|
14
|
+
snippet.request_stubs,
|
15
|
+
"="*60
|
16
|
+
].compact.join("\n\n")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# hijack creation of Twirp errors
|
23
|
+
module WebMock
|
24
|
+
class NetConnectNotAllowedError
|
25
|
+
def self.new(request_signature)
|
26
|
+
allocate.tap do |instance|
|
27
|
+
if request_signature.is_a?(WebMock::Twirp::RequestSignature)
|
28
|
+
instance.extend(WebMock::Twirp::NetConnectNotAllowedError)
|
29
|
+
end
|
30
|
+
|
31
|
+
instance.send(:initialize, request_signature)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module WebMock
|
2
|
+
module Twirp
|
3
|
+
module Matchers
|
4
|
+
class MakeTwirpRequest
|
5
|
+
def initialize(*matchers)
|
6
|
+
@stub = WebMock::Twirp::RequestStub.new(*matchers)
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(name, *args, **kwargs, &block)
|
10
|
+
super unless respond_to?(name)
|
11
|
+
@stub.send(name, *args, **kwargs, &block)
|
12
|
+
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def respond_to?(method_name, include_private = false)
|
17
|
+
super || @stub.respond_to?(method_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(block)
|
21
|
+
unless block.is_a?(Proc)
|
22
|
+
raise ArgumentError, "expected block, found: #{block}"
|
23
|
+
end
|
24
|
+
|
25
|
+
WebMock::StubRegistry.instance.register_request_stub(@stub)
|
26
|
+
block.call
|
27
|
+
WebMock::StubRegistry.instance.remove_request_stub(@stub)
|
28
|
+
|
29
|
+
RequestRegistry.instance.times_executed(@stub) > 0
|
30
|
+
end
|
31
|
+
|
32
|
+
def failure_message
|
33
|
+
"expected a Twirp request but received none"
|
34
|
+
end
|
35
|
+
|
36
|
+
def failure_message_when_negated
|
37
|
+
"did not expect a Twirp request, but received one"
|
38
|
+
end
|
39
|
+
|
40
|
+
def supports_block_expectations?
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
using WebMock::Twirp::Refinements
|
2
|
+
|
3
|
+
module WebMock
|
4
|
+
module Twirp
|
5
|
+
module RequestBodyDiff
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def request_signature_diffable?
|
10
|
+
!!request_signature.twirp_request
|
11
|
+
end
|
12
|
+
|
13
|
+
def request_stub_diffable?
|
14
|
+
!!request_stub.with_attrs && !request_stub.with_attrs.is_a?(Proc)
|
15
|
+
end
|
16
|
+
|
17
|
+
def request_signature_body_hash
|
18
|
+
request_signature.twirp_request.normalized_hash
|
19
|
+
end
|
20
|
+
|
21
|
+
def request_stub_body_hash
|
22
|
+
request_stub.with_attrs
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# hijack creation of Twirp snippets
|
29
|
+
module WebMock
|
30
|
+
class RequestBodyDiff
|
31
|
+
def self.new(request_signature, request_stub)
|
32
|
+
super.tap do |instance|
|
33
|
+
if request_signature.proto_headers? && request_stub.is_a?(WebMock::Twirp::RequestStub)
|
34
|
+
instance.extend(WebMock::Twirp::RequestBodyDiff)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
using WebMock::Twirp::Refinements
|
2
|
+
|
3
|
+
module WebMock
|
4
|
+
module Twirp
|
5
|
+
module RequestSignature
|
6
|
+
def twirp_client
|
7
|
+
@twirp_client ||= begin
|
8
|
+
service_full_name, rpc_name = uri.path.split("/").last(2)
|
9
|
+
|
10
|
+
# find matching client
|
11
|
+
client = ObjectSpace.each_object(::Twirp::Client.singleton_class).find do |obj|
|
12
|
+
next unless obj < ::Twirp::Client && obj.name
|
13
|
+
obj.service_full_name == service_full_name && obj.rpcs.key?(rpc_name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def twirp_rpc
|
19
|
+
@twirp_rpc ||= begin
|
20
|
+
rpc_name = uri.path.split("/").last
|
21
|
+
client = twirp_client.rpcs[rpc_name] if twirp_client
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def twirp_request
|
26
|
+
twirp_rpc[:input_class].decode(body) if twirp_rpc
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
return super unless twirp_rpc
|
31
|
+
|
32
|
+
uri = WebMock::Util::URI.strip_default_port_from_uri_string(self.uri.to_s)
|
33
|
+
params = twirp_request.normalized_hash.map do |k, v|
|
34
|
+
"#{k}: #{v.inspect}"
|
35
|
+
end.join(", ")
|
36
|
+
|
37
|
+
string = "#{twirp_client}(#{uri})"
|
38
|
+
string << ".#{twirp_rpc[:ruby_method]}"
|
39
|
+
string << "(#{params.empty? ? "{}" : params})"
|
40
|
+
|
41
|
+
string
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module WebMock
|
48
|
+
class RequestSignature
|
49
|
+
def self.new(...)
|
50
|
+
super(...).tap do |instance|
|
51
|
+
instance.extend(WebMock::Twirp::RequestSignature) if instance.proto_headers?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
using WebMock::Twirp::Refinements
|
2
|
+
|
3
|
+
module WebMock
|
4
|
+
module Twirp
|
5
|
+
module RequestSignatureSnippet
|
6
|
+
def stubbing_instructions
|
7
|
+
return unless WebMock.show_stubbing_instructions?
|
8
|
+
|
9
|
+
client = @request_signature.twirp_client
|
10
|
+
rpc = @request_signature.twirp_rpc
|
11
|
+
|
12
|
+
return super unless client
|
13
|
+
|
14
|
+
string = "You can stub this request with the following snippet:\n\n"
|
15
|
+
string << "stub_twirp_request(#{rpc[:ruby_method].inspect})"
|
16
|
+
|
17
|
+
if request = @request_signature.twirp_request
|
18
|
+
params = request.normalized_hash.map do |k, v|
|
19
|
+
" #{k}: #{v.inspect},"
|
20
|
+
end.join("\n")
|
21
|
+
|
22
|
+
string << ".with(\n#{params}\n)" unless params.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
string << ".to_return(...)"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# hijack creation of Twirp snippets
|
32
|
+
module WebMock
|
33
|
+
class RequestSignatureSnippet
|
34
|
+
def self.new(request_signature)
|
35
|
+
super.tap do |instance|
|
36
|
+
instance.extend(WebMock::Twirp::RequestSignatureSnippet) if request_signature.proto_headers?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -3,6 +3,8 @@ module WebMock
|
|
3
3
|
class RequestStub < WebMock::RequestStub
|
4
4
|
using Refinements
|
5
5
|
|
6
|
+
attr_reader :twirp_client, :rpc_name, :with_attrs
|
7
|
+
|
6
8
|
def initialize(*filters)
|
7
9
|
rpc_name = filters.snag { |x| x.is_a?(Symbol) }
|
8
10
|
|
@@ -25,15 +27,11 @@ module WebMock
|
|
25
27
|
end
|
26
28
|
|
27
29
|
if klass
|
28
|
-
@
|
30
|
+
@twirp_client = klass
|
29
31
|
uri += "/#{klass.service_full_name}"
|
30
|
-
else
|
31
|
-
uri += "/[^/]+"
|
32
|
-
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
# kindly convert ruby method to rpc method name
|
33
|
+
if rpc_name
|
34
|
+
# type check and kindly convert ruby method to rpc method name
|
37
35
|
rpc_info = klass.rpcs.values.find do |x|
|
38
36
|
x[:rpc_method] == rpc_name || x[:ruby_method] == rpc_name
|
39
37
|
end
|
@@ -41,19 +39,29 @@ module WebMock
|
|
41
39
|
raise ArgumentError, "invalid rpc method: #{rpc_name}" unless rpc_info
|
42
40
|
|
43
41
|
uri += "/#{rpc_info[:rpc_method]}"
|
44
|
-
else
|
45
|
-
uri += "/#{rpc_name}"
|
46
42
|
end
|
47
|
-
else
|
48
|
-
uri += "/[^/]+"
|
49
43
|
end
|
50
44
|
|
51
|
-
super(:post, /#{uri}
|
45
|
+
super(:post, /#{uri}/)
|
52
46
|
|
53
47
|
# filter on Twirp header
|
54
48
|
@request_pattern.with(
|
55
49
|
headers: { "Content-Type" => ::Twirp::Encoding::PROTO },
|
56
50
|
)
|
51
|
+
|
52
|
+
if rpc_name
|
53
|
+
# match rpc dynamically after client resolves
|
54
|
+
@rpc_name = rpc_name
|
55
|
+
|
56
|
+
@request_pattern.with do |request|
|
57
|
+
rpc_info = request.twirp_rpc
|
58
|
+
|
59
|
+
!!rpc_info && (
|
60
|
+
rpc_info[:rpc_method] == rpc_name ||
|
61
|
+
rpc_info[:ruby_method] == rpc_name
|
62
|
+
)
|
63
|
+
end
|
64
|
+
end
|
57
65
|
end
|
58
66
|
|
59
67
|
def with(request = nil, **attrs, &block)
|
@@ -69,16 +77,31 @@ module WebMock
|
|
69
77
|
{ body: request.to_proto }
|
70
78
|
end
|
71
79
|
|
72
|
-
|
73
|
-
|
74
|
-
|
80
|
+
# save for diffing
|
81
|
+
@with_attrs = if block_given?
|
82
|
+
block
|
83
|
+
elsif request
|
84
|
+
request
|
85
|
+
elsif attrs.any?
|
86
|
+
attrs
|
87
|
+
end
|
75
88
|
|
89
|
+
decoder = ->(request_signature) do
|
76
90
|
matched = true
|
77
|
-
|
78
|
-
|
91
|
+
|
92
|
+
request = request_signature.twirp_request
|
93
|
+
matched &&= !!request
|
94
|
+
|
95
|
+
# match request attributes
|
96
|
+
if attrs.any?
|
97
|
+
matched &&= request.include?(attrs)
|
98
|
+
end
|
99
|
+
|
100
|
+
# match block
|
101
|
+
matched &&= block.call(request) if block_given?
|
79
102
|
|
80
103
|
matched
|
81
|
-
end
|
104
|
+
end
|
82
105
|
|
83
106
|
super(request_matcher || {}, &decoder)
|
84
107
|
end
|
@@ -93,17 +116,15 @@ module WebMock
|
|
93
116
|
|
94
117
|
response_hashes = responses.map do |response|
|
95
118
|
->(request) do
|
96
|
-
|
97
|
-
output_class = rpc_from_request(request)[:output_class]
|
98
|
-
generate_http_response(output_class, response)
|
119
|
+
generate_http_response(request, response)
|
99
120
|
end
|
100
121
|
end
|
101
122
|
|
102
123
|
decoder = ->(request) do
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
124
|
+
generate_http_response(
|
125
|
+
request,
|
126
|
+
block.call(request.twirp_request),
|
127
|
+
)
|
107
128
|
end if block_given?
|
108
129
|
|
109
130
|
super(*response_hashes, &decoder)
|
@@ -116,27 +137,13 @@ module WebMock
|
|
116
137
|
|
117
138
|
private
|
118
139
|
|
119
|
-
def
|
120
|
-
|
140
|
+
def generate_http_response(request, obj)
|
141
|
+
msg_class = request.twirp_rpc&.fetch(:output_class)
|
121
142
|
|
122
|
-
|
123
|
-
|
124
|
-
client = ObjectSpace.each_object(::Twirp::Client).find do |client|
|
125
|
-
service_full_name == client.class.service_full_name && \
|
126
|
-
client.class.rpcs.key?(rpc_name)
|
127
|
-
end
|
128
|
-
|
129
|
-
unless client
|
130
|
-
raise "could not determine Twirp::Client for call to: #{service_full_name}/#{rpc_name}"
|
131
|
-
end
|
132
|
-
|
133
|
-
client.class.rpcs
|
143
|
+
if msg_class.nil?
|
144
|
+
raise "could not determine Twirp::Client for request: #{request}"
|
134
145
|
end
|
135
146
|
|
136
|
-
rpcs[rpc_name]
|
137
|
-
end
|
138
|
-
|
139
|
-
def generate_http_response(msg_class, obj)
|
140
147
|
res = case obj
|
141
148
|
when nil
|
142
149
|
msg_class.new
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module WebMock
|
2
|
+
module Twirp
|
3
|
+
module StubRequestSnippet
|
4
|
+
def to_s(with_response = true)
|
5
|
+
string = "stub_twirp_request"
|
6
|
+
|
7
|
+
filters = [
|
8
|
+
@request_stub.twirp_client,
|
9
|
+
@request_stub.rpc_name&.inspect,
|
10
|
+
].compact.join(", ")
|
11
|
+
string << "(#{filters})" unless filters.empty?
|
12
|
+
|
13
|
+
if attrs = @request_stub.with_attrs
|
14
|
+
string << ".with(\n"
|
15
|
+
|
16
|
+
if attrs.is_a?(Hash)
|
17
|
+
string << attrs.map do |k, v|
|
18
|
+
" #{k}: #{v.inspect},"
|
19
|
+
end.join("\n")
|
20
|
+
elsif attrs.is_a?(Proc)
|
21
|
+
string << " { ... }"
|
22
|
+
else
|
23
|
+
string << " #{attrs}"
|
24
|
+
end
|
25
|
+
|
26
|
+
string << "\n)"
|
27
|
+
end
|
28
|
+
|
29
|
+
string
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# hijack creation of Twirp snippets
|
36
|
+
module WebMock
|
37
|
+
class StubRequestSnippet
|
38
|
+
def self.new(request_stub)
|
39
|
+
super.tap do |instance|
|
40
|
+
if request_stub.is_a?(WebMock::Twirp::RequestStub)
|
41
|
+
instance.extend(WebMock::Twirp::StubRequestSnippet)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/webmock/twirp.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
require "google/protobuf"
|
2
2
|
require "twirp"
|
3
3
|
require "webmock"
|
4
|
+
require "webmock/twirp/error"
|
5
|
+
require "webmock/twirp/matchers"
|
4
6
|
require "webmock/twirp/refinements"
|
7
|
+
require "webmock/twirp/request_body_diff"
|
8
|
+
require "webmock/twirp/request_signature"
|
9
|
+
require "webmock/twirp/request_signature_snippet"
|
5
10
|
require "webmock/twirp/request_stub"
|
11
|
+
require "webmock/twirp/stub_request_snippet"
|
6
12
|
require "webmock/twirp/version"
|
7
13
|
|
8
|
-
|
9
14
|
module WebMock
|
10
15
|
module Twirp
|
11
16
|
extend self
|
@@ -26,15 +31,22 @@ module WebMock
|
|
26
31
|
end
|
27
32
|
end
|
28
33
|
|
34
|
+
# patch WebMock to export Twirp helpers
|
35
|
+
module WebMock
|
36
|
+
module API
|
37
|
+
include WebMock::Twirp::API
|
38
|
+
end
|
29
39
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
module Matchers
|
41
|
+
include WebMock::Twirp::Matchers
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if $LOADED_FEATURES.find { |x| x =~ %r{/webmock/rspec.rb$} }
|
46
|
+
# require "webmock/rspec" was already called, so load helpers
|
47
|
+
|
48
|
+
RSpec.configure do |conf|
|
49
|
+
conf.include WebMock::Twirp::API
|
50
|
+
conf.include WebMock::Twirp::Matchers
|
39
51
|
end
|
40
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: webmock-twirp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Pepper
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: webmock
|
@@ -133,8 +133,15 @@ files:
|
|
133
133
|
- README.md
|
134
134
|
- lib/webmock-twirp.rb
|
135
135
|
- lib/webmock/twirp.rb
|
136
|
+
- lib/webmock/twirp/error.rb
|
137
|
+
- lib/webmock/twirp/matchers.rb
|
138
|
+
- lib/webmock/twirp/matchers/make_twirp_request.rb
|
136
139
|
- lib/webmock/twirp/refinements.rb
|
140
|
+
- lib/webmock/twirp/request_body_diff.rb
|
141
|
+
- lib/webmock/twirp/request_signature.rb
|
142
|
+
- lib/webmock/twirp/request_signature_snippet.rb
|
137
143
|
- lib/webmock/twirp/request_stub.rb
|
144
|
+
- lib/webmock/twirp/stub_request_snippet.rb
|
138
145
|
- lib/webmock/twirp/version.rb
|
139
146
|
- webmock-twirp.gemspec
|
140
147
|
homepage: https://github.com/dpep/webmock-twirp
|