webmock 0.9.1 → 1.0.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 (40) hide show
  1. data/CHANGELOG.md +47 -2
  2. data/README.md +68 -6
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/lib/webmock.rb +2 -2
  6. data/lib/webmock/adapters/rspec.rb +1 -1
  7. data/lib/webmock/adapters/rspec/matchers.rb +2 -2
  8. data/lib/webmock/adapters/rspec/{request_profile_matcher.rb → request_pattern_matcher.rb} +5 -5
  9. data/lib/webmock/adapters/rspec/webmock_matcher.rb +2 -2
  10. data/lib/webmock/config.rb +2 -1
  11. data/lib/webmock/http_lib_adapters/httpclient.rb +5 -4
  12. data/lib/webmock/http_lib_adapters/net_http.rb +5 -3
  13. data/lib/webmock/http_lib_adapters/patron.rb +82 -0
  14. data/lib/webmock/request_execution_verifier.rb +8 -8
  15. data/lib/webmock/request_pattern.rb +130 -0
  16. data/lib/webmock/request_registry.rb +4 -9
  17. data/lib/webmock/request_signature.rb +18 -37
  18. data/lib/webmock/request_stub.rb +17 -6
  19. data/lib/webmock/response.rb +87 -31
  20. data/lib/webmock/util/headers.rb +5 -0
  21. data/lib/webmock/webmock.rb +10 -6
  22. data/spec/httpclient_spec.rb +0 -1
  23. data/spec/httpclient_spec_helper.rb +11 -1
  24. data/spec/net_http_spec.rb +8 -1
  25. data/spec/net_http_spec_helper.rb +11 -1
  26. data/spec/patron_spec.rb +83 -0
  27. data/spec/patron_spec_helper.rb +44 -0
  28. data/spec/request_execution_verifier_spec.rb +8 -8
  29. data/spec/request_pattern_spec.rb +243 -0
  30. data/spec/request_registry_spec.rb +34 -19
  31. data/spec/request_signature_spec.rb +23 -191
  32. data/spec/request_stub_spec.rb +32 -11
  33. data/spec/response_spec.rb +98 -5
  34. data/spec/spec_helper.rb +8 -16
  35. data/spec/webmock_spec.rb +154 -49
  36. data/webmock.gemspec +14 -7
  37. metadata +21 -7
  38. data/lib/webmock/request.rb +0 -29
  39. data/lib/webmock/request_profile.rb +0 -50
  40. data/spec/request_profile_spec.rb +0 -68
data/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  #Changelog
2
2
 
3
+ ## 1.0.0
4
+
5
+ * Added support for [Patron](http://toland.github.com/patron/)
6
+
7
+ * Responses dynamically evaluated from block (idea and implementation by Tom Ward)
8
+
9
+ stub_request(:any, 'www.example.net').
10
+ to_return { |request| {:body => request.body} }
11
+
12
+ RestClient.post('www.example.net', 'abc') # ===> "abc\n"
13
+
14
+ * Responses dynamically evaluated from lambda (idea and implementation by Tom Ward)
15
+
16
+ stub_request(:any, 'www.example.net').
17
+ to_return(lambda { |request| {:body => request.body} })
18
+
19
+ RestClient.post('www.example.net', 'abc') # ===> "abc\n"
20
+
21
+ * Response with custom status message
22
+
23
+ stub_request(:any, "www.example.com").to_return(:status => [500, "Internal Server Error"])
24
+
25
+ req = Net::HTTP::Get.new("/")
26
+ Net::HTTP.start("www.example.com") { |http| http.request(req) }.message # ===> "Internal Server Error"
27
+
28
+ * Raising timeout errors (suggested by Jeffrey Jones) (compatibility with Ruby 1.8.6 by Mack Earnhardt)
29
+
30
+ stub_request(:any, 'www.example.net').to_timeout
31
+
32
+ RestClient.post('www.example.net', 'abc') # ===> RestClient::RequestTimeout
33
+
34
+ * External requests can be disabled while allowing localhost (idea and implementation by Mack Earnhardt)
35
+
36
+ WebMock.disable_net_connect!(:allow_localhost => true)
37
+
38
+ Net::HTTP.get('www.something.com', '/') # ===> Failure
39
+
40
+ Net::HTTP.get('localhost:9887', '/') # ===> Allowed. Perhaps to Selenium?
41
+
42
+
43
+ ### Bug fixes
44
+
45
+ * Fixed issue where Net::HTTP adapter didn't work for requests with body responding to read (reported by Tekin Suleyman)
46
+ * Fixed issue where request stub with headers declared as nil was matching requests with non empty headers
47
+
3
48
  ## 0.9.1
4
49
 
5
50
  * Fixed issue where response status code was not read from raw (curl -is) responses
@@ -14,12 +59,12 @@
14
59
  #or
15
60
  assert_requested(:post, "www.example.com") { |req| req.body == "abc" }
16
61
 
17
- * Matching request body against regular expressions
62
+ * Matching request body against regular expressions (suggested by Ben Pickles)
18
63
 
19
64
  stub_request(:post, "www.example.com").with(:body => /^.*world$/).to_return(:body => "abc")
20
65
  RestClient.post('www.example.com', 'hello world') # ===> "abc\n"
21
66
 
22
- * Matching request headers against regular expressions
67
+ * Matching request headers against regular expressions (suggested by Ben Pickles)
23
68
 
24
69
  stub_request(:post, "www.example.com").with(:headers => {"Content-Type" => /image\/.+/}).to_return(:body => "abc")
25
70
  RestClient.post('www.example.com', '', {'Content-Type' => 'image/png'}) # ===> "abc\n"
data/README.md CHANGED
@@ -14,6 +14,7 @@ Features
14
14
  * Support for Test::Unit and RSpec (and can be easily extended to other frameworks)
15
15
  * Support for Net::HTTP and other http libraries based on Net::HTTP (i.e RightHttpConnection, rest-client, HTTParty)
16
16
  * Support for HTTPClient library (both sync and async requests)
17
+ * Support for Patron library
17
18
  * Easy to extend to other HTTP libraries
18
19
 
19
20
  Installation
@@ -113,7 +114,7 @@ You can also use WebMock without RSpec or Test::Unit support:
113
114
 
114
115
  Net::HTTP.get("www.example.com", '/') # ===> "abc"
115
116
 
116
- ### Custom response with body specified as IO object
117
+ ### Response with body specified as IO object
117
118
 
118
119
  File.open('/tmp/response_body.txt', 'w') { |f| f.puts 'abc' }
119
120
 
@@ -121,6 +122,13 @@ You can also use WebMock without RSpec or Test::Unit support:
121
122
 
122
123
  Net::HTTP.get('www.example.com', '/') # ===> "abc\n"
123
124
 
125
+ ### Response with custom status message
126
+
127
+ stub_request(:any, "www.example.com").to_return(:status => [500, "Internal Server Error"])
128
+
129
+ req = Net::HTTP::Get.new("/")
130
+ Net::HTTP.start("www.example.com") { |http| http.request(req) }.message # ===> "Internal Server Error"
131
+
124
132
  ### Replaying raw responses recorded with `curl -is`
125
133
 
126
134
  `curl -is www.example.com > /tmp/example_curl_-is_output.txt`
@@ -134,13 +142,39 @@ You can also use WebMock without RSpec or Test::Unit support:
134
142
 
135
143
  stub_request(:get, "www.example.com").to_return(raw_response_file.read)
136
144
 
137
- ### Custom response with dynamically evaluated response
138
-
145
+ ### Responses dynamically evaluated from block
146
+
147
+ stub_request(:any, 'www.example.net').
148
+ to_return { |request| {:body => request.body} }
149
+
150
+ RestClient.post('www.example.net', 'abc') # ===> "abc\n"
151
+
152
+ ### Responses dynamically evaluated from lambda
153
+
154
+ stub_request(:any, 'www.example.net').
155
+ to_return(lambda { |request| {:body => request.body} })
156
+
157
+ RestClient.post('www.example.net', 'abc') # ===> "abc\n"
158
+
159
+ ### Responses with dynamically evaluated parts
160
+
139
161
  stub_request(:any, 'www.example.net').
140
162
  to_return(:body => lambda { |request| request.body })
141
163
 
142
164
  RestClient.post('www.example.net', 'abc') # ===> "abc\n"
143
165
 
166
+ ### Raising errors
167
+
168
+ stub_request(:any, 'www.example.net').to_raise(StandardError)
169
+
170
+ RestClient.post('www.example.net', 'abc') # ===> StandardError
171
+
172
+ ### Raising timeout errors
173
+
174
+ stub_request(:any, 'www.example.net').to_timeout
175
+
176
+ RestClient.post('www.example.net', 'abc') # ===> RestClient::RequestTimeout
177
+
144
178
  ### Multiple responses for repeated requests
145
179
 
146
180
  stub_request(:get, "www.example.com").to_return({:body => "abc"}, {:body => "def"})
@@ -151,10 +185,10 @@ You can also use WebMock without RSpec or Test::Unit support:
151
185
 
152
186
  Net::HTTP.get('www.example.com', '/') # ===> "def\n"
153
187
 
154
- ### Multiple responses using chained `to_return()` or `to_raise()` declarations
188
+ ### Multiple responses using chained `to_return()`, `to_raise()` or `to_timeout` declarations
155
189
 
156
190
  stub_request(:get, "www.example.com").
157
- to_return({:body => "abc"}).then. #then() just is a syntactic sugar
191
+ to_return({:body => "abc"}).then. #then() is just a syntactic sugar
158
192
  to_return({:body => "def"}).then.
159
193
  to_raise(MyException)
160
194
  Net::HTTP.get('www.example.com', '/') # ===> "abc\n"
@@ -186,6 +220,13 @@ You can also use WebMock without RSpec or Test::Unit support:
186
220
 
187
221
  Net::HTTP.get('www.something.com', '/') # ===> Failure
188
222
 
223
+ ### External requests can be disabled while allowing localhost
224
+
225
+ WebMock.disable_net_connect!(:allow_localhost => true)
226
+
227
+ Net::HTTP.get('www.something.com', '/') # ===> Failure
228
+
229
+ Net::HTTP.get('localhost:9887', '/') # ===> Allowed. Perhaps to Selenium?
189
230
 
190
231
  ## Setting Expectations
191
232
 
@@ -356,10 +397,31 @@ I'm particularly interested in how the DSL could be improved.
356
397
 
357
398
  ## Credits
358
399
 
400
+ The initial lines of this project were written during New Bamboo [Hack Day](http://blog.new-bamboo.co.uk/2009/11/13/hackday-results)
359
401
  Thanks to my fellow [Bambinos](http://new-bamboo.co.uk/) for all the great suggestions!
360
402
 
403
+ People who submitted patches and new features or suggested improvements. Many thanks to these people:
404
+
405
+ * Ben Pickles
406
+ * Mark Evans
407
+ * Ivan Vega
408
+ * Piotr Usewicz
409
+ * Nick Plante
410
+ * Nick Quaranto
411
+ * Diego E. "Flameeyes" Pettenò
412
+ * Niels Meersschaert
413
+ * Mack Earnhardt
414
+ * Arvicco
415
+ * Sergio Gil
416
+ * Jeffrey Jones
417
+ * Tekin Suleyman
418
+ * Tom Ward
419
+ * Nadim Bitar
420
+
421
+ ## Background
422
+
361
423
  Thank you Fakeweb! This library was inspired by [FakeWeb](fakeweb.rubyforge.org).
362
- I took couple of solutions from that project. I also copied some code i.e Net:HTTP adapter.
424
+ I imported some solutions from that project to WebMock. I also copied some code i.e Net:HTTP adapter.
363
425
  Fakeweb architecture unfortunately didn't allow me to extend it easily with the features I needed.
364
426
  I also preferred some things to work differently i.e request stub precedence.
365
427
 
data/Rakefile CHANGED
@@ -13,7 +13,7 @@ begin
13
13
  gem.add_dependency "addressable", ">= 2.1.1"
14
14
  gem.add_development_dependency "rspec", ">= 1.2.9"
15
15
  gem.add_development_dependency "httpclient", ">= 2.1.5.2"
16
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ gem.add_development_dependency "patron", ">= 0.4.5" unless RUBY_PLATFORM =~ /java/
17
17
  end
18
18
  Jeweler::GemcutterTasks.new
19
19
  rescue LoadError
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.1
1
+ 1.0.0
data/lib/webmock.rb CHANGED
@@ -4,6 +4,7 @@ require 'addressable/uri'
4
4
 
5
5
  require 'webmock/http_lib_adapters/net_http'
6
6
  require 'webmock/http_lib_adapters/httpclient'
7
+ require 'webmock/http_lib_adapters/patron'
7
8
 
8
9
  require 'webmock/errors'
9
10
 
@@ -11,8 +12,7 @@ require 'webmock/util/uri'
11
12
  require 'webmock/util/headers'
12
13
  require 'webmock/util/hash_counter'
13
14
 
14
- require 'webmock/request'
15
- require 'webmock/request_profile'
15
+ require 'webmock/request_pattern'
16
16
  require 'webmock/request_signature'
17
17
  require 'webmock/responses_sequence'
18
18
  require 'webmock/request_stub'
@@ -1,6 +1,6 @@
1
1
  require 'webmock'
2
2
  require 'spec'
3
- require 'webmock/adapters/rspec/request_profile_matcher'
3
+ require 'webmock/adapters/rspec/request_pattern_matcher'
4
4
  require 'webmock/adapters/rspec/webmock_matcher'
5
5
  require 'webmock/adapters/rspec/matchers'
6
6
 
@@ -1,11 +1,11 @@
1
1
  module WebMock
2
2
  module Matchers
3
3
  def have_been_made
4
- WebMock::RequestProfileMatcher.new
4
+ WebMock::RequestPatternMatcher.new
5
5
  end
6
6
 
7
7
  def have_not_been_made
8
- WebMock::RequestProfileMatcher.new.times(0)
8
+ WebMock::RequestPatternMatcher.new.times(0)
9
9
  end
10
10
 
11
11
  def have_requested(method, uri)
@@ -1,5 +1,5 @@
1
1
  module WebMock
2
- class RequestProfileMatcher
2
+ class RequestPatternMatcher
3
3
 
4
4
  def initialize
5
5
  @request_execution_verifier = RequestExecutionVerifier.new
@@ -20,13 +20,13 @@ module WebMock
20
20
  self
21
21
  end
22
22
 
23
- def matches?(request_profile)
24
- @request_execution_verifier.request_profile = request_profile
23
+ def matches?(request_pattern)
24
+ @request_execution_verifier.request_pattern = request_pattern
25
25
  @request_execution_verifier.matches?
26
26
  end
27
27
 
28
- def does_not_match?(request_profile)
29
- @request_execution_verifier.request_profile = request_profile
28
+ def does_not_match?(request_pattern)
29
+ @request_execution_verifier.request_pattern = request_pattern
30
30
  @request_execution_verifier.does_not_match?
31
31
  end
32
32
 
@@ -3,7 +3,7 @@ module WebMock
3
3
 
4
4
  def initialize(method, uri)
5
5
  @request_execution_verifier = RequestExecutionVerifier.new
6
- @request_execution_verifier.request_profile = RequestProfile.new(method, uri)
6
+ @request_execution_verifier.request_pattern = RequestPattern.new(method, uri)
7
7
  end
8
8
 
9
9
  def once
@@ -17,7 +17,7 @@ module WebMock
17
17
  end
18
18
 
19
19
  def with(options = {}, &block)
20
- @request_execution_verifier.request_profile.with(options, &block)
20
+ @request_execution_verifier.request_pattern.with(options, &block)
21
21
  self
22
22
  end
23
23
 
@@ -3,5 +3,6 @@ module WebMock
3
3
  include Singleton
4
4
 
5
5
  attr_accessor :allow_net_connect
6
+ attr_accessor :allow_localhost
6
7
  end
7
- end
8
+ end
@@ -19,7 +19,7 @@ if defined?(HTTPClient)
19
19
  webmock_response = WebMock.response_for_request(request_signature)
20
20
  response = build_httpclient_response(webmock_response, stream, &block)
21
21
  conn.push(response)
22
- elsif WebMock.net_connect_allowed?
22
+ elsif WebMock.net_connect_allowed?(req.header.request_uri)
23
23
  if stream
24
24
  do_get_stream_without_webmock(req, proxy, conn, &block)
25
25
  else
@@ -35,7 +35,7 @@ if defined?(HTTPClient)
35
35
  req = create_request(method, uri, query, body, extheader)
36
36
  request_signature = build_request_signature(req)
37
37
 
38
- if WebMock.registered_request?(request_signature) || WebMock.net_connect_allowed?
38
+ if WebMock.registered_request?(request_signature) || WebMock.net_connect_allowed?(uri)
39
39
  do_request_async_without_webmock(method, uri, query, body, extheader)
40
40
  else
41
41
  message = "Real HTTP connections are disabled. Unregistered request: #{request_signature}"
@@ -55,10 +55,11 @@ if defined?(HTTPClient)
55
55
  def build_httpclient_response(webmock_response, stream = false, &block)
56
56
  body = stream ? StringIO.new(webmock_response.body) : webmock_response.body
57
57
  response = HTTP::Message.new_response(body)
58
- response.header.init_response(webmock_response.status)
59
-
58
+ response.header.init_response(webmock_response.status[0])
59
+ response.reason=webmock_response.status[1]
60
60
  webmock_response.headers.to_a.each { |name, value| response.header.set(name, value) }
61
61
 
62
+ raise HTTPClient::TimeoutError if webmock_response.should_timeout
62
63
  webmock_response.raise_error_if_any
63
64
 
64
65
  block.call(nil, body) if block
@@ -91,9 +91,9 @@ module Net #:nodoc: all
91
91
  @socket = Net::HTTP.socket_type.new
92
92
  webmock_response = WebMock.response_for_request(request_signature)
93
93
  build_net_http_response(webmock_response, &block)
94
- elsif WebMock.net_connect_allowed?
94
+ elsif WebMock.net_connect_allowed?(uri)
95
95
  connect_without_webmock
96
- request_without_webmock(request, body, &block)
96
+ request_without_webmock(request, nil, &block)
97
97
  else
98
98
  message = "Real HTTP connections are disabled. Unregistered request: #{request_signature}"
99
99
  WebMock.assertion_failure(message)
@@ -114,7 +114,7 @@ module Net #:nodoc: all
114
114
  alias_method :connect, :connect_with_webmock
115
115
 
116
116
  def build_net_http_response(webmock_response, &block)
117
- response = Net::HTTPResponse.send(:response_class, webmock_response.status.to_s).new("1.0", webmock_response.status.to_s, "")
117
+ response = Net::HTTPResponse.send(:response_class, webmock_response.status[0].to_s).new("1.0", webmock_response.status[0].to_s, webmock_response.status[1])
118
118
  response.instance_variable_set(:@body, webmock_response.body)
119
119
  webmock_response.headers.to_a.each { |name, value| response[name] = value }
120
120
 
@@ -122,6 +122,8 @@ module Net #:nodoc: all
122
122
 
123
123
  response.extend StubResponse
124
124
 
125
+ raise Timeout::Error, "execution expired" if webmock_response.should_timeout
126
+
125
127
  webmock_response.raise_error_if_any
126
128
 
127
129
  yield response if block_given?
@@ -0,0 +1,82 @@
1
+ if defined?(Patron)
2
+
3
+ module Patron
4
+ class Session
5
+
6
+ def handle_request_with_webmock(req)
7
+ request_signature = build_request_signature(req)
8
+
9
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
10
+
11
+ if WebMock.registered_request?(request_signature)
12
+ webmock_response = WebMock.response_for_request(request_signature)
13
+ handle_file_name(req, webmock_response)
14
+ build_patron_response(webmock_response)
15
+ elsif WebMock.net_connect_allowed?(req.url)
16
+ handle_request_without_webmock(req)
17
+ else
18
+ message = "Real HTTP connections are disabled. Unregistered request: #{request_signature}"
19
+ WebMock.assertion_failure(message)
20
+ end
21
+ end
22
+
23
+ alias_method :handle_request_without_webmock, :handle_request
24
+ alias_method :handle_request, :handle_request_with_webmock
25
+
26
+
27
+
28
+ def handle_file_name(req, webmock_response)
29
+ if req.action == :get && req.file_name
30
+ begin
31
+ File.open(req.file_name, "w") do |f|
32
+ f.write webmock_response.body
33
+ end
34
+ rescue Errno::EACCES
35
+ raise ArgumentError.new("Unable to open specified file.")
36
+ end
37
+ end
38
+ end
39
+
40
+ def build_request_signature(req)
41
+ uri = Addressable::URI.heuristic_parse(req.url)
42
+ uri.path = uri.normalized_path.gsub("[^:]//","/")
43
+ uri.user = req.username
44
+ uri.password = req.password
45
+
46
+ if [:put, :post].include?(req.action)
47
+ if req.file_name
48
+ if !File.exist?(req.file_name) || !File.readable?(req.file_name)
49
+ raise ArgumentError.new("Unable to open specified file.")
50
+ end
51
+ request_body = File.read(req.file_name)
52
+ elsif req.upload_data
53
+ request_body = req.upload_data
54
+ else
55
+ raise ArgumentError.new("Must provide either data or a filename when doing a PUT or POST")
56
+ end
57
+ end
58
+
59
+ request_signature = WebMock::RequestSignature.new(
60
+ req.action,
61
+ uri.to_s,
62
+ :body => request_body,
63
+ :headers => req.headers
64
+ )
65
+ request_signature
66
+ end
67
+
68
+ def build_patron_response(webmock_response)
69
+ raise Patron::TimeoutError if webmock_response.should_timeout
70
+ webmock_response.raise_error_if_any
71
+ res = Patron::Response.new
72
+ res.instance_variable_set(:@body, webmock_response.body)
73
+ res.instance_variable_set(:@status, webmock_response.status[0])
74
+ res.instance_variable_set(:@status_line, webmock_response.status[1])
75
+ res.instance_variable_set(:@headers, webmock_response.headers)
76
+ res
77
+ end
78
+
79
+ end
80
+ end
81
+
82
+ end