vcr 1.1.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/.gitmodules +3 -0
- data/CHANGELOG.md +58 -1
- data/Gemfile +2 -4
- data/Gemfile.lock +26 -49
- data/README.md +92 -23
- data/Rakefile +6 -3
- data/features/fixtures/vcr_cassettes/1.9.1/record_all.yml +62 -0
- data/features/fixtures/vcr_cassettes/not_1.9.1/record_all.yml +61 -0
- data/features/http_client.feature +2 -2
- data/features/net_http.feature +15 -4
- data/features/record_response.feature +36 -7
- data/features/replay_recorded_response.feature +8 -8
- data/features/rspec.feature +100 -0
- data/features/step_definitions/net_http_steps.rb +13 -1
- data/features/step_definitions/vcr_steps.rb +26 -16
- data/features/support/env.rb +13 -5
- data/features/webmock.feature +4 -4
- data/lib/vcr.rb +5 -5
- data/lib/vcr/cassette.rb +55 -19
- data/lib/vcr/extensions/net_http.rb +2 -4
- data/lib/vcr/http_stubbing_adapters/fakeweb.rb +0 -4
- data/lib/vcr/http_stubbing_adapters/webmock.rb +2 -9
- data/lib/vcr/internet_connection.rb +13 -0
- data/lib/vcr/ping.rb +26 -0
- data/lib/vcr/rspec.rb +39 -0
- data/lib/vcr/version.rb +1 -1
- data/spec/cassette_spec.rb +160 -69
- data/spec/config_spec.rb +1 -1
- data/spec/cucumber_tags_spec.rb +3 -2
- data/spec/deprecations_spec.rb +1 -1
- data/spec/extensions/net_http_response_spec.rb +2 -4
- data/spec/extensions/net_http_spec.rb +3 -1
- data/spec/fixtures/1.9.1/cassette_spec/with_localhost_requests.yml +23 -0
- data/spec/fixtures/not_1.9.1/cassette_spec/with_localhost_requests.yml +23 -0
- data/spec/http_stubbing_adapters/fakeweb_spec.rb +3 -9
- data/spec/http_stubbing_adapters/webmock_spec.rb +7 -18
- data/spec/internet_connection_spec.rb +19 -0
- data/spec/monkey_patches.rb +45 -6
- data/spec/request_matcher_spec.rb +1 -1
- data/spec/rspec_spec.rb +46 -0
- data/spec/spec_helper.rb +6 -11
- data/spec/structs_spec.rb +1 -1
- data/spec/support/fixnum_extension.rb +10 -0
- data/spec/support/http_library_adapters.rb +31 -2
- data/spec/support/ruby_interpreter.rb +7 -0
- data/spec/support/vcr_localhost_server.rb +86 -44
- data/spec/support/webmock_macros.rb +14 -0
- data/spec/vcr_spec.rb +1 -1
- data/spec/version_spec.rb +1 -1
- data/vcr.gemspec +10 -6
- 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
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe VCR::HttpStubbingAdapters::FakeWeb do
|
4
|
-
|
5
|
-
|
6
|
-
describe '#should_unwind_response?' do
|
7
|
-
let(:response) { ::Net::HTTPOK.new('1.1', 200, 'OK') }
|
4
|
+
without_webmock_callbacks
|
8
5
|
|
9
|
-
|
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
|
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.
|
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.
|
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.
|
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
|
data/spec/monkey_patches.rb
CHANGED
@@ -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
|
-
|
15
|
-
|
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
|
-
|
21
|
-
|
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
|
data/spec/rspec_spec.rb
ADDED
@@ -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 =
|
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
@@ -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) &&
|
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}")
|
@@ -1,53 +1,95 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
44
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
+
|