vcr 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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