webmock 1.6.2 → 1.6.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/.gitignore +4 -0
- data/.rvmrc +1 -0
- data/CHANGELOG.md +39 -1
- data/Gemfile +2 -0
- data/README.md +229 -175
- data/Rakefile +15 -29
- data/lib/webmock.rb +2 -1
- data/lib/webmock/http_lib_adapters/curb.rb +33 -0
- data/lib/webmock/http_lib_adapters/em_http_request.rb +7 -0
- data/lib/webmock/http_lib_adapters/httpclient.rb +1 -1
- data/lib/webmock/request_pattern.rb +40 -3
- data/lib/webmock/version.rb +3 -0
- data/lib/webmock/webmock.rb +1 -3
- data/spec/curb_spec.rb +119 -1
- data/spec/em_http_request_spec.rb +5 -0
- data/spec/httpclient_spec_helper.rb +7 -3
- data/spec/net_http_spec.rb +3 -3
- data/spec/patron_spec.rb +11 -4
- data/spec/patron_spec_helper.rb +1 -0
- data/spec/request_pattern_spec.rb +10 -1
- data/spec/spec_helper.rb +5 -2
- data/spec/webmock_shared.rb +8 -10
- data/webmock.gemspec +24 -167
- metadata +24 -61
- data/VERSION +0 -1
data/Rakefile
CHANGED
@@ -1,31 +1,20 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
gem.authors = ["Bartosz Blimke"]
|
13
|
-
gem.add_dependency "addressable", ">= 2.2.2"
|
14
|
-
gem.add_dependency "crack", ">=0.1.7"
|
15
|
-
gem.add_development_dependency "rspec", ">= 2.0.0"
|
16
|
-
gem.add_development_dependency "httpclient", ">= 2.1.5.2"
|
17
|
-
gem.add_development_dependency "patron", ">= 0.4.9" unless RUBY_PLATFORM =~ /java/
|
18
|
-
gem.add_development_dependency "em-http-request", ">= 0.2.14" unless RUBY_PLATFORM =~ /java/
|
19
|
-
gem.add_development_dependency "curb", ">= 0.7.8" unless RUBY_PLATFORM =~ /java/
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
namespace :spec do
|
5
|
+
desc 'Run specs against 1.8.6, REE, 1.8.7, 1.9.2 and jRuby'
|
6
|
+
task :rubies do
|
7
|
+
# JCF: I'd love to be able to use RVM's `rvm {rubies} specs` command but
|
8
|
+
# the require tests in spec/other_net_http_libs_spec.rb break when doing
|
9
|
+
# so.
|
10
|
+
spec_files = Dir[File.dirname(__FILE__) + '/spec/**/*_spec.rb'].join(' ')
|
11
|
+
sh "rvm 1.8.6@webmock,ree@webmock,1.8.7@webmock,1.9.2@webmock,jruby@webmock exec rspec #{spec_files}"
|
20
12
|
end
|
21
|
-
Jeweler::GemcutterTasks.new
|
22
|
-
rescue LoadError
|
23
|
-
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
24
13
|
end
|
25
14
|
|
26
15
|
require "rspec/core/rake_task"
|
27
16
|
RSpec::Core::RakeTask.new do |t|
|
28
|
-
t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
|
17
|
+
t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
|
29
18
|
t.pattern = 'spec/**/*_spec.rb'
|
30
19
|
end
|
31
20
|
|
@@ -36,18 +25,15 @@ Rake::TestTask.new(:test) do |test|
|
|
36
25
|
test.warning = false
|
37
26
|
end
|
38
27
|
|
39
|
-
task :spec => :check_dependencies
|
40
|
-
|
41
|
-
task :test => :check_dependencies
|
42
|
-
|
43
28
|
task :default => [:spec, :test]
|
44
29
|
|
45
30
|
require 'rake/rdoctask'
|
46
31
|
Rake::RDocTask.new do |rdoc|
|
47
|
-
|
32
|
+
$:.push File.expand_path('../lib', __FILE__)
|
33
|
+
require 'webmock/version'
|
48
34
|
|
49
35
|
rdoc.rdoc_dir = 'rdoc'
|
50
|
-
rdoc.title = "webmock #{
|
36
|
+
rdoc.title = "webmock #{WebMock::VERSION}"
|
51
37
|
rdoc.rdoc_files.include('README*')
|
52
38
|
rdoc.rdoc_files.include('lib/webmock/webmock.rb')
|
53
39
|
end
|
data/lib/webmock.rb
CHANGED
@@ -4,6 +4,7 @@ require 'addressable/uri'
|
|
4
4
|
require 'crack'
|
5
5
|
|
6
6
|
require 'webmock/deprecation'
|
7
|
+
require 'webmock/version'
|
7
8
|
|
8
9
|
require 'webmock/http_lib_adapters/net_http'
|
9
10
|
require 'webmock/http_lib_adapters/httpclient'
|
@@ -33,4 +34,4 @@ require 'webmock/callback_registry'
|
|
33
34
|
require 'webmock/request_registry'
|
34
35
|
require 'webmock/stub_registry'
|
35
36
|
require 'webmock/api'
|
36
|
-
require 'webmock/webmock'
|
37
|
+
require 'webmock/webmock'
|
@@ -65,7 +65,28 @@ if defined?(Curl)
|
|
65
65
|
@header_str << webmock_response.headers.map do |k,v|
|
66
66
|
"#{k}: #{v.is_a?(Array) ? v.join(", ") : v}"
|
67
67
|
end.join("\r\n")
|
68
|
+
|
69
|
+
location = webmock_response.headers['Location']
|
70
|
+
if self.follow_location? && location
|
71
|
+
@last_effective_url = location
|
72
|
+
webmock_follow_location(location)
|
73
|
+
end
|
74
|
+
|
75
|
+
@content_type = webmock_response.headers["Content-Type"]
|
76
|
+
end
|
77
|
+
|
78
|
+
@last_effective_url ||= self.url
|
79
|
+
end
|
80
|
+
|
81
|
+
def webmock_follow_location(location)
|
82
|
+
first_url = self.url
|
83
|
+
self.url = location
|
84
|
+
|
85
|
+
curb_or_webmock do
|
86
|
+
send( "http_#{@webmock_method}_without_webmock" )
|
68
87
|
end
|
88
|
+
|
89
|
+
self.url = first_url
|
69
90
|
end
|
70
91
|
|
71
92
|
def invoke_curb_callbacks
|
@@ -194,6 +215,18 @@ if defined?(Curl)
|
|
194
215
|
alias :header_str_without_webmock :header_str
|
195
216
|
alias :header_str :header_str_with_webmock
|
196
217
|
|
218
|
+
def last_effective_url_with_webmock
|
219
|
+
@last_effective_url || last_effective_url_without_webmock
|
220
|
+
end
|
221
|
+
alias :last_effective_url_without_webmock :last_effective_url
|
222
|
+
alias :last_effective_url :last_effective_url_with_webmock
|
223
|
+
|
224
|
+
def content_type_with_webmock
|
225
|
+
@content_type || content_type_without_webmock
|
226
|
+
end
|
227
|
+
alias :content_type_without_webmock :content_type
|
228
|
+
alias :content_type :content_type_with_webmock
|
229
|
+
|
197
230
|
%w[ success failure header body complete progress ].each do |callback|
|
198
231
|
class_eval <<-METHOD, __FILE__, __LINE__
|
199
232
|
def on_#{callback}_with_webmock &block
|
@@ -113,6 +113,13 @@ if defined?(EventMachine::HttpRequest)
|
|
113
113
|
|
114
114
|
headers.each do |header, value|
|
115
115
|
value = value.join(", ") if value.is_a?(Array)
|
116
|
+
|
117
|
+
# WebMock's internal processing will not handle the body
|
118
|
+
# correctly if the header indicates that it is chunked, unless
|
119
|
+
# we also create all the chunks.
|
120
|
+
# It's far easier just to remove the header.
|
121
|
+
next if header =~ /transfer-encoding/i && value =~/chunked/i
|
122
|
+
|
116
123
|
response_string << "#{header}: #{value}"
|
117
124
|
end if headers
|
118
125
|
|
@@ -143,11 +143,11 @@ module WebMock
|
|
143
143
|
|
144
144
|
case BODY_FORMATS[content_type]
|
145
145
|
when :json then
|
146
|
-
Crack::JSON.parse(body)
|
146
|
+
matching_hashes?(Crack::JSON.parse(body), @pattern)
|
147
147
|
when :xml then
|
148
|
-
Crack::XML.parse(body)
|
148
|
+
matching_hashes?(Crack::XML.parse(body), @pattern)
|
149
149
|
else
|
150
|
-
Addressable::URI.parse('?' + body).query_values
|
150
|
+
matching_hashes?(Addressable::URI.parse('?' + body).query_values, @pattern)
|
151
151
|
end
|
152
152
|
else
|
153
153
|
empty_string?(@pattern) && empty_string?(body) ||
|
@@ -162,6 +162,43 @@ module WebMock
|
|
162
162
|
|
163
163
|
private
|
164
164
|
|
165
|
+
# Compare two hashes for equality
|
166
|
+
#
|
167
|
+
# For two hashes to match they must have the same length and all
|
168
|
+
# values must match when compared using `#===`.
|
169
|
+
#
|
170
|
+
# The following hashes are examples of matches:
|
171
|
+
#
|
172
|
+
# {a: /\d+/} and {a: '123'}
|
173
|
+
#
|
174
|
+
# {a: '123'} and {a: '123'}
|
175
|
+
#
|
176
|
+
# {a: {b: /\d+/}} and {a: {b: '123'}}
|
177
|
+
#
|
178
|
+
# {a: {b: 'wow'}} and {a: {b: 'wow'}}
|
179
|
+
#
|
180
|
+
# @param [Hash] query_parameters typically the result of parsing
|
181
|
+
# JSON, XML or URL encoded parameters.
|
182
|
+
#
|
183
|
+
# @param [Hash] pattern which contains keys with a string, hash or
|
184
|
+
# regular expression value to use for comparison.
|
185
|
+
#
|
186
|
+
# @return [Boolean] true if the paramaters match the comparison
|
187
|
+
# hash, false if not.
|
188
|
+
def matching_hashes?(query_parameters, pattern)
|
189
|
+
return false unless query_parameters.size == pattern.size
|
190
|
+
query_parameters.each do |key, actual|
|
191
|
+
expected = pattern[key]
|
192
|
+
|
193
|
+
if actual.is_a?(Hash) && expected.is_a?(Hash)
|
194
|
+
return false unless matching_hashes?(actual, expected)
|
195
|
+
else
|
196
|
+
return false unless expected === actual
|
197
|
+
end
|
198
|
+
end
|
199
|
+
true
|
200
|
+
end
|
201
|
+
|
165
202
|
def empty_string?(string)
|
166
203
|
string.nil? || string == ""
|
167
204
|
end
|
data/lib/webmock/webmock.rb
CHANGED
data/spec/curb_spec.rb
CHANGED
@@ -77,7 +77,7 @@ unless RUBY_PLATFORM =~ /java/
|
|
77
77
|
test = data
|
78
78
|
end
|
79
79
|
@curl.http_get
|
80
|
-
test.should match
|
80
|
+
test.should match(/One: 1/)
|
81
81
|
end
|
82
82
|
|
83
83
|
it "should call on_complete when request is complete" do
|
@@ -133,6 +133,124 @@ unless RUBY_PLATFORM =~ /java/
|
|
133
133
|
order.should == [:on_progress,:on_header,:on_body,:on_complete,:on_failure]
|
134
134
|
end
|
135
135
|
end
|
136
|
+
|
137
|
+
describe '#last_effective_url' do
|
138
|
+
before(:each) do
|
139
|
+
@curl = Curl::Easy.new
|
140
|
+
@curl.url = "http://example.com"
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'when not following redirects' do
|
144
|
+
before { @curl.follow_location = false }
|
145
|
+
|
146
|
+
it 'should be the same as #url even with a location header' do
|
147
|
+
stub_request(:any, 'example.com').
|
148
|
+
to_return(:body => "abc",
|
149
|
+
:status => 302,
|
150
|
+
:headers => { 'Location' => 'http://www.example.com' })
|
151
|
+
|
152
|
+
@curl.http_get
|
153
|
+
@curl.last_effective_url.should == 'http://example.com'
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'when following redirects' do
|
158
|
+
before { @curl.follow_location = true }
|
159
|
+
|
160
|
+
it 'should be the same as #url when no location header is present' do
|
161
|
+
stub_request(:any, "example.com")
|
162
|
+
@curl.http_get
|
163
|
+
@curl.last_effective_url.should == 'http://example.com'
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should be the value of the location header when present' do
|
167
|
+
stub_request(:any, 'example.com').
|
168
|
+
to_return(:headers => { 'Location' => 'http://www.example.com' })
|
169
|
+
stub_request(:any, 'www.example.com')
|
170
|
+
|
171
|
+
@curl.http_get
|
172
|
+
@curl.last_effective_url.should == 'http://www.example.com'
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should work with more than one redirect' do
|
176
|
+
stub_request(:any, 'example.com').
|
177
|
+
to_return(:headers => { 'Location' => 'http://www.example.com' })
|
178
|
+
stub_request(:any, 'www.example.com').
|
179
|
+
to_return(:headers => { 'Location' => 'http://blog.example.com' })
|
180
|
+
stub_request(:any, 'blog.example.com')
|
181
|
+
|
182
|
+
@curl.http_get
|
183
|
+
@curl.last_effective_url.should == 'http://blog.example.com'
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should maintain the original url' do
|
187
|
+
stub_request(:any, 'example.com').
|
188
|
+
to_return(:headers => { 'Location' => 'http://www.example.com' })
|
189
|
+
stub_request(:any, 'www.example.com')
|
190
|
+
|
191
|
+
@curl.http_get
|
192
|
+
@curl.url.should == 'http://example.com'
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'should have the redirected-to attrs (body, response code)' do
|
196
|
+
stub_request(:any, 'example.com').
|
197
|
+
to_return(:body => 'request A',
|
198
|
+
:status => 302,
|
199
|
+
:headers => { 'Location' => 'http://www.example.com' })
|
200
|
+
stub_request(:any, 'www.example.com').to_return(:body => 'request B')
|
201
|
+
|
202
|
+
@curl.http_get
|
203
|
+
@curl.body_str.should == 'request B'
|
204
|
+
@curl.response_code.should == 200
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should follow more than one redirect' do
|
208
|
+
stub_request(:any, 'example.com').
|
209
|
+
to_return(:headers => { 'Location' => 'http://www.example.com' })
|
210
|
+
stub_request(:any, 'www.example.com').
|
211
|
+
to_return(:headers => { 'Location' => 'http://blog.example.com' })
|
212
|
+
stub_request(:any, 'blog.example.com').to_return(:body => 'blog post')
|
213
|
+
|
214
|
+
@curl.http_get
|
215
|
+
@curl.url.should == 'http://example.com'
|
216
|
+
@curl.body_str.should == 'blog post'
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe "#content_type" do
|
222
|
+
before(:each) do
|
223
|
+
@curl = Curl::Easy.new
|
224
|
+
@curl.url = "http://example.com"
|
225
|
+
end
|
226
|
+
|
227
|
+
context "when response includes Content-Type header" do
|
228
|
+
it "returns correct content_type" do
|
229
|
+
content_type = "application/json"
|
230
|
+
|
231
|
+
stub_request(:any, 'example.com').
|
232
|
+
to_return(:body => "abc",
|
233
|
+
:status => 200,
|
234
|
+
:headers => { 'Content-Type' => content_type })
|
235
|
+
|
236
|
+
@curl.http_get
|
237
|
+
@curl.content_type.should == content_type
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context "when response does not include Content-Type header" do
|
242
|
+
it "returns nil for content_type" do
|
243
|
+
content_type = "application/json"
|
244
|
+
|
245
|
+
stub_request(:any, 'example.com').
|
246
|
+
to_return(:body => "abc",
|
247
|
+
:status => 200 )
|
248
|
+
|
249
|
+
@curl.http_get
|
250
|
+
@curl.content_type.should be_nil
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
136
254
|
end
|
137
255
|
|
138
256
|
describe "Webmock with Curb" do
|
@@ -20,6 +20,11 @@ unless RUBY_PLATFORM =~ /java/
|
|
20
20
|
response.should == "abc"
|
21
21
|
end
|
22
22
|
|
23
|
+
it "should work with responses that use chunked transfer encoding" do
|
24
|
+
stub_http_request(:get, "www.example.com").to_return(:body => "abc", :headers => { 'Transfer-Encoding' => 'chunked' })
|
25
|
+
http_request(:get, "http://www.example.com").body.should == "abc"
|
26
|
+
end
|
27
|
+
|
23
28
|
it "should work with optional query params" do
|
24
29
|
stub_http_request(:get, "www.example.com/?x=3&a[]=b&a[]=c").to_return(:body => "abc")
|
25
30
|
http_request(:get, "http://www.example.com/?x=3", :query => {"a" => ["b", "c"]}).body.should == "abc"
|
@@ -64,12 +64,16 @@ module HTTPClientSpecHelper
|
|
64
64
|
|
65
65
|
socket.stub!(:sync=).with(true)
|
66
66
|
|
67
|
-
socket.should_receive(:gets).with("\n").
|
67
|
+
socket.should_receive(:gets).with("\n").
|
68
|
+
and_return("HTTP/1.1 #{options[:response_code]} #{options[:response_message]}\r\n",
|
69
|
+
"Content-Length: #{options[:response_body].length}\r\n",
|
70
|
+
"\r\n")
|
71
|
+
socket.stub(:readpartial) do |size, buf|
|
72
|
+
buf << options[:response_body]
|
73
|
+
end
|
68
74
|
|
69
75
|
socket.stub!(:eof?).and_return(true)
|
70
76
|
socket.stub!(:close).and_return(true)
|
71
|
-
|
72
|
-
socket.should_receive(:readpartial).any_number_of_times.and_raise(EOFError)
|
73
77
|
end
|
74
78
|
|
75
79
|
def http_library
|
data/spec/net_http_spec.rb
CHANGED
@@ -57,7 +57,7 @@ describe "Webmock with Net:HTTP" do
|
|
57
57
|
req = Net::HTTP::Post.new("/")
|
58
58
|
Net::HTTP.start("www.example.com") {|http|
|
59
59
|
http.request(req, StringIO.new("my_params"))
|
60
|
-
}.body.should =~
|
60
|
+
}.body.should =~ /^$/
|
61
61
|
end
|
62
62
|
|
63
63
|
it "should handle requests with block passed to read_body", :net_connect => true do
|
@@ -71,7 +71,7 @@ describe "Webmock with Net:HTTP" do
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
74
|
-
body.should =~
|
74
|
+
body.should =~ /^$/
|
75
75
|
end
|
76
76
|
|
77
77
|
it "should return a Net::ReadAdapter from response.body when a stubbed request is made with a block and #read_body" do
|
@@ -128,7 +128,7 @@ describe "Webmock with Net:HTTP" do
|
|
128
128
|
end
|
129
129
|
|
130
130
|
describe 'after_request callback support', :net_connect => true do
|
131
|
-
let(:expected_body_regex) {
|
131
|
+
let(:expected_body_regex) { /^$/ }
|
132
132
|
|
133
133
|
before(:each) do
|
134
134
|
WebMock.allow_net_connect!
|
data/spec/patron_spec.rb
CHANGED
@@ -38,9 +38,16 @@ unless RUBY_PLATFORM =~ /java/
|
|
38
38
|
|
39
39
|
it "should raise same error as Patron if file is not readable for get request" do
|
40
40
|
stub_http_request(:get, "www.example.com")
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
File.open("/tmp/read_only_file", "w") do |tmpfile|
|
42
|
+
tmpfile.chmod(0400)
|
43
|
+
end
|
44
|
+
begin
|
45
|
+
lambda {
|
46
|
+
@sess.get_file("/", "/tmp/read_only_file")
|
47
|
+
}.should raise_error(ArgumentError, "Unable to open specified file.")
|
48
|
+
ensure
|
49
|
+
File.unlink("/tmp/read_only_file")
|
50
|
+
end
|
44
51
|
end
|
45
52
|
|
46
53
|
it "should work with put_file" do
|
@@ -58,7 +65,7 @@ unless RUBY_PLATFORM =~ /java/
|
|
58
65
|
it "should raise same error as Patron if file is not readable for post request" do
|
59
66
|
stub_http_request(:post, "www.example.com").with(:body => "abc")
|
60
67
|
lambda {
|
61
|
-
@sess.post_file("/", "/
|
68
|
+
@sess.post_file("/", "/path/to/non/existing/file")
|
62
69
|
}.should raise_error(ArgumentError, "Unable to open specified file.")
|
63
70
|
end
|
64
71
|
|