webmock 0.9.1 → 1.0.0

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