vcr 2.0.0.rc1 → 2.0.0.rc2

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 (113) hide show
  1. data/.gitignore +2 -0
  2. data/.limited_red +1 -0
  3. data/.travis.yml +10 -1
  4. data/.yardopts +9 -0
  5. data/CHANGELOG.md +51 -1
  6. data/Gemfile +5 -1
  7. data/LICENSE +1 -1
  8. data/README.md +23 -28
  9. data/Rakefile +63 -18
  10. data/Upgrade.md +200 -0
  11. data/features/.nav +2 -0
  12. data/features/cassettes/automatic_re_recording.feature +19 -15
  13. data/features/cassettes/dynamic_erb.feature +12 -4
  14. data/features/cassettes/exclusive.feature +31 -23
  15. data/features/cassettes/format.feature +54 -30
  16. data/features/cassettes/naming.feature +1 -1
  17. data/features/cassettes/update_content_length_header.feature +16 -12
  18. data/features/configuration/allow_http_connections_when_no_cassette.feature +1 -1
  19. data/features/configuration/debug_logging.feature +52 -0
  20. data/features/configuration/filter_sensitive_data.feature +4 -4
  21. data/features/configuration/hook_into.feature +5 -2
  22. data/features/configuration/ignore_request.feature +5 -3
  23. data/features/configuration/preserve_exact_body_bytes.feature +103 -0
  24. data/features/hooks/after_http_request.feature +17 -4
  25. data/features/hooks/around_http_request.feature +2 -1
  26. data/features/hooks/before_http_request.feature +25 -8
  27. data/features/hooks/before_playback.feature +16 -12
  28. data/features/hooks/before_record.feature +2 -2
  29. data/features/http_libraries/em_http_request.feature +82 -58
  30. data/features/http_libraries/net_http.feature +6 -6
  31. data/features/middleware/faraday.feature +2 -1
  32. data/features/middleware/rack.feature +2 -2
  33. data/features/record_modes/all.feature +19 -15
  34. data/features/record_modes/new_episodes.feature +17 -13
  35. data/features/record_modes/none.feature +15 -11
  36. data/features/record_modes/once.feature +16 -12
  37. data/features/request_matching/body.feature +28 -20
  38. data/features/request_matching/custom_matcher.feature +28 -20
  39. data/features/request_matching/headers.feature +34 -26
  40. data/features/request_matching/host.feature +28 -20
  41. data/features/request_matching/identical_request_sequence.feature +28 -20
  42. data/features/request_matching/method.feature +28 -20
  43. data/features/request_matching/path.feature +28 -20
  44. data/features/request_matching/playback_repeats.feature +28 -20
  45. data/features/request_matching/uri.feature +28 -20
  46. data/features/request_matching/uri_without_param.feature +28 -20
  47. data/features/support/env.rb +7 -6
  48. data/features/support/vcr_cucumber_helpers.rb +1 -0
  49. data/features/test_frameworks/cucumber.feature +8 -8
  50. data/features/test_frameworks/rspec_macro.feature +4 -4
  51. data/features/test_frameworks/rspec_metadata.feature +6 -6
  52. data/features/test_frameworks/shoulda.feature +1 -1
  53. data/features/test_frameworks/test_unit.feature +1 -1
  54. data/lib/vcr.rb +156 -5
  55. data/lib/vcr/cassette.rb +80 -30
  56. data/lib/vcr/cassette/http_interaction_list.rb +33 -4
  57. data/lib/vcr/cassette/migrator.rb +2 -3
  58. data/lib/vcr/cassette/reader.rb +1 -0
  59. data/lib/vcr/cassette/serializers.rb +22 -0
  60. data/lib/vcr/cassette/serializers/json.rb +27 -2
  61. data/lib/vcr/cassette/serializers/psych.rb +26 -2
  62. data/lib/vcr/cassette/serializers/syck.rb +28 -2
  63. data/lib/vcr/cassette/serializers/yaml.rb +28 -2
  64. data/lib/vcr/configuration.rb +348 -10
  65. data/lib/vcr/deprecations.rb +8 -0
  66. data/lib/vcr/errors.rb +40 -0
  67. data/lib/vcr/extensions/net_http_response.rb +12 -11
  68. data/lib/vcr/library_hooks.rb +1 -0
  69. data/lib/vcr/library_hooks/excon.rb +24 -3
  70. data/lib/vcr/library_hooks/fakeweb.rb +32 -16
  71. data/lib/vcr/library_hooks/faraday.rb +3 -0
  72. data/lib/vcr/library_hooks/typhoeus.rb +40 -37
  73. data/lib/vcr/library_hooks/webmock.rb +54 -34
  74. data/lib/vcr/middleware/faraday.rb +13 -0
  75. data/lib/vcr/middleware/rack.rb +35 -0
  76. data/lib/vcr/request_handler.rb +60 -8
  77. data/lib/vcr/request_ignorer.rb +1 -0
  78. data/lib/vcr/request_matcher_registry.rb +28 -0
  79. data/lib/vcr/structs.rb +245 -38
  80. data/lib/vcr/test_frameworks/cucumber.rb +10 -0
  81. data/lib/vcr/test_frameworks/rspec.rb +26 -1
  82. data/lib/vcr/util/hooks.rb +29 -27
  83. data/lib/vcr/util/internet_connection.rb +2 -0
  84. data/lib/vcr/util/logger.rb +25 -0
  85. data/lib/vcr/util/variable_args_block_caller.rb +1 -0
  86. data/lib/vcr/util/version_checker.rb +1 -0
  87. data/lib/vcr/version.rb +8 -1
  88. data/spec/capture_warnings.rb +3 -3
  89. data/spec/monkey_patches.rb +28 -13
  90. data/spec/spec_helper.rb +17 -0
  91. data/spec/support/http_library_adapters.rb +7 -4
  92. data/spec/support/shared_example_groups/hook_into_http_library.rb +96 -32
  93. data/spec/support/shared_example_groups/request_hooks.rb +9 -8
  94. data/spec/support/sinatra_app.rb +3 -1
  95. data/spec/support/vcr_localhost_server.rb +1 -0
  96. data/spec/vcr/cassette/http_interaction_list_spec.rb +119 -54
  97. data/spec/vcr/cassette/migrator_spec.rb +19 -6
  98. data/spec/vcr/cassette/serializers_spec.rb +51 -6
  99. data/spec/vcr/cassette_spec.rb +44 -19
  100. data/spec/vcr/configuration_spec.rb +91 -6
  101. data/spec/vcr/library_hooks/excon_spec.rb +54 -16
  102. data/spec/vcr/library_hooks/fakeweb_spec.rb +12 -21
  103. data/spec/vcr/library_hooks/typhoeus_spec.rb +2 -29
  104. data/spec/vcr/library_hooks/webmock_spec.rb +4 -18
  105. data/spec/vcr/middleware/faraday_spec.rb +1 -16
  106. data/spec/vcr/structs_spec.rb +194 -61
  107. data/spec/vcr/test_frameworks/rspec_spec.rb +10 -0
  108. data/spec/vcr/util/hooks_spec.rb +104 -56
  109. data/spec/vcr/util/version_checker_spec.rb +45 -0
  110. data/spec/vcr_spec.rb +11 -0
  111. data/vcr.gemspec +30 -34
  112. metadata +149 -95
  113. data/spec/support/shared_example_groups/version_checking.rb +0 -34
@@ -1,20 +1,30 @@
1
1
  module VCR
2
+ # Provides integration with Cucumber using tags.
2
3
  class CucumberTags
3
4
  class << self
5
+ # @private
4
6
  def tags
5
7
  @tags.dup
6
8
  end
7
9
 
10
+ # @private
8
11
  def add_tag(tag)
9
12
  @tags << tag
10
13
  end
11
14
  end
12
15
 
13
16
  @tags = []
17
+
18
+ # @private
14
19
  def initialize(main_object)
15
20
  @main_object = main_object
16
21
  end
17
22
 
23
+ # Adds +Before+ and +After+ cucumber hooks for the named tags that
24
+ # will cause a VCR cassette to be used for scenarios with matching tags.
25
+ #
26
+ # @param [Array<String>] tag_names the cucumber scenario tags
27
+ # @param [(optional) Hash] options the cassette options
18
28
  def tags(*tag_names)
19
29
  options = tag_names.last.is_a?(::Hash) ? tag_names.pop : {}
20
30
  tag_names.each do |tag_name|
@@ -1,6 +1,30 @@
1
1
  module VCR
2
+ # Integrates VCR with RSpec.
2
3
  module RSpec
4
+
5
+ # Contains macro methods to assist with VCR usage. These methods are
6
+ # intended to be used directly in an RSpec example group. To make these
7
+ # available in your RSpec example groups, extend the module in an individual
8
+ # example group, or configure RSpec to extend the module in all example groups.
9
+ #
10
+ # @example
11
+ # RSpec.configure do |c|
12
+ # c.extend VCR::RSpec::Macros
13
+ # end
14
+ #
3
15
  module Macros
16
+
17
+ # Sets up a +before+ and +after+ hook that will insert and eject a
18
+ # cassette, respectively.
19
+ #
20
+ # @example
21
+ # describe "Some API Client" do
22
+ # use_vcr_cassette "some_api", :record => :new_episodes
23
+ # end
24
+ #
25
+ # @param [(optional) String] name the cassette name; it will be inferred by the example
26
+ # group descriptions if not given.
27
+ # @param [(optional) Hash] options the cassette options
4
28
  def use_vcr_cassette(*args)
5
29
  options = args.last.is_a?(Hash) ? args.pop : {}
6
30
  name = args.first || infer_cassette_name
@@ -33,6 +57,7 @@ module VCR
33
57
  end
34
58
  end
35
59
 
60
+ # @private
36
61
  module Metadata
37
62
  extend self
38
63
 
@@ -50,7 +75,7 @@ module VCR
50
75
 
51
76
  config.around(:each, :vcr => lambda { |v| !!v }) do |example|
52
77
  options = example.metadata[:vcr]
53
- options = {} unless options.is_a?(Hash) # in case it's just :vcr => true
78
+ options = options.is_a?(Hash) ? options.dup : {} # in case it's just :vcr => true
54
79
 
55
80
  cassette_name = options.delete(:cassette_name) ||
56
81
  vcr_cassette_name_for[example.metadata]
@@ -1,20 +1,32 @@
1
1
  require 'vcr/util/variable_args_block_caller'
2
2
 
3
3
  module VCR
4
+ # @private
4
5
  module Hooks
5
6
  include VariableArgsBlockCaller
6
7
 
7
- def self.included(klass)
8
- klass.extend(ClassMethods)
8
+ FilteredHook = Struct.new(:hook, :filters) do
9
+ include VariableArgsBlockCaller
10
+
11
+ def conditionally_invoke(*args)
12
+ filters = Array(self.filters)
13
+ return if filters.any? { |f| !call_block(f.to_proc, *args) }
14
+ call_block(hook, *args)
15
+ end
9
16
  end
10
17
 
11
- def invoke_hook(hook, *args)
12
- invoke_tagged_hook(hook, nil, *args)
18
+ def self.included(klass)
19
+ klass.class_eval do
20
+ extend ClassMethods
21
+ hooks_module = Module.new
22
+ const_set("DefinedHooks", hooks_module)
23
+ include hooks_module
24
+ end
13
25
  end
14
26
 
15
- def invoke_tagged_hook(hook, tag, *args)
16
- hooks_for(hook, tag).map do |callback|
17
- call_block(callback, *args)
27
+ def invoke_hook(hook_type, *args)
28
+ hooks[hook_type].map do |hook|
29
+ hook.conditionally_invoke(*args)
18
30
  end
19
31
  end
20
32
 
@@ -22,36 +34,26 @@ module VCR
22
34
  hooks.clear
23
35
  end
24
36
 
25
- private
26
-
27
37
  def hooks
28
- @hooks ||= Hash.new do |hook_type_hash, hook_type|
29
- hook_type_hash[hook_type] = Hash.new do |tag_hash, tag|
30
- tag_hash[tag] = []
31
- end
38
+ @hooks ||= Hash.new do |hash, hook_type|
39
+ hash[hook_type] = []
32
40
  end
33
41
  end
34
42
 
35
- def hooks_for(hook, tag)
36
- for_hook = hooks[hook]
37
- hooks = for_hook[tag] # matching tagged hooks
38
- hooks += for_hook[nil] unless tag.nil? # untagged hooks
39
- hooks
43
+ def has_hooks_for?(hook_type)
44
+ hooks[hook_type].any?
40
45
  end
41
46
 
47
+ # @private
42
48
  module ClassMethods
43
- def define_hook(hook, prepend = false)
49
+ def define_hook(hook_type, prepend = false)
44
50
  placement_method = prepend ? :unshift : :<<
45
51
 
46
- # We use splat args here because 1.8.7 doesn't allow default
47
- # values for block arguments, so we have to fake it.
48
- define_method hook do |*args, &block|
49
- if args.size > 1
50
- raise ArgumentError.new("wrong number of arguments (#{args.size} for 1)")
52
+ # Put the hook methods in a module so we can override and super to these methods.
53
+ self::DefinedHooks.module_eval do
54
+ define_method hook_type do |*filters, &hook|
55
+ hooks[hook_type].send(placement_method, FilteredHook.new(hook, filters))
51
56
  end
52
-
53
- tag = args.first
54
- hooks[hook][tag].send(placement_method, block)
55
57
  end
56
58
  end
57
59
  end
@@ -9,6 +9,7 @@ module VCR
9
9
  require 'timeout'
10
10
  require "socket"
11
11
 
12
+ # @private
12
13
  module Ping
13
14
  def pingecho(host, timeout=5, service="echo")
14
15
  begin
@@ -27,6 +28,7 @@ module VCR
27
28
  end
28
29
  end
29
30
 
31
+ # @private
30
32
  module InternetConnection
31
33
  extend self
32
34
 
@@ -0,0 +1,25 @@
1
+ module VCR
2
+ # @private
3
+ module Logger
4
+ def log(message, indentation_level = 0)
5
+ indentation = ' ' * indentation_level
6
+ log_message = indentation + log_prefix + message
7
+ VCR.configuration.debug_logger.puts log_message
8
+ end
9
+
10
+ def log_prefix
11
+ ''
12
+ end
13
+
14
+ def request_summary(request, request_matchers)
15
+ attributes = [request.method, request.uri]
16
+ attributes << request.body.to_s[0, 80].inspect if request_matchers.include?(:body)
17
+ attributes << request.headers.inspect if request_matchers.include?(:headers)
18
+ "[#{attributes.join(" ")}]"
19
+ end
20
+
21
+ def response_summary(response)
22
+ "[#{response.status.code} #{response.body[0, 80].inspect}]"
23
+ end
24
+ end
25
+ end
@@ -1,4 +1,5 @@
1
1
  module VCR
2
+ # @private
2
3
  module VariableArgsBlockCaller
3
4
  def call_block(block, *args)
4
5
  if block.arity >= 0
@@ -1,4 +1,5 @@
1
1
  module VCR
2
+ # @private
2
3
  class VersionChecker
3
4
  def initialize(library_name, library_version, min_patch_level, max_minor_version)
4
5
  @library_name, @library_version = library_name, library_version
data/lib/vcr/version.rb CHANGED
@@ -1,9 +1,16 @@
1
1
  module VCR
2
2
  extend self
3
3
 
4
+ # @return [String] the current VCR version.
5
+ # @note This string also has singleton methods:
6
+ #
7
+ # * `major` [Integer] The major version.
8
+ # * `minor` [Integer] The minor version.
9
+ # * `patch` [Integer] The patch version.
10
+ # * `parts` [Array<Integer>] List of the version parts.
4
11
  def version
5
12
  @version ||= begin
6
- string = '2.0.0.rc1'
13
+ string = '2.0.0.rc2'
7
14
 
8
15
  def string.parts
9
16
  split('.').map { |p| p.to_i }
@@ -1,11 +1,11 @@
1
+ require 'rubygems' if RUBY_VERSION =~ /^1\.8/
2
+ require 'rspec/core'
3
+ require 'rspec/expectations'
1
4
  require 'tempfile'
2
5
  stderr_file = Tempfile.new("vcr.stderr")
3
6
  $stderr.reopen(stderr_file.path)
4
7
  current_dir = Dir.pwd
5
8
 
6
- require 'rubygems' if RUBY_VERSION =~ /^1\.8/
7
- require 'rspec/core'
8
-
9
9
  RSpec.configure do |config|
10
10
  config.after(:suite) do
11
11
  stderr_file.rewind
@@ -14,8 +14,6 @@ module MonkeyPatches
14
14
 
15
15
  ALL_MONKEY_PATCHES = NET_HTTP_MONKEY_PATCHES.dup
16
16
 
17
- ALL_MONKEY_PATCHES << [Typhoeus::Hydra::Stubbing::SharedMethods, :find_stub_from_request] if defined?(::Typhoeus)
18
-
19
17
  def enable!(scope)
20
18
  case scope
21
19
  when :fakeweb
@@ -25,12 +23,21 @@ module MonkeyPatches
25
23
  ::WebMock.reset!
26
24
  ::WebMock::HttpLibAdapters::NetHttpAdapter.enable!
27
25
  ::WebMock::HttpLibAdapters::TyphoeusAdapter.enable! if defined?(::Typhoeus)
26
+ ::WebMock::HttpLibAdapters::ExconAdapter.enable! if defined?(::Excon)
28
27
  $original_webmock_callbacks.each do |cb|
29
28
  ::WebMock::CallbackRegistry.add_callback(cb[:options], cb[:block])
30
29
  end
31
30
  when :typhoeus
32
- Typhoeus::Hydra.global_hooks = $original_typhoeus_hooks
33
- realias Typhoeus::Hydra::Stubbing::SharedMethods, :find_stub_from_request, :with_vcr
31
+ ::Typhoeus::Hydra.global_hooks = $original_typhoeus_global_hooks
32
+ ::Typhoeus::Hydra.stub_finders.clear
33
+ $original_typhoeus_stub_finders.each do |finder|
34
+ ::Typhoeus::Hydra.stub_finders << finder
35
+ end
36
+ when :excon
37
+ $original_excon_stubs.each do |stub|
38
+ ::Excon.stubs << stub
39
+ end
40
+ ::Excon.defaults[:mock] = true
34
41
  when :vcr
35
42
  realias Net::HTTP, :request, :with_vcr
36
43
  else raise ArgumentError.new("Unexpected scope: #{scope}")
@@ -43,12 +50,19 @@ module MonkeyPatches
43
50
  if defined?(::WebMock::HttpLibAdapters)
44
51
  ::WebMock::HttpLibAdapters::NetHttpAdapter.disable!
45
52
  ::WebMock::HttpLibAdapters::TyphoeusAdapter.disable! if defined?(::Typhoeus)
53
+ ::WebMock::HttpLibAdapters::ExconAdapter.disable! if defined?(::Excon)
46
54
  ::WebMock::CallbackRegistry.reset
47
55
  ::WebMock::StubRegistry.instance.request_stubs = []
48
56
  end
49
57
 
50
58
  if defined?(::Typhoeus)
51
- Typhoeus::Hydra.clear_global_hooks
59
+ ::Typhoeus::Hydra.clear_global_hooks
60
+ ::Typhoeus::Hydra.stub_finders.clear
61
+ end
62
+
63
+ if defined?(::Excon)
64
+ ::Excon.stubs.clear
65
+ ::Excon.defaults[:mock] = false
52
66
  end
53
67
  end
54
68
 
@@ -118,15 +132,13 @@ unless RUBY_INTERPRETER == :jruby
118
132
  require 'curb'
119
133
 
120
134
  require 'vcr/library_hooks/typhoeus'
121
- $original_typhoeus_hooks = Typhoeus::Hydra.global_hooks.dup
122
-
123
- # define an alias that we can re-alias to in the future
124
- Typhoeus::Hydra::Stubbing::SharedMethods.class_eval do
125
- alias find_stub_from_request_with_vcr find_stub_from_request
126
- end
135
+ $typhoeus_after_loaded_hook = VCR.configuration.hooks[:after_library_hooks_loaded].last
136
+ $original_typhoeus_global_hooks = Typhoeus::Hydra.global_hooks.dup
137
+ $original_typhoeus_stub_finders = Typhoeus::Hydra.stub_finders.dup
127
138
  end
128
139
 
129
140
  require 'vcr/library_hooks/fakeweb'
141
+ $fakeweb_after_loaded_hook = VCR.configuration.hooks[:after_library_hooks_loaded].last
130
142
 
131
143
  # All Net::HTTP monkey patches have now been loaded, so capture the
132
144
  # appropriate method definitions so we can disable them later.
@@ -139,14 +151,17 @@ MonkeyPatches.disable_all!
139
151
  require 'vcr/library_hooks/webmock'
140
152
  $original_webmock_callbacks = ::WebMock::CallbackRegistry.callbacks
141
153
 
154
+ require 'vcr/library_hooks/excon'
155
+ $excon_after_loaded_hook = VCR.configuration.hooks[:after_library_hooks_loaded].last
156
+ $original_excon_stubs = ::Excon.stubs.dup
157
+
142
158
  # disable all by default; we'll enable specific ones when we need them
143
159
  MonkeyPatches.disable_all!
144
160
 
145
161
  RSpec.configure do |config|
146
- [:fakeweb, :webmock, :vcr, :typhoeus].each do |scope|
162
+ [:fakeweb, :webmock, :vcr, :typhoeus, :excon].each do |scope|
147
163
  config.before(:all, :with_monkey_patches => scope) { MonkeyPatches.enable!(scope) }
148
164
  config.after(:all, :with_monkey_patches => scope) { MonkeyPatches.disable_all! }
149
165
  end
150
166
  end
151
167
 
152
- require 'vcr/library_hooks/excon'
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,21 @@
1
1
  require 'rubygems'
2
+ require 'simplecov'
3
+
4
+ SimpleCov.start do
5
+ add_filter "/spec"
6
+ add_filter "/features"
7
+
8
+ # internet_connection mostly contains logic copied from the ruby 1.8.7
9
+ # stdlib for which I haven't written tests.
10
+ add_filter "internet_connection"
11
+ end
12
+
13
+ SimpleCov.at_exit do
14
+ File.open(File.join(SimpleCov.coverage_path, 'coverage_percent.txt'), 'w') do |f|
15
+ f.write SimpleCov.result.covered_percent
16
+ end
17
+ SimpleCov.result.format!
18
+ end
2
19
 
3
20
  using_git = File.exist?(File.expand_path('../../.git/', __FILE__))
4
21
  if using_git
@@ -90,7 +90,8 @@ HTTP_LIBRARY_ADAPTERS['em-http-request'] = Module.new do
90
90
  end
91
91
 
92
92
  def get_header(header_key, response)
93
- response.response_header[header_key.upcase.gsub('-', '_')].split(', ')
93
+ values = response.response_header[header_key.upcase.gsub('-', '_')]
94
+ values.is_a?(Array) ? values : values.split(', ')
94
95
  end
95
96
 
96
97
  def make_http_request(method, url, body = nil, headers = {})
@@ -116,11 +117,13 @@ HTTP_LIBRARY_ADAPTERS['curb'] = Module.new do
116
117
 
117
118
  def get_header(header_key, response)
118
119
  headers = response.header_str.split("\r\n")[1..-1]
120
+ value = nil
119
121
  headers.each do |h|
120
- if h =~ /^#{Regexp.escape(header_key)}: (.*)$/
121
- return $1.split(', ')
122
- end
122
+ next unless h =~ /^#{Regexp.escape(header_key)}: (.*)$/
123
+ new_value = $1.split(', ')
124
+ value = value ? Array(value) + Array(new_value) : new_value
123
125
  end
126
+ value
124
127
  end
125
128
 
126
129
  def make_http_request(method, url, body = nil, headers = {})
@@ -27,28 +27,10 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
27
27
  end
28
28
  end
29
29
 
30
- define_method :should_be_pending do
31
- if header_count == 2
32
- [
33
- 'HTTP Client',
34
- 'EM HTTP Request',
35
- 'Curb'
36
- ].include?(adapter_module.http_library_name)
37
- end
38
- end
39
-
40
30
  it 'returns the same header value when recording and replaying' do
41
- pending "There appears to be a bug in the the HTTP stubbing library", :if => should_be_pending do
42
- (recorded_val = get_set_cookie_header).should_not be_nil
43
- replayed_val = get_set_cookie_header
44
-
45
- # we don't care about order differences if the values are arrays
46
- if recorded_val.is_a?(Array) && replayed_val.is_a?(Array)
47
- replayed_val.should =~ recorded_val
48
- else
49
- replayed_val.should eq(recorded_val)
50
- end
51
- end
31
+ (recorded_val = get_set_cookie_header).should_not be_nil
32
+ replayed_val = get_set_cookie_header
33
+ replayed_val.should eq(recorded_val)
52
34
  end
53
35
  end
54
36
  end
@@ -109,6 +91,22 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
109
91
  test_playback "with an encoded ampersand", "http://example.com:80/search?q=#{CGI.escape("Q&A")}"
110
92
  end
111
93
 
94
+ it 'does not query the http interaction list excessively' do
95
+ call_count = 0
96
+ [:has_interaction_matching?, :response_for].each do |method_name|
97
+ orig_meth = VCR.http_interactions.method(method_name)
98
+ VCR.http_interactions.stub(method_name) do |*args|
99
+ call_count += 1
100
+ orig_meth.call(*args)
101
+ end
102
+ end
103
+
104
+ VCR.insert_cassette('foo')
105
+ make_http_request(:get, "http://localhost:#{VCR::SinatraApp.port}/foo")
106
+
107
+ call_count.should eq(1)
108
+ end
109
+
112
110
  describe "using the library's stubbing/disconnection APIs" do
113
111
  let!(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
114
112
 
@@ -151,13 +149,6 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
151
149
 
152
150
  describe "request hooks" do
153
151
  context 'when there is an around_http_request hook' do
154
- before(:each) do
155
- # ensure that all the other library hooks are disabled so that we don't
156
- # get double-hookage (such as for WebMock and Typhoeus both invoking the
157
- # hooks for a typhoeus request)
158
- VCR.library_hooks.stub(:disabled?) { |lib_name| lib_name != library_hook_name }
159
- end
160
-
161
152
  let(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
162
153
 
163
154
  it 'yields the request to the block' do
@@ -175,6 +166,19 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
175
166
  yielded_request.uri.should eq(request_url)
176
167
  end
177
168
 
169
+ it 'returns the response from request.proceed' do
170
+ response = nil
171
+ VCR.configuration.around_http_request do |request|
172
+ response = request.proceed
173
+ end
174
+
175
+ VCR.use_cassette('new_cassette') do
176
+ make_http_request(:get, request_url)
177
+ end
178
+
179
+ response.body.should eq("FOO!")
180
+ end
181
+
178
182
  it 'can be used to use a cassette for a request' do
179
183
  VCR.configuration.around_http_request do |request|
180
184
  VCR.use_cassette('new_cassette', &request)
@@ -232,20 +236,80 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
232
236
 
233
237
  3.times { make_http_request(:get, request_url) }
234
238
  end
239
+
240
+ it 'allows the hook to be filtered' do
241
+ order = []
242
+ VCR.configure do |c|
243
+ c.ignore_request { |r| true }
244
+ c.around_http_request(lambda { |r| r.uri =~ /foo/}) do |request|
245
+ order << :before_foo
246
+ request.proceed
247
+ order << :after_foo
248
+ end
249
+
250
+ c.around_http_request(lambda { |r| r.uri !~ /foo/}) do |request|
251
+ order << :before_not_foo
252
+ request.proceed
253
+ order << :after_not_foo
254
+ end
255
+ end
256
+
257
+ make_http_request(:get, request_url)
258
+ order.should eq([:before_foo, :after_foo])
259
+ end
260
+
261
+ it 'ensures that both around/before are invoked or neither' do
262
+ order = []
263
+ allow_1, allow_2 = false, true
264
+ VCR.configure do |c|
265
+ c.ignore_request { |r| true }
266
+ c.around_http_request(lambda { |r| allow_1 = !allow_1 }) do |request|
267
+ order << :before_1
268
+ request.proceed
269
+ order << :after_1
270
+ end
271
+
272
+ c.around_http_request(lambda { |r| allow_2 = !allow_2 }) do |request|
273
+ order << :before_2
274
+ request.proceed
275
+ order << :after_2
276
+ end
277
+ end
278
+
279
+ make_http_request(:get, request_url)
280
+ order.should eq([:before_1, :after_1])
281
+ end
235
282
  end if RUBY_VERSION >= '1.9'
236
283
 
284
+ it 'correctly assigns the correct type to both before and after request hooks, even if they are different' do
285
+ before_type = after_type = nil
286
+ VCR.configuration.before_http_request do |request|
287
+ before_type = request.type
288
+ VCR.insert_cassette('example')
289
+ end
290
+
291
+ VCR.configuration.after_http_request do |request|
292
+ after_type = request.type
293
+ VCR.eject_cassette
294
+ end
295
+
296
+ make_http_request(:get, "http://localhost:#{VCR::SinatraApp.port}/foo")
297
+ before_type.should be(:unhandled)
298
+ after_type.should be(:recordable)
299
+ end
300
+
237
301
  context "when the request is ignored" do
238
302
  before(:each) do
239
303
  VCR.configuration.ignore_request { |r| true }
240
304
  end
241
305
 
242
- it_behaves_like "request hooks", library_hook_name
306
+ it_behaves_like "request hooks", library_hook_name, :ignored
243
307
  end
244
308
 
245
309
  context 'when the request is recorded' do
246
310
  let!(:inserted_cassette) { VCR.insert_cassette('new_cassette') }
247
311
 
248
- it_behaves_like "request hooks", library_hook_name do
312
+ it_behaves_like "request hooks", library_hook_name, :recordable do
249
313
  let(:string_in_cassette) { 'example.com get response 1 with path=foo' }
250
314
 
251
315
  it 'plays back the cassette when a request is made' do
@@ -277,11 +341,11 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
277
341
  stub_requests([http_interaction(request_url)], [:method, :uri])
278
342
  end
279
343
 
280
- it_behaves_like "request hooks", library_hook_name
344
+ it_behaves_like "request hooks", library_hook_name, :stubbed
281
345
  end
282
346
 
283
347
  context 'when the request is not allowed' do
284
- it_behaves_like "request hooks", library_hook_name do
348
+ it_behaves_like "request hooks", library_hook_name, :unhandled do
285
349
  undef assert_expected_response
286
350
  def assert_expected_response(response)
287
351
  response.should be_nil