vcr 1.1.2 → 1.2.0

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.
Files changed (52) hide show
  1. data/.gitignore +1 -1
  2. data/.gitmodules +3 -0
  3. data/CHANGELOG.md +58 -1
  4. data/Gemfile +2 -4
  5. data/Gemfile.lock +26 -49
  6. data/README.md +92 -23
  7. data/Rakefile +6 -3
  8. data/features/fixtures/vcr_cassettes/1.9.1/record_all.yml +62 -0
  9. data/features/fixtures/vcr_cassettes/not_1.9.1/record_all.yml +61 -0
  10. data/features/http_client.feature +2 -2
  11. data/features/net_http.feature +15 -4
  12. data/features/record_response.feature +36 -7
  13. data/features/replay_recorded_response.feature +8 -8
  14. data/features/rspec.feature +100 -0
  15. data/features/step_definitions/net_http_steps.rb +13 -1
  16. data/features/step_definitions/vcr_steps.rb +26 -16
  17. data/features/support/env.rb +13 -5
  18. data/features/webmock.feature +4 -4
  19. data/lib/vcr.rb +5 -5
  20. data/lib/vcr/cassette.rb +55 -19
  21. data/lib/vcr/extensions/net_http.rb +2 -4
  22. data/lib/vcr/http_stubbing_adapters/fakeweb.rb +0 -4
  23. data/lib/vcr/http_stubbing_adapters/webmock.rb +2 -9
  24. data/lib/vcr/internet_connection.rb +13 -0
  25. data/lib/vcr/ping.rb +26 -0
  26. data/lib/vcr/rspec.rb +39 -0
  27. data/lib/vcr/version.rb +1 -1
  28. data/spec/cassette_spec.rb +160 -69
  29. data/spec/config_spec.rb +1 -1
  30. data/spec/cucumber_tags_spec.rb +3 -2
  31. data/spec/deprecations_spec.rb +1 -1
  32. data/spec/extensions/net_http_response_spec.rb +2 -4
  33. data/spec/extensions/net_http_spec.rb +3 -1
  34. data/spec/fixtures/1.9.1/cassette_spec/with_localhost_requests.yml +23 -0
  35. data/spec/fixtures/not_1.9.1/cassette_spec/with_localhost_requests.yml +23 -0
  36. data/spec/http_stubbing_adapters/fakeweb_spec.rb +3 -9
  37. data/spec/http_stubbing_adapters/webmock_spec.rb +7 -18
  38. data/spec/internet_connection_spec.rb +19 -0
  39. data/spec/monkey_patches.rb +45 -6
  40. data/spec/request_matcher_spec.rb +1 -1
  41. data/spec/rspec_spec.rb +46 -0
  42. data/spec/spec_helper.rb +6 -11
  43. data/spec/structs_spec.rb +1 -1
  44. data/spec/support/fixnum_extension.rb +10 -0
  45. data/spec/support/http_library_adapters.rb +31 -2
  46. data/spec/support/ruby_interpreter.rb +7 -0
  47. data/spec/support/vcr_localhost_server.rb +86 -44
  48. data/spec/support/webmock_macros.rb +14 -0
  49. data/spec/vcr_spec.rb +1 -1
  50. data/spec/version_spec.rb +1 -1
  51. data/vcr.gemspec +10 -6
  52. metadata +155 -39
@@ -53,6 +53,29 @@
53
53
  - "277"
54
54
  body: 127.0.0.1 response
55
55
  http_version: "1.1"
56
+ - !ruby/struct:VCR::HTTPInteraction
57
+ request: !ruby/struct:VCR::Request
58
+ method: :get
59
+ uri: http://0.0.0.0:80/
60
+ body:
61
+ headers:
62
+ response: !ruby/struct:VCR::Response
63
+ status: !ruby/struct:VCR::ResponseStatus
64
+ code: 404
65
+ message: Not Found
66
+ headers:
67
+ content-type:
68
+ - text/html; charset=iso-8859-1
69
+ connection:
70
+ - close
71
+ server:
72
+ - Apache/2.2.3 (CentOS)
73
+ date:
74
+ - Thu, 25 Feb 2010 07:53:52 GMT
75
+ content-length:
76
+ - "277"
77
+ body: 127.0.0.1 response
78
+ http_version: "1.1"
56
79
  - !ruby/struct:VCR::HTTPInteraction
57
80
  request: !ruby/struct:VCR::Request
58
81
  method: :get
@@ -1,15 +1,9 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe VCR::HttpStubbingAdapters::FakeWeb do
4
- it_should_behave_like 'an http stubbing adapter', ['net/http'], [:method, :uri, :host, :path]
5
-
6
- describe '#should_unwind_response?' do
7
- let(:response) { ::Net::HTTPOK.new('1.1', 200, 'OK') }
4
+ without_webmock_callbacks
8
5
 
9
- it 'returns true' do
10
- described_class.should_unwind_response?(response).should be_true
11
- end
12
- end
6
+ it_should_behave_like 'an http stubbing adapter', ['net/http'], [:method, :uri, :host, :path]
13
7
 
14
8
  describe '#check_version!' do
15
9
  disable_warnings
@@ -1,27 +1,16 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe VCR::HttpStubbingAdapters::WebMock do
4
+ without_monkey_patches :vcr
5
+
4
6
  it_should_behave_like 'an http stubbing adapter',
5
- %w[net/http patron httpclient em-http-request],
7
+ %w[net/http patron httpclient em-http-request curb],
6
8
  [:method, :uri, :host, :path, :body, :headers]
7
9
 
8
- describe '#should_unwind_response?' do
9
- let(:response) { ::Net::HTTPOK.new('1.1', 200, 'OK') }
10
-
11
- it 'returns true when the response has not been extended with WebMock::Net::HTTPResponse' do
12
- described_class.should_unwind_response?(response).should be_true
13
- end
14
-
15
- it 'returns false when the response has been extended with WebMock::Net::HTTPResponse' do
16
- response.extend WebMock::Net::HTTPResponse
17
- described_class.should_unwind_response?(response).should be_false
18
- end
19
- end
20
-
21
10
  describe '#check_version!' do
22
11
  before(:each) { WebMock.should respond_to(:version) }
23
12
 
24
- %w( 0.9.9 0.9.10 0.1.30 1.0.30 1.2.9 1.3.2 ).each do |version|
13
+ %w( 0.9.9 0.9.10 0.1.30 1.0.30 1.2.9 1.3.9 ).each do |version|
25
14
  it "raises an error when WebMock's version is #{version}" do
26
15
  WebMock.stub!(:version).and_return(version)
27
16
  described_class.should_not_receive(:warn)
@@ -29,7 +18,7 @@ describe VCR::HttpStubbingAdapters::WebMock do
29
18
  end
30
19
  end
31
20
 
32
- %w( 1.3.3 1.3.10 1.3.99 ).each do |version|
21
+ %w( 1.4.0 1.4.10 1.4.99 ).each do |version|
33
22
  it "does nothing when WebMock's version is #{version}" do
34
23
  WebMock.stub!(:version).and_return(version)
35
24
  described_class.should_not_receive(:warn)
@@ -37,7 +26,7 @@ describe VCR::HttpStubbingAdapters::WebMock do
37
26
  end
38
27
  end
39
28
 
40
- %w( 1.4.0 1.10.0 2.0.0 ).each do |version|
29
+ %w( 1.5.0 1.10.0 2.0.0 ).each do |version|
41
30
  it "does nothing when WebMock's version is #{version}" do
42
31
  WebMock.stub!(:version).and_return(version)
43
32
  described_class.should_receive(:warn).with(/VCR is known to work with WebMock ~> .*\./)
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe VCR::InternetConnection do
4
+ describe '.available?' do
5
+ before(:each) do
6
+ described_class.instance_variable_set(:@available, nil)
7
+ end
8
+
9
+ it 'returns true when pinging example.com succeeds' do
10
+ Ping.stub(:pingecho).with("example.com", anything, anything).and_return(true)
11
+ described_class.available?.should be_true
12
+ end
13
+
14
+ it 'returns false when pinging example.com fails' do
15
+ Ping.stub(:pingecho).with("example.com", anything, anything).and_return(false)
16
+ described_class.available?.should be_false
17
+ end
18
+ end
19
+ end
@@ -1,6 +1,13 @@
1
1
  module MonkeyPatches
2
2
  extend self
3
3
 
4
+ module RSpecMacros
5
+ def without_monkey_patches(scope)
6
+ before(:each) { MonkeyPatches.disable!(scope) }
7
+ after(:each) { MonkeyPatches.enable!(scope) }
8
+ end
9
+ end
10
+
4
11
  NET_HTTP_SINGLETON = class << Net::HTTP; self; end
5
12
 
6
13
  MONKEY_PATCHES = [
@@ -10,15 +17,27 @@ module MonkeyPatches
10
17
  [NET_HTTP_SINGLETON, :socket_type]
11
18
  ]
12
19
 
13
- def enable!
14
- MONKEY_PATCHES.each do |mp|
15
- realias mp.first, mp.last, :with_monkeypatches
20
+ def enable!(scope)
21
+ case scope
22
+ when :all
23
+ MONKEY_PATCHES.each do |mp|
24
+ realias mp.first, mp.last, :with_monkeypatches
25
+ end
26
+ when :vcr
27
+ realias Net::HTTP, :request, :with_vcr
28
+ else raise ArgumentError.new("Unexpected scope: #{scope}")
16
29
  end
17
30
  end
18
31
 
19
- def disable!
20
- MONKEY_PATCHES.each do |mp|
21
- realias mp.first, mp.last, :without_monkeypatches
32
+ def disable!(scope)
33
+ case scope
34
+ when :all
35
+ MONKEY_PATCHES.each do |mp|
36
+ realias mp.first, mp.last, :without_monkeypatches
37
+ end
38
+ when :vcr
39
+ realias Net::HTTP, :request, :without_vcr
40
+ else raise ArgumentError.new("Unexpected scope: #{scope}")
22
41
  end
23
42
  end
24
43
 
@@ -68,3 +87,23 @@ module MonkeyPatches
68
87
  end
69
88
  end
70
89
 
90
+ # Require all the HTTP libraries--these must be required before WebMock
91
+ # for WebMock to work with them.
92
+ require 'httpclient'
93
+
94
+ if RUBY_INTERPRETER == :mri
95
+ require 'patron'
96
+ require 'em-http-request'
97
+ require 'curb'
98
+ end
99
+
100
+ # The FakeWeb adapter must be required after WebMock's so
101
+ # that VCR's Net::HTTP monkey patch is loaded last.
102
+ # This allows us to disable it (i.e. by realiasing to
103
+ # the version of Net::HTTP's methods before it was loaded)
104
+ require 'vcr/http_stubbing_adapters/webmock'
105
+ require 'vcr/http_stubbing_adapters/fakeweb'
106
+
107
+ # All Net::HTTP monkey patches have now been loaded, so capture the
108
+ # appropriate method definitions so we can disable them later.
109
+ MonkeyPatches.init
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe VCR::RequestMatcher do
4
4
  it 'raises an error when given invalid match attributes' do
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe VCR::RSpec::Macros do
4
+ extend described_class
5
+
6
+ describe '#use_vcr_cassette' do
7
+ def self.perform_test(context_name, expected_cassette_name, *args, &block)
8
+ context context_name do
9
+ after(:each) do
10
+ if @test_ejection
11
+ VCR.current_cassette.should be_nil
12
+ end
13
+ end
14
+
15
+ use_vcr_cassette *args
16
+
17
+ it 'ejects the cassette in an after hook' do
18
+ VCR.current_cassette.should be_a(VCR::Cassette)
19
+ @test_ejection = true
20
+ end
21
+
22
+ it "creates a cassette named '#{expected_cassette_name}" do
23
+ VCR.current_cassette.name.should == expected_cassette_name
24
+ end
25
+
26
+ module_eval(&block) if block
27
+ end
28
+ end
29
+
30
+ perform_test 'when called with an explicit name', 'explicit_name', 'explicit_name'
31
+
32
+ perform_test 'when called with an explicit name and some options', 'explicit_name', 'explicit_name', :match_requests_on => [:method, :host] do
33
+ it 'uses the provided cassette options' do
34
+ VCR.current_cassette.match_requests_on.should == [:method, :host]
35
+ end
36
+ end
37
+
38
+ perform_test 'when called with nothing', 'VCR::RSpec::Macros/#use_vcr_cassette/when called with nothing'
39
+
40
+ perform_test 'when called with some options', 'VCR::RSpec::Macros/#use_vcr_cassette/when called with some options', :match_requests_on => [:method, :host] do
41
+ it 'uses the provided cassette options' do
42
+ VCR.current_cassette.match_requests_on.should == [:method, :host]
43
+ end
44
+ end
45
+ end
46
+ end
data/spec/spec_helper.rb CHANGED
@@ -2,14 +2,6 @@ require 'rubygems'
2
2
  require 'bundler'
3
3
  Bundler.setup
4
4
 
5
- require 'monkey_patches'
6
-
7
- require 'patron' unless RUBY_PLATFORM =~ /java/
8
- require 'httpclient'
9
- require 'em-http-request' unless RUBY_PLATFORM =~ /java/
10
- require 'vcr'
11
- require 'vcr/http_stubbing_adapters/fakeweb'
12
- require 'vcr/http_stubbing_adapters/webmock'
13
5
  require 'rspec'
14
6
 
15
7
  # Ruby 1.9.1 has a different yaml serialization format.
@@ -19,12 +11,17 @@ YAML_SERIALIZATION_VERSION = RUBY_VERSION == '1.9.1' ? '1.9.1' : 'not_1.9.1'
19
11
  # in ./support/ and its subdirectories.
20
12
  Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
21
13
 
14
+ require 'vcr'
15
+ require 'monkey_patches'
16
+
22
17
  RSpec.configure do |config|
23
18
  config.extend TempCassetteLibraryDir
24
19
  config.extend DisableWarnings
20
+ config.extend MonkeyPatches::RSpecMacros
21
+ config.extend WebMockMacros
25
22
 
26
23
  config.color_enabled = true
27
- config.debug = RUBY_PLATFORM != 'java'
24
+ config.debug = RUBY_INTERPRETER == :mri
28
25
 
29
26
  config.before(:each) do
30
27
  VCR::Config.default_cassette_options = { :record => :new_episodes }
@@ -41,5 +38,3 @@ RSpec.configure do |config|
41
38
  config.run_all_when_everything_filtered = true
42
39
  end
43
40
 
44
- MonkeyPatches.init
45
-
data/spec/structs_spec.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  shared_examples_for "a header normalizer" do
4
4
  let(:instance) do
@@ -0,0 +1,10 @@
1
+ unless 7.respond_to?(:days)
2
+ class Fixnum
3
+ def days
4
+ self *
5
+ 24 * # hours
6
+ 60 * # minutes
7
+ 60 # seconds
8
+ end
9
+ end
10
+ end
@@ -67,12 +67,41 @@ HTTP_LIBRARY_ADAPTERS['em-http-request'] = Module.new do
67
67
  end
68
68
  end
69
69
 
70
+ HTTP_LIBRARY_ADAPTERS['curb'] = Module.new do
71
+ def self.http_library_name; "Curb"; end
72
+
73
+ def get_body_string(response)
74
+ response.body_str
75
+ end
76
+
77
+ def get_header(header_key, response)
78
+ headers = response.header_str.split("\r\n")[1..-1]
79
+ headers.each do |h|
80
+ if h =~ /^#{Regexp.escape(header_key)}: (.*)$/
81
+ return $1.split(', ')
82
+ end
83
+ end
84
+ end
85
+
86
+ def make_http_request(method, url, body = nil, headers = {})
87
+ Curl::Easy.new(url) do |c|
88
+ c.headers = headers
89
+
90
+ if [:post, :put].include?(method)
91
+ c.send("http_#{method}", body)
92
+ else
93
+ c.send("http_#{method}")
94
+ end
95
+ end
96
+ end
97
+ end
98
+
70
99
  NET_CONNECT_NOT_ALLOWED_ERROR = /You can use VCR to automatically record this request and replay it later/
71
100
 
72
101
  module HttpLibrarySpecs
73
102
  def test_http_library(library, supported_request_match_attributes)
74
- # patron and em-http-client cannot be installed on jruby
75
- return if %w[patron em-http-request].include?(library) && RUBY_PLATFORM == 'java'
103
+ # curb, patron and em-http-client cannot be installed on jruby
104
+ return if %w[curb patron em-http-request].include?(library) && RUBY_INTERPRETER != :mri
76
105
 
77
106
  unless adapter_module = HTTP_LIBRARY_ADAPTERS[library]
78
107
  raise ArgumentError.new("No http library adapter module could be found for #{library}")
@@ -0,0 +1,7 @@
1
+ RUBY_INTERPRETER = if RUBY_PLATFORM == 'java'
2
+ :jruby
3
+ elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
4
+ :rubinius
5
+ else
6
+ :mri
7
+ end
@@ -1,53 +1,95 @@
1
- require 'capybara'
2
- require 'capybara/wait_until'
3
-
4
- # This uses a separate process rather than a separate thread to run the server.
5
- # Patron always times out when this is running in a thread in the same process.
6
- #
7
- # However, jruby doesn't support forking, and patron doesn't work on jruby...
8
- # so we just leave Capybara::Server's definition of #boot.
9
- class VCR::LocalhostServer < Capybara::Server
10
-
11
- def boot
12
- return self unless @app
13
- find_available_port
14
- Capybara.log "application has already booted" and return self if responsive?
15
- Capybara.log "booting Rack applicartion on port #{port}"
16
-
17
- pid = Process.fork do
18
- trap(:INT) { Rack::Handler::WEBrick.shutdown }
19
- Rack::Handler::WEBrick.run(Identify.new(@app), :Port => port, :AccessLog => [], :Logger => WEBrick::BasicLog.new(StringIO.new))
20
- exit # manually exit; otherwise this sub-process will re-run the specs that haven't run yet.
21
- end
22
- Capybara.log "checking if application has booted"
23
-
24
- Capybara::WaitUntil.timeout(10) do
25
- if responsive?
26
- Capybara.log("application has booted")
27
- true
28
- else
29
- sleep 0.5
30
- false
1
+ require 'rack'
2
+ require 'rack/handler/webrick'
3
+
4
+ # The code for this is inspired by Capybara's server:
5
+ # http://github.com/jnicklas/capybara/blob/0.3.9/lib/capybara/server.rb
6
+ module VCR
7
+ class LocalhostServer
8
+ class Identify
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ if env["PATH_INFO"] == "/__identify__"
15
+ [200, {}, @app.object_id.to_s]
16
+ else
17
+ @app.call(env)
18
+ end
31
19
  end
32
20
  end
33
21
 
34
- at_exit do
35
- Process.kill('INT', pid)
36
- begin
37
- Process.wait(pid)
38
- rescue Errno::ECHILD
39
- # ignore this error...I think it means the child process has already exited.
22
+ attr_reader :port
23
+
24
+ def initialize(rack_app)
25
+ @port = find_available_port
26
+ @rack_app = rack_app
27
+ concurrently { boot }
28
+ wait_until(10, "Boot failed.") { booted? }
29
+ end
30
+
31
+ private
32
+
33
+ def find_available_port
34
+ server = TCPServer.new('127.0.0.1', 0)
35
+ server.addr[1]
36
+ ensure
37
+ server.close if server
38
+ end
39
+
40
+ def boot
41
+ # Use WEBrick since it's part of the ruby standard library and is available on all ruby interpreters.
42
+ Rack::Handler::WEBrick.run(Identify.new(@rack_app), :Port => port, :AccessLog => [], :Logger => WEBrick::BasicLog.new(StringIO.new))
43
+ end
44
+
45
+ def booted?
46
+ res = ::Net::HTTP.get_response("localhost", '/__identify__', port)
47
+
48
+ if res.is_a?(::Net::HTTPSuccess) or res.is_a?(::Net::HTTPRedirection)
49
+ return res.body == @rack_app.object_id.to_s
50
+ end
51
+ rescue Errno::ECONNREFUSED, Errno::EBADF
52
+ return false
53
+ end
54
+
55
+ def concurrently
56
+ if RUBY_INTERPRETER == :mri
57
+ # Patron times out when the server is running in a separate thread in the same process,
58
+ # so use a separate process.
59
+ pid = Process.fork do
60
+ yield
61
+ exit # manually exit; otherwise this sub-process will re-run the specs that haven't run yet.
62
+ end
63
+
64
+ at_exit do
65
+ Process.kill('INT', pid)
66
+ begin
67
+ Process.wait(pid)
68
+ rescue Errno::ECHILD
69
+ # ignore this error...I think it means the child process has already exited.
70
+ end
71
+ end
72
+ else
73
+ # JRuby doesn't support forking.
74
+ # Rubinius does, but there's a weird issue with the booted? check not working,
75
+ # so we're just using a thread for now.
76
+ Thread.new { yield }
40
77
  end
41
78
  end
42
79
 
43
- self
44
- rescue Timeout::Error
45
- Capybara.log "Rack application timed out during boot"
46
- exit
47
- end unless RUBY_PLATFORM =~ /java/
80
+ def wait_until(timeout, error_message, &block)
81
+ start_time = Time.now
48
82
 
49
- STATIC_SERVERS = Hash.new do |h, k|
50
- h[k] = server = new(lambda { |env| [200, {}, StringIO.new(k)] })
51
- server.boot
83
+ while true
84
+ return if yield
85
+ raise TimeoutError.new(error_message) if (Time.now - start_time) > timeout
86
+ sleep(0.05)
87
+ end
88
+ end
89
+
90
+ STATIC_SERVERS = Hash.new do |h, k|
91
+ h[k] = new(lambda { |env| [200, {}, StringIO.new(k)] })
92
+ end
52
93
  end
53
94
  end
95
+