webmock 1.7.10 → 1.8.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 (50) hide show
  1. data/.travis.yml +2 -2
  2. data/CHANGELOG.md +98 -24
  3. data/Gemfile +2 -3
  4. data/README.md +45 -4
  5. data/Rakefile +2 -2
  6. data/lib/webmock.rb +3 -0
  7. data/lib/webmock/api.rb +34 -6
  8. data/lib/webmock/http_lib_adapters/curb_adapter.rb +4 -41
  9. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +1 -1
  10. data/lib/webmock/http_lib_adapters/excon_adapter.rb +94 -0
  11. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +31 -4
  12. data/lib/webmock/http_lib_adapters/net_http.rb +2 -0
  13. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +4 -3
  14. data/lib/webmock/matchers/hash_including_matcher.rb +25 -0
  15. data/lib/webmock/rack_response.rb +8 -1
  16. data/lib/webmock/request_pattern.rb +108 -77
  17. data/lib/webmock/request_signature.rb +1 -0
  18. data/lib/webmock/stub_registry.rb +9 -8
  19. data/lib/webmock/version.rb +1 -1
  20. data/lib/webmock/webmock.rb +5 -2
  21. data/minitest/webmock_spec.rb +22 -2
  22. data/spec/acceptance/curb/curb_spec_helper.rb +12 -2
  23. data/spec/acceptance/em_http_request/em_http_request_spec.rb +42 -33
  24. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +4 -2
  25. data/spec/acceptance/excon/excon_spec.rb +15 -0
  26. data/spec/acceptance/excon/excon_spec_helper.rb +37 -0
  27. data/spec/acceptance/net_http/net_http_spec.rb +7 -0
  28. data/spec/acceptance/net_http/net_http_spec_helper.rb +3 -1
  29. data/spec/acceptance/patron/patron_spec.rb +12 -3
  30. data/spec/acceptance/patron/patron_spec_helper.rb +2 -2
  31. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +3 -3
  32. data/spec/acceptance/shared/callbacks.rb +22 -6
  33. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +21 -0
  34. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +10 -11
  35. data/spec/acceptance/shared/precedence_of_stubs.rb +1 -1
  36. data/spec/acceptance/shared/request_expectations.rb +49 -3
  37. data/spec/acceptance/shared/returning_declared_responses.rb +9 -21
  38. data/spec/acceptance/shared/stubbing_requests.rb +80 -4
  39. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
  40. data/spec/acceptance/webmock_shared.rb +11 -8
  41. data/spec/spec_helper.rb +3 -3
  42. data/spec/support/my_rack_app.rb +25 -1
  43. data/spec/support/webmock_server.rb +9 -6
  44. data/spec/unit/rack_response_spec.rb +18 -0
  45. data/spec/unit/request_pattern_spec.rb +205 -96
  46. data/spec/unit/request_signature_spec.rb +36 -34
  47. data/spec/unit/util/uri_spec.rb +14 -2
  48. data/test/shared_test.rb +31 -2
  49. data/webmock.gemspec +9 -7
  50. metadata +86 -73
@@ -32,6 +32,7 @@ module WebMock
32
32
  def eql?(other)
33
33
  self.to_s == other.to_s
34
34
  end
35
+ alias == eql?
35
36
 
36
37
  def url_encoded?
37
38
  headers && headers['Content-Type'] == 'application/x-www-form-urlencoded'
@@ -4,30 +4,31 @@ module WebMock
4
4
  include Singleton
5
5
 
6
6
  attr_accessor :request_stubs
7
- attr_accessor :global_stub
8
7
 
9
8
  def initialize
10
9
  reset!
11
10
  end
12
11
 
13
- def reset!
14
- self.request_stubs = global_stub ? [global_stub] : []
12
+ def global_stubs
13
+ @global_stubs ||= []
15
14
  end
16
15
 
17
- def global_stub_block=(block)
18
- self.global_stub = ::WebMock::RequestStub.new(:any, /.*/)
16
+ def reset!
17
+ self.request_stubs = []
18
+ end
19
19
 
20
+ def register_global_stub(&block)
20
21
  # This hash contains the responses returned by the block,
21
22
  # keyed by the exact request (using the object_id).
22
23
  # That way, there's no race condition in case #to_return
23
24
  # doesn't run immediately after stub.with.
24
25
  responses = {}
25
26
 
26
- self.global_stub.with { |request|
27
+ stub = ::WebMock::RequestStub.new(:any, /.*/).with { |request|
27
28
  responses[request.object_id] = block.call(request)
28
29
  }.to_return(lambda { |request| responses.delete(request.object_id) })
29
30
 
30
- register_request_stub(self.global_stub)
31
+ global_stubs.push stub
31
32
  end
32
33
 
33
34
  def register_request_stub(stub)
@@ -47,7 +48,7 @@ module WebMock
47
48
  private
48
49
 
49
50
  def request_stub_for(request_signature)
50
- request_stubs.detect { |registered_request_stub|
51
+ (global_stubs + request_stubs).detect { |registered_request_stub|
51
52
  registered_request_stub.request_pattern.matches?(request_signature)
52
53
  }
53
54
  end
@@ -1,3 +1,3 @@
1
1
  module WebMock
2
- VERSION = '1.7.10' unless defined?(::WebMock::VERSION)
2
+ VERSION = '1.8.0' unless defined?(::WebMock::VERSION)
3
3
  end
@@ -59,7 +59,10 @@ module WebMock
59
59
  end
60
60
  Config.instance.allow_net_connect ||
61
61
  (Config.instance.allow_localhost && WebMock::Util::URI.is_uri_localhost?(uri)) ||
62
- Config.instance.allow && (Config.instance.allow.include?(uri.host) || Config.instance.allow.include?("#{uri.host}:#{uri.port}"))
62
+ Config.instance.allow && (
63
+ (Config.instance.allow.kind_of?(Regexp) && uri.to_s =~ Config.instance.allow) ||
64
+ (Config.instance.allow.respond_to?(:include?) &&
65
+ Config.instance.allow.include?(uri.host) || Config.instance.allow.include?("#{uri.host}:#{uri.port}")))
63
66
  end
64
67
 
65
68
  def self.reset!
@@ -89,7 +92,7 @@ module WebMock
89
92
  end
90
93
 
91
94
  def self.globally_stub_request(&block)
92
- WebMock::StubRegistry.instance.global_stub_block = block
95
+ WebMock::StubRegistry.instance.register_global_stub(&block)
93
96
  end
94
97
 
95
98
  %w(
@@ -4,8 +4,8 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
4
4
  include HttpRequestTestHelper
5
5
 
6
6
  before do
7
- stub_http_request(:any, "http://www.example.com")
8
- stub_http_request(:any, "https://www.example.com")
7
+ @stub_http = stub_http_request(:any, "http://www.example.com")
8
+ @stub_https = stub_http_request(:any, "https://www.example.com")
9
9
  end
10
10
 
11
11
  it "should raise error on non stubbed request" do
@@ -18,6 +18,18 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
18
18
  assert_requested(:get, "http://www.example.com")
19
19
  end
20
20
 
21
+ it "should verify that expected http stub occured" do
22
+ http_request(:get, "http://www.example.com/")
23
+ assert_requested(@stub_http, :times => 1)
24
+ assert_requested(@stub_http)
25
+ end
26
+
27
+ it "should verify that expected https stub occured" do
28
+ http_request(:get, "https://www.example.com/")
29
+ http_request(:get, "https://www.example.com/")
30
+ assert_requested(@stub_https, :times => 2)
31
+ end
32
+
21
33
  it "should verify that expect request didn't occur" do
22
34
  expected_message = "The request GET http://www.example.com/ was expected to execute 1 time but it executed 0 times"
23
35
  expected_message << "\n\nThe following requests were made:\n\nNo requests were made.\n============================================================"
@@ -26,5 +38,13 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
26
38
  end
27
39
  end
28
40
 
41
+ it "should verify that expect stub didn't occur" do
42
+ expected_message = "The request ANY http://www.example.com/ was expected to execute 1 time but it executed 0 times"
43
+ expected_message << "\n\nThe following requests were made:\n\nNo requests were made.\n============================================================"
44
+ assert_fail(expected_message) do
45
+ assert_requested(@stub_http)
46
+ end
47
+ end
48
+
29
49
  end
30
50
 
@@ -10,6 +10,16 @@ module CurbSpecHelper
10
10
  status, response_headers =
11
11
  WebMock::HttpLibAdapters::CurbAdapter.parse_header_string(curl.header_str)
12
12
 
13
+ # Deal with the fact that the HTTP spec allows multi-values headers
14
+ # to either be a single entry with a comma-separated listed of
15
+ # values, or multiple separate entries
16
+ response_headers.keys.each do |k|
17
+ v = response_headers[k]
18
+ if v.is_a?(Array)
19
+ response_headers[k] = v.join(', ')
20
+ end
21
+ end
22
+
13
23
  OpenStruct.new(
14
24
  :body => curl.body_str,
15
25
  :headers => WebMock::Util::Headers.normalize_headers(response_headers),
@@ -23,8 +33,8 @@ module CurbSpecHelper
23
33
  curl.url = uri.omit(:userinfo).to_s
24
34
  curl.username = uri.user
25
35
  curl.password = uri.password
26
- curl.timeout = 10
27
- curl.connect_timeout = 10
36
+ curl.timeout = 30
37
+ curl.connect_timeout = 30
28
38
 
29
39
  if headers = options[:headers]
30
40
  headers.each {|k,v| curl.headers[k] = v }
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  require 'spec_helper'
2
3
  require 'acceptance/webmock_shared'
3
4
  require 'ostruct'
@@ -8,7 +9,7 @@ unless RUBY_PLATFORM =~ /java/
8
9
  describe "EM::HttpRequest" do
9
10
  include EMHttpRequestSpecHelper
10
11
 
11
- include_examples "with WebMock"
12
+ include_context "with WebMock", :no_status_message
12
13
 
13
14
  #functionality only supported for em-http-request 1.x
14
15
  if defined?(EventMachine::HttpConnection)
@@ -62,41 +63,43 @@ unless RUBY_PLATFORM =~ /java/
62
63
  end
63
64
 
64
65
  # not pretty, but it works
65
- describe "with synchrony" do
66
- let(:webmock_em_http) { File.expand_path(File.join(File.dirname(__FILE__), "../lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb")) }
67
-
68
- before(:each) do
69
- # need to reload the webmock em-http adapter after we require synchrony
70
- WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable!
71
- $".delete webmock_em_http
72
- $".delete File.expand_path(File.join(File.dirname(__FILE__), "../lib/webmock/http_lib_adapters/em_http_request_adapter.rb"))
73
- require 'em-synchrony'
74
- require 'em-synchrony/em-http'
75
- require File.expand_path(File.join(File.dirname(__FILE__), "../lib/webmock/http_lib_adapters/em_http_request_adapter.rb"))
76
- end
66
+ if defined?(EventMachine::Synchrony)
67
+ describe "with synchrony" do
68
+ let(:webmock_em_http) { File.expand_path(File.join(File.dirname(__FILE__), "../lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb")) }
69
+
70
+ before(:each) do
71
+ # need to reload the webmock em-http adapter after we require synchrony
72
+ WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable!
73
+ $".delete webmock_em_http
74
+ $".delete File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb"))
75
+ require 'em-synchrony'
76
+ require 'em-synchrony/em-http'
77
+ require File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb"))
78
+ end
77
79
 
78
- it "should work" do
79
- stub_request(:post, /.*.testserver.com*/).to_return(:status => 200, :body => 'ok')
80
- lambda {
81
- EM.run do
82
- fiber = Fiber.new do
83
- http = EM::HttpRequest.new("http://www.testserver.com").post :body => "foo=bar&baz=bang", :timeout => 60
84
- EM.stop
80
+ it "should work" do
81
+ stub_request(:post, /.*.testserver.com*/).to_return(:status => 200, :body => 'ok')
82
+ lambda {
83
+ EM.run do
84
+ fiber = Fiber.new do
85
+ http = EM::HttpRequest.new("http://www.testserver.com").post :body => "foo=bar&baz=bang", :timeout => 60
86
+ EM.stop
87
+ end
88
+ fiber.resume
85
89
  end
86
- fiber.resume
87
- end
88
- }.should_not raise_error
89
- end
90
+ }.should_not raise_error
91
+ end
90
92
 
91
- after(:each) do
92
- EM.send(:remove_const, :Synchrony)
93
- EM.send(:remove_const, :HTTPMethods)
94
- WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable!
95
- $".reject! {|path| path.include? "em-http-request"}
96
- $".delete webmock_em_http
97
- $".delete File.expand_path(File.join(File.dirname(__FILE__), "../lib/webmock/http_lib_adapters/em_http_request_adapter.rb"))
98
- require 'em-http-request'
99
- require File.expand_path(File.join(File.dirname(__FILE__), "../lib/webmock/http_lib_adapters/em_http_request_adapter.rb"))
93
+ after(:each) do
94
+ EM.send(:remove_const, :Synchrony)
95
+ EM.send(:remove_const, :HTTPMethods)
96
+ WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable!
97
+ $".reject! {|path| path.include? "em-http-request"}
98
+ $".delete webmock_em_http
99
+ $".delete File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb"))
100
+ require 'em-http-request'
101
+ require File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb"))
102
+ end
100
103
  end
101
104
  end
102
105
  end
@@ -131,6 +134,12 @@ unless RUBY_PLATFORM =~ /java/
131
134
  http_request(:post, "http://www.example.com", :body => {:a => "1", :b => "2"}).body.should == "ok"
132
135
  end
133
136
 
137
+ it "should work with UTF-8 strings" do
138
+ body = "Привет, Мир!"
139
+ stub_request(:post, "www.example.com").to_return(:body => body)
140
+ http_request(:post, "http://www.example.com").body.bytesize.should == body.bytesize
141
+ end
142
+
134
143
  describe "mocking EM::HttpClient API" do
135
144
  before do
136
145
  stub_request(:get, "www.example.com/")
@@ -10,16 +10,18 @@ module EMHttpRequestSpecHelper
10
10
  head = options[:headers] || {}
11
11
  response = nil
12
12
  error = nil
13
+ error_set = false
13
14
  uri = Addressable::URI.heuristic_parse(uri)
14
15
  EventMachine.run {
15
16
  request = EventMachine::HttpRequest.new("#{uri.normalize.to_s}")
16
17
  http = request.send(method, {
17
- :timeout => 10,
18
+ :timeout => 30,
18
19
  :body => options[:body],
19
20
  :query => options[:query],
20
21
  :head => head.merge('authorization' => [uri.user, uri.password])
21
22
  }, &block)
22
23
  http.errback {
24
+ error_set = true
23
25
  error = if http.respond_to?(:errors)
24
26
  http.errors
25
27
  else
@@ -38,7 +40,7 @@ module EMHttpRequestSpecHelper
38
40
  }
39
41
  @http = http
40
42
  }
41
- raise error if error
43
+ raise error.to_s if error_set
42
44
  response
43
45
  end
44
46
 
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'acceptance/webmock_shared'
3
+ require 'acceptance/excon/excon_spec_helper'
4
+
5
+ describe "Excon" do
6
+ include ExconSpecHelper
7
+ include_context "with WebMock", :no_status_message, :no_url_auth
8
+
9
+ it 'should allow Excon requests to use query hash paramters' do
10
+ stub_request(:get, "http://example.com/resource/?a=1&b=2").to_return(:body => "abc")
11
+ Excon.get('http://example.com', :path => "resource/", :query => {:a => 1, :b => 2}).body.should == "abc"
12
+ end
13
+
14
+ end
15
+
@@ -0,0 +1,37 @@
1
+ require 'ostruct'
2
+
3
+ module ExconSpecHelper
4
+
5
+ def http_request(method, uri, options = {}, &block)
6
+ uri = Addressable::URI.heuristic_parse(uri)
7
+ uri = uri.omit(:userinfo).to_s.gsub(' ', '+')
8
+
9
+ options = options.merge(:method => method) # Dup and merge
10
+ response = Excon.new(uri).request(options, &block)
11
+
12
+ headers = WebMock::Util::Headers.normalize_headers(response.headers)
13
+ headers = headers.inject({}) do |res, (name, value)|
14
+ res[name] = value.is_a?(Array) ? value.flatten.join(', ') : value
15
+ res
16
+ end
17
+
18
+ OpenStruct.new \
19
+ :body => response.body,
20
+ :headers => headers,
21
+ :status => response.status.to_s,
22
+ :message => ""
23
+ end
24
+
25
+ def client_timeout_exception_class
26
+ Excon::Errors::Timeout
27
+ end
28
+
29
+ def connection_refused_exception_class
30
+ Excon::Errors::SocketError
31
+ end
32
+
33
+ def http_library
34
+ :excon
35
+ end
36
+
37
+ end
@@ -184,7 +184,14 @@ describe "Net:HTTP" do
184
184
  cert.should be_a(OpenSSL::X509::Certificate)
185
185
  }
186
186
  end
187
+
188
+ it "should connect to the server if the URI matches an regex", :net_connect => true do
189
+ WebMock.disable_net_connect!(:allow => /google.com/)
190
+ response = Net::HTTP.get('www.google.com','/')
191
+ end
192
+
187
193
  end
194
+
188
195
  end
189
196
 
190
197
  describe "when net_http_connect_on_start is true" do
@@ -23,7 +23,9 @@ module NetHTTPSpecHelper
23
23
  if uri.scheme == "https"
24
24
  http.use_ssl = true
25
25
  #1.9.1 has a bug with ssl_timeout
26
- http.ssl_timeout = 10 unless RUBY_VERSION == "1.9.1"
26
+ http.ssl_timeout = 20 unless RUBY_VERSION == "1.9.1"
27
+ http.open_timeout = 60
28
+ http.read_timeout = 60
27
29
  end
28
30
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
29
31
  response = http.start {|http|
@@ -96,7 +96,14 @@ unless RUBY_PLATFORM =~ /java/
96
96
  Encoding.default_internal = @encoding
97
97
  end
98
98
 
99
- it "should encode body based on charset in headers" do
99
+ it "should encode body with default encoding" do
100
+ stub_request(:get, "www.example.com").
101
+ to_return(:body => "Øl")
102
+
103
+ @sess.get("").body.encoding.should == Encoding::UTF_8
104
+ end
105
+
106
+ it "should encode body to default internal" do
100
107
  stub_request(:get, "www.example.com").
101
108
  to_return(:headers => {'Content-Type' => 'text/html; charset=iso-8859-1'},
102
109
  :body => "Øl".encode("iso-8859-1"))
@@ -109,17 +116,19 @@ unless RUBY_PLATFORM =~ /java/
109
116
  to_return(:body => "<?xml encoding=\"iso-8859-1\">Øl</xml>".encode("iso-8859-1"))
110
117
 
111
118
 
112
- @sess.get("").body.encoding.should == Encoding.default_internal
119
+ @sess.get("").body.encoding.should == Encoding::ISO_8859_1
113
120
  end
114
121
 
122
+
115
123
  it "should encode body based on Session#default_response_charset" do
116
124
  stub_request(:get, "www.example.com").
117
125
  to_return(:body => "Øl".encode("iso-8859-1"))
118
126
 
119
127
  @sess.default_response_charset = "iso-8859-1"
120
128
 
121
- @sess.get("").body.encoding.should == Encoding.default_internal
129
+ @sess.get("").body.encoding.should == Encoding::ISO_8859_1
122
130
  end
131
+
123
132
  end
124
133
  end
125
134
  end
@@ -8,8 +8,8 @@ module PatronSpecHelper
8
8
  sess.username = uri.user
9
9
  sess.password = uri.password
10
10
 
11
- sess.connect_timeout = 10
12
- sess.timeout = 10
11
+ sess.connect_timeout = 30
12
+ sess.timeout = 30
13
13
  sess.max_redirects = 0
14
14
  uri = "#{uri.path}#{uri.query ? '?' : ''}#{uri.query}"
15
15
  uri.gsub!(' ','+')
@@ -1,4 +1,4 @@
1
- shared_context "allowing and disabling net connect" do
1
+ shared_context "allowing and disabling net connect" do |*adapter_info|
2
2
  describe "when net connect" do
3
3
  describe "is allowed", :net_connect => true do
4
4
  before(:each) do
@@ -11,8 +11,8 @@ shared_context "allowing and disabling net connect" do
11
11
 
12
12
  it "should make a real https request if request is not stubbed" do
13
13
  unless http_library == :httpclient
14
- http_request(:get, "https://www.paypal.com/uk/cgi-bin/webscr").
15
- body.should =~ /.*paypal.*/
14
+ http_request(:get, "https://www.google.com/").
15
+ body.should =~ /.*google.*/
16
16
  end
17
17
  end
18
18
 
@@ -1,4 +1,4 @@
1
- shared_context "callbacks" do
1
+ shared_context "callbacks" do |*adapter_info|
2
2
  describe "when after_request callback is declared" do
3
3
  before(:each) do
4
4
  WebMock.reset_callbacks
@@ -44,6 +44,25 @@ shared_context "callbacks" do
44
44
  @request_signature.uri.to_s.should == "http://www.example.com:80/"
45
45
  end
46
46
 
47
+ after(:each) do
48
+ WebMock::StubRegistry.instance.global_stubs.clear
49
+ end
50
+
51
+ it 'passes the same request signature instance to the callback that was passed to the global stub callback' do
52
+ global_stub_request_sig = after_request_request_sig = nil
53
+ WebMock.globally_stub_request do |request_sig|
54
+ global_stub_request_sig = request_sig
55
+ nil
56
+ end
57
+
58
+ WebMock.after_request do |request_sig, _|
59
+ after_request_request_sig = request_sig
60
+ end
61
+
62
+ http_request(:get, "http://www.example.com/")
63
+ global_stub_request_sig.should be(after_request_request_sig)
64
+ end
65
+
47
66
  context "passing response to callback" do
48
67
  context "when request is stubbed" do
49
68
  before(:each) do
@@ -86,11 +105,8 @@ shared_context "callbacks" do
86
105
  end
87
106
 
88
107
  it "should pass real response to callback with status and message" do
89
- # not supported by em-http-request, it always returns "unknown" for http_reason
90
- unless http_library == :em_http_request
91
- @response.status[0].should == 302
92
- @response.status[1].should == "Found"
93
- end
108
+ @response.status[0].should == 302
109
+ @response.status[1].should == "Found" unless adapter_info.include?(:no_status_message)
94
110
  end
95
111
 
96
112
  it "should pass real response to callback with headers" do