webmock 1.7.10 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
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