rspec-twirp 0.1.0 → 0.2.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/lib/rspec/twirp/error_matcher.rb +14 -1
- data/lib/rspec/twirp/helpers.rb +16 -0
- data/lib/rspec/twirp/make_request_matcher.rb +169 -0
- data/lib/rspec/twirp/message_matcher.rb +61 -0
- data/lib/rspec/twirp/mock_client.rb +59 -0
- data/lib/rspec/twirp/mock_connection.rb +54 -0
- data/lib/rspec/twirp/response_matcher.rb +36 -16
- data/lib/rspec/twirp/version.rb +1 -1
- data/lib/rspec/twirp.rb +42 -20
- data/spec/error_spec.rb +30 -0
- data/spec/make_request_spec.rb +181 -0
- data/spec/message_spec.rb +83 -0
- data/spec/mock_connection_spec.rb +103 -0
- data/spec/response_spec.rb +100 -32
- metadata +24 -19
- data/lib/rspec/twirp/client_response_matcher.rb +0 -48
- data/lib/rspec/twirp/request_matcher.rb +0 -37
- data/spec/client_response_spec.rb +0 -112
- data/spec/request_spec.rb +0 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2634a27685c93991e30afab03cf80cb5fe650047a2b91342eeadff69a7bb920e
|
4
|
+
data.tar.gz: f87529044e688df0b546e65cd527989842e983576bf62a70ea7aabf72d920d09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01e6fbab6f8649f6c8dbcc8df72269699e099eb1d924d6ff6656781d5339d060c6138e465a5c43b5ec2a9320614d7924bed1c79eac088c9c10f881d99c6588d7
|
7
|
+
data.tar.gz: ce566d5fd9e1e91cca7a100d205e4a1b0615c743f808383d5180bd007049aeaaaf1298bfe382ad6e0385404dd3c04fadb5980ac53fbc9de54c9aeee335b4834b
|
@@ -14,9 +14,22 @@ RSpec::Matchers.define :be_a_twirp_error do |*matchers, **meta_matcher|
|
|
14
14
|
|
15
15
|
@fail_msg = "Expected #{actual} to have code: #{matcher.inspect}, found #{actual.code}"
|
16
16
|
return false unless actual.code == matcher
|
17
|
+
when Integer
|
18
|
+
# match http status code
|
19
|
+
|
20
|
+
if code = Twirp::ERROR_CODES_TO_HTTP_STATUS.key(matcher)
|
21
|
+
@fail_msg = "Expected #{actual} to have status: #{matcher}, found #{actual.code}"
|
22
|
+
return false unless actual.code == code
|
23
|
+
else
|
24
|
+
raise ArgumentError, "invalid error status code: #{matcher}"
|
25
|
+
end
|
26
|
+
when Twirp::Error
|
27
|
+
# match instance
|
28
|
+
|
29
|
+
@fail_msg = "Expected #{actual} to equal #{matcher}"
|
30
|
+
return false unless values_match?(matcher.to_h, actual.to_h)
|
17
31
|
else
|
18
32
|
# match msg
|
19
|
-
|
20
33
|
@fail_msg = "Expected #{actual} to have msg: #{matcher.inspect}, found #{actual.msg}"
|
21
34
|
return false unless values_match?(matcher, actual.msg)
|
22
35
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "rspec/twirp/mock_client"
|
2
|
+
require "rspec/twirp/mock_connection"
|
3
|
+
|
4
|
+
module RSpec
|
5
|
+
module Twirp
|
6
|
+
module Helpers
|
7
|
+
def mock_twirp_connection(...)
|
8
|
+
RSpec::Twirp.mock_connection(...)
|
9
|
+
end
|
10
|
+
|
11
|
+
def mock_twirp_client(...)
|
12
|
+
RSpec::Twirp.mock_client(...)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# expect {
|
2
|
+
# do_the_thing
|
3
|
+
# }.to make_twirp_request(client | service | rpc_method).with(request | attrs)
|
4
|
+
#
|
5
|
+
# expect(client).to make_twirp_request(rpc_method).with(...)
|
6
|
+
|
7
|
+
RSpec::Matchers.define :make_twirp_request do |*matchers|
|
8
|
+
chain :with do |input_matcher = nil|
|
9
|
+
@input_matcher = if input_matcher
|
10
|
+
if input_matcher.is_a?(Google::Protobuf::MessageExts)
|
11
|
+
defaults = input_matcher.class.new.to_h
|
12
|
+
hash_form = input_matcher.to_h.reject {|k,v| v == defaults[k] }
|
13
|
+
|
14
|
+
->(input) do
|
15
|
+
if input.is_a?(Google::Protobuf::MessageExts)
|
16
|
+
values_match?(input_matcher, input)
|
17
|
+
else
|
18
|
+
values_match?(include(**hash_form), input.to_h)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
elsif input_matcher.is_a?(Class) && input_matcher < Google::Protobuf::MessageExts
|
22
|
+
->(input) { values_match?(be_a(input_matcher), input) }
|
23
|
+
elsif input_matcher.is_a?(Hash)
|
24
|
+
->(input) { values_match?(include(**input_matcher), input.to_h) }
|
25
|
+
else
|
26
|
+
raise TypeError, "Expected an input_matcher of type `Google::Protobuf::MessageExts`, found #{input_matcher.class}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
chain(:and_call_original) { @and_call_original = true }
|
32
|
+
chain(:and_return) do |arg|
|
33
|
+
@and_return = case arg
|
34
|
+
when Google::Protobuf::MessageExts
|
35
|
+
Twirp::ClientResp.new(arg, nil)
|
36
|
+
when Twirp::Error
|
37
|
+
Twirp::ClientResp.new(nil, arg)
|
38
|
+
when Class
|
39
|
+
if arg < Google::Protobuf::MessageExts
|
40
|
+
Twirp::ClientResp.new(arg.new, nil)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
unless @and_return
|
45
|
+
raise TypeError, "Expected type `Google::Protobuf::MessageExts`, found #{arg}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
supports_block_expectations
|
50
|
+
|
51
|
+
match do |client_or_block|
|
52
|
+
@input_matcher ||= ->(*){ true }
|
53
|
+
|
54
|
+
if @and_call_original && @and_return
|
55
|
+
raise ArgumentError, "use `and_call_original` or `and_return`, but not both"
|
56
|
+
end
|
57
|
+
|
58
|
+
if client_or_block.is_a? Proc
|
59
|
+
RSpec::Mocks.with_temporary_scope do
|
60
|
+
match_block_request(client_or_block, matchers)
|
61
|
+
end
|
62
|
+
elsif client_or_block.is_a?(Twirp::Client)
|
63
|
+
match_client_request(client_or_block, matchers)
|
64
|
+
else
|
65
|
+
raise ArgumentError, "Expected Twirp::Client or block, found: #{client_or_block}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def match_block_request(block, matchers)
|
70
|
+
expected_client = be_a(Twirp::Client)
|
71
|
+
expected_service = anything
|
72
|
+
expected_rpc_name = anything
|
73
|
+
|
74
|
+
matchers.each do |matcher|
|
75
|
+
case matcher
|
76
|
+
when Twirp::Client
|
77
|
+
expected_client = be(matcher)
|
78
|
+
when Class
|
79
|
+
if matcher <= Twirp::Client
|
80
|
+
expected_client = be_a(matcher)
|
81
|
+
elsif matcher <= Twirp::Service
|
82
|
+
expected_service = matcher.service_full_name
|
83
|
+
else
|
84
|
+
raise TypeError
|
85
|
+
end
|
86
|
+
when String
|
87
|
+
expected_rpc_name = be(matcher.to_sym)
|
88
|
+
when Symbol
|
89
|
+
expected_rpc_name = be(matcher)
|
90
|
+
when Regexp
|
91
|
+
expected_rpc_name = matcher
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
@twirp_request_made = false
|
96
|
+
|
97
|
+
# stub pre-existing client instances
|
98
|
+
ObjectSpace.each_object(Twirp::Client) do |client|
|
99
|
+
if expected_client === client && (expected_service === client.class.service_full_name)
|
100
|
+
stub_client(client, expected_rpc_name)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# stub future client instances
|
105
|
+
ObjectSpace.each_object(Twirp::Client.singleton_class).select do |obj|
|
106
|
+
obj < Twirp::Client && obj != Twirp::ClientJSON
|
107
|
+
end.each do |client_type|
|
108
|
+
next unless client_type.name && expected_service === client_type.service_full_name
|
109
|
+
|
110
|
+
allow(client_type).to receive(:new).and_wrap_original do |orig, *args, **kwargs|
|
111
|
+
orig.call(*args, **kwargs).tap do |client|
|
112
|
+
if expected_client === client
|
113
|
+
stub_client(client, expected_rpc_name)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
block.call
|
120
|
+
|
121
|
+
@twirp_request_made
|
122
|
+
end
|
123
|
+
|
124
|
+
def stub_client(client, rpc_matcher)
|
125
|
+
allow(client).to receive(:rpc).and_wrap_original do |orig, rpc_name, input, req_opts|
|
126
|
+
if values_match?(rpc_matcher, rpc_name) && @input_matcher.call(input)
|
127
|
+
@twirp_request_made = true
|
128
|
+
end
|
129
|
+
|
130
|
+
if @and_call_original
|
131
|
+
orig.call(rpc_name, input, req_opts)
|
132
|
+
elsif @and_return
|
133
|
+
@and_return
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def match_client_request(client, matchers)
|
139
|
+
rpc_name = matchers.first.to_sym if matchers.any?
|
140
|
+
|
141
|
+
if rpc_name
|
142
|
+
rpc_info = client.class.rpcs.values.find do |x|
|
143
|
+
x[:rpc_method] == rpc_name || x[:ruby_method] == rpc_name
|
144
|
+
end
|
145
|
+
|
146
|
+
raise ArgumentError, "invalid rpc method: #{rpc_name}" unless rpc_info
|
147
|
+
|
148
|
+
msg = "Expected #{client} to make a twirp request to #{rpc_name}"
|
149
|
+
rpc_matcher = eq(rpc_info[:rpc_method])
|
150
|
+
else
|
151
|
+
rpc_info = nil
|
152
|
+
msg = "Expected #{client} to make a twirp request"
|
153
|
+
rpc_matcher = anything
|
154
|
+
end
|
155
|
+
|
156
|
+
expect(client).to receive(:rpc) do |rpc_name, input, req_opts|
|
157
|
+
expect(rpc_name).to match(rpc_matcher), msg
|
158
|
+
expect(@input_matcher.call(input)).to be(true), msg
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
description do
|
163
|
+
"make a Twirp request"
|
164
|
+
end
|
165
|
+
|
166
|
+
failure_message { @fail_msg || super() }
|
167
|
+
end
|
168
|
+
|
169
|
+
RSpec::Matchers.alias_matcher :make_a_twirp_request, :make_twirp_request
|
@@ -0,0 +1,61 @@
|
|
1
|
+
RSpec::Matchers.define :be_a_twirp_message do |type = nil, **attrs|
|
2
|
+
match do |actual|
|
3
|
+
# ensure type is a valid twirp message type
|
4
|
+
if type && !(type < Google::Protobuf::MessageExts)
|
5
|
+
raise TypeError, "Expected `type` to be a Google::Protobuf::MessageExts, found: #{type}"
|
6
|
+
end
|
7
|
+
|
8
|
+
@fail_msg = "Expected a Twirp message, found #{actual}"
|
9
|
+
return false unless actual.is_a?(Google::Protobuf::MessageExts)
|
10
|
+
|
11
|
+
# match expected message type
|
12
|
+
@fail_msg = "Expected a Twirp message of type #{type}, found #{actual.class}"
|
13
|
+
return false if type && actual.class != type
|
14
|
+
|
15
|
+
return true if attrs.empty?
|
16
|
+
|
17
|
+
# sanity check inputs
|
18
|
+
validate_types(attrs, actual.class)
|
19
|
+
|
20
|
+
# match attributes which are present
|
21
|
+
attrs.each do |attr_name, expected_attr|
|
22
|
+
actual_attr = actual.send(attr_name)
|
23
|
+
|
24
|
+
@fail_msg = "Expected #{actual} to have #{attr_name}: #{expected_attr.inspect}, found #{actual_attr}"
|
25
|
+
return false unless values_match?(expected_attr, actual_attr)
|
26
|
+
end
|
27
|
+
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
description do
|
32
|
+
type ? "a #{type} Twirp message" : "a Twirp message"
|
33
|
+
end
|
34
|
+
|
35
|
+
failure_message { @fail_msg }
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def validate_types(attrs, klass)
|
40
|
+
# check names and types of attrs by constructing an actual proto object
|
41
|
+
discrete_attrs = attrs.transform_values do |attr|
|
42
|
+
case attr
|
43
|
+
when Regexp
|
44
|
+
attr.inspect
|
45
|
+
when Range
|
46
|
+
attr.first
|
47
|
+
when RSpec::Matchers::BuiltIn::BaseMatcher
|
48
|
+
# no good substitute, so skip attr and hope for the best
|
49
|
+
nil
|
50
|
+
else
|
51
|
+
attr
|
52
|
+
end
|
53
|
+
end.compact
|
54
|
+
|
55
|
+
klass.new(**discrete_attrs)
|
56
|
+
rescue Google::Protobuf::TypeError => e
|
57
|
+
raise TypeError, e.message
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
RSpec::Matchers.alias_matcher :a_twirp_message, :be_a_twirp_message
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# mock_twirp_client(Twirp::Client, { rpc => response } )
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Twirp
|
5
|
+
def mock_client(client, **responses)
|
6
|
+
unless client.is_a?(Class) && client < ::Twirp::Client
|
7
|
+
raise ArgumentError, "Expected Twirp::Client, found: #{client.class}"
|
8
|
+
end
|
9
|
+
|
10
|
+
rpcs = client.rpcs.values
|
11
|
+
|
12
|
+
client_instance = client.new(mock_connection)
|
13
|
+
|
14
|
+
unless responses.empty?
|
15
|
+
# validate input
|
16
|
+
responses.transform_keys! do |rpc_name|
|
17
|
+
rpc_info = rpcs.find do |x|
|
18
|
+
x[:rpc_method] == rpc_name || x[:ruby_method] == rpc_name
|
19
|
+
end
|
20
|
+
|
21
|
+
unless rpc_info
|
22
|
+
raise ArgumentError, "invalid rpc method: #{rpc_name}"
|
23
|
+
end
|
24
|
+
|
25
|
+
rpc_info[:rpc_method]
|
26
|
+
end
|
27
|
+
|
28
|
+
# repackage responses
|
29
|
+
client_responses = {}
|
30
|
+
responses.each do |rpc_method, response|
|
31
|
+
if response.is_a?(Hash)
|
32
|
+
response = client.rpcs[rpc_method.to_s][:output_class].new(**response)
|
33
|
+
end
|
34
|
+
|
35
|
+
client_responses[rpc_method] = RSpec::Twirp.generate_client_response(response)
|
36
|
+
end
|
37
|
+
|
38
|
+
# mock
|
39
|
+
rpcs.each do |info|
|
40
|
+
response = client_responses[info[:rpc_method]]
|
41
|
+
|
42
|
+
if response
|
43
|
+
allow(client_instance).to receive(:rpc).with(
|
44
|
+
info[:rpc_method],
|
45
|
+
any_args,
|
46
|
+
).and_return(response)
|
47
|
+
else
|
48
|
+
allow(client_instance).to receive(:rpc).with(
|
49
|
+
info[:rpc_method],
|
50
|
+
any_args,
|
51
|
+
).and_raise(::RSpec::Mocks::MockExpectationError)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
client_instance
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# GoodbyeClient.new(mock_twirp_connection)
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Twirp
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def mock_connection(response = nil)
|
8
|
+
if block_given? && response
|
9
|
+
raise ArgumentError, "can not specify both block and response"
|
10
|
+
end
|
11
|
+
|
12
|
+
Faraday.new do |conn|
|
13
|
+
conn.adapter :test do |stub|
|
14
|
+
stub.post(/.*/) do |env|
|
15
|
+
response = yield(env) if block_given?
|
16
|
+
response ||= {}
|
17
|
+
|
18
|
+
if response.is_a?(Hash)
|
19
|
+
# create default response
|
20
|
+
|
21
|
+
# determine which client would make this rpc call
|
22
|
+
service_full_name, rpc_method = env.url.path.split("/").last(2)
|
23
|
+
client = ObjectSpace.each_object(::Twirp::Client.singleton_class).find do |client|
|
24
|
+
next unless client.name
|
25
|
+
|
26
|
+
client.service_full_name == service_full_name && client.rpcs.key?(rpc_method)
|
27
|
+
end
|
28
|
+
|
29
|
+
unless client
|
30
|
+
raise TypeError, "could not determine Twirp::Client for: #{env.url.path}"
|
31
|
+
end
|
32
|
+
|
33
|
+
response = client.rpcs[rpc_method][:output_class].new(**response)
|
34
|
+
end
|
35
|
+
|
36
|
+
res = RSpec::Twirp.generate_client_response(response)
|
37
|
+
|
38
|
+
if res.data
|
39
|
+
status = 200
|
40
|
+
headers = { "Content-Type" => ::Twirp::Encoding::PROTO }
|
41
|
+
body = res.data.to_proto
|
42
|
+
else
|
43
|
+
status = ::Twirp::ERROR_CODES_TO_HTTP_STATUS[res.error.code]
|
44
|
+
headers = { "Content-Type" => ::Twirp::Encoding::JSON } # errors are always JSON
|
45
|
+
body = ::Twirp::Encoding.encode_json(res.error.to_h)
|
46
|
+
end
|
47
|
+
|
48
|
+
[ status, headers, body ]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -1,30 +1,50 @@
|
|
1
1
|
RSpec::Matchers.define :be_a_twirp_response do |type = nil, **attrs|
|
2
|
+
chain :with_error do |*matchers, **meta_matchers|
|
3
|
+
# code, msg, meta
|
4
|
+
@with_error = [ matchers, meta_matchers ]
|
5
|
+
end
|
6
|
+
|
2
7
|
match do |actual|
|
3
|
-
# ensure type is a valid twirp
|
4
|
-
if type
|
5
|
-
|
8
|
+
# ensure type is a valid twirp request type
|
9
|
+
if type
|
10
|
+
if type.is_a?(Google::Protobuf::MessageExts)
|
11
|
+
unless attrs.empty?
|
12
|
+
raise ArgumentError, "Expected Google::Protobuf::MessageExts instance or attrs, but not both"
|
13
|
+
end
|
14
|
+
|
15
|
+
attrs = type.to_h
|
16
|
+
type = type.class
|
17
|
+
elsif !(type < Google::Protobuf::MessageExts)
|
18
|
+
raise ArgumentError, "Expected `type` to be a Google::Protobuf::MessageExts, found: #{type}"
|
19
|
+
end
|
6
20
|
end
|
7
21
|
|
8
|
-
@fail_msg = "Expected a Twirp
|
9
|
-
return false unless actual.is_a?(
|
22
|
+
@fail_msg = "Expected a Twirp::ClientResp, found #{actual}"
|
23
|
+
return false unless actual.is_a?(Twirp::ClientResp)
|
10
24
|
|
11
25
|
# match expected response type
|
12
|
-
@fail_msg = "Expected a Twirp
|
13
|
-
return false if type && actual.class != type
|
26
|
+
@fail_msg = "Expected a Twirp::ClientResp of type #{type}, found #{actual.data&.class}"
|
27
|
+
return false if type && actual.data&.class != type
|
14
28
|
|
15
|
-
|
29
|
+
if @with_error
|
30
|
+
unless attrs.empty?
|
31
|
+
raise ArgumentError, "match data attributes or error, but not both"
|
32
|
+
end
|
16
33
|
|
17
|
-
|
34
|
+
@fail_msg = "Expected #{actual} to have an error, but found none"
|
35
|
+
return false unless actual.error
|
18
36
|
|
19
|
-
|
20
|
-
|
21
|
-
|
37
|
+
matchers, meta_matchers = @with_error
|
38
|
+
expect(actual.error).to be_a_twirp_error(*matchers, **meta_matchers)
|
39
|
+
else
|
40
|
+
@fail_msg = "Expected #{actual} to have data, but found none"
|
41
|
+
return false unless actual.data
|
22
42
|
|
23
|
-
|
24
|
-
return false unless values_match?(expected_attr, actual_attr)
|
43
|
+
expect(actual.data).to be_a_twirp_message(**attrs)
|
25
44
|
end
|
26
|
-
|
27
|
-
|
45
|
+
rescue RSpec::Expectations::ExpectationNotMetError => err
|
46
|
+
@fail_msg = err.message
|
47
|
+
false
|
28
48
|
end
|
29
49
|
|
30
50
|
description do
|
data/lib/rspec/twirp/version.rb
CHANGED
data/lib/rspec/twirp.rb
CHANGED
@@ -1,32 +1,54 @@
|
|
1
|
-
require "
|
2
|
-
require "rspec
|
1
|
+
require "google/protobuf"
|
2
|
+
require "rspec"
|
3
|
+
require "twirp"
|
4
|
+
|
5
|
+
require "rspec/twirp/helpers"
|
6
|
+
require "rspec/twirp/make_request_matcher"
|
3
7
|
require "rspec/twirp/error_matcher"
|
4
|
-
require "rspec/twirp/
|
8
|
+
require "rspec/twirp/message_matcher"
|
5
9
|
require "rspec/twirp/response_matcher"
|
6
10
|
|
7
11
|
module RSpec
|
8
12
|
module Twirp
|
9
|
-
extend
|
13
|
+
extend RSpec::Mocks::ExampleMethods
|
14
|
+
|
15
|
+
# private
|
10
16
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
def generate_client_response(arg)
|
18
|
+
res = case arg
|
19
|
+
when Google::Protobuf::MessageExts, ::Twirp::Error
|
20
|
+
arg
|
21
|
+
when Class
|
22
|
+
if arg < Google::Protobuf::MessageExts
|
23
|
+
arg.new
|
24
|
+
else
|
25
|
+
raise TypeError, "Expected type `Google::Protobuf::MessageExts`, found: #{arg}"
|
26
|
+
end
|
27
|
+
when Symbol
|
28
|
+
if ::Twirp::Error.valid_code?(arg)
|
29
|
+
::Twirp::Error.new(arg, arg)
|
22
30
|
else
|
23
|
-
|
31
|
+
raise ArgumentError, "invalid error code: #{arg}"
|
24
32
|
end
|
25
|
-
|
33
|
+
when Integer
|
34
|
+
if code = ::Twirp::ERROR_CODES_TO_HTTP_STATUS.key(arg)
|
35
|
+
::Twirp::Error.new(code, code)
|
36
|
+
else
|
37
|
+
raise ArgumentError, "invalid error code: #{arg}"
|
38
|
+
end
|
39
|
+
else
|
40
|
+
raise NotImplementedError
|
41
|
+
end
|
26
42
|
|
27
|
-
|
28
|
-
|
29
|
-
|
43
|
+
if res.is_a?(Google::Protobuf::MessageExts)
|
44
|
+
::Twirp::ClientResp.new(res, nil)
|
45
|
+
else
|
46
|
+
::Twirp::ClientResp.new(nil, res)
|
47
|
+
end
|
30
48
|
end
|
31
49
|
end
|
32
50
|
end
|
51
|
+
|
52
|
+
RSpec.configure do |config|
|
53
|
+
config.include(RSpec::Twirp::Helpers)
|
54
|
+
end
|
data/spec/error_spec.rb
CHANGED
@@ -13,6 +13,18 @@ describe "be_a_twirp_error" do
|
|
13
13
|
}.to fail_with /to be a Twirp::Error/
|
14
14
|
end
|
15
15
|
|
16
|
+
describe "status matches" do
|
17
|
+
it { is_expected.to be_a_twirp_error(404) }
|
18
|
+
|
19
|
+
it { expect { is_expected.to be_a_twirp_error(400) }.to fail }
|
20
|
+
|
21
|
+
it "catches erroneous codes" do
|
22
|
+
expect {
|
23
|
+
is_expected.to be_a_twirp_error(123)
|
24
|
+
}.to raise_error(ArgumentError, /invalid error/)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
16
28
|
describe "code matches" do
|
17
29
|
it { is_expected.to be_a_twirp_error(:not_found) }
|
18
30
|
|
@@ -64,6 +76,24 @@ describe "be_a_twirp_error" do
|
|
64
76
|
end
|
65
77
|
end
|
66
78
|
|
79
|
+
describe "instance matches" do
|
80
|
+
it "matches similar looking instances" do
|
81
|
+
error = Twirp::Error.new(code, msg, meta)
|
82
|
+
is_expected.to be_a_twirp_error(error)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "catches mismatches" do
|
86
|
+
# no meta
|
87
|
+
expect {
|
88
|
+
is_expected.to be_a_twirp_error(Twirp::Error.not_found("Not Found"))
|
89
|
+
}.to fail
|
90
|
+
|
91
|
+
expect {
|
92
|
+
is_expected.to be_a_twirp_error(Twirp::Error.internal("boom"))
|
93
|
+
}.to fail
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
67
97
|
describe "multi matches" do
|
68
98
|
it { is_expected.to be_a_twirp_error(:not_found, "Not Found") }
|
69
99
|
it { is_expected.to be_a_twirp_error(:not_found, /Not/) }
|