vcr 1.3.3 → 1.4.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.
- data/.rspec +2 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +23 -4
- data/Guardfile +6 -0
- data/README.md +5 -4
- data/Rakefile +1 -3
- data/TODO.md +3 -0
- data/cucumber.yml +3 -3
- data/features/cassettes/no_cassette.feature +67 -0
- data/features/configuration/allow_http_connections_when_no_cassette.feature +54 -0
- data/features/configuration/ignore_localhost.feature +2 -2
- data/features/configuration/stub_with.feature +40 -16
- data/features/http_libraries/em_http_request.feature +217 -0
- data/features/middleware/faraday.feature +89 -0
- data/features/middleware/rack.feature +95 -0
- data/features/step_definitions/cli_steps.rb +7 -0
- data/lib/vcr.rb +48 -4
- data/lib/vcr/config.rb +11 -2
- data/lib/vcr/extensions/net_http.rb +4 -0
- data/lib/vcr/http_stubbing_adapters/common.rb +35 -4
- data/lib/vcr/http_stubbing_adapters/faraday.rb +80 -0
- data/lib/vcr/http_stubbing_adapters/typhoeus.rb +1 -1
- data/lib/vcr/http_stubbing_adapters/webmock.rb +18 -16
- data/lib/vcr/middleware/cassette_arguments.rb +18 -0
- data/lib/vcr/middleware/common.rb +22 -0
- data/lib/vcr/middleware/faraday.rb +79 -0
- data/lib/vcr/middleware/rack.rb +13 -0
- data/lib/vcr/request_matcher.rb +16 -1
- data/lib/vcr/version.rb +1 -1
- data/spec/config_spec.rb +27 -2
- data/spec/fixtures/1.9.1/fake_example.com_responses.yml +0 -29
- data/spec/fixtures/1.9.1/match_requests_on.yml +2 -2
- data/spec/fixtures/not_1.9.1/fake_example.com_responses.yml +0 -29
- data/spec/http_stubbing_adapters/faraday_spec.rb +84 -0
- data/spec/middleware/cassette_arguments_spec.rb +32 -0
- data/spec/middleware/faraday_spec.rb +52 -0
- data/spec/middleware/rack_spec.rb +54 -0
- data/spec/monkey_patches.rb +1 -0
- data/spec/request_matcher_spec.rb +36 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/http_library_adapters.rb +113 -25
- data/spec/support/http_stubbing_adapter.rb +55 -16
- data/spec/vcr_spec.rb +92 -4
- data/vcr.gemspec +1 -0
- metadata +72 -34
data/lib/vcr/config.rb
CHANGED
@@ -25,12 +25,21 @@ module VCR
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def ignore_localhost=(value)
|
28
|
-
VCR.http_stubbing_adapter && VCR.http_stubbing_adapter.ignore_localhost = value
|
29
28
|
@ignore_localhost = value
|
29
|
+
VCR.http_stubbing_adapter.ignore_localhost = value if http_stubbing_libraries.any?
|
30
30
|
end
|
31
31
|
|
32
32
|
def ignore_localhost?
|
33
|
-
|
33
|
+
@ignore_localhost
|
34
|
+
end
|
35
|
+
|
36
|
+
def allow_http_connections_when_no_cassette=(value)
|
37
|
+
@allow_http_connections_when_no_cassette = value
|
38
|
+
VCR.http_stubbing_adapter.set_http_connections_allowed_to_default if http_stubbing_libraries.any?
|
39
|
+
end
|
40
|
+
|
41
|
+
def allow_http_connections_when_no_cassette?
|
42
|
+
!!@allow_http_connections_when_no_cassette
|
34
43
|
end
|
35
44
|
end
|
36
45
|
end
|
@@ -4,6 +4,10 @@ require 'vcr/extensions/net_http_response'
|
|
4
4
|
module Net
|
5
5
|
class HTTP
|
6
6
|
def request_with_vcr(request, body = nil, &block)
|
7
|
+
unless VCR::HttpStubbingAdapters::FakeWeb.enabled?
|
8
|
+
return request_without_vcr(request, body, &block)
|
9
|
+
end
|
10
|
+
|
7
11
|
vcr_request = VCR::Request.from_net_http_request(self, request)
|
8
12
|
response = request_without_vcr(request, body)
|
9
13
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module VCR
|
2
2
|
module HttpStubbingAdapters
|
3
3
|
autoload :FakeWeb, 'vcr/http_stubbing_adapters/fakeweb'
|
4
|
+
autoload :Faraday, 'vcr/http_stubbing_adapters/faraday'
|
4
5
|
autoload :MultiObjectProxy, 'vcr/http_stubbing_adapters/multi_object_proxy'
|
5
6
|
autoload :Typhoeus, 'vcr/http_stubbing_adapters/typhoeus'
|
6
7
|
autoload :WebMock, 'vcr/http_stubbing_adapters/webmock'
|
@@ -8,17 +9,43 @@ module VCR
|
|
8
9
|
class UnsupportedRequestMatchAttributeError < ArgumentError; end
|
9
10
|
|
10
11
|
module Common
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
class << self
|
13
|
+
attr_accessor :exclusively_enabled_adapter
|
14
|
+
|
15
|
+
def add_vcr_info_to_exception_message(exception_klass)
|
16
|
+
exception_klass.class_eval do
|
17
|
+
def initialize(message)
|
18
|
+
super(message + '. ' + VCR::HttpStubbingAdapters::Common::RECORDING_INSTRUCTIONS)
|
19
|
+
end
|
15
20
|
end
|
16
21
|
end
|
22
|
+
|
23
|
+
def adapters
|
24
|
+
@adapters ||= []
|
25
|
+
end
|
26
|
+
|
27
|
+
def included(adapter)
|
28
|
+
adapters << adapter
|
29
|
+
end
|
17
30
|
end
|
18
31
|
|
19
32
|
RECORDING_INSTRUCTIONS = "You can use VCR to automatically record this request and replay it later. " +
|
20
33
|
"For more details, visit the VCR wiki at: http://github.com/myronmarston/vcr/wiki"
|
21
34
|
|
35
|
+
def enabled?
|
36
|
+
[nil, self].include? VCR::HttpStubbingAdapters::Common.exclusively_enabled_adapter
|
37
|
+
end
|
38
|
+
|
39
|
+
def exclusively_enabled
|
40
|
+
VCR::HttpStubbingAdapters::Common.exclusively_enabled_adapter = self
|
41
|
+
|
42
|
+
begin
|
43
|
+
yield
|
44
|
+
ensure
|
45
|
+
VCR::HttpStubbingAdapters::Common.exclusively_enabled_adapter = nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
22
49
|
def check_version!
|
23
50
|
version_too_low, version_too_high = compare_version
|
24
51
|
|
@@ -33,6 +60,10 @@ module VCR
|
|
33
60
|
@library_name ||= self.to_s.split('::').last
|
34
61
|
end
|
35
62
|
|
63
|
+
def set_http_connections_allowed_to_default
|
64
|
+
self.http_connections_allowed = VCR::Config.allow_http_connections_when_no_cassette?
|
65
|
+
end
|
66
|
+
|
36
67
|
private
|
37
68
|
|
38
69
|
def compare_version
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module VCR
|
4
|
+
module HttpStubbingAdapters
|
5
|
+
module Faraday
|
6
|
+
include Common
|
7
|
+
extend self
|
8
|
+
|
9
|
+
MINIMUM_VERSION = '0.5.3'
|
10
|
+
MAXIMUM_VERSION = '0.5'
|
11
|
+
|
12
|
+
attr_writer :http_connections_allowed, :ignore_localhost
|
13
|
+
|
14
|
+
def http_connections_allowed?
|
15
|
+
!!@http_connections_allowed
|
16
|
+
end
|
17
|
+
|
18
|
+
def ignore_localhost?
|
19
|
+
!!@ignore_localhost
|
20
|
+
end
|
21
|
+
|
22
|
+
def stub_requests(http_interactions, match_attributes)
|
23
|
+
grouped_responses(http_interactions, match_attributes).each do |request_matcher, responses|
|
24
|
+
queue = stub_queues[request_matcher]
|
25
|
+
responses.each { |res| queue << res }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_stubs_checkpoint(checkpoint_name)
|
30
|
+
checkpoints[checkpoint_name] = stub_queue_dup
|
31
|
+
end
|
32
|
+
|
33
|
+
def restore_stubs_checkpoint(checkpoint_name)
|
34
|
+
@stub_queues = checkpoints.delete(checkpoint_name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def stubbed_response_for(request_matcher)
|
38
|
+
queue = stub_queues[request_matcher]
|
39
|
+
return queue.shift if queue.size > 1
|
40
|
+
queue.first
|
41
|
+
end
|
42
|
+
|
43
|
+
def reset!
|
44
|
+
instance_variables.each do |ivar|
|
45
|
+
remove_instance_variable(ivar)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def version
|
52
|
+
::Faraday::VERSION
|
53
|
+
end
|
54
|
+
|
55
|
+
def checkpoints
|
56
|
+
@checkpoints ||= {}
|
57
|
+
end
|
58
|
+
|
59
|
+
def stub_queues
|
60
|
+
@stub_queues ||= hash_of_arrays
|
61
|
+
end
|
62
|
+
|
63
|
+
def stub_queue_dup
|
64
|
+
dup = hash_of_arrays
|
65
|
+
|
66
|
+
stub_queues.each do |k, v|
|
67
|
+
dup[k] = v.dup
|
68
|
+
end
|
69
|
+
|
70
|
+
dup
|
71
|
+
end
|
72
|
+
|
73
|
+
def hash_of_arrays
|
74
|
+
Hash.new { |h, k| h[k] = [] }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
VCR::HttpStubbingAdapters::Common.add_vcr_info_to_exception_message(VCR::Middleware::Faraday::HttpConnectionNotAllowedError)
|
@@ -74,7 +74,7 @@ module VCR
|
|
74
74
|
end
|
75
75
|
|
76
76
|
Typhoeus::Hydra.after_request_before_on_complete do |request|
|
77
|
-
|
77
|
+
if VCR::HttpStubbingAdapters::Typhoeus.enabled? && !request.response.mock?
|
78
78
|
http_interaction = VCR::HTTPInteraction.new(
|
79
79
|
VCR::Request.new(
|
80
80
|
request.method,
|
@@ -73,25 +73,27 @@ module VCR
|
|
73
73
|
end
|
74
74
|
|
75
75
|
WebMock.after_request(:real_requests_only => true) do |request, response|
|
76
|
-
|
77
|
-
VCR::
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
VCR::Response.new(
|
84
|
-
VCR::ResponseStatus.new(
|
85
|
-
response.status.first,
|
86
|
-
response.status.last
|
76
|
+
if VCR::HttpStubbingAdapters::WebMock.enabled?
|
77
|
+
http_interaction = VCR::HTTPInteraction.new(
|
78
|
+
VCR::Request.new(
|
79
|
+
request.method,
|
80
|
+
request.uri.to_s,
|
81
|
+
request.body,
|
82
|
+
request.headers
|
87
83
|
),
|
88
|
-
|
89
|
-
|
90
|
-
|
84
|
+
VCR::Response.new(
|
85
|
+
VCR::ResponseStatus.new(
|
86
|
+
response.status.first,
|
87
|
+
response.status.last
|
88
|
+
),
|
89
|
+
response.headers,
|
90
|
+
response.body,
|
91
|
+
'1.1'
|
92
|
+
)
|
91
93
|
)
|
92
|
-
)
|
93
94
|
|
94
|
-
|
95
|
+
VCR.record_http_interaction(http_interaction)
|
96
|
+
end
|
95
97
|
end
|
96
98
|
|
97
99
|
WebMock::NetConnectNotAllowedError.class_eval do
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module VCR
|
2
|
+
module Middleware
|
3
|
+
class CassetteArguments
|
4
|
+
def initialize
|
5
|
+
@options = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def name(name = nil)
|
9
|
+
@name = name if name
|
10
|
+
@name
|
11
|
+
end
|
12
|
+
|
13
|
+
def options(options = {})
|
14
|
+
@options.merge!(options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module VCR
|
2
|
+
module Middleware
|
3
|
+
module Common
|
4
|
+
def initialize(app, &block)
|
5
|
+
raise ArgumentError.new("You must provide a block to set the cassette options") unless block
|
6
|
+
@app, @cassette_arguments_block = app, block
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def cassette_arguments(env)
|
12
|
+
arguments = CassetteArguments.new
|
13
|
+
|
14
|
+
block_args = [arguments]
|
15
|
+
block_args << env unless @cassette_arguments_block.arity == 1
|
16
|
+
|
17
|
+
@cassette_arguments_block.call(*block_args)
|
18
|
+
[arguments.name, arguments.options]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module VCR
|
4
|
+
module Middleware
|
5
|
+
class Faraday < ::Faraday::Middleware
|
6
|
+
include Common
|
7
|
+
|
8
|
+
class HttpConnectionNotAllowedError < StandardError; end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
VCR::HttpStubbingAdapters::Faraday.exclusively_enabled do
|
12
|
+
VCR.use_cassette(*cassette_arguments(env)) do |cassette|
|
13
|
+
request = request_for(env)
|
14
|
+
request_matcher = request.matcher(cassette.match_requests_on)
|
15
|
+
|
16
|
+
if VCR::HttpStubbingAdapters::Faraday.ignore_localhost? && VCR::LOCALHOST_ALIASES.include?(URI.parse(request.uri).host)
|
17
|
+
@app.call(env)
|
18
|
+
elsif response = VCR::HttpStubbingAdapters::Faraday.stubbed_response_for(request_matcher)
|
19
|
+
env.update(
|
20
|
+
:status => response.status.code,
|
21
|
+
:response_headers => correctly_cased_headers(response.headers),
|
22
|
+
:body => response.body
|
23
|
+
)
|
24
|
+
|
25
|
+
env[:response].finish(env)
|
26
|
+
elsif VCR::HttpStubbingAdapters::Faraday.http_connections_allowed?
|
27
|
+
response = @app.call(env)
|
28
|
+
|
29
|
+
# Checking #enabled? isn't strictly needed, but conforms
|
30
|
+
# the Faraday adapter to the behavior of the other adapters
|
31
|
+
if VCR::HttpStubbingAdapters::Faraday.enabled?
|
32
|
+
VCR.record_http_interaction(VCR::HTTPInteraction.new(request, response_for(env)))
|
33
|
+
end
|
34
|
+
|
35
|
+
response
|
36
|
+
else
|
37
|
+
raise HttpConnectionNotAllowedError.new(
|
38
|
+
"Real HTTP connections are disabled. Request: #{request.method.inspect} #{request.uri}"
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def request_for(env)
|
48
|
+
VCR::Request.new(
|
49
|
+
env[:method],
|
50
|
+
env[:url].to_s,
|
51
|
+
env[:body],
|
52
|
+
env[:request_headers]
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def response_for(env)
|
57
|
+
response = env[:response]
|
58
|
+
|
59
|
+
VCR::Response.new(
|
60
|
+
VCR::ResponseStatus.new(response.status, nil),
|
61
|
+
response.headers,
|
62
|
+
response.body,
|
63
|
+
'1.1'
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def correctly_cased_headers(headers)
|
68
|
+
correctly_cased_hash = {}
|
69
|
+
|
70
|
+
headers.each do |key, value|
|
71
|
+
key = key.to_s.split('-').map { |segment| segment.capitalize }.join("-")
|
72
|
+
correctly_cased_hash[key] = value
|
73
|
+
end
|
74
|
+
|
75
|
+
correctly_cased_hash
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/vcr/request_matcher.rb
CHANGED
@@ -69,7 +69,22 @@ module VCR
|
|
69
69
|
# on Ruby 1.8.6, identical sets have different hash values,
|
70
70
|
# but identical arrays have the same hash values,
|
71
71
|
# so we convert match_attributes to an array here.
|
72
|
-
[match_attributes.to_a, method, uri,
|
72
|
+
[match_attributes.to_a, method, uri, sorted_header_array, body].hash
|
73
73
|
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def sorted_header_array
|
78
|
+
header_hash = headers
|
79
|
+
return header_hash unless header_hash.is_a?(Hash)
|
80
|
+
|
81
|
+
array = []
|
82
|
+
header_hash.each do |k, v|
|
83
|
+
v = v.sort if v.respond_to?(:sort)
|
84
|
+
array << [k, v]
|
85
|
+
end
|
86
|
+
|
87
|
+
array.sort! { |a1, a2| a1.first <=> a2.first }
|
88
|
+
end
|
74
89
|
end
|
75
90
|
end
|
data/lib/vcr/version.rb
CHANGED
data/spec/config_spec.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe VCR::Config do
|
4
|
+
def stub_no_http_stubbing_adapter
|
5
|
+
VCR.stub(:http_stubbing_adapter).and_raise(ArgumentError)
|
6
|
+
VCR::Config.stub(:http_stubbing_libraries).and_return([])
|
7
|
+
end
|
8
|
+
|
4
9
|
describe '#cassette_library_dir=' do
|
5
10
|
temp_dir(File.expand_path(File.dirname(__FILE__) + '/fixtures/config_spec'))
|
6
11
|
|
@@ -52,12 +57,32 @@ describe VCR::Config do
|
|
52
57
|
end
|
53
58
|
end
|
54
59
|
|
55
|
-
it 'stores the value even when VCR.http_stubbing_adapter is
|
56
|
-
|
60
|
+
it 'stores the value even when VCR.http_stubbing_adapter is not set' do
|
61
|
+
stub_no_http_stubbing_adapter
|
57
62
|
[true, false].each do |val|
|
58
63
|
VCR::Config.ignore_localhost = val
|
59
64
|
VCR::Config.ignore_localhost?.should == val
|
60
65
|
end
|
61
66
|
end
|
62
67
|
end
|
68
|
+
|
69
|
+
describe '#allow_http_connections_when_no_cassette=' do
|
70
|
+
[true, false].each do |val|
|
71
|
+
it "sets the allow_http_connections_when_no_cassette to #{val} when set to #{val}" do
|
72
|
+
VCR::Config.allow_http_connections_when_no_cassette = val
|
73
|
+
VCR::Config.allow_http_connections_when_no_cassette?.should == val
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'sets http_connnections_allowed to the default' do
|
78
|
+
VCR.http_stubbing_adapter.should respond_to(:set_http_connections_allowed_to_default)
|
79
|
+
VCR.http_stubbing_adapter.should_receive(:set_http_connections_allowed_to_default)
|
80
|
+
VCR::Config.allow_http_connections_when_no_cassette = true
|
81
|
+
end
|
82
|
+
|
83
|
+
it "works when the adapter hasn't been set yet" do
|
84
|
+
stub_no_http_stubbing_adapter
|
85
|
+
VCR::Config.allow_http_connections_when_no_cassette = true
|
86
|
+
end
|
87
|
+
end
|
63
88
|
end
|