Permalink
Browse files

Fixed our Net::HTTP monkey patch so that it only stores a the recorde…

…d response once per request. Internally, Net::HTTP#request recursively calls itself (passing slightly different arguments) in certain circumstances.
  • Loading branch information...
1 parent 22ddcf3 commit 6abe5518d000d7f126dcb963d6be52e2256a0e05 @myronmarston committed Mar 4, 2010
@@ -57,4 +57,10 @@ Feature: Record response
Scenario: Record an asynchronous request (such as for mechanize)
Given we do not have a "temp/asynchronous" cassette
When I make an asynchronous HTTP get request to "http://example.com" within the "temp/asynchronous" unregistered cassette
- Then the "temp/asynchronous" cache file should have a response for "http://example.com" that matches /You have reached this web page by typing.*example\.com/
+ Then the "temp/asynchronous" cache file should have a response for "http://example.com" that matches /You have reached this web page by typing.*example\.com/
+
+ Scenario: Record a recursive post request
+ Given we do not have a "temp/recursive_post" cassette
+ When I make a recursive HTTP post request to "http://example.com" within the "temp/recursive_post" unregistered cassette
+ Then the "temp/recursive_post" cache file should have a response for "http://example.com" that matches /You have reached this web page by typing.*example\.com/
+ And the "temp/recursive_post" cache file should have exactly 1 response
@@ -9,6 +9,12 @@ def have_expected_response(url, regex_str)
response.response.body.should =~ regex
end
end
+
+ def recorded_responses_for(cassette_name)
+ yaml_file = File.join(VCR::Config.cache_dir, "#{cassette_name}.yml")
+ yaml = File.open(yaml_file, 'r') { |f| f.read }
+ responses = YAML.load(yaml)
+ end
end
World(VCRHelpers)
@@ -42,40 +48,47 @@ def have_expected_response(url, regex_str)
VCR::CucumberTags.tags.should include(tag)
end
-When /^I make an( asynchronous)? HTTP get request to "([^\"]*)"$/ do |asynchronous, url|
+When /^I make an(.*)? HTTP (?:get|post) request to "([^\"]*)"$/ do |request_type, url|
@http_requests ||= {}
+ uri = URI.parse(url)
+ path = uri.path.to_s == '' ? '/' : uri.path
begin
- if asynchronous =~ /asynchronous/
- uri = URI.parse(url)
- path = uri.path.to_s == '' ? '/' : uri.path
- result = Net::HTTP.new(uri.host, uri.port).request_get(path) { |r| r.read_body { } }
- result.body.should be_a(Net::ReadAdapter)
- else
- result = Net::HTTP.get_response(URI.parse(url))
+ case request_type
+ when /asynchronous/
+ result = Net::HTTP.new(uri.host, uri.port).request_get(path) { |r| r.read_body { } }
+ result.body.should be_a(Net::ReadAdapter)
+ when /recursive/
+ result = Net::HTTP.new(uri.host, uri.port).post(path, nil)
+ else
+ result = Net::HTTP.get_response(uri)
end
rescue => e
result = e
end
@http_requests[url] = result
end
-When /^I make(?: an)?( asynchronous)? HTTP get requests? to "([^\"]*)"(?: and "([^\"]*)")? within the "([^\"]*)" ?(#{VCR::Cassette::VALID_RECORD_MODES.join('|')})? cassette$/ do |asynchronous, url1, url2, cassette_name, record_mode|
+When /^I make(?: an)?(.*)? HTTP (get|post) requests? to "([^\"]*)"(?: and "([^\"]*)")? within the "([^\"]*)" ?(#{VCR::Cassette::VALID_RECORD_MODES.join('|')})? cassette$/ do |request_type, method, url1, url2, cassette_name, record_mode|
record_mode ||= :unregistered
record_mode = record_mode.to_sym
urls = [url1, url2].select { |u| u.to_s.size > 0 }
VCR.with_cassette(cassette_name, :record => record_mode) do
urls.each do |url|
- When %{I make an#{asynchronous} HTTP get request to "#{url}"}
+ When %{I make an#{request_type} HTTP #{method} request to "#{url}"}
end
end
end
Then /^the "([^\"]*)" cache file should have a response for "([^\"]*)" that matches \/(.+)\/$/ do |cassette_name, url, regex_str|
- yaml_file = File.join(VCR::Config.cache_dir, "#{cassette_name}.yml")
- responses = File.open(yaml_file, 'r') { |f| YAML.load(f.read) }
+ responses = recorded_responses_for(cassette_name)
responses.should have_expected_response(url, regex_str)
end
+Then /^the "([^\"]*)" cache file should have exactly (\d+) response$/ do |cassette_name, response_count|
+ responses = recorded_responses_for(cassette_name)
+ responses.should have(response_count.to_i).responses
+end
+
Then /^I can test the scenario cassette's recorded responses in the next scenario, after the cassette has been destroyed$/ do
# do nothing...
end
@@ -3,9 +3,12 @@
module Net
class HTTP
def request_with_vcr(request, body = nil, &block)
+ @__request_with_vcr_call_count = (@__request_with_vcr_call_count || 0) + 1
response = request_without_vcr(request, body, &block)
- __store_response_with_vcr__(response, request)
+ __store_response_with_vcr__(response, request) if @__request_with_vcr_call_count == 1
response
+ ensure
+ @__request_with_vcr_call_count -= 1
end
alias_method :request_without_vcr, :request
alias_method :request, :request_with_vcr
@@ -24,6 +24,11 @@
Net::HTTP.get(URI.parse('http://example.com'))
end
+ it 'calls #store_recorded_response! only once, even when Net::HTTP internally recursively calls #request' do
+ @current_cassette.should_receive(:store_recorded_response!).once
+ Net::HTTP.new('example.com', 80).post('/', nil)
+ end
+
it 'does not have an error if there is no current cassette' do
VCR.stub!(:current_cassette).and_return(nil)
lambda { Net::HTTP.get(URI.parse('http://example.com')) }.should_not raise_error

0 comments on commit 6abe551

Please sign in to comment.