rspec-twirp 0.2.0 → 0.3.1

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.
@@ -1,169 +0,0 @@
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
@@ -1,59 +0,0 @@
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
@@ -1,54 +0,0 @@
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
data/spec/error_spec.rb DELETED
@@ -1,116 +0,0 @@
1
- describe "be_a_twirp_error" do
2
- subject { Twirp::Error.new(code, msg, meta) }
3
-
4
- let(:code) { :not_found }
5
- let(:msg) { "Not Found" }
6
- let(:meta) { { is_meta: "true" } }
7
-
8
- it { is_expected.to be_a_twirp_error }
9
-
10
- it "catches type mismatches" do
11
- expect {
12
- expect(Object).to be_a_twirp_error
13
- }.to fail_with /to be a Twirp::Error/
14
- end
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
-
28
- describe "code matches" do
29
- it { is_expected.to be_a_twirp_error(:not_found) }
30
-
31
- it { expect { is_expected.to be_a_twirp_error(:unknown) }.to fail }
32
-
33
- it "catches erroneous codes" do
34
- expect {
35
- is_expected.to be_a_twirp_error(:not_a_valid_code)
36
- }.to raise_error(ArgumentError, /:not_a_valid_code/)
37
- end
38
- end
39
-
40
- describe "msg matches" do
41
- it { is_expected.to be_a_twirp_error("Not Found") }
42
-
43
- it "supports Regex matches" do
44
- is_expected.to be_a_twirp_error(/Not/)
45
- end
46
-
47
- it "catches mismatches" do
48
- expect {
49
- is_expected.to be_a_twirp_error("Not")
50
- }.to fail_with /to have msg: "Not"/
51
-
52
- expect {
53
- is_expected.to be_a_twirp_error(/Nope/)
54
- }.to fail_with /to have msg: \/Nope\//
55
- end
56
- end
57
-
58
- describe "meta matches" do
59
- it { is_expected.to be_a_twirp_error(is_meta: "true") }
60
- it { is_expected.to be_a_twirp_error(is_meta: /^t/) }
61
-
62
- it "catches mismatches" do
63
- expect {
64
- is_expected.to be_a_twirp_error(is_meta: "false")
65
- }.to fail_with /to have meta.*is_meta/
66
-
67
- expect {
68
- is_expected.to be_a_twirp_error(not_meta: "")
69
- }.to fail_with /to have meta.*not_meta/
70
- end
71
-
72
- it "catches type errors" do
73
- expect {
74
- is_expected.to be_a_twirp_error(is_meta: true)
75
- }.to raise_error(ArgumentError, /meta values must be Strings/)
76
- end
77
- end
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
-
97
- describe "multi matches" do
98
- it { is_expected.to be_a_twirp_error(:not_found, "Not Found") }
99
- it { is_expected.to be_a_twirp_error(:not_found, /Not/) }
100
- it { is_expected.to be_a_twirp_error(:not_found, is_meta: "true") }
101
-
102
- it "catches mismatches" do
103
- expect {
104
- is_expected.to be_a_twirp_error(:unknown, "Not Found")
105
- }.to fail_with /unknown/
106
-
107
- expect {
108
- is_expected.to be_a_twirp_error(:not_found, "Nope")
109
- }.to fail_with /Nope/
110
-
111
- expect {
112
- is_expected.to be_a_twirp_error(:not_found, is_meta: "false")
113
- }.to fail_with /false/
114
- end
115
- end
116
- end
@@ -1,181 +0,0 @@
1
- describe "make_twirp_request" do
2
- let(:client) { HelloWorldClient.new(conn) }
3
- let(:conn) { mock_twirp_connection(response) }
4
- let!(:other_client) { HelloWorldClient.new(conn) }
5
- let(:request) { HelloRequest.new(name: "World", count: 3) }
6
- let(:response) { HelloResponse.new(message: ["Hello", "Hello", "Hello World"]) }
7
-
8
- def hello
9
- client.hello(request)
10
- end
11
-
12
- describe "block mode" do
13
- it { expect { hello }.to make_twirp_request }
14
-
15
- it "returns nil" do
16
- expect {
17
- expect(hello).to be_nil
18
- }.to make_twirp_request
19
- end
20
-
21
- context "with client instance matcher" do
22
- it "matches a client instance" do
23
- expect { hello }.to make_twirp_request(client)
24
- end
25
-
26
- it "matches a pre-existing client instance" do
27
- expect {
28
- other_client.hello(HelloRequest.new)
29
- }.to make_twirp_request(other_client)
30
- end
31
-
32
- it "catches client mismaches" do
33
- expect {
34
- expect { hello }.to make_twirp_request(other_client)
35
- }.to fail
36
- end
37
- end
38
-
39
- context "with client class matcher" do
40
- it "matches a client class" do
41
- expect { hello }.to make_twirp_request(HelloWorldClient)
42
- end
43
-
44
- it "matches subclasses" do
45
- expect { hello }.to make_twirp_request(Twirp::Client)
46
- end
47
-
48
- it "catches mismatches" do
49
- expect {
50
- expect { hello }.to make_twirp_request(GoodbyeClient)
51
- }.to fail
52
- end
53
-
54
- it "catches type errors" do
55
- expect {
56
- expect { hello }.to make_twirp_request(Object)
57
- }.to raise_error(TypeError)
58
- end
59
- end
60
-
61
- context "with rpc name matcher" do
62
- it { expect { hello }.to make_twirp_request(:Hello) }
63
- it { expect { hello }.to make_twirp_request("Hello") }
64
- it { expect { hello }.to make_twirp_request(/^He/) }
65
-
66
- it "catches mismatches" do
67
- expect {
68
- expect { hello }.to make_twirp_request(:Bye)
69
- }.to fail
70
-
71
- expect {
72
- expect { hello }.to make_twirp_request(/B/)
73
- }.to fail
74
- end
75
- end
76
-
77
- context "with input matcher" do
78
- it "matches messages" do
79
- expect { hello }.to make_twirp_request.with(request)
80
- end
81
-
82
- it "matches message type" do
83
- expect { hello }.to make_twirp_request.with(HelloRequest)
84
- end
85
-
86
- it "matches with attrs" do
87
- expect { hello }.to make_twirp_request.with(count: 3)
88
- end
89
-
90
- context "with json request" do
91
- it "matches messages" do
92
- expect {
93
- client.hello(name: "World")
94
- }.to make_twirp_request.with(HelloRequest.new(name: "World"))
95
- end
96
-
97
- it "matches attrs" do
98
- expect {
99
- client.hello(name: "World")
100
- }.to make_twirp_request.with(name: "World")
101
- end
102
- end
103
-
104
- it "catches mismatches" do
105
- expect {
106
- expect { hello }.to make_twirp_request.with(count: 1)
107
- }.to fail
108
- end
109
- end
110
-
111
- context "with service matcher" do
112
- it { expect { hello }.to make_twirp_request(HelloWorldService) }
113
-
114
- it "catches mismatches" do
115
- expect {
116
- expect { hello }.to make_twirp_request(GoodbyeService)
117
- }.to fail
118
- end
119
- end
120
-
121
- describe ".and_call_original" do
122
- it "calls original" do
123
- expect {
124
- expect(hello).to be_a_twirp_response(response)
125
- }.to make_twirp_request.and_call_original
126
- end
127
- end
128
-
129
- describe ".and_return" do
130
- it "returns the specified value" do
131
- expect {
132
- expect(hello).to be_a_twirp_response(HelloRequest.new)
133
- }.to make_twirp_request.and_return(HelloRequest.new)
134
- end
135
-
136
- it "returns the specified value" do
137
- expect {
138
- expect(hello).to be_a_twirp_response(HelloRequest.new)
139
- }.to make_twirp_request.and_return(HelloRequest)
140
- end
141
- end
142
- end
143
-
144
- describe "inline mode" do
145
- after { hello }
146
-
147
- it { expect(client).to make_twirp_request }
148
-
149
- it "matches a specific rpc method" do
150
- expect(client).to make_twirp_request(:hello)
151
- end
152
-
153
- it "matches a specific rpc method by rpc name" do
154
- expect(client).to make_twirp_request(:Hello)
155
- end
156
-
157
- it "fails when client is not called within scope" do
158
- expect_expectation_failure {
159
- expect(client).to make_twirp_request
160
- }
161
- end
162
-
163
- describe ".with" do
164
- let(:request) { HelloRequest.new(name: "Daniel") }
165
-
166
- it "matches the request message" do
167
- expect(client).to make_twirp_request(:hello).with(request)
168
- end
169
-
170
- it "matches the request arguments" do
171
- expect(client).to make_twirp_request(:hello).with(name: "Daniel")
172
- end
173
-
174
- it "fails when params don't match" do
175
- expect_expectation_failure {
176
- expect(client).to make_twirp_request.with(name: "Bob")
177
- }
178
- end
179
- end
180
- end
181
- end
data/spec/message_spec.rb DELETED
@@ -1,83 +0,0 @@
1
- describe "be_a_twirp_message" do
2
- subject { HelloRequest.new(**attrs) }
3
-
4
- let(:attrs) { {} }
5
-
6
- it { is_expected.to be_a_twirp_message }
7
-
8
- it "works with responses also" do
9
- expect(HelloResponse.new).to be_a_twirp_message
10
- end
11
-
12
- it "supports compound matchers" do
13
- expect([ subject ]).to include(a_twirp_message)
14
- end
15
-
16
- it "catches non-twirp subjects" do
17
- expect {
18
- expect(Object).to be_a_twirp_message
19
- }.to fail_with /Expected a Twirp message, found Object/
20
- end
21
-
22
- it "matches a specific message type" do
23
- is_expected.to be_a_twirp_message(HelloRequest)
24
- end
25
-
26
- it "catches type mismatches" do
27
- expect {
28
- is_expected.to be_a_twirp_message(GoodbyeRequest)
29
- }.to fail_with /message of type GoodbyeRequest/
30
- end
31
-
32
- it "catches erroneous message types" do
33
- expect {
34
- is_expected.to be_a_twirp_message(Object)
35
- }.to raise_error(TypeError, /Object/)
36
- end
37
-
38
- context "with attributes" do
39
- let(:attrs) { { name: "Bob", count: 3 } }
40
-
41
- it "can match attributes" do
42
- is_expected.to be_a_twirp_message(HelloRequest, **attrs)
43
- end
44
-
45
- it "supports regex matches" do
46
- is_expected.to be_a_twirp_message(name: /^B/)
47
- end
48
-
49
- it "supports range matches" do
50
- is_expected.to be_a_twirp_message(count: 1..5)
51
- end
52
-
53
- it "catches mismatches" do
54
- expect {
55
- is_expected.to be_a_twirp_message(GoodbyeRequest, name: "Bob")
56
- }.to fail_with /message of type/
57
-
58
- expect {
59
- is_expected.to be_a_twirp_message(name: "nope")
60
- }.to fail_with /to have name: "nope"/
61
-
62
- expect {
63
- is_expected.to be_a_twirp_message(name: /no/)
64
- }.to fail_with /to have name: \/no\//
65
-
66
- expect {
67
- is_expected.to be_a_twirp_message(count: 1)
68
- }.to fail_with /to have count: 1/
69
- end
70
-
71
- it "catches the erroneous attribute matches" do
72
- expect {
73
- is_expected.to be_a_twirp_message(namezzz: "Bob")
74
- }.to raise_error(ArgumentError, /namezzz/)
75
- end
76
-
77
- it "catches type mismatches" do
78
- expect {
79
- is_expected.to be_a_twirp_message(name: 123)
80
- }.to raise_error(TypeError, /string field.*given Integer/)
81
- end
82
- end
83
- end