fakeweb 1.2.3 → 1.2.4

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.
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?