webmock 1.15.2 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.16.0
4
+
5
+ * Allow a Pathname to be passed as a Response body
6
+
7
+ stub_request(:get, /example.com/).to_return(
8
+ body: Rails.root.join('test/fixtures/foo.txt')
9
+ )
10
+
11
+ Thanks to [Ben Pickles](https://github.com/benpickles)
12
+
13
+ * `hash_including` matcher can be initialized with empty keys to match any values.
14
+
15
+ stub_request(:post, "www.example.com").with(:body => hash_including(:a, :b => {'c'}))
16
+ RestClient.post('www.example.com', '{"a":"1","b":"c"}', :content_type => 'application/json')
17
+
18
+ Thanks to [Stefano Uliari](https://github.com/steookk)
19
+
3
20
  ## 1.15.2
4
21
 
5
22
  * Fixed `hash_including` to accept a splat of solitary keys.
data/README.md CHANGED
@@ -864,6 +864,7 @@ People who submitted patches and new features or suggested improvements. Many th
864
864
  * Brian D. Burns
865
865
  * Riley Strong
866
866
  * Tamir Duberstein
867
+ * Stefano Uliari
867
868
 
868
869
  For a full list of contributors you can visit the
869
870
  [contributors](https://github.com/bblimke/webmock/contributors) page.
@@ -35,11 +35,21 @@ module WebMock
35
35
  assert_request_not_requested(*args)
36
36
  end
37
37
 
38
- def hash_including(*expected)
38
+ # Similar to RSpec::Mocks::ArgumentMatchers#hash_including()
39
+ #
40
+ # Matches a hash that includes the specified key(s) or key/value pairs.
41
+ # Ignores any additional keys.
42
+ #
43
+ # @example
44
+ #
45
+ # object.should_receive(:message).with(hash_including(:key => val))
46
+ # object.should_receive(:message).with(hash_including(:key))
47
+ # object.should_receive(:message).with(hash_including(:key, :key2 => val2))
48
+ def hash_including(*args)
39
49
  if defined?(super)
40
50
  super
41
51
  else
42
- WebMock::Matchers::HashIncludingMatcher.new(expected)
52
+ WebMock::Matchers::HashIncludingMatcher.new(anythingize_lonely_keys(*args))
43
53
  end
44
54
  end
45
55
 
@@ -64,5 +74,12 @@ module WebMock
64
74
  WebMock::AssertionFailure.failure(verifier.negative_failure_message) unless verifier.does_not_match?
65
75
  end
66
76
 
77
+ #this is a based on RSpec::Mocks::ArgumentMatchers#anythingize_lonely_keys
78
+ def anythingize_lonely_keys(*args)
79
+ hash = args.last.class == Hash ? args.delete_at(-1) : {}
80
+ args.each { | arg | hash[arg] = WebMock::Matchers::AnyArgMatcher.new(nil) }
81
+ hash
82
+ end
83
+
67
84
  end
68
85
  end
@@ -148,6 +148,7 @@ if defined?(::HTTPClient)
148
148
  hdrs[header[0]] << header[1]
149
149
  hdrs
150
150
  end
151
+ headers = headers_from_session(uri).merge(headers)
151
152
 
152
153
  if (auth_cred = auth.get(req)) && auth.scheme == 'Basic'
153
154
  userinfo = WebMock::Util::Headers.decode_userinfo_from_header(auth_cred)
@@ -186,4 +187,28 @@ if defined?(::HTTPClient)
186
187
  webmock_request_signatures.delete_at(index)
187
188
  end
188
189
 
190
+ private
191
+
192
+ # some of the headers sent by HTTPClient are derived from
193
+ # the client session
194
+ def headers_from_session(uri)
195
+ session_headers = HTTP::Message::Headers.new
196
+ @session_manager.send(:open, uri).send(:set_header, MessageMock.new(session_headers))
197
+ session_headers.all.inject({}) do |hdrs, header|
198
+ hdrs[header[0]] = header[1]
199
+ hdrs
200
+ end
201
+ end
202
+
203
+ # Mocks a HTTPClient HTTP::Message
204
+ class MessageMock
205
+ attr_reader :header
206
+
207
+ def initialize(headers)
208
+ @header = headers
209
+ end
210
+
211
+ def http_version=(value);end
212
+ end
213
+
189
214
  end
@@ -97,7 +97,7 @@ module WebMock
97
97
  response = super(request, nil, &nil)
98
98
  after_request.call(response)
99
99
  }
100
- response = if started?
100
+ if started?
101
101
  if WebMock::Config.instance.net_http_connect_on_start
102
102
  super_with_after_request.call
103
103
  else
@@ -21,5 +21,16 @@ module WebMock
21
21
  new(matcher.instance_variable_get(:@expected))
22
22
  end
23
23
  end
24
+
25
+ #this is a based on RSpec::Mocks::ArgumentMatchers::AnyArgMatcher
26
+ class AnyArgMatcher
27
+ def initialize(ignore)
28
+ end
29
+
30
+ def ==(other)
31
+ true
32
+ end
33
+ end
34
+
24
35
  end
25
36
  end
@@ -100,10 +100,10 @@ module WebMock
100
100
  private
101
101
 
102
102
  def stringify_body!
103
- if @body.is_a?(IO)
103
+ if @body.is_a?(IO) || @body.is_a?(Pathname)
104
104
  io = @body
105
105
  @body = io.read
106
- io.close
106
+ io.close if io.respond_to?(:close)
107
107
  end
108
108
  end
109
109
 
@@ -1,3 +1,3 @@
1
1
  module WebMock
2
- VERSION = '1.15.2' unless defined?(::WebMock::VERSION)
2
+ VERSION = '1.16.0' unless defined?(::WebMock::VERSION)
3
3
  end
@@ -48,7 +48,7 @@ describe "Excon" do
48
48
  end
49
49
 
50
50
  let(:file) { File.new(__FILE__) }
51
- let(:file_contents) { File.new(__FILE__).read }
51
+ let(:file_contents) { File.read(__FILE__) }
52
52
 
53
53
  it 'handles file uploads correctly' do
54
54
  stub_request(:put, "http://example.com/upload").with(:body => file_contents)
@@ -127,4 +127,29 @@ describe "HTTPClient" do
127
127
  end
128
128
  end
129
129
 
130
+ context 'session headers' do
131
+
132
+ it "client sends a User-Agent header when given an agent_name explicitly to the client" do
133
+ user_agent = "Client/0.1"
134
+ stub_request(:get, "www.example.com").with(:headers => { 'User-agent' => "#{user_agent} #{HTTPClient::LIB_NAME}" })
135
+ HTTPClient.new(:agent_name => user_agent).get("www.example.com")
136
+ end
137
+
138
+ it "client sends the Accept, User-Agent, and Date by default" do
139
+ stub_request(:get, "www.example.com").with do |req|
140
+ req.headers["Accept"].should == "*/*"
141
+ req.headers["User-Agent"].should == "#{HTTPClient::DEFAULT_AGENT_NAME} #{HTTPClient::LIB_NAME}"
142
+ req.headers["Date"].should_not be_nil
143
+ end
144
+ http_request(:get, "www.example.com")
145
+ end
146
+
147
+ it "explicitly defined headers take precedence over session defaults" do
148
+ headers = { 'Accept' => 'foo/bar', 'User-Agent' => 'custom', 'Date' => 'today' }
149
+ stub_request(:get, "www.example.com").with(:headers => headers)
150
+ HTTPClient.new.get("www.example.com", nil, headers)
151
+ end
152
+
153
+ end
154
+
130
155
  end
@@ -92,13 +92,13 @@ shared_context "declared responses" do |*adapter_info|
92
92
  describe "when response body was declared as IO" do
93
93
  it "should return response body" do
94
94
  stub_request(:get, "www.example.com").to_return(:body => File.new(__FILE__))
95
- http_request(:get, "http://www.example.com/").body.should == File.new(__FILE__).read
95
+ http_request(:get, "http://www.example.com/").body.should == File.read(__FILE__)
96
96
  end
97
97
 
98
98
  it "should return response body if requested many times" do
99
99
  stub_request(:get, "www.example.com").to_return(:body => File.new(__FILE__))
100
100
  2.times do
101
- http_request(:get, "http://www.example.com/").body.should == File.new(__FILE__).read
101
+ http_request(:get, "http://www.example.com/").body.should == File.read(__FILE__)
102
102
  end
103
103
  end
104
104
 
@@ -199,7 +199,7 @@ shared_context "declared responses" do |*adapter_info|
199
199
 
200
200
  describe "when response was declared as a string with a raw response" do
201
201
  before(:each) do
202
- @input = File.new(CURL_EXAMPLE_OUTPUT_PATH).read
202
+ @input = File.read(CURL_EXAMPLE_OUTPUT_PATH)
203
203
  stub_request(:get, "www.example.com").to_return(@input)
204
204
  @response = http_request(:get, "http://www.example.com/")
205
205
  end
@@ -210,34 +210,62 @@ shared_examples_for "stubbing requests" do |*adapter_info|
210
210
  end
211
211
 
212
212
  describe "when body is declared as partial hash matcher" do
213
- before(:each) do
214
- stub_request(:post, "www.example.com").
215
- with(:body => hash_including({:a => '1', 'c' => {'d' => ['e', 'f']} }))
216
- end
213
+ subject(:request) { http_request( :post, "http://www.example.com/",
214
+ :body => 'a=1&c[d][]=e&c[d][]=f&b=five') }
217
215
 
218
- describe "for request with url encoded body" do
219
- it "should match request if hash matches body" do
220
- http_request(
221
- :post, "http://www.example.com/",
222
- :body => 'a=1&c[d][]=e&c[d][]=f&b=five').status.should == "200"
216
+ subject(:wrong_request) { http_request(:post, "http://www.example.com/",
217
+ :body => 'c[d][]=f&a=1&c[d][]=e').status }
218
+
219
+ describe "when using 'RSpec:Mocks::ArgumentMatchers#hash_including'" do
220
+ before(:each) do
221
+ stub_request(:post, "www.example.com").
222
+ with(:body => hash_including(:a, :c => {'d' => ['e', 'f']} ))
223
223
  end
224
224
 
225
- it "should not match if hash doesn't match url encoded body" do
226
- lambda {
225
+ describe "for request with url encoded body" do
226
+ it "should match request if hash matches body" do
227
+ expect(request.status).to eq("200")
228
+ end
229
+
230
+ it "should not match if hash doesn't match url encoded body" do
231
+ lambda { wrong_request }.should raise_error
232
+ end
233
+ end
234
+
235
+ describe "for request with json body and content type is set to json" do
236
+ it "should match if hash matches body" do
227
237
  http_request(
228
- :post, "http://www.example.com/",
229
- :body => 'c[d][]=f&a=1&c[d][]=e').status
230
- }.should raise_error
238
+ :post, "http://www.example.com/", :headers => {'Content-Type' => 'application/json'},
239
+ :body => "{\"a\":\"1\",\"c\":{\"d\":[\"e\",\"f\"]},\"b\":\"five\"}").status.should == "200"
240
+ end
231
241
  end
232
242
  end
233
243
 
234
- describe "for request with json body and content type is set to json" do
235
- it "should match if hash matches body" do
236
- http_request(
237
- :post, "http://www.example.com/", :headers => {'Content-Type' => 'application/json'},
238
- :body => "{\"a\":\"1\",\"c\":{\"d\":[\"e\",\"f\"]},\"b\":\"five\"}").status.should == "200"
244
+ describe "when using 'WebMock::API#hash_including'" do
245
+ before(:each) do
246
+ stub_request(:post, "www.example.com").
247
+ with(:body => WebMock::API.hash_including(:a, :c => {'d' => ['e', 'f']} ))
248
+ end
249
+
250
+ describe "for request with url encoded body" do
251
+ it "should match request if hash matches body" do
252
+ expect(request.status).to eq("200")
253
+ end
254
+
255
+ it "should not match if hash doesn't match url encoded body" do
256
+ lambda { wrong_request }.should raise_error
257
+ end
258
+ end
259
+
260
+ describe "for request with json body and content type is set to json" do
261
+ it "should match if hash matches body" do
262
+ http_request(
263
+ :post, "http://www.example.com/", :headers => {'Content-Type' => 'application/json'},
264
+ :body => "{\"a\":\"1\",\"c\":{\"d\":[\"e\",\"f\"]},\"b\":\"five\"}").status.should == "200"
265
+ end
239
266
  end
240
267
  end
268
+
241
269
  end
242
270
  end
243
271
 
@@ -17,7 +17,7 @@ require 'support/network_connection'
17
17
  require 'support/webmock_server'
18
18
  require 'support/my_rack_app'
19
19
 
20
- CURL_EXAMPLE_OUTPUT_PATH = File.expand_path(File.dirname(__FILE__)) + "/support/example_curl_output.txt" unless defined? CURL_EXAMPLE_OUTPUT_PATH
20
+ CURL_EXAMPLE_OUTPUT_PATH = File.expand_path('../support/example_curl_output.txt', __FILE__)
21
21
 
22
22
  RSpec.configure do |config|
23
23
  unless NetworkConnection.is_network_available?
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe WebMock::API do
4
+ describe '#hash_including' do
5
+
6
+ subject { klass.new.hash_including(args) }
7
+ let(:args) { {:data => :one} }
8
+
9
+ context 'when mixed into a class that does not define `hash_including`' do
10
+ let(:klass) do
11
+ Class.new do
12
+ include WebMock::API
13
+ end
14
+ end
15
+
16
+ it 'uses WebMock::Matchers::HashIncludingMatcher' do
17
+ expect(subject).to be_a(WebMock::Matchers::HashIncludingMatcher)
18
+ end
19
+
20
+ # by testing equality for HashIncludingMatcher (which stringifies the passed hash) we are
21
+ # testing HashIncludingMatcher.initialize behavior as well
22
+ context "when args correspond to an hash" do
23
+ it "creates 'HashIncludingMatcher'" do
24
+ expect(subject).to eq("data" => :one)
25
+ end
26
+ end
27
+
28
+ context "when args are one or many keys" do
29
+ subject {klass.new.hash_including(:foo, :bar)}
30
+ let(:anything) { WebMock::Matchers::AnyArgMatcher.new(nil) }
31
+
32
+ it "creates 'HashIncludingMatcher' with keys anythingized" do
33
+ expect(subject).to eq("foo" => anything, "bar" => anything )
34
+ end
35
+ end
36
+
37
+ context "when args are both keys and key/value pairs" do
38
+ subject {klass.new.hash_including(:foo, :bar, :data => :one)}
39
+ let(:anything) { WebMock::Matchers::AnyArgMatcher.new(nil) }
40
+
41
+ it "creates 'HashIncludingMatcher' with keys anythingized" do
42
+ expect(subject).to eq("foo" => anything, "bar" => anything, "data" => :one)
43
+ end
44
+ end
45
+
46
+ context "when args are an emtpy hash" do
47
+ subject {klass.new.hash_including({})}
48
+
49
+ it "creates 'HashIncludingMatcher' with an empty hash" do
50
+ expect(subject).to eq({})
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+ context 'when mixed into a class with a parent that defines `hash_including`' do
57
+ subject {klass.new.hash_including(*args)}
58
+ let(:args) { %w(:foo, :bar, {:data => :one}) }
59
+ let(:klass) do
60
+ Class.new(
61
+ Class.new do
62
+ def hash_including(*args)
63
+ args
64
+ end
65
+ end
66
+ ) { include WebMock::API }
67
+ end
68
+
69
+ it 'uses super and passes the args untampered' do
70
+ expect(subject).to eq(args)
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ module WebMock
4
+ module Matchers
5
+
6
+ describe HashIncludingMatcher do
7
+
8
+ it "stringifies the given hash keys" do
9
+ expect(HashIncludingMatcher.new(:a => 1, :b => 2)).to eq("a" => 1, "b" => 2)
10
+ end
11
+
12
+ it "sorts elements in the hash" do
13
+ expect(HashIncludingMatcher.new(:b => 2, :a => 1)).to eq("a" => 1, "b" => 2)
14
+ end
15
+
16
+ it "describes itself properly" do
17
+ expect(HashIncludingMatcher.new(:a => 1).inspect).to eq "hash_including({\"a\"=>1})"
18
+ end
19
+
20
+ describe "success" do
21
+ it "matches the same hash" do
22
+ expect(HashIncludingMatcher.new("a" => 1, "b" => 2)).to eq("a" => 1, "b" => 2)
23
+ end
24
+
25
+ it "matches a hash with extra stuff" do
26
+ expect(HashIncludingMatcher.new(:a => 1)).to eq("a" => 1, "b" => 2)
27
+ end
28
+
29
+ describe "when matching anythingized keys" do
30
+ let(:anything) { WebMock::Matchers::AnyArgMatcher.new(nil) }
31
+
32
+ it "matches an int against anything()" do
33
+ expect(HashIncludingMatcher.new(:a => anything, :b => 2)).to eq({'a' => 1, 'b' => 2})
34
+ end
35
+
36
+ it "matches a string against anything()" do
37
+ expect(HashIncludingMatcher.new(:a => anything, :b => 2)).to eq({'a' => "1", 'b' => 2})
38
+ end
39
+
40
+ it "matches if the key is present" do
41
+ expect(HashIncludingMatcher.new(:a => anything)).to eq({'a' => 1, 'b' => 2})
42
+ end
43
+
44
+ it "matches if more keys are present" do
45
+ expect(HashIncludingMatcher.new(:a => anything, :b => anything)).to eq({'a' => 1, 'b' => 2, 'c' => 3})
46
+ end
47
+
48
+ it "matches if passed many keys and many key/value pairs" do
49
+ expect(HashIncludingMatcher.new(:a => anything, :b => anything, :c => 3, :e => 5)).to eq({'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5})
50
+ end
51
+ end
52
+
53
+ describe "when matching an empty hash" do
54
+ it "matches against any hash" do
55
+ expect(HashIncludingMatcher.new({})).to eq({:a => 1, :b => 2, :c => 3})
56
+ end
57
+ end
58
+ end
59
+
60
+ describe "failing" do
61
+ it "does not match a non-hash" do
62
+ expect(HashIncludingMatcher.new(:a => 1)).not_to eq 1
63
+ end
64
+
65
+ it "does not match a hash with a missing key" do
66
+ expect(HashIncludingMatcher.new(:a => 1)).not_to eq('b' => 2)
67
+ end
68
+
69
+ it "does not match an empty hash with a given key" do
70
+ expect(HashIncludingMatcher.new(:a => 1)).not_to eq({})
71
+ end
72
+
73
+ it "does not match a hash with a missing key when one pair is matching" do
74
+ expect(HashIncludingMatcher.new(:a => 1, :b => 2)).not_to eq('b' => 2)
75
+ end
76
+
77
+ it "does not match a hash with an incorrect value" do
78
+ expect(HashIncludingMatcher.new(:a => 1, :b => 2)).not_to eq('a' => 1, 'b' => 3)
79
+ end
80
+
81
+ it "does not match when values are nil but keys are different" do
82
+ expect(HashIncludingMatcher.new(:a => nil)).not_to eq('b' => nil)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -111,15 +111,19 @@ describe WebMock::Response do
111
111
 
112
112
  it "should report content of a IO object if provided" do
113
113
  @response = WebMock::Response.new(:body => File.new(__FILE__))
114
- @response.body.should == File.new(__FILE__).read
114
+ @response.body.should == File.read(__FILE__)
115
115
  end
116
116
 
117
117
  it "should report many times content of a IO object if provided" do
118
118
  @response = WebMock::Response.new(:body => File.new(__FILE__))
119
- @response.body.should == File.new(__FILE__).read
120
- @response.body.should == File.new(__FILE__).read
119
+ @response.body.should == File.read(__FILE__)
120
+ @response.body.should == File.read(__FILE__)
121
121
  end
122
122
 
123
+ it "should work with Pathnames" do
124
+ @response = WebMock::Response.new(:body => Pathname.new(__FILE__))
125
+ @response.body.should == File.read(__FILE__)
126
+ end
123
127
  end
124
128
 
125
129
  describe "from raw response" do
@@ -157,7 +161,7 @@ describe WebMock::Response do
157
161
 
158
162
  describe "when input is String" do
159
163
  before(:each) do
160
- @input = File.new(CURL_EXAMPLE_OUTPUT_PATH).read
164
+ @input = File.read(CURL_EXAMPLE_OUTPUT_PATH)
161
165
  @response = WebMock::Response.new(@input)
162
166
  end
163
167
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webmock
3
3
  version: !ruby/object:Gem::Version
4
- hash: 47
4
+ hash: 87
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 15
9
- - 2
10
- version: 1.15.2
8
+ - 16
9
+ - 0
10
+ version: 1.16.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Bartosz Blimke
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2013-10-30 00:00:00 Z
18
+ date: 2013-11-17 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: addressable
@@ -291,10 +291,11 @@ files:
291
291
  - spec/support/my_rack_app.rb
292
292
  - spec/support/network_connection.rb
293
293
  - spec/support/webmock_server.rb
294
+ - spec/unit/api_spec.rb
294
295
  - spec/unit/errors_spec.rb
295
296
  - spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb
296
297
  - spec/unit/http_lib_adapters/http_lib_adapter_spec.rb
297
- - spec/unit/include_spec.rb
298
+ - spec/unit/matchers/hash_including_matcher_spec.rb
298
299
  - spec/unit/rack_response_spec.rb
299
300
  - spec/unit/request_execution_verifier_spec.rb
300
301
  - spec/unit/request_pattern_spec.rb
@@ -381,10 +382,11 @@ test_files:
381
382
  - spec/support/my_rack_app.rb
382
383
  - spec/support/network_connection.rb
383
384
  - spec/support/webmock_server.rb
385
+ - spec/unit/api_spec.rb
384
386
  - spec/unit/errors_spec.rb
385
387
  - spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb
386
388
  - spec/unit/http_lib_adapters/http_lib_adapter_spec.rb
387
- - spec/unit/include_spec.rb
389
+ - spec/unit/matchers/hash_including_matcher_spec.rb
388
390
  - spec/unit/rack_response_spec.rb
389
391
  - spec/unit/request_execution_verifier_spec.rb
390
392
  - spec/unit/request_pattern_spec.rb
@@ -1,37 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe WebMock::API do
4
- describe '#hash_including' do
5
- subject { klass.new.hash_including(*args) }
6
-
7
- let(:args) { %w(foo bar) }
8
-
9
- context 'when mixed into a class that does not define `hash_including`' do
10
- let(:klass) do
11
- Class.new do
12
- include WebMock::API
13
- end
14
- end
15
-
16
- it 'uses WebMock::Matchers::HashIncludingMatcher' do
17
- expect(subject).to be_a(WebMock::Matchers::HashIncludingMatcher)
18
- end
19
- end
20
-
21
- context 'when mixed into a class with a parent that defines `hash_including`' do
22
- let(:klass) do
23
- Class.new(
24
- Class.new do
25
- def hash_including(*args)
26
- args
27
- end
28
- end
29
- ) { include WebMock::API }
30
- end
31
-
32
- it 'uses super and passes the args untampered' do
33
- expect(subject).to eq(args)
34
- end
35
- end
36
- end
37
- end