vcr 2.0.0.beta2 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +0 -2
  3. data/CHANGELOG.md +22 -1
  4. data/CONTRIBUTING.md +46 -0
  5. data/Gemfile +1 -9
  6. data/README.md +8 -2
  7. data/Rakefile +12 -1
  8. data/cucumber.yml +8 -9
  9. data/features/.nav +10 -4
  10. data/features/cassettes/format.feature +1 -1
  11. data/features/cassettes/no_cassette.feature +8 -11
  12. data/features/configuration/allow_http_connections_when_no_cassette.feature +4 -4
  13. data/features/configuration/filter_sensitive_data.feature +3 -0
  14. data/features/configuration/hook_into.feature +4 -8
  15. data/features/configuration/ignore_request.feature +191 -0
  16. data/features/getting_started.md +38 -21
  17. data/features/hooks/after_http_request.feature +44 -0
  18. data/features/hooks/around_http_request.feature +56 -0
  19. data/features/hooks/before_http_request.feature +44 -0
  20. data/features/hooks/before_playback.feature +181 -0
  21. data/features/hooks/before_record.feature +172 -0
  22. data/features/middleware/faraday.feature +7 -3
  23. data/features/record_modes/none.feature +2 -1
  24. data/features/record_modes/once.feature +2 -1
  25. data/features/request_matching/body.feature +2 -2
  26. data/features/request_matching/custom_matcher.feature +2 -2
  27. data/features/request_matching/headers.feature +2 -2
  28. data/features/request_matching/host.feature +2 -2
  29. data/features/request_matching/identical_request_sequence.feature +2 -2
  30. data/features/request_matching/method.feature +2 -2
  31. data/features/request_matching/path.feature +2 -2
  32. data/features/request_matching/playback_repeats.feature +2 -1
  33. data/features/request_matching/uri.feature +2 -2
  34. data/features/support/env.rb +21 -12
  35. data/features/test_frameworks/cucumber.feature +9 -4
  36. data/features/test_frameworks/{rspec.feature → rspec_macro.feature} +7 -7
  37. data/features/test_frameworks/rspec_metadata.feature +90 -0
  38. data/lib/vcr.rb +1 -1
  39. data/lib/vcr/cassette.rb +3 -3
  40. data/lib/vcr/cassette/http_interaction_list.rb +13 -9
  41. data/lib/vcr/cassette/migrator.rb +1 -1
  42. data/lib/vcr/configuration.rb +37 -0
  43. data/lib/vcr/errors.rb +172 -6
  44. data/lib/vcr/library_hooks.rb +4 -6
  45. data/lib/vcr/library_hooks/excon.rb +23 -11
  46. data/lib/vcr/library_hooks/fakeweb.rb +85 -24
  47. data/lib/vcr/library_hooks/faraday.rb +30 -2
  48. data/lib/vcr/library_hooks/typhoeus.rb +25 -3
  49. data/lib/vcr/library_hooks/webmock.rb +25 -36
  50. data/lib/vcr/middleware/faraday.rb +23 -5
  51. data/lib/vcr/request_handler.rb +12 -1
  52. data/lib/vcr/request_ignorer.rb +12 -1
  53. data/lib/vcr/request_matcher_registry.rb +1 -9
  54. data/lib/vcr/structs.rb +32 -2
  55. data/lib/vcr/test_frameworks/rspec.rb +28 -0
  56. data/lib/vcr/util/hooks.rb +12 -4
  57. data/lib/vcr/util/version_checker.rb +2 -0
  58. data/lib/vcr/version.rb +1 -1
  59. data/spec/fixtures/cassette_spec/example.yml +1 -1
  60. data/spec/fixtures/{fake_example.com_responses.yml → fake_example_responses.yml} +0 -0
  61. data/spec/monkey_patches.rb +1 -1
  62. data/spec/spec_helper.rb +3 -1
  63. data/spec/support/http_library_adapters.rb +4 -3
  64. data/spec/support/shared_example_groups/hook_into_http_library.rb +194 -12
  65. data/spec/support/shared_example_groups/request_hooks.rb +58 -0
  66. data/spec/support/shared_example_groups/version_checking.rb +5 -0
  67. data/spec/support/sinatra_app.rb +17 -9
  68. data/spec/support/vcr_stub_helpers.rb +17 -0
  69. data/spec/vcr/cassette/http_interaction_list_spec.rb +28 -29
  70. data/spec/vcr/cassette/migrator_spec.rb +6 -7
  71. data/spec/vcr/cassette_spec.rb +5 -5
  72. data/spec/vcr/configuration_spec.rb +51 -32
  73. data/spec/vcr/deprecations_spec.rb +0 -8
  74. data/spec/vcr/errors_spec.rb +129 -0
  75. data/spec/vcr/library_hooks/excon_spec.rb +21 -4
  76. data/spec/vcr/library_hooks/fakeweb_spec.rb +71 -3
  77. data/spec/vcr/library_hooks/faraday_spec.rb +45 -0
  78. data/spec/vcr/library_hooks/typhoeus_spec.rb +31 -1
  79. data/spec/vcr/library_hooks/webmock_spec.rb +26 -3
  80. data/spec/vcr/middleware/faraday_spec.rb +84 -1
  81. data/spec/vcr/request_ignorer_spec.rb +16 -0
  82. data/spec/vcr/request_matcher_registry_spec.rb +0 -26
  83. data/spec/vcr/structs_spec.rb +61 -1
  84. data/spec/vcr/test_frameworks/rspec_spec.rb +32 -0
  85. data/spec/vcr/util/hooks_spec.rb +73 -63
  86. data/spec/vcr_spec.rb +2 -2
  87. data/vcr.gemspec +5 -5
  88. metadata +51 -31
  89. data/features/configuration/hooks.feature +0 -270
  90. data/features/configuration/ignore_hosts.feature +0 -61
  91. data/features/configuration/ignore_localhost.feature +0 -97
@@ -1,3 +1,31 @@
1
- Kernel.warn "WARNING: `VCR.config { |c| c.stub_with :faraday }` is deprecated. " +
2
- "Just use `VCR::Middleware::Faraday` in your faraday stack."
1
+ require 'faraday'
2
+
3
+ module VCR
4
+ class LibraryHooks
5
+ module Faraday
6
+ module BuilderClassExtension
7
+ def new(*args)
8
+ super.extend BuilderInstanceExtension
9
+ end
10
+
11
+ ::Faraday::Builder.extend self
12
+ end
13
+
14
+ module BuilderInstanceExtension
15
+ def lock!(*args)
16
+ insert_vcr_middleware
17
+ super
18
+ end
19
+
20
+ private
21
+
22
+ def insert_vcr_middleware
23
+ return if handlers.any? { |h| h.klass == VCR::Middleware::Faraday }
24
+ adapter_index = handlers.index { |h| h.klass < ::Faraday::Adapter }
25
+ insert_before(adapter_index, VCR::Middleware::Faraday)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
3
31
 
@@ -35,6 +35,11 @@ module VCR
35
35
 
36
36
  private
37
37
 
38
+ def on_connection_not_allowed
39
+ invoke_after_request_hook(nil)
40
+ super
41
+ end
42
+
38
43
  def vcr_request
39
44
  @vcr_request ||= vcr_request_from(request)
40
45
  end
@@ -59,9 +64,15 @@ module VCR
59
64
 
60
65
  extend Helpers
61
66
  ::Typhoeus::Hydra.after_request_before_on_complete do |request|
62
- unless VCR.library_hooks.disabled?(:typhoeus) || request.response.mock?
63
- http_interaction = VCR::HTTPInteraction.new(vcr_request_from(request), vcr_response_from(request.response))
64
- VCR.record_http_interaction(http_interaction)
67
+ unless VCR.library_hooks.disabled?(:typhoeus)
68
+ vcr_request, vcr_response = vcr_request_from(request), vcr_response_from(request.response)
69
+
70
+ unless request.response.mock?
71
+ http_interaction = VCR::HTTPInteraction.new(vcr_request, vcr_response)
72
+ VCR.record_http_interaction(http_interaction)
73
+ end
74
+
75
+ VCR.configuration.invoke_hook(:after_http_request, vcr_request, vcr_response)
65
76
  end
66
77
  end
67
78
 
@@ -72,6 +83,17 @@ module VCR
72
83
  end
73
84
  end
74
85
 
86
+ class << Typhoeus::Hydra
87
+ # ensure HTTP requests are always allowed; VCR takes care of disallowing
88
+ # them at the appropriate times in its hook
89
+ def allow_net_connect_with_vcr?(*args)
90
+ VCR.turned_on? ? true : allow_net_connect_without_vcr?
91
+ end
92
+
93
+ alias allow_net_connect_without_vcr? allow_net_connect?
94
+ alias allow_net_connect? allow_net_connect_with_vcr?
95
+ end unless Typhoeus::Hydra.respond_to?(:allow_net_connect_with_vcr?)
96
+
75
97
  VCR.configuration.after_library_hooks_loaded do
76
98
  # ensure WebMock's Typhoeus adapter does not conflict with us here
77
99
  # (i.e. to double record requests or whatever).
@@ -2,20 +2,12 @@ require 'vcr/util/version_checker'
2
2
  require 'vcr/request_handler'
3
3
  require 'webmock'
4
4
 
5
- VCR::VersionChecker.new('WebMock', WebMock.version, '1.7.0', '1.7').check_version!
5
+ VCR::VersionChecker.new('WebMock', WebMock.version, '1.7.8', '1.7').check_version!
6
6
 
7
7
  module VCR
8
8
  class LibraryHooks
9
9
  module WebMock
10
10
  module Helpers
11
- def response_hash_for(response)
12
- {
13
- :body => response.body,
14
- :status => [response.status.code.to_i, response.status.message],
15
- :headers => response.headers
16
- }
17
- end
18
-
19
11
  def vcr_request_from(webmock_request)
20
12
  VCR::Request.new \
21
13
  webmock_request.method,
@@ -43,30 +35,27 @@ module VCR
43
35
 
44
36
  private
45
37
 
46
- def stubbed_response
47
- VCR.http_interactions.has_interaction_matching?(vcr_request)
48
- end
49
-
50
38
  def vcr_request
51
39
  @vcr_request ||= vcr_request_from(request)
52
40
  end
53
41
 
54
- def on_ignored_request; false; end
55
- def on_stubbed_request; true; end
56
- def on_recordable_request; false; end
42
+ def on_connection_not_allowed
43
+ invoke_after_request_hook(nil)
44
+ super
45
+ end
46
+
47
+ def on_stubbed_request
48
+ {
49
+ :body => stubbed_response.body,
50
+ :status => [stubbed_response.status.code.to_i, stubbed_response.status.message],
51
+ :headers => stubbed_response.headers
52
+ }
53
+ end
57
54
  end
58
55
 
59
56
  extend Helpers
60
57
 
61
- GLOBAL_VCR_HOOK = ::WebMock::RequestStub.new(:any, /.*/).tap do |stub|
62
- stub.with { |request|
63
- RequestHandler.new(request).handle
64
- }.to_return(lambda { |request|
65
- response_hash_for VCR.http_interactions.response_for(vcr_request_from(request))
66
- })
67
- end
68
-
69
- ::WebMock::StubRegistry.instance.register_request_stub(GLOBAL_VCR_HOOK)
58
+ ::WebMock.globally_stub_request { |req| RequestHandler.new(req).handle }
70
59
 
71
60
  ::WebMock.after_request(:real_requests_only => true) do |request, response|
72
61
  unless VCR.library_hooks.disabled?(:webmock)
@@ -77,6 +66,12 @@ module VCR
77
66
  VCR.record_http_interaction(http_interaction)
78
67
  end
79
68
  end
69
+
70
+ ::WebMock.after_request do |request, response|
71
+ unless VCR.library_hooks.disabled?(:webmock)
72
+ VCR.configuration.invoke_hook(:after_http_request, vcr_request_from(request), vcr_response_from(response))
73
+ end
74
+ end
80
75
  end
81
76
  end
82
77
  end
@@ -84,17 +79,11 @@ end
84
79
  class << WebMock
85
80
  # ensure HTTP requests are always allowed; VCR takes care of disallowing
86
81
  # them at the appropriate times in its hook
87
- undef net_connect_allowed?
88
- def net_connect_allowed?(*args)
89
- true
82
+ def net_connect_allowed_with_vcr?(*args)
83
+ VCR.turned_on? ? true : net_connect_allowed_without_vcr?
90
84
  end
91
- end
92
85
 
93
- WebMock::StubRegistry.class_eval do
94
- # ensure our VCR hook is not removed when WebMock is reset
95
- undef reset!
96
- def reset!
97
- self.request_stubs = [VCR::LibraryHooks::WebMock::GLOBAL_VCR_HOOK]
98
- end
99
- end
86
+ alias net_connect_allowed_without_vcr? net_connect_allowed?
87
+ alias net_connect_allowed? net_connect_allowed_with_vcr?
88
+ end unless WebMock.respond_to?(:net_connect_allowed_with_vcr?)
100
89
 
@@ -15,21 +15,31 @@ module VCR
15
15
  end
16
16
 
17
17
  def call(env)
18
- # Faraday must be exlusive here in case another library hook is being used.
19
- # We don't want double recording/double playback.
20
- VCR.library_hooks.exclusively_enabled(:faraday) do
21
- RequestHandler.new(@app, env).handle
22
- end
18
+ RequestHandler.new(@app, env).handle
23
19
  end
24
20
 
25
21
  class RequestHandler < ::VCR::RequestHandler
26
22
  attr_reader :app, :env
27
23
  def initialize(app, env)
28
24
  @app, @env = app, env
25
+ @has_on_complete_hook = false
26
+ end
27
+
28
+ def handle
29
+ # Faraday must be exlusive here in case another library hook is being used.
30
+ # We don't want double recording/double playback.
31
+ VCR.library_hooks.exclusive_hook = :faraday
32
+ super
33
+ ensure
34
+ invoke_after_request_hook(response_for(env)) unless delay_finishing?
29
35
  end
30
36
 
31
37
  private
32
38
 
39
+ def delay_finishing?
40
+ !!env[:parallel_manager] && @has_on_complete_hook
41
+ end
42
+
33
43
  def vcr_request
34
44
  @vcr_request ||= VCR::Request.new \
35
45
  env[:method],
@@ -40,6 +50,7 @@ module VCR
40
50
 
41
51
  def response_for(env)
42
52
  response = env[:response]
53
+ return nil unless response
43
54
 
44
55
  VCR::Response.new(
45
56
  VCR::ResponseStatus.new(response.status, nil),
@@ -64,10 +75,17 @@ module VCR
64
75
  end
65
76
 
66
77
  def on_recordable_request
78
+ @has_on_complete_hook = true
67
79
  app.call(env).on_complete do |env|
68
80
  VCR.record_http_interaction(VCR::HTTPInteraction.new(vcr_request, response_for(env)))
81
+ invoke_after_request_hook(response_for(env)) if delay_finishing?
69
82
  end
70
83
  end
84
+
85
+ def invoke_after_request_hook(response)
86
+ super
87
+ VCR.library_hooks.exclusive_hook = nil
88
+ end
71
89
  end
72
90
  end
73
91
  end
@@ -1,6 +1,7 @@
1
1
  module VCR
2
2
  class RequestHandler
3
3
  def handle
4
+ invoke_before_request_hook
4
5
  return on_ignored_request if should_ignore?
5
6
  return on_stubbed_request if stubbed_response
6
7
  return on_recordable_request if VCR.real_http_connections_allowed?
@@ -9,6 +10,16 @@ module VCR
9
10
 
10
11
  private
11
12
 
13
+ def invoke_before_request_hook
14
+ return if disabled?
15
+ VCR.configuration.invoke_hook(:before_http_request, vcr_request)
16
+ end
17
+
18
+ def invoke_after_request_hook(vcr_response)
19
+ return if disabled?
20
+ VCR.configuration.invoke_hook(:after_http_request, vcr_request, vcr_response)
21
+ end
22
+
12
23
  def should_ignore?
13
24
  disabled? || VCR.request_ignorer.ignore?(vcr_request)
14
25
  end
@@ -37,7 +48,7 @@ module VCR
37
48
  end
38
49
 
39
50
  def on_connection_not_allowed
40
- raise VCR::HTTPConnectionNotAllowedError.new(vcr_request)
51
+ raise VCR::Errors::UnhandledHTTPRequestError.new(vcr_request)
41
52
  end
42
53
  end
43
54
  end
@@ -1,10 +1,21 @@
1
1
  require 'uri'
2
2
  require 'set'
3
+ require 'vcr/util/hooks'
3
4
 
4
5
  module VCR
5
6
  class RequestIgnorer
7
+ include VCR::Hooks
8
+
9
+ define_hook :ignore_request
10
+
6
11
  LOCALHOST_ALIASES = %w( localhost 127.0.0.1 0.0.0.0 )
7
12
 
13
+ def initialize
14
+ ignore_request do |request|
15
+ ignored_hosts.include?(URI(request.uri).host)
16
+ end
17
+ end
18
+
8
19
  def ignore_localhost=(value)
9
20
  if value
10
21
  ignore_hosts(*LOCALHOST_ALIASES)
@@ -18,7 +29,7 @@ module VCR
18
29
  end
19
30
 
20
31
  def ignore?(request)
21
- ignored_hosts.include?(URI(request.uri).host)
32
+ invoke_hook(:ignore_request, request).any?
22
33
  end
23
34
 
24
35
  private
@@ -80,20 +80,12 @@ module VCR
80
80
 
81
81
  def register_built_ins
82
82
  register(:method) { |r1, r2| r1.method == r2.method }
83
- register(:uri) { |r1, r2| without_standard_port(r1.uri) == without_standard_port(r2.uri) }
83
+ register(:uri) { |r1, r2| r1.uri == r2.uri }
84
84
  register(:host) { |r1, r2| URI(r1.uri).host == URI(r2.uri).host }
85
85
  register(:path) { |r1, r2| URI(r1.uri).path == URI(r2.uri).path }
86
86
  register(:body) { |r1, r2| r1.body == r2.body }
87
87
  register(:headers) { |r1, r2| r1.headers == r2.headers }
88
88
  end
89
-
90
- def without_standard_port(uri)
91
- URI(uri).tap do |u|
92
- if [['http', 80], ['https', 443]].include?([u.scheme, u.port])
93
- u.port = nil
94
- end
95
- end
96
- end
97
89
  end
98
90
  end
99
91
 
data/lib/vcr/structs.rb CHANGED
@@ -78,6 +78,12 @@ module VCR
78
78
  include Normalizers::Header
79
79
  include Normalizers::Body
80
80
 
81
+ def initialize(*args)
82
+ super
83
+ self.method = self.method.to_s.downcase.to_sym if self.method
84
+ self.uri = without_standard_port(self.uri)
85
+ end
86
+
81
87
  def to_hash
82
88
  {
83
89
  'method' => method.to_s,
@@ -101,6 +107,31 @@ module VCR
101
107
  return super if args.empty?
102
108
  @@object_method.bind(self).call(*args)
103
109
  end
110
+
111
+ # transforms the request into a fiber aware one
112
+ def fiber_aware
113
+ extend FiberAware
114
+ end
115
+
116
+ module FiberAware
117
+ def proceed
118
+ Fiber.yield
119
+ end
120
+
121
+ def to_proc
122
+ lambda { proceed }
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def without_standard_port(uri)
129
+ return uri if uri.nil?
130
+ u = URI(uri)
131
+ return uri unless [['http', 80], ['https', 443]].include?([u.scheme, u.port])
132
+ u.port = nil
133
+ u.to_s
134
+ end
104
135
  end
105
136
 
106
137
  class HTTPInteraction < Struct.new(:request, :response, :recorded_at)
@@ -188,8 +219,7 @@ module VCR
188
219
  end
189
220
 
190
221
  def update_content_length_header
191
- # TODO: should this be the bytesize?
192
- value = body ? body.length.to_s : '0'
222
+ value = body ? body.bytesize.to_s : '0'
193
223
  key = %w[ Content-Length content-length ].find { |k| headers.has_key?(k) }
194
224
  headers[key] = [value] if key
195
225
  end
@@ -32,6 +32,34 @@ module VCR
32
32
  group_descriptions.compact.reverse.join('/')
33
33
  end
34
34
  end
35
+
36
+ module Metadata
37
+ extend self
38
+
39
+ def configure!
40
+ ::RSpec.configure do |config|
41
+ vcr_cassette_name_for = lambda do |metadata|
42
+ description = metadata[:description]
43
+
44
+ if example_group = metadata[:example_group]
45
+ [vcr_cassette_name_for[example_group], description].join('/')
46
+ else
47
+ description
48
+ end
49
+ end
50
+
51
+ config.around(:each, :vcr => lambda { |v| !!v }) do |example|
52
+ options = example.metadata[:vcr]
53
+ options = {} unless options.is_a?(Hash) # in case it's just :vcr => true
54
+
55
+ cassette_name = options.delete(:cassette_name) ||
56
+ vcr_cassette_name_for[example.metadata]
57
+
58
+ VCR.use_cassette(cassette_name, options, &example)
59
+ end
60
+ end
61
+ end
62
+ end
35
63
  end
36
64
  end
37
65
 
@@ -1,3 +1,5 @@
1
+ require 'vcr/util/variable_args_block_caller'
2
+
1
3
  module VCR
2
4
  module Hooks
3
5
  include VariableArgsBlockCaller
@@ -6,8 +8,12 @@ module VCR
6
8
  klass.extend(ClassMethods)
7
9
  end
8
10
 
9
- def invoke_hook(hook, tag=nil, *args)
10
- hooks_for(hook, tag).each do |callback|
11
+ def invoke_hook(hook, *args)
12
+ invoke_tagged_hook(hook, nil, *args)
13
+ end
14
+
15
+ def invoke_tagged_hook(hook, tag, *args)
16
+ hooks_for(hook, tag).map do |callback|
11
17
  call_block(callback, *args)
12
18
  end
13
19
  end
@@ -34,7 +40,9 @@ module VCR
34
40
  end
35
41
 
36
42
  module ClassMethods
37
- def define_hook(hook)
43
+ def define_hook(hook, prepend = false)
44
+ placement_method = prepend ? :unshift : :<<
45
+
38
46
  # We use splat args here because 1.8.7 doesn't allow default
39
47
  # values for block arguments, so we have to fake it.
40
48
  define_method hook do |*args, &block|
@@ -43,7 +51,7 @@ module VCR
43
51
  end
44
52
 
45
53
  tag = args.first
46
- hooks[hook][tag] << block
54
+ hooks[hook][tag].send(placement_method, block)
47
55
  end
48
56
  end
49
57
  end