webmock 1.6.4 → 1.7.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 (95) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +3 -1
  3. data/.travis.yml +6 -0
  4. data/CHANGELOG.md +211 -118
  5. data/Gemfile +16 -1
  6. data/Guardfile +24 -0
  7. data/LICENSE +1 -1
  8. data/README.md +64 -15
  9. data/Rakefile +19 -6
  10. data/lib/webmock.rb +9 -4
  11. data/lib/webmock/api.rb +5 -4
  12. data/lib/webmock/assertion_failure.rb +1 -1
  13. data/lib/webmock/callback_registry.rb +1 -1
  14. data/lib/webmock/config.rb +2 -2
  15. data/lib/webmock/cucumber.rb +1 -1
  16. data/lib/webmock/errors.rb +17 -5
  17. data/lib/webmock/http_lib_adapters/{curb.rb → curb_adapter.rb} +79 -49
  18. data/lib/webmock/http_lib_adapters/{em_http_request.rb → em_http_request/em_http_request_0_x.rb} +20 -15
  19. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +201 -0
  20. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +11 -0
  21. data/lib/webmock/http_lib_adapters/http_lib_adapter.rb +7 -0
  22. data/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb +19 -0
  23. data/lib/webmock/http_lib_adapters/{httpclient.rb → httpclient_adapter.rb} +35 -8
  24. data/lib/webmock/http_lib_adapters/net_http.rb +84 -25
  25. data/lib/webmock/http_lib_adapters/net_http_response.rb +17 -17
  26. data/lib/webmock/http_lib_adapters/patron_adapter.rb +124 -0
  27. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +166 -0
  28. data/lib/webmock/minitest.rb +15 -0
  29. data/lib/webmock/rack_response.rb +52 -0
  30. data/lib/webmock/request_pattern.rb +4 -2
  31. data/lib/webmock/request_signature.rb +4 -0
  32. data/lib/webmock/request_stub.rb +35 -2
  33. data/lib/webmock/responses_sequence.rb +2 -2
  34. data/lib/webmock/rspec.rb +2 -2
  35. data/lib/webmock/rspec/matchers.rb +9 -4
  36. data/lib/webmock/rspec/matchers/webmock_matcher.rb +1 -1
  37. data/lib/webmock/stub_registry.rb +1 -1
  38. data/lib/webmock/stub_request_snippet.rb +14 -11
  39. data/lib/webmock/util/hash_keys_stringifier.rb +4 -4
  40. data/lib/webmock/util/headers.rb +3 -3
  41. data/lib/webmock/util/json.rb +54 -0
  42. data/lib/webmock/util/uri.rb +1 -1
  43. data/lib/webmock/version.rb +1 -1
  44. data/lib/webmock/webmock.rb +20 -3
  45. data/minitest/test_helper.rb +29 -0
  46. data/minitest/test_webmock.rb +6 -0
  47. data/minitest/webmock_spec.rb +30 -0
  48. data/spec/curb_spec.rb +26 -8
  49. data/spec/curb_spec_helper.rb +6 -6
  50. data/spec/em_http_request_spec.rb +95 -1
  51. data/spec/em_http_request_spec_helper.rb +16 -16
  52. data/spec/errors_spec.rb +19 -4
  53. data/spec/example_curl_output.txt +22 -22
  54. data/spec/http_lib_adapters/http_lib_adapter_registry_spec.rb +17 -0
  55. data/spec/http_lib_adapters/http_lib_adapter_spec.rb +12 -0
  56. data/spec/httpclient_spec.rb +1 -1
  57. data/spec/httpclient_spec_helper.rb +3 -38
  58. data/spec/my_rack_app.rb +18 -0
  59. data/spec/net_http_shared.rb +125 -0
  60. data/spec/net_http_spec.rb +27 -31
  61. data/spec/net_http_spec_helper.rb +4 -34
  62. data/spec/network_connection.rb +1 -1
  63. data/spec/patron_spec_helper.rb +4 -7
  64. data/spec/quality_spec.rb +60 -0
  65. data/spec/rack_response_spec.rb +33 -0
  66. data/spec/real_net_http_spec.rb +20 -0
  67. data/spec/request_execution_verifier_spec.rb +8 -8
  68. data/spec/request_pattern_spec.rb +3 -3
  69. data/spec/request_stub_spec.rb +19 -19
  70. data/spec/response_spec.rb +8 -8
  71. data/spec/spec_helper.rb +14 -11
  72. data/spec/stub_request_snippet_spec.rb +85 -37
  73. data/spec/support/webmock_server.rb +62 -0
  74. data/spec/typhoeus_hydra_spec.rb +53 -0
  75. data/spec/typhoeus_hydra_spec_helper.rb +50 -0
  76. data/spec/util/headers_spec.rb +5 -5
  77. data/spec/util/json_spec.rb +7 -0
  78. data/spec/util/uri_spec.rb +1 -1
  79. data/spec/vendor/addressable/lib/uri.rb +1 -0
  80. data/spec/vendor/crack/lib/crack.rb +1 -0
  81. data/spec/vendor/right_http_connection-1.2.4/History.txt +4 -4
  82. data/spec/vendor/right_http_connection-1.2.4/README.txt +4 -4
  83. data/spec/vendor/right_http_connection-1.2.4/Rakefile +3 -3
  84. data/spec/vendor/right_http_connection-1.2.4/lib/net_fix.rb +4 -4
  85. data/spec/vendor/right_http_connection-1.2.4/setup.rb +4 -4
  86. data/spec/webmock_shared.rb +375 -143
  87. data/spec/webmock_spec.rb +7 -0
  88. data/test/http_request.rb +24 -0
  89. data/test/shared_test.rb +47 -0
  90. data/test/test_helper.rb +6 -3
  91. data/test/test_webmock.rb +2 -67
  92. data/webmock.gemspec +8 -7
  93. metadata +153 -88
  94. data/lib/webmock/http_lib_adapters/patron.rb +0 -100
  95. data/spec/other_net_http_libs_spec.rb +0 -30
@@ -1,4 +1,4 @@
1
- # This code is entierly copied from VCR (http://github.com/myronmarston/vcr) by courtesy of Myron Marston
1
+ # This code is entierly copied from VCR (http://github.com/myronmarston/vcr) by courtesy of Myron Marston
2
2
 
3
3
  # A Net::HTTP response that has already been read raises an IOError when #read_body
4
4
  # is called with a destination string or block.
@@ -11,22 +11,22 @@
11
11
  # after it has aleady been read. This attemps to preserve the behavior of
12
12
  # #read_body, acting just as if it had never been read.
13
13
 
14
- module WebMock
15
- module Net
16
- module HTTPResponse
17
- def read_body(dest = nil, &block)
18
- return super if @__read_body_previously_called
19
- return @body if dest.nil? && block.nil?
20
- raise ArgumentError.new("both arg and block given for HTTP method") if dest && block
21
- return nil if @body.nil?
22
14
 
23
- dest ||= ::Net::ReadAdapter.new(block)
24
- dest << @body
25
- @body = dest
26
- ensure
27
- # allow subsequent calls to #read_body to proceed as normal, without our hack...
28
- @__read_body_previously_called = true
29
- end
15
+ module Net
16
+ module WebMockHTTPResponse
17
+ def read_body(dest = nil, &block)
18
+ return super if @__read_body_previously_called
19
+ return @body if dest.nil? && block.nil?
20
+ raise ArgumentError.new("both arg and block given for HTTP method") if dest && block
21
+ return nil if @body.nil?
22
+
23
+ dest ||= ::Net::ReadAdapter.new(block)
24
+ dest << @body
25
+ @body = dest
26
+ ensure
27
+ # allow subsequent calls to #read_body to proceed as normal, without our hack...
28
+ @__read_body_previously_called = true
30
29
  end
31
30
  end
32
- end
31
+ end
32
+
@@ -0,0 +1,124 @@
1
+ begin
2
+ require 'patron'
3
+ rescue LoadError
4
+ # patron not found
5
+ end
6
+
7
+ if defined?(::Patron)
8
+ module WebMock
9
+ module HttpLibAdapters
10
+ class PatronAdapter < ::WebMock::HttpLibAdapter
11
+ adapter_for :patron
12
+
13
+ OriginalPatronSession = ::Patron::Session unless const_defined?(:OriginalPatronSession)
14
+
15
+ class WebMockPatronSession < ::Patron::Session
16
+ def handle_request_with_webmock(req)
17
+ request_signature =
18
+ WebMock::HttpLibAdapters::PatronAdapter.build_request_signature(req)
19
+
20
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
21
+
22
+ if WebMock::StubRegistry.instance.registered_request?(request_signature)
23
+ webmock_response =
24
+ WebMock::StubRegistry.instance.response_for_request(request_signature)
25
+ WebMock::HttpLibAdapters::PatronAdapter.
26
+ handle_file_name(req, webmock_response)
27
+ res = WebMock::HttpLibAdapters::PatronAdapter.
28
+ build_patron_response(webmock_response)
29
+ WebMock::CallbackRegistry.invoke_callbacks(
30
+ {:lib => :patron}, request_signature, webmock_response)
31
+ res
32
+ elsif WebMock.net_connect_allowed?(request_signature.uri)
33
+ res = handle_request_without_webmock(req)
34
+ if WebMock::CallbackRegistry.any_callbacks?
35
+ webmock_response = WebMock::HttpLibAdapters::PatronAdapter.
36
+ build_webmock_response(res)
37
+ WebMock::CallbackRegistry.invoke_callbacks(
38
+ {:lib => :patron, :real_request => true}, request_signature,
39
+ webmock_response)
40
+ end
41
+ res
42
+ else
43
+ raise WebMock::NetConnectNotAllowedError.new(request_signature)
44
+ end
45
+ end
46
+
47
+ alias_method :handle_request_without_webmock, :handle_request
48
+ alias_method :handle_request, :handle_request_with_webmock
49
+ end
50
+
51
+ def self.enable!
52
+ Patron.send(:remove_const, :Session)
53
+ Patron.send(:const_set, :Session, WebMockPatronSession)
54
+ end
55
+
56
+ def self.disable!
57
+ Patron.send(:remove_const, :Session)
58
+ Patron.send(:const_set, :Session, OriginalPatronSession)
59
+ end
60
+
61
+ def self.handle_file_name(req, webmock_response)
62
+ if req.action == :get && req.file_name
63
+ begin
64
+ File.open(req.file_name, "w") do |f|
65
+ f.write webmock_response.body
66
+ end
67
+ rescue Errno::EACCES
68
+ raise ArgumentError.new("Unable to open specified file.")
69
+ end
70
+ end
71
+ end
72
+
73
+ def self.build_request_signature(req)
74
+ uri = WebMock::Util::URI.heuristic_parse(req.url)
75
+ uri.path = uri.normalized_path.gsub("[^:]//","/")
76
+ uri.user = req.username
77
+ uri.password = req.password
78
+
79
+ if [:put, :post].include?(req.action)
80
+ if req.file_name
81
+ if !File.exist?(req.file_name) || !File.readable?(req.file_name)
82
+ raise ArgumentError.new("Unable to open specified file.")
83
+ end
84
+ request_body = File.read(req.file_name)
85
+ elsif req.upload_data
86
+ request_body = req.upload_data
87
+ else
88
+ raise ArgumentError.new("Must provide either data or a filename when doing a PUT or POST")
89
+ end
90
+ end
91
+
92
+ request_signature = WebMock::RequestSignature.new(
93
+ req.action,
94
+ uri.to_s,
95
+ :body => request_body,
96
+ :headers => req.headers
97
+ )
98
+ request_signature
99
+ end
100
+
101
+ def self.build_patron_response(webmock_response)
102
+ raise ::Patron::TimeoutError if webmock_response.should_timeout
103
+ webmock_response.raise_error_if_any
104
+ res = ::Patron::Response.new
105
+ res.instance_variable_set(:@body, webmock_response.body)
106
+ res.instance_variable_set(:@status, webmock_response.status[0])
107
+ res.instance_variable_set(:@status_line, webmock_response.status[1])
108
+ res.instance_variable_set(:@headers, webmock_response.headers)
109
+ res
110
+ end
111
+
112
+ def self.build_webmock_response(patron_response)
113
+ webmock_response = WebMock::Response.new
114
+ reason = patron_response.status_line.
115
+ scan(%r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?))[0][2]
116
+ webmock_response.status = [patron_response.status, reason]
117
+ webmock_response.body = patron_response.body
118
+ webmock_response.headers = patron_response.headers
119
+ webmock_response
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,166 @@
1
+ begin
2
+ require 'typhoeus'
3
+ rescue LoadError
4
+ # typhoeus not found
5
+ end
6
+
7
+ if defined?(Typhoeus)
8
+
9
+ module WebMock
10
+ module HttpLibAdapters
11
+ class TyphoeusAdapter < HttpLibAdapter
12
+ adapter_for :typhoeus
13
+
14
+ def self.enable!
15
+ @disabled = false
16
+ add_after_request_callback
17
+ ::Typhoeus::Hydra.allow_net_connect = true
18
+ end
19
+
20
+ def self.disable!
21
+ @disabled = true
22
+ remove_after_request_callback
23
+ ::Typhoeus::Hydra.allow_net_connect = true
24
+ end
25
+
26
+ def self.disabled?
27
+ !!@disabled
28
+ end
29
+
30
+ def self.add_after_request_callback
31
+ unless Typhoeus::Hydra.
32
+ global_hooks[:after_request_before_on_complete].
33
+ include?(AFTER_REQUEST_CALLBACK)
34
+ Typhoeus::Hydra.
35
+ global_hooks[:after_request_before_on_complete] << AFTER_REQUEST_CALLBACK
36
+ end
37
+ end
38
+
39
+ def self.remove_after_request_callback
40
+ Typhoeus::Hydra.global_hooks[:after_request_before_on_complete].
41
+ delete_if {|v| v == AFTER_REQUEST_CALLBACK }
42
+ end
43
+
44
+ def self.build_request_signature(req)
45
+ uri = WebMock::Util::URI.heuristic_parse(req.url)
46
+ uri.path = uri.normalized_path.gsub("[^:]//","/")
47
+ if req.username
48
+ uri.user = req.username
49
+ uri.password = req.password
50
+ end
51
+
52
+ request_signature = WebMock::RequestSignature.new(
53
+ req.method,
54
+ uri.to_s,
55
+ :body => req.body,
56
+ :headers => req.headers
57
+ )
58
+ request_signature
59
+ end
60
+
61
+ def self.build_webmock_response(typhoeus_response)
62
+ webmock_response = WebMock::Response.new
63
+ webmock_response.status = [typhoeus_response.code, typhoeus_response.status_message]
64
+ webmock_response.body = typhoeus_response.body
65
+ webmock_response.headers = typhoeus_response.headers_hash
66
+ webmock_response
67
+ end
68
+
69
+ def self.stub_typhoeus(request_signature, webmock_response, typhoeus)
70
+ response = if webmock_response.should_timeout
71
+ ::Typhoeus::Response.new(
72
+ :code => 0,
73
+ :status_message => "",
74
+ :body => "",
75
+ :headers_hash => {}
76
+ )
77
+ else
78
+ ::Typhoeus::Response.new(
79
+ :code => webmock_response.status[0],
80
+ :status_message => webmock_response.status[1],
81
+ :body => webmock_response.body,
82
+ :headers_hash => webmock_response.headers
83
+ )
84
+ end
85
+
86
+
87
+ typhoeus.stub(
88
+ request_signature.method || :any,
89
+ /.*/,
90
+ :webmock_stub => true
91
+ ).and_return(response)
92
+ end
93
+
94
+ def self.request_hash(request_signature)
95
+ hash = {}
96
+
97
+ hash[:body] = request_signature.body
98
+ hash[:headers] = request_signature.headers
99
+
100
+ hash
101
+ end
102
+
103
+ AFTER_REQUEST_CALLBACK = Proc.new do |request|
104
+ request_signature =
105
+ ::WebMock::HttpLibAdapters::TyphoeusAdapter.
106
+ build_request_signature(request)
107
+ webmock_response =
108
+ ::WebMock::HttpLibAdapters::TyphoeusAdapter.
109
+ build_webmock_response(request.response)
110
+ if request.response.mock?
111
+ WebMock::CallbackRegistry.invoke_callbacks(
112
+ {:lib => :typhoeus},
113
+ request_signature,
114
+ webmock_response
115
+ )
116
+ else
117
+ WebMock::CallbackRegistry.invoke_callbacks(
118
+ {:lib => :typhoeus, :real_request => true},
119
+ request_signature,
120
+ webmock_response
121
+ )
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+
129
+ module Typhoeus
130
+ class Hydra
131
+ def queue_with_webmock(request)
132
+ self.clear_webmock_stubs
133
+
134
+ if WebMock::HttpLibAdapters::TyphoeusAdapter.disabled?
135
+ return queue_without_webmock(request)
136
+ end
137
+
138
+ request_signature =
139
+ ::WebMock::HttpLibAdapters::TyphoeusAdapter.build_request_signature(request)
140
+
141
+ ::WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
142
+
143
+ if ::WebMock::StubRegistry.instance.registered_request?(request_signature)
144
+ webmock_response =
145
+ ::WebMock::StubRegistry.instance.response_for_request(request_signature)
146
+ ::WebMock::HttpLibAdapters::TyphoeusAdapter.
147
+ stub_typhoeus(request_signature, webmock_response, self)
148
+ webmock_response.raise_error_if_any
149
+ elsif !WebMock.net_connect_allowed?(request_signature.uri)
150
+ raise WebMock::NetConnectNotAllowedError.new(request_signature)
151
+ end
152
+ queue_without_webmock(request)
153
+ end
154
+
155
+ alias_method :queue_without_webmock, :queue
156
+ alias_method :queue, :queue_with_webmock
157
+
158
+ def clear_webmock_stubs
159
+ self.stubs = [] unless self.stubs
160
+ self.stubs.delete_if {|s|
161
+ s.instance_variable_get(:@options)[:webmock_stub]
162
+ }
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,15 @@
1
+ require 'minitest/unit'
2
+ require 'webmock'
3
+
4
+ MiniTest::Unit::TestCase.class_eval do
5
+ include WebMock::API
6
+
7
+ alias_method :teardown_without_webmock, :teardown
8
+ def teardown_with_webmock
9
+ teardown_without_webmock
10
+ WebMock.reset!
11
+ end
12
+ alias_method :teardown, :teardown_with_webmock
13
+ end
14
+
15
+ WebMock::AssertionFailure.error_class = MiniTest::Assertion
@@ -0,0 +1,52 @@
1
+ module WebMock
2
+ class RackResponse < Response
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def evaluate(request)
8
+ env = build_rack_env(request)
9
+
10
+ status, headers, response = @app.call(env)
11
+
12
+ Response.new(
13
+ :body => response.join,
14
+ :headers => headers,
15
+ :status => status
16
+ )
17
+ end
18
+
19
+ def build_rack_env(request)
20
+ uri = request.uri
21
+ headers = request.headers || {}
22
+ body = request.body || ''
23
+
24
+ env = {
25
+ # CGI variables specified by Rack
26
+ 'REQUEST_METHOD' => request.method.to_s.upcase,
27
+ 'CONTENT_TYPE' => headers.delete('Content-Type'),
28
+ 'CONTENT_LENGTH' => body.size,
29
+ 'PATH_INFO' => uri.path,
30
+ 'QUERY_STRING' => uri.query || '',
31
+ 'SERVER_NAME' => uri.host
32
+ }
33
+
34
+ # Rack-specific variables
35
+ env['rack.input'] = StringIO.new(body)
36
+ env['rack.version'] = Rack::VERSION
37
+ env['rack.url_scheme'] = uri.scheme
38
+ env['rack.run_once'] = true
39
+ env['rack.session'] = session
40
+
41
+ headers.each do |k, v|
42
+ env["HTTP_#{k.tr('-','_').upcase}"] = v
43
+ end
44
+
45
+ env
46
+ end
47
+
48
+ def session
49
+ @session ||= {}
50
+ end
51
+ end
52
+ end
@@ -2,6 +2,8 @@ module WebMock
2
2
 
3
3
  class RequestPattern
4
4
 
5
+ attr_reader :method_pattern, :uri_pattern, :body_pattern, :headers_pattern
6
+
5
7
  def initialize(method, uri, options = {})
6
8
  @method_pattern = MethodPattern.new(method)
7
9
  @uri_pattern = create_uri_pattern(uri)
@@ -40,7 +42,7 @@ module WebMock
40
42
  @headers_pattern = HeadersPattern.new(options[:headers]) if options.has_key?(:headers)
41
43
  @uri_pattern.add_query_params(options[:query]) if options.has_key?(:query)
42
44
  end
43
-
45
+
44
46
  def create_uri_pattern(uri)
45
47
  if uri.is_a?(Regexp)
46
48
  URIRegexpPattern.new(uri)
@@ -143,7 +145,7 @@ module WebMock
143
145
 
144
146
  case BODY_FORMATS[content_type]
145
147
  when :json then
146
- matching_hashes?(Crack::JSON.parse(body), @pattern)
148
+ matching_hashes?(WebMock::Util::JSON.parse(body), @pattern)
147
149
  when :xml then
148
150
  matching_hashes?(Crack::XML.parse(body), @pattern)
149
151
  else
@@ -33,6 +33,10 @@ module WebMock
33
33
  self.to_s == other.to_s
34
34
  end
35
35
 
36
+ def url_encoded?
37
+ headers && headers['Content-Type'] == 'application/x-www-form-urlencoded'
38
+ end
39
+
36
40
  private
37
41
 
38
42
  def assign_options(options)
@@ -23,13 +23,17 @@ module WebMock
23
23
  self
24
24
  end
25
25
 
26
+ def to_rack(app, options={})
27
+ @responses_sequences << ResponsesSequence.new([RackResponse.new(app)])
28
+ end
29
+
26
30
  def to_raise(*exceptions)
27
- @responses_sequences << ResponsesSequence.new([*exceptions].flatten.map {|e|
31
+ @responses_sequences << ResponsesSequence.new([*exceptions].flatten.map {|e|
28
32
  ResponseFactory.response_for(:exception => e)
29
33
  })
30
34
  self
31
35
  end
32
-
36
+
33
37
  def to_timeout
34
38
  @responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(:should_timeout => true)])
35
39
  self
@@ -46,6 +50,10 @@ module WebMock
46
50
  end
47
51
  end
48
52
 
53
+ def has_responses?
54
+ !@responses_sequences.empty?
55
+ end
56
+
49
57
  def then
50
58
  self
51
59
  end
@@ -60,5 +68,30 @@ module WebMock
60
68
  self
61
69
  end
62
70
 
71
+ def matches?(request_signature)
72
+ self.request_pattern.matches?(request_signature)
73
+ end
74
+
75
+ def to_s
76
+ self.request_pattern.to_s
77
+ end
78
+
79
+ def self.from_request_signature(signature)
80
+ stub = self.new(signature.method.to_sym, signature.uri.to_s)
81
+
82
+ if signature.body.to_s != ''
83
+ body = if signature.url_encoded?
84
+ Addressable::URI.parse('?' + signature.body).query_values
85
+ else
86
+ signature.body
87
+ end
88
+ stub.with(:body => body)
89
+ end
90
+
91
+ if (signature.headers && !signature.headers.empty?)
92
+ stub.with(:headers => signature.headers)
93
+ end
94
+ stub
95
+ end
63
96
  end
64
97
  end