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.
Files changed (49) hide show
  1. data/CHANGELOG.md +11 -1
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +4 -3
  4. data/benchmarks/http_stubbing_libraries.rb +4 -4
  5. data/features/.nav +1 -0
  6. data/features/configuration/ignore_hosts.feature +61 -0
  7. data/features/http_libraries/net_http.feature +34 -0
  8. data/features/step_definitions/cli_steps.rb +16 -1
  9. data/lib/vcr.rb +23 -14
  10. data/lib/vcr/cassette.rb +2 -4
  11. data/lib/vcr/config.rb +20 -5
  12. data/lib/vcr/deprecations.rb +27 -14
  13. data/lib/vcr/http_stubbing_adapters/fakeweb.rb +14 -9
  14. data/lib/vcr/http_stubbing_adapters/faraday.rb +12 -3
  15. data/lib/vcr/http_stubbing_adapters/typhoeus.rb +3 -7
  16. data/lib/vcr/http_stubbing_adapters/webmock.rb +17 -7
  17. data/lib/vcr/middleware/faraday.rb +1 -1
  18. data/lib/vcr/request_matcher.rb +12 -8
  19. data/lib/vcr/structs/http_interaction.rb +16 -0
  20. data/lib/vcr/structs/normalizers/body.rb +24 -0
  21. data/lib/vcr/structs/normalizers/header.rb +56 -0
  22. data/lib/vcr/structs/normalizers/status_message.rb +17 -0
  23. data/lib/vcr/structs/normalizers/uri.rb +34 -0
  24. data/lib/vcr/structs/request.rb +20 -0
  25. data/lib/vcr/structs/response.rb +16 -0
  26. data/lib/vcr/structs/response_status.rb +9 -0
  27. data/lib/vcr/util/regexes.rb +37 -0
  28. data/lib/vcr/version.rb +16 -5
  29. data/spec/spec_helper.rb +26 -3
  30. data/spec/support/http_library_adapters.rb +11 -12
  31. data/spec/support/http_stubbing_adapter.rb +2 -16
  32. data/spec/support/normalizers.rb +84 -0
  33. data/spec/support/version_checker.rb +1 -1
  34. data/spec/vcr/cassette_spec.rb +8 -10
  35. data/spec/vcr/config_spec.rb +63 -17
  36. data/spec/vcr/deprecations_spec.rb +83 -24
  37. data/spec/vcr/http_stubbing_adapters/multi_object_proxy_spec.rb +1 -1
  38. data/spec/vcr/http_stubbing_adapters/typhoeus_spec.rb +2 -2
  39. data/spec/vcr/middleware/faraday_spec.rb +1 -1
  40. data/spec/vcr/structs/http_interaction_spec.rb +23 -0
  41. data/spec/vcr/structs/request_spec.rb +54 -0
  42. data/spec/vcr/structs/response_spec.rb +39 -0
  43. data/spec/vcr/structs/response_status_spec.rb +18 -0
  44. data/spec/vcr_spec.rb +26 -54
  45. data/vcr.gemspec +1 -1
  46. metadata +48 -31
  47. data/TODO.md +0 -5
  48. data/lib/vcr/structs.rb +0 -176
  49. 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, :ignore_localhost
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 ignore_localhost?
19
- !!@ignore_localhost
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.0'
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 ignore_localhost=(value)
21
- ::Typhoeus::Hydra.ignore_localhost = value
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
- ::WebMock::Config.instance.allow_net_connect = value
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 ignore_localhost=(value)
21
- ::WebMock::Config.instance.allow_localhost = value
22
- end
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.ignore_localhost? && VCR::LOCALHOST_ALIASES.include?(URI.parse(request.uri).host)
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(
@@ -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 = Set.new(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 Set.new(uri_matchers)
31
- when Set.new then /.*/
32
- when Set.new([:uri]) then request.uri
33
- when Set.new([:host]) then %r{\Ahttps?://((\w+:)?\w+@)?#{Regexp.escape(URI(request.uri).host)}(:\d+)?/}i
34
- when Set.new([:path]) then %r{\Ahttps?://[^/]+#{Regexp.escape(URI(request.uri).path)}/?(\?.*)?\z}i
35
- when Set.new([:host, :path])
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
- %r{\Ahttps?://((\w+:)?\w+@)?#{Regexp.escape(uri.host)}(:\d+)?#{Regexp.escape(uri.path)}/?(\?.*)?\z}i
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,9 @@
1
+ module VCR
2
+ class ResponseStatus < Struct.new(:code, :message)
3
+ include Normalizers::StatusMessage
4
+
5
+ def self.from_net_http_response(response)
6
+ new(response.code.to_i, response.message)
7
+ end
8
+ end
9
+ end
@@ -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
@@ -3,12 +3,23 @@ module VCR
3
3
 
4
4
  def version
5
5
  @version ||= begin
6
- string = '1.5.1'
6
+ string = '1.6.0'
7
7
 
8
- def string.parts; VCR.version.split('.').map { |p| p.to_i }; end
9
- def string.major; parts[0]; end
10
- def string.minor; parts[1]; end
11
- def string.patch; parts[2]; end
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
@@ -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.default_cassette_options = { :record => :new_episodes }
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