fakeweb 1.2.3 → 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,25 @@
1
+ fakeweb (1.2.4)
2
+
3
+ * add experimental support for matching URIs via regular expressions
4
+ [Jacqui Maher, Tiago Albineli Motta, Peter Wagene]
5
+
6
+ * fix an exception when registering with the :response option and a string that
7
+ is the same as the name of a directory in the current path [Chris Kampmeier]
8
+
9
+ * DEPRECATION: Calling FakeWeb.register_uri with a :string or :file option is
10
+ now deprecated. Both options have been replaced with a unified :body option,
11
+ since they supply the response body (as opposed to :response, which supplies
12
+ the full response including headers) [Chris Kampmeier]
13
+
14
+ * add support for specifying HTTP headers as options to FakeWeb.register_uri
15
+ when using the :string or :file response types, since those methods only
16
+ specify a response body [David Michael, Chris Kampmeier]
17
+
18
+ * DEPRECATION: Calling FakeWeb.register_uri and FakeWeb.registered_uri? without
19
+ an HTTP method as the first argument is now deprecated. To match against any
20
+ HTTP method (the pre-1.2.0 behavior), use :any [Chris Kampmeier]
21
+
22
+
1
23
  fakeweb (1.2.3)
2
24
 
3
25
  * fix the #http_version of :file and :string responses, which was returning the
@@ -34,7 +34,7 @@ Start by requiring FakeWeb:
34
34
 
35
35
  === Registering basic string responses
36
36
 
37
- FakeWeb.register_uri(:get, "http://example.com/test1", :string => "Hello World!")
37
+ FakeWeb.register_uri(:get, "http://example.com/test1", :body => "Hello World!")
38
38
 
39
39
  Net::HTTP.get(URI.parse("http://example.com/test1"))
40
40
  => "Hello World!"
@@ -42,6 +42,9 @@ Start by requiring FakeWeb:
42
42
  Net::HTTP.get(URI.parse("http://example.com/test2"))
43
43
  => FakeWeb is bypassed and the response from a real request is returned
44
44
 
45
+ You can also call <tt>register_uri</tt> with a regular expression, to match
46
+ more than one URI.
47
+
45
48
  === Replaying a recorded response
46
49
 
47
50
  page = `curl -is http://www.google.com/`
@@ -52,7 +55,7 @@ Start by requiring FakeWeb:
52
55
 
53
56
  === Adding a custom status to the response
54
57
 
55
- FakeWeb.register_uri(:get, "http://example.com/", :string => "Nothing to be found 'round here",
58
+ FakeWeb.register_uri(:get, "http://example.com/", :body => "Nothing to be found 'round here",
56
59
  :status => ["404", "Not Found"])
57
60
 
58
61
  Net::HTTP.start("example.com") do |req|
@@ -64,14 +67,13 @@ Start by requiring FakeWeb:
64
67
 
65
68
  === Responding to any HTTP method
66
69
 
67
- FakeWeb.register_uri(:any, "http://example.com", :string => "response for any HTTP method")
70
+ FakeWeb.register_uri(:any, "http://example.com", :body => "response for any HTTP method")
68
71
 
69
72
  If you use the <tt>:any</tt> symbol, the URI you specify will be completely
70
73
  stubbed out (regardless of the HTTP method of the request). This can be useful
71
74
  for RPC-like services, where the HTTP method isn't significant. (Older
72
75
  versions of FakeWeb always behaved like this, and didn't accept the first
73
- +method+ argument above; this syntax is still supported, for
74
- backwards-compatibility, but it will probably be deprecated at some point.)
76
+ +method+ argument above; this syntax is now deprecated.)
75
77
 
76
78
  === Rotating responses
77
79
 
@@ -82,8 +84,8 @@ a response more than once before rotating, by specifying a <tt>:times</tt>
82
84
  option for that response.)
83
85
 
84
86
  FakeWeb.register_uri(:delete, "http://example.com/posts/1",
85
- [{:string => "Post 1 deleted.", :status => ["200", "OK"]},
86
- {:string => "Post not found", :status => ["404", "Not Found"]}])
87
+ [{:body => "Post 1 deleted.", :status => ["200", "OK"]},
88
+ {:body => "Post not found", :status => ["404", "Not Found"]}])
87
89
 
88
90
  Net::HTTP.start("example.com") do |req|
89
91
  req.delete("/posts/1").body # => "Post 1 deleted"
@@ -96,8 +98,8 @@ option for that response.)
96
98
  You can stub requests that use basic authentication with +userinfo+ strings in
97
99
  the URIs:
98
100
 
99
- FakeWeb.register_uri("http://example.com/secret", :string => "Unauthorized", :status => ["401", "Unauthorized"])
100
- FakeWeb.register_uri("http://user:pass@example.com/secret", :string => "Authorized")
101
+ FakeWeb.register_uri(:get, "http://example.com/secret", :body => "Unauthorized", :status => ["401", "Unauthorized"])
102
+ FakeWeb.register_uri(:get, "http://user:pass@example.com/secret", :body => "Authorized")
101
103
 
102
104
  Net::HTTP.start("example.com") do |http|
103
105
  req = Net::HTTP::Get.new("/secret")
@@ -132,6 +134,15 @@ This is handy when you want to make sure your tests are self-contained, or you
132
134
  want to catch the scenario when a URI is changed in implementation code
133
135
  without a corresponding test change.
134
136
 
137
+ === Specifying HTTP response headers
138
+
139
+ When you register a response using the <tt>:body</tt> option, you're only
140
+ setting the body of the response. If you want to add headers to these responses,
141
+ simply add the header as an option to +register_uri+:
142
+
143
+ FakeWeb.register_uri(:get, "http://example.com/hello.txt", :body => "Hello", :content_type => "text/plain")
144
+
145
+ This sets the "Content-Type" header in the response.
135
146
 
136
147
  == More info
137
148
 
@@ -47,24 +47,22 @@ module FakeWeb
47
47
 
48
48
  # call-seq:
49
49
  # FakeWeb.register_uri(method, uri, options)
50
- # FakeWeb.register_uri(uri, options)
51
50
  #
52
- # Register requests using the HTTP method specified by the symbol +method+ for
53
- # +uri+ to be handled according to +options+. If no +method+ is specified, or
54
- # you explicitly specify <tt>:any</tt>, the response will be reigstered for
55
- # any request for +uri+. +uri+ can be a +String+ or a +URI+ object. +options+
56
- # must be either a +Hash+ or an +Array+ of +Hashes+ (see below) that must
57
- # contain any one of the following keys:
51
+ # Register requests using the HTTP method specified by the symbol +method+
52
+ # for +uri+ to be handled according to +options+. If you specify the method
53
+ # <tt>:any</tt>, the response will be reigstered for any request for +uri+.
54
+ # +uri+ can be a +String+, +URI+, or +Regexp+ object. +options+ must be either
55
+ # a +Hash+ or an +Array+ of +Hashes+ (see below), which must contain one of
56
+ # these two keys:
58
57
  #
59
- # <tt>:string</tt>::
60
- # Takes a +String+ argument that is returned as the body of the response.
61
- # FakeWeb.register_uri(:get, 'http://example.com/', :string => "Hello World!")
62
- # <tt>:file</tt>::
63
- # Takes a valid filesystem path to a file that is slurped and returned as
64
- # the body of the response.
65
- # FakeWeb.register_uri(:post, 'http://example.com/', :file => "/tmp/my_response_body.txt")
58
+ # <tt>:body</tt>::
59
+ # A string which is used as the body of the response. If the string refers
60
+ # to a valid filesystem path, the contents of that file will be read and used
61
+ # as the body of the response instead. (This used to be two options,
62
+ # <tt>:string</tt> and <tt>:file</tt>, respectively. These are now deprecated.)
66
63
  # <tt>:response</tt>::
67
- # Either an <tt>Net::HTTPResponse</tt>, an +IO+ or a +String+.
64
+ # Either an <tt>Net::HTTPResponse</tt>, an +IO+, or a +String+ which is used
65
+ # as the full response for the request.
68
66
  #
69
67
  # The easier way by far is to pass the <tt>:response</tt> option to
70
68
  # +register_uri+ as a +String+ or an (open for reads) +IO+ object which
@@ -85,9 +83,9 @@ module FakeWeb
85
83
  # documentation[http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html]
86
84
  # for more information on creating custom response objects.
87
85
  #
88
- # +options+ may also be an +Array+ containing a list of the above-described +Hash+.
89
- # In this case, FakeWeb will rotate through each provided response, you may optionally
90
- # provide:
86
+ # +options+ may also be an +Array+ containing a list of the above-described
87
+ # +Hash+. In this case, FakeWeb will rotate through each provided response,
88
+ # you may optionally provide:
91
89
  #
92
90
  # <tt>:times</tt>::
93
91
  # The number of times this response will be used. Decremented by one each time it's called.
@@ -99,55 +97,70 @@ module FakeWeb
99
97
  # Passing <tt>:status</tt> as a two-value array will set the response code
100
98
  # and message. The defaults are <tt>200</tt> and <tt>OK</tt>, respectively.
101
99
  # Example:
102
- # FakeWeb.register_uri('http://www.example.com/', :response => "Go away!", :status => [ 404, "Not Found" ])
100
+ # FakeWeb.register_uri("http://www.example.com/", :body => "Go away!", :status => [404, "Not Found"])
103
101
  # <tt>:exception</tt>::
104
102
  # The argument passed via <tt>:exception</tt> will be raised when the
105
103
  # specified URL is requested. Any +Exception+ class is valid. Example:
106
104
  # FakeWeb.register_uri('http://www.example.com/', :exception => Net::HTTPError)
107
105
  #
106
+ # If you're using the <tt>:body</tt> response type, you can pass additional
107
+ # options to specify the HTTP headers to be used in the response. Example:
108
+ #
109
+ # FakeWeb.register_uri(:get, "http://example.com/index.txt", :body => "Hello", :content_type => "text/plain")
108
110
  def self.register_uri(*args)
109
- method = :any
110
111
  case args.length
111
- when 3 then method, uri, options = *args
112
- when 2 then uri, options = *args
113
- else raise ArgumentError.new("wrong number of arguments (#{args.length} for method = :any, uri, options)")
112
+ when 3
113
+ Registry.instance.register_uri(*args)
114
+ when 2
115
+ print_missing_http_method_deprecation_warning(*args)
116
+ Registry.instance.register_uri(:any, *args)
117
+ else
118
+ raise ArgumentError.new("wrong number of arguments (#{args.length} for 3)")
114
119
  end
115
-
116
- Registry.instance.register_uri(method, uri, options)
117
120
  end
118
121
 
119
122
  # call-seq:
120
123
  # FakeWeb.response_for(method, uri)
121
- # FakeWeb.response_for(uri)
122
124
  #
123
- # Returns the faked Net::HTTPResponse object associated with +uri+.
125
+ # Returns the faked Net::HTTPResponse object associated with +method+ and +uri+.
124
126
  def self.response_for(*args, &block) #:nodoc: :yields: response
125
- method = :any
126
127
  case args.length
127
- when 2 then method, uri = args
128
- when 1 then uri = args.first
129
- else raise ArgumentError.new("wrong number of arguments (#{args.length} for method = :any, uri)")
128
+ when 2
129
+ Registry.instance.response_for(*args, &block)
130
+ when 1
131
+ print_missing_http_method_deprecation_warning(*args)
132
+ Registry.instance.response_for(:any, *args, &block)
133
+ else
134
+ raise ArgumentError.new("wrong number of arguments (#{args.length} for 2)")
130
135
  end
131
-
132
- Registry.instance.response_for(method, uri, &block)
133
136
  end
134
137
 
135
138
  # call-seq:
136
139
  # FakeWeb.registered_uri?(method, uri)
137
- # FakeWeb.registered_uri?(uri)
138
140
  #
139
- # Returns true if +uri+ is registered with FakeWeb. You can optionally
140
- # specify +method+ to limit the search to a certain HTTP method (or use
141
- # <tt>:any</tt> to explicitly check against any method).
141
+ # Returns true if a +method+ request for +uri+ is registered with FakeWeb.
142
+ # Specify a method of <tt>:any</tt> to check for against all HTTP methods.
142
143
  def self.registered_uri?(*args)
143
- method = :any
144
144
  case args.length
145
- when 2 then method, uri = args
146
- when 1 then uri = args.first
147
- else raise ArgumentError.new("wrong number of arguments (#{args.length} for method = :any, uri)")
145
+ when 2
146
+ Registry.instance.registered_uri?(*args)
147
+ when 1
148
+ print_missing_http_method_deprecation_warning(*args)
149
+ Registry.instance.registered_uri?(:any, *args)
150
+ else
151
+ raise ArgumentError.new("wrong number of arguments (#{args.length} for 2)")
148
152
  end
149
-
150
- Registry.instance.registered_uri?(method, uri)
151
153
  end
152
154
 
153
- end
155
+ private
156
+
157
+ def self.print_missing_http_method_deprecation_warning(*args)
158
+ method = caller.first.match(/`(.*?)'/)[1]
159
+ new_args = args.map { |a| a.inspect }.unshift(":any")
160
+ new_args.last.gsub!(/^\{|\}$/, "").gsub!("=>", " => ") if args.last.is_a?(Hash)
161
+ $stderr.puts
162
+ $stderr.puts "Deprecation warning: FakeWeb requires an HTTP method argument (or use :any). Try this:"
163
+ $stderr.puts " FakeWeb.#{method}(#{new_args.join(', ')})"
164
+ $stderr.puts "Called at #{caller[1]}"
165
+ end
166
+ end
@@ -15,7 +15,7 @@ module Net #:nodoc: all
15
15
  when Socket, OpenSSL::SSL::SSLSocket, IO
16
16
  io
17
17
  when String
18
- if !io.include?("\0") && File.exists?(io)
18
+ if !io.include?("\0") && File.exists?(io) && !File.directory?(io)
19
19
  File.open(io, "r")
20
20
  else
21
21
  StringIO.new(io)
@@ -56,6 +56,8 @@ module Net #:nodoc: all
56
56
  connect_without_fakeweb
57
57
  request_without_fakeweb(request, body, &block)
58
58
  else
59
+ uri.sub!(":80/", "/") if uri =~ %r|^http://|
60
+ uri.sub!(":443/", "/") if uri =~ %r|^https://|
59
61
  raise FakeWeb::NetConnectNotAllowedError,
60
62
  "Real HTTP connections are disabled. Unregistered request: #{request.method} #{uri}"
61
63
  end
@@ -2,7 +2,7 @@ module FakeWeb
2
2
  class Registry #:nodoc:
3
3
  include Singleton
4
4
 
5
- attr_accessor :uri_map
5
+ attr_accessor :uri_map, :pattern_map
6
6
 
7
7
  def initialize
8
8
  clean_registry
@@ -12,17 +12,29 @@ module FakeWeb
12
12
  self.uri_map = Hash.new do |hash, key|
13
13
  hash[key] = Hash.new(&hash.default_proc)
14
14
  end
15
+ self.pattern_map = []
15
16
  end
16
17
 
17
18
  def register_uri(method, uri, options)
18
- uri_map[normalize_uri(uri)][method] = [*[options]].flatten.collect do |option|
19
- FakeWeb::Responder.new(method, uri, option, option[:times])
19
+ case uri
20
+ when URI, String
21
+ uri_map[normalize_uri(uri)][method] = [*[options]].flatten.collect do |option|
22
+ FakeWeb::Responder.new(method, uri, option, option[:times])
23
+ end
24
+ when Regexp
25
+ responders = [*[options]].flatten.collect do |option|
26
+ FakeWeb::Responder.new(method, uri, option, option[:times])
27
+ end
28
+ pattern_map << {:pattern => uri,
29
+ :responders => responders,
30
+ :method => method }
31
+
20
32
  end
21
33
  end
22
34
 
23
35
  def registered_uri?(method, uri)
24
36
  normalized_uri = normalize_uri(uri)
25
- uri_map[normalized_uri].has_key?(method) || uri_map[normalized_uri].has_key?(:any)
37
+ uri_map[normalized_uri].has_key?(method) || uri_map[normalized_uri].has_key?(:any) || pattern_map_matches?(method, uri) || pattern_map_matches?(:any, uri)
26
38
  end
27
39
 
28
40
  def registered_uri(method, uri)
@@ -30,8 +42,12 @@ module FakeWeb
30
42
  registered = registered_uri?(method, uri)
31
43
  if registered && uri_map[uri].has_key?(method)
32
44
  uri_map[uri][method]
33
- elsif registered
45
+ elsif registered && pattern_map_matches?(method, uri)
46
+ pattern_map_matches(method, uri).map{|m| m[:responders]}.flatten
47
+ elsif registered && uri_map[uri].has_key?(:any)
34
48
  uri_map[uri][:any]
49
+ elsif registered && pattern_map_matches(:any, uri)
50
+ pattern_map_matches(:any, uri).map{|m| m[:responders]}.flatten
35
51
  else
36
52
  nil
37
53
  end
@@ -53,13 +69,28 @@ module FakeWeb
53
69
  next_response.response(&block)
54
70
  end
55
71
 
72
+ def pattern_map_matches?(method, uri)
73
+ !pattern_map_matches(method, uri).empty?
74
+ end
75
+
76
+ def pattern_map_matches(method, uri)
77
+ uri = uri.to_s
78
+ uri.sub!(":80/", "/") if uri =~ %r|^http://|
79
+ uri.sub!(":443/", "/") if uri =~ %r|^https://|
80
+ pattern_map.select { |p| uri.match(p[:pattern]) && p[:method] == method }
81
+ end
82
+
83
+ def pattern_map_match(method, uri)
84
+ pattern_map_matches(method, uri).first
85
+ end
86
+
56
87
  private
57
88
 
58
89
  def normalize_uri(uri)
59
90
  normalized_uri =
60
91
  case uri
61
92
  when URI then uri
62
- else
93
+ when String
63
94
  uri = 'http://' + uri unless uri.match('^https?://')
64
95
  URI.parse(uri)
65
96
  end
@@ -76,4 +107,4 @@ module FakeWeb
76
107
  end
77
108
 
78
109
  end
79
- end
110
+ end
@@ -2,12 +2,18 @@ module FakeWeb
2
2
  class Responder #:nodoc:
3
3
 
4
4
  attr_accessor :method, :uri, :options, :times
5
+ KNOWN_OPTIONS = [:body, :exception, :response, :status].freeze
5
6
 
6
7
  def initialize(method, uri, options, times)
7
8
  self.method = method
8
9
  self.uri = uri
9
10
  self.options = options
10
11
  self.times = times ? times : 1
12
+
13
+ if options.has_key?(:file) || options.has_key?(:string)
14
+ print_file_string_options_deprecation_warning
15
+ options[:body] = options.delete(:file) || options.delete(:string)
16
+ end
11
17
  end
12
18
 
13
19
  def response(&block)
@@ -16,7 +22,8 @@ module FakeWeb
16
22
  else
17
23
  code, msg = meta_information
18
24
  response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
19
- response.instance_variable_set(:@body, content)
25
+ response.instance_variable_set(:@body, body)
26
+ headers_extracted_from_options.each { |name, value| response[name] = value }
20
27
  end
21
28
 
22
29
  response.instance_variable_set(:@read, true)
@@ -31,21 +38,20 @@ module FakeWeb
31
38
 
32
39
  private
33
40
 
34
- def content
35
- [ :file, :string ].each do |map_option|
36
- next unless options.has_key?(map_option)
37
- return self.send("#{map_option}_response", options[map_option])
38
- end
39
-
40
- return ''
41
+ def headers_extracted_from_options
42
+ options.reject {|name, _| KNOWN_OPTIONS.include?(name) }.map { |name, value|
43
+ [name.to_s.split("_").map { |segment| segment.capitalize }.join("-"), value]
44
+ }
41
45
  end
42
46
 
43
- def file_response(path)
44
- IO.read(path)
45
- end
47
+ def body
48
+ return '' unless options.has_key?(:body)
46
49
 
47
- def string_response(string)
48
- string
50
+ if !options[:body].include?("\0") && File.exists?(options[:body]) && !File.directory?(options[:body])
51
+ File.read(options[:body])
52
+ else
53
+ options[:body]
54
+ end
49
55
  end
50
56
 
51
57
  def baked_response
@@ -95,5 +101,13 @@ module FakeWeb
95
101
  options.has_key?(:status) ? options[:status] : [200, 'OK']
96
102
  end
97
103
 
104
+ def print_file_string_options_deprecation_warning
105
+ which = options.has_key?(:file) ? :file : :string
106
+ $stderr.puts
107
+ $stderr.puts "Deprecation warning: FakeWeb's :#{which} option has been renamed to :body."
108
+ $stderr.puts "Just replace :#{which} with :body in your FakeWeb.register_uri calls."
109
+ $stderr.puts "Called at #{caller[6]}"
110
+ end
111
+
98
112
  end
99
113
  end
@@ -20,10 +20,55 @@ class TestFakeWebAllowNetConnect < Test::Unit::TestCase
20
20
  def test_raises_for_unregistered_requests_when_allow_net_connect_is_false
21
21
  FakeWeb.allow_net_connect = false
22
22
  exception = assert_raise FakeWeb::NetConnectNotAllowedError do
23
- Net::HTTP.get(URI.parse('http://example.com/'))
23
+ Net::HTTP.get(URI.parse("http://example.com/"))
24
24
  end
25
25
  end
26
26
 
27
+ def test_exception_message_includes_unregistered_request_method_and_uri_but_no_default_port
28
+ FakeWeb.allow_net_connect = false
29
+ exception = assert_raise FakeWeb::NetConnectNotAllowedError do
30
+ Net::HTTP.get(URI.parse("http://example.com/"))
31
+ end
32
+ assert exception.message.include?("GET http://example.com/")
33
+
34
+ exception = assert_raise FakeWeb::NetConnectNotAllowedError do
35
+ http = Net::HTTP.new("example.com", 443)
36
+ http.use_ssl = true
37
+ http.get("/")
38
+ end
39
+ assert exception.message.include?("GET https://example.com/")
40
+ end
41
+
42
+ def test_exception_message_includes_unregistered_request_port_when_not_default
43
+ FakeWeb.allow_net_connect = false
44
+ exception = assert_raise FakeWeb::NetConnectNotAllowedError do
45
+ Net::HTTP.start("example.com", 8000) { |http| http.get("/") }
46
+ end
47
+ assert exception.message.include?("GET http://example.com:8000/")
48
+
49
+ exception = assert_raise FakeWeb::NetConnectNotAllowedError do
50
+ http = Net::HTTP.new("example.com", 4433)
51
+ http.use_ssl = true
52
+ http.get("/")
53
+ end
54
+ assert exception.message.include?("GET https://example.com:4433/")
55
+ end
56
+
57
+ def test_exception_message_includes_unregistered_request_port_when_not_default_with_path
58
+ FakeWeb.allow_net_connect = false
59
+ exception = assert_raise FakeWeb::NetConnectNotAllowedError do
60
+ Net::HTTP.start("example.com", 8000) { |http| http.get("/test") }
61
+ end
62
+ assert exception.message.include?("GET http://example.com:8000/test")
63
+
64
+ exception = assert_raise FakeWeb::NetConnectNotAllowedError do
65
+ http = Net::HTTP.new("example.com", 4433)
66
+ http.use_ssl = true
67
+ http.get("/test")
68
+ end
69
+ assert exception.message.include?("GET https://example.com:4433/test")
70
+ end
71
+
27
72
  def test_question_mark_method_returns_true_after_setting_allow_net_connect_to_true
28
73
  FakeWeb.allow_net_connect = true
29
74
  assert FakeWeb.allow_net_connect?