vcr 1.5.1 → 1.6.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/CHANGELOG.md +11 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +4 -3
- data/benchmarks/http_stubbing_libraries.rb +4 -4
- data/features/.nav +1 -0
- data/features/configuration/ignore_hosts.feature +61 -0
- data/features/http_libraries/net_http.feature +34 -0
- data/features/step_definitions/cli_steps.rb +16 -1
- data/lib/vcr.rb +23 -14
- data/lib/vcr/cassette.rb +2 -4
- data/lib/vcr/config.rb +20 -5
- data/lib/vcr/deprecations.rb +27 -14
- data/lib/vcr/http_stubbing_adapters/fakeweb.rb +14 -9
- data/lib/vcr/http_stubbing_adapters/faraday.rb +12 -3
- data/lib/vcr/http_stubbing_adapters/typhoeus.rb +3 -7
- data/lib/vcr/http_stubbing_adapters/webmock.rb +17 -7
- data/lib/vcr/middleware/faraday.rb +1 -1
- data/lib/vcr/request_matcher.rb +12 -8
- data/lib/vcr/structs/http_interaction.rb +16 -0
- data/lib/vcr/structs/normalizers/body.rb +24 -0
- data/lib/vcr/structs/normalizers/header.rb +56 -0
- data/lib/vcr/structs/normalizers/status_message.rb +17 -0
- data/lib/vcr/structs/normalizers/uri.rb +34 -0
- data/lib/vcr/structs/request.rb +20 -0
- data/lib/vcr/structs/response.rb +16 -0
- data/lib/vcr/structs/response_status.rb +9 -0
- data/lib/vcr/util/regexes.rb +37 -0
- data/lib/vcr/version.rb +16 -5
- data/spec/spec_helper.rb +26 -3
- data/spec/support/http_library_adapters.rb +11 -12
- data/spec/support/http_stubbing_adapter.rb +2 -16
- data/spec/support/normalizers.rb +84 -0
- data/spec/support/version_checker.rb +1 -1
- data/spec/vcr/cassette_spec.rb +8 -10
- data/spec/vcr/config_spec.rb +63 -17
- data/spec/vcr/deprecations_spec.rb +83 -24
- data/spec/vcr/http_stubbing_adapters/multi_object_proxy_spec.rb +1 -1
- data/spec/vcr/http_stubbing_adapters/typhoeus_spec.rb +2 -2
- data/spec/vcr/middleware/faraday_spec.rb +1 -1
- data/spec/vcr/structs/http_interaction_spec.rb +23 -0
- data/spec/vcr/structs/request_spec.rb +54 -0
- data/spec/vcr/structs/response_spec.rb +39 -0
- data/spec/vcr/structs/response_status_spec.rb +18 -0
- data/spec/vcr_spec.rb +26 -54
- data/vcr.gemspec +1 -1
- metadata +48 -31
- data/TODO.md +0 -5
- data/lib/vcr/structs.rb +0 -176
- data/spec/vcr/structs_spec.rb +0 -201
@@ -9,14 +9,19 @@ module VCR
|
|
9
9
|
MINIMUM_VERSION = '0.5.3'
|
10
10
|
MAXIMUM_VERSION = '0.5'
|
11
11
|
|
12
|
-
attr_writer :http_connections_allowed
|
12
|
+
attr_writer :http_connections_allowed
|
13
13
|
|
14
14
|
def http_connections_allowed?
|
15
15
|
!!@http_connections_allowed
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
|
18
|
+
def ignored_hosts=(hosts)
|
19
|
+
@ignored_hosts = hosts
|
20
|
+
end
|
21
|
+
|
22
|
+
def uri_should_be_ignored?(uri)
|
23
|
+
uri = URI.parse(uri) unless uri.respond_to?(:host)
|
24
|
+
ignored_hosts.include?(uri.host)
|
20
25
|
end
|
21
26
|
|
22
27
|
def stub_requests(http_interactions, match_attributes)
|
@@ -52,6 +57,10 @@ module VCR
|
|
52
57
|
::Faraday::VERSION
|
53
58
|
end
|
54
59
|
|
60
|
+
def ignored_hosts
|
61
|
+
@ignored_hosts ||= []
|
62
|
+
end
|
63
|
+
|
55
64
|
def checkpoints
|
56
65
|
@checkpoints ||= {}
|
57
66
|
end
|
@@ -6,7 +6,7 @@ module VCR
|
|
6
6
|
include VCR::HttpStubbingAdapters::Common
|
7
7
|
extend self
|
8
8
|
|
9
|
-
MINIMUM_VERSION = '0.2.
|
9
|
+
MINIMUM_VERSION = '0.2.1'
|
10
10
|
MAXIMUM_VERSION = '0.2'
|
11
11
|
|
12
12
|
def http_connections_allowed=(value)
|
@@ -17,12 +17,8 @@ module VCR
|
|
17
17
|
!!::Typhoeus::Hydra.allow_net_connect?
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
::Typhoeus::Hydra.
|
22
|
-
end
|
23
|
-
|
24
|
-
def ignore_localhost?
|
25
|
-
!!::Typhoeus::Hydra.ignore_localhost?
|
20
|
+
def ignored_hosts=(hosts)
|
21
|
+
::Typhoeus::Hydra.ignore_hosts = hosts
|
26
22
|
end
|
27
23
|
|
28
24
|
def stub_requests(http_interactions, match_attributes)
|
@@ -10,19 +10,17 @@ module VCR
|
|
10
10
|
MAXIMUM_VERSION = '1.6'
|
11
11
|
|
12
12
|
def http_connections_allowed=(value)
|
13
|
-
|
13
|
+
@http_connections_allowed = value
|
14
|
+
update_webmock_allow_net_connect
|
14
15
|
end
|
15
16
|
|
16
17
|
def http_connections_allowed?
|
17
18
|
!!::WebMock::Config.instance.allow_net_connect
|
18
19
|
end
|
19
20
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
def ignore_localhost?
|
25
|
-
!!::WebMock::Config.instance.allow_localhost
|
21
|
+
def ignored_hosts=(hosts)
|
22
|
+
@ignored_hosts = hosts
|
23
|
+
update_webmock_allow_net_connect
|
26
24
|
end
|
27
25
|
|
28
26
|
def stub_requests(http_interactions, match_attributes)
|
@@ -50,6 +48,18 @@ module VCR
|
|
50
48
|
::WebMock.version
|
51
49
|
end
|
52
50
|
|
51
|
+
def ignored_hosts
|
52
|
+
@ignored_hosts ||= []
|
53
|
+
end
|
54
|
+
|
55
|
+
def update_webmock_allow_net_connect
|
56
|
+
if @http_connections_allowed
|
57
|
+
::WebMock.allow_net_connect!
|
58
|
+
else
|
59
|
+
::WebMock.disable_net_connect!(:allow => ignored_hosts)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
53
63
|
def request_signature_hash(request_matcher)
|
54
64
|
signature = {}
|
55
65
|
signature[:body] = request_matcher.body if request_matcher.match_requests_on?(:body)
|
@@ -13,7 +13,7 @@ module VCR
|
|
13
13
|
request = request_for(env)
|
14
14
|
request_matcher = request.matcher(cassette.match_requests_on)
|
15
15
|
|
16
|
-
if VCR::HttpStubbingAdapters::Faraday.
|
16
|
+
if VCR::HttpStubbingAdapters::Faraday.uri_should_be_ignored?(request.uri)
|
17
17
|
@app.call(env)
|
18
18
|
elsif response = VCR::HttpStubbingAdapters::Faraday.stubbed_response_for(request_matcher)
|
19
19
|
env.update(
|
data/lib/vcr/request_matcher.rb
CHANGED
@@ -20,21 +20,21 @@ module VCR
|
|
20
20
|
# for two sets of the same elements unless they are ordered
|
21
21
|
# the same, so we sort the attributes here.
|
22
22
|
attributes = attributes.sort { |a, b| a.to_s <=> b.to_s }
|
23
|
-
@match_attributes =
|
23
|
+
@match_attributes = set(attributes)
|
24
24
|
end
|
25
25
|
|
26
26
|
def uri
|
27
27
|
return request.uri unless request.uri.is_a?(String)
|
28
28
|
uri_matchers = match_attributes.to_a & [:uri, :host, :path]
|
29
29
|
|
30
|
-
case
|
31
|
-
when
|
32
|
-
when
|
33
|
-
when
|
34
|
-
when
|
35
|
-
when
|
30
|
+
case set(uri_matchers)
|
31
|
+
when set then /.*/
|
32
|
+
when set(:uri) then request.uri
|
33
|
+
when set(:host) then VCR::Regexes.url_regex_for_hosts([URI(request.uri).host])
|
34
|
+
when set(:path) then VCR::Regexes.url_regex_for_path(URI(request.uri).path)
|
35
|
+
when set(:host, :path)
|
36
36
|
uri = URI(request.uri)
|
37
|
-
|
37
|
+
VCR::Regexes.url_regex_for_host_and_path(uri.host, uri.path)
|
38
38
|
else raise ArgumentError.new("match_attributes cannot include #{uri_matchers.join(' and ')}")
|
39
39
|
end
|
40
40
|
end
|
@@ -74,6 +74,10 @@ module VCR
|
|
74
74
|
|
75
75
|
private
|
76
76
|
|
77
|
+
def set(*elements)
|
78
|
+
Set.new(elements.flatten)
|
79
|
+
end
|
80
|
+
|
77
81
|
def sorted_header_array
|
78
82
|
header_hash = headers
|
79
83
|
return header_hash unless header_hash.is_a?(Hash)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module VCR
|
4
|
+
class HTTPInteraction < Struct.new(:request, :response)
|
5
|
+
extend ::Forwardable
|
6
|
+
def_delegators :request, :uri, :method
|
7
|
+
|
8
|
+
def ignore!
|
9
|
+
@ignored = true
|
10
|
+
end
|
11
|
+
|
12
|
+
def ignored?
|
13
|
+
@ignored
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module VCR
|
2
|
+
module Normalizers
|
3
|
+
module Body
|
4
|
+
def initialize(*args)
|
5
|
+
super
|
6
|
+
normalize_body
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def normalize_body
|
12
|
+
# Ensure that the body is a raw string, in case the string instance
|
13
|
+
# has been subclassed or extended with additional instance variables
|
14
|
+
# or attributes, so that it is serialized to YAML as a raw string.
|
15
|
+
# This is needed for rest-client. See this ticket for more info:
|
16
|
+
# http://github.com/myronmarston/vcr/issues/4
|
17
|
+
self.body = case body
|
18
|
+
when nil, ''; nil
|
19
|
+
else String.new(body)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module VCR
|
2
|
+
module Normalizers
|
3
|
+
module Header
|
4
|
+
# These headers get added by the various HTTP clients automatically,
|
5
|
+
# and we don't care about them. We store the headers for the purposes
|
6
|
+
# of request matching, and we only care to match on headers users
|
7
|
+
# explicitly set.
|
8
|
+
HEADERS_TO_SKIP = {
|
9
|
+
'connection' => %w[ close Keep-Alive ],
|
10
|
+
'accept' => %w[ */* ],
|
11
|
+
'expect' => [''],
|
12
|
+
'user-agent' => ["Typhoeus - http://github.com/dbalatero/typhoeus/tree/master", 'Ruby']
|
13
|
+
}
|
14
|
+
|
15
|
+
def initialize(*args)
|
16
|
+
super
|
17
|
+
normalize_headers
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def important_header_values(k, values)
|
23
|
+
skip_values = HEADERS_TO_SKIP[k] || []
|
24
|
+
values - skip_values
|
25
|
+
end
|
26
|
+
|
27
|
+
def normalize_headers
|
28
|
+
new_headers = {}
|
29
|
+
|
30
|
+
headers.each do |k, v|
|
31
|
+
k = k.downcase
|
32
|
+
|
33
|
+
val_array = case v
|
34
|
+
when Array then v
|
35
|
+
when nil then []
|
36
|
+
else [v]
|
37
|
+
end
|
38
|
+
|
39
|
+
important_vals = important_header_values(k, val_array)
|
40
|
+
next unless important_vals.size > 0
|
41
|
+
|
42
|
+
# Ensure the values are raw strings.
|
43
|
+
# Apparently for Paperclip uploads to S3, headers
|
44
|
+
# get serialized with some extra stuff which leads
|
45
|
+
# to a seg fault. See this issue for more info:
|
46
|
+
# https://github.com/myronmarston/vcr/issues#issue/39
|
47
|
+
string_vals = important_vals.map { |v| String.new(v) }
|
48
|
+
|
49
|
+
new_headers[k] = string_vals
|
50
|
+
end if headers
|
51
|
+
|
52
|
+
self.headers = new_headers.empty? ? nil : new_headers
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module VCR
|
2
|
+
module Normalizers
|
3
|
+
module StatusMessage
|
4
|
+
def initialize(*args)
|
5
|
+
super
|
6
|
+
normalize_status_message
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def normalize_status_message
|
12
|
+
self.message = message.strip if message
|
13
|
+
self.message = nil if message == ''
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module VCR
|
2
|
+
module Normalizers
|
3
|
+
module URI
|
4
|
+
DEFAULT_PORTS = {
|
5
|
+
'http' => 80,
|
6
|
+
'https' => 443
|
7
|
+
}
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
super
|
11
|
+
normalize_uri
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def normalize_uri
|
17
|
+
u = begin
|
18
|
+
::URI.parse(uri)
|
19
|
+
rescue ::URI::InvalidURIError
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
u.port ||= DEFAULT_PORTS[u.scheme]
|
24
|
+
|
25
|
+
# URI#to_s only includes the port if it's not the default
|
26
|
+
# but we want to always include it (since FakeWeb/WebMock
|
27
|
+
# urls have always included it). We force it to be included
|
28
|
+
# here by redefining default_port so that URI#to_s will include it.
|
29
|
+
def u.default_port; nil; end
|
30
|
+
self.uri = u.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module VCR
|
2
|
+
class Request < Struct.new(:method, :uri, :body, :headers)
|
3
|
+
include Normalizers::Header
|
4
|
+
include Normalizers::URI
|
5
|
+
include Normalizers::Body
|
6
|
+
|
7
|
+
def self.from_net_http_request(net_http, request)
|
8
|
+
new(
|
9
|
+
request.method.downcase.to_sym,
|
10
|
+
VCR.http_stubbing_adapter.request_uri(net_http, request),
|
11
|
+
request.body,
|
12
|
+
request.to_hash
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def matcher(match_attributes)
|
17
|
+
RequestMatcher.new(self, match_attributes)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module VCR
|
2
|
+
class Response < Struct.new(:status, :headers, :body, :http_version)
|
3
|
+
include Normalizers::Header
|
4
|
+
include Normalizers::Body
|
5
|
+
|
6
|
+
def self.from_net_http_response(response)
|
7
|
+
new(
|
8
|
+
ResponseStatus.from_net_http_response(response),
|
9
|
+
response.to_hash,
|
10
|
+
response.body,
|
11
|
+
response.http_version
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module VCR
|
2
|
+
module Regexes
|
3
|
+
extend self
|
4
|
+
|
5
|
+
PROTOCOL = '\Ahttps?://'
|
6
|
+
CREDENTIALS = '((\w+:)?\w+@)?'
|
7
|
+
PORT = '(:\d+)?'
|
8
|
+
THE_REST = '/?(\?.*)?\z'
|
9
|
+
|
10
|
+
@@url_host_regexes = Hash.new do |hash, hosts|
|
11
|
+
hash[hosts] = begin
|
12
|
+
host_regex = hosts.map { |h| Regexp.escape(h) }.join('|')
|
13
|
+
%r|#{PROTOCOL}#{CREDENTIALS}(#{host_regex})#{PORT}/|i
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def url_regex_for_hosts(hosts)
|
18
|
+
@@url_host_regexes[hosts.sort]
|
19
|
+
end
|
20
|
+
|
21
|
+
@@url_path_regexes = Hash.new do |hash, path|
|
22
|
+
%r|#{PROTOCOL}[^/]+#{Regexp.escape(path)}#{THE_REST}|i
|
23
|
+
end
|
24
|
+
|
25
|
+
def url_regex_for_path(path)
|
26
|
+
@@url_path_regexes[path]
|
27
|
+
end
|
28
|
+
|
29
|
+
@@url_host_and_path_regexes = Hash.new do |hash, (host, path)|
|
30
|
+
%r|#{PROTOCOL}#{CREDENTIALS}#{Regexp.escape(host)}#{PORT}#{Regexp.escape(path)}#{THE_REST}|i
|
31
|
+
end
|
32
|
+
|
33
|
+
def url_regex_for_host_and_path(host, path)
|
34
|
+
@@url_host_and_path_regexes[[host, path]]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/vcr/version.rb
CHANGED
@@ -3,12 +3,23 @@ module VCR
|
|
3
3
|
|
4
4
|
def version
|
5
5
|
@version ||= begin
|
6
|
-
string = '1.
|
6
|
+
string = '1.6.0'
|
7
7
|
|
8
|
-
def string.parts
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def string.parts
|
9
|
+
split('.').map { |p| p.to_i }
|
10
|
+
end
|
11
|
+
|
12
|
+
def string.major
|
13
|
+
parts[0]
|
14
|
+
end
|
15
|
+
|
16
|
+
def string.minor
|
17
|
+
parts[1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def string.patch
|
21
|
+
parts[2]
|
22
|
+
end
|
12
23
|
|
13
24
|
string
|
14
25
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -14,6 +14,25 @@ require 'monkey_patches'
|
|
14
14
|
|
15
15
|
module VCR
|
16
16
|
SPEC_ROOT = File.dirname(__FILE__)
|
17
|
+
|
18
|
+
module Config
|
19
|
+
def reset!(stubbing_lib = :fakeweb)
|
20
|
+
self.default_cassette_options = { :record => :new_episodes }
|
21
|
+
|
22
|
+
if stubbing_lib
|
23
|
+
stub_with stubbing_lib
|
24
|
+
else
|
25
|
+
http_stubbing_libraries.clear
|
26
|
+
end
|
27
|
+
|
28
|
+
clear_hooks
|
29
|
+
@ignored_hosts = []
|
30
|
+
|
31
|
+
VCR.instance_eval do
|
32
|
+
instance_variables.each { |ivar| remove_instance_variable(ivar) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
17
36
|
end
|
18
37
|
|
19
38
|
RSpec.configure do |config|
|
@@ -29,9 +48,7 @@ RSpec.configure do |config|
|
|
29
48
|
VCR.turn_on! unless VCR.turned_on?
|
30
49
|
VCR.eject_cassette while VCR.current_cassette
|
31
50
|
|
32
|
-
VCR::Config.
|
33
|
-
VCR::Config.stub_with :fakeweb
|
34
|
-
VCR::Config.clear_hooks
|
51
|
+
VCR::Config.reset!
|
35
52
|
|
36
53
|
WebMock.allow_net_connect!
|
37
54
|
WebMock.reset!
|
@@ -42,6 +59,12 @@ RSpec.configure do |config|
|
|
42
59
|
VCR::HttpStubbingAdapters::Faraday.reset!
|
43
60
|
end
|
44
61
|
|
62
|
+
config.after(:each) do
|
63
|
+
VCR::HttpStubbingAdapters::Common.adapters.each do |a|
|
64
|
+
a.ignored_hosts = []
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
45
68
|
config.filter_run :focus => true
|
46
69
|
config.run_all_when_everything_filtered = true
|
47
70
|
|