vcr 2.5.0 → 2.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 (79) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +0 -3
  3. data/CHANGELOG.md +32 -3
  4. data/Gemfile +33 -0
  5. data/Gemfile.lock +99 -119
  6. data/README.md +19 -7
  7. data/Rakefile +5 -9
  8. data/benchmarks/null_logging.rb +62 -0
  9. data/features/.nav +0 -1
  10. data/features/about_these_examples.md +1 -2
  11. data/features/cassettes/allow_unused_http_interactions.feature +15 -1
  12. data/features/cassettes/decompress.feature +6 -2
  13. data/features/cassettes/format.feature +20 -12
  14. data/features/cassettes/freezing_time.feature +68 -0
  15. data/features/configuration/cassette_library_dir.feature +5 -0
  16. data/features/configuration/preserve_exact_body_bytes.feature +5 -0
  17. data/features/configuration/uri_parser.feature +2 -4
  18. data/features/http_libraries/net_http.feature +1 -1
  19. data/features/request_matching/headers.feature +0 -1
  20. data/features/step_definitions/cli_steps.rb +1 -4
  21. data/features/test_frameworks/cucumber.feature +59 -0
  22. data/features/test_frameworks/rspec_metadata.feature +59 -1
  23. data/gemfiles/typhoeus_old.gemfile +19 -0
  24. data/gemfiles/typhoeus_old.gemfile.lock +84 -86
  25. data/lib/vcr.rb +12 -3
  26. data/lib/vcr/cassette.rb +32 -11
  27. data/lib/vcr/cassette/http_interaction_list.rb +3 -2
  28. data/lib/vcr/cassette/migrator.rb +1 -0
  29. data/lib/vcr/cassette/serializers/json.rb +1 -1
  30. data/lib/vcr/configuration.rb +17 -9
  31. data/lib/vcr/library_hooks/typhoeus.rb +3 -2
  32. data/lib/vcr/library_hooks/webmock.rb +1 -1
  33. data/lib/vcr/middleware/excon.rb +13 -1
  34. data/lib/vcr/middleware/faraday.rb +1 -0
  35. data/lib/vcr/request_handler.rb +1 -1
  36. data/lib/vcr/structs.rb +19 -4
  37. data/lib/vcr/test_frameworks/cucumber.rb +2 -2
  38. data/lib/vcr/test_frameworks/rspec.rb +10 -2
  39. data/lib/vcr/util/logger.rb +41 -7
  40. data/lib/vcr/version.rb +1 -1
  41. data/script/ci.sh +8 -1
  42. data/spec/acceptance/threading_spec.rb +6 -0
  43. data/spec/capture_warnings.rb +9 -1
  44. data/spec/spec_helper.rb +6 -2
  45. data/spec/support/configuration_stubbing.rb +8 -0
  46. data/spec/support/http_library_adapters.rb +1 -1
  47. data/spec/support/limited_uri.rb +1 -0
  48. data/spec/support/shared_example_groups/excon.rb +23 -1
  49. data/spec/support/shared_example_groups/hook_into_http_library.rb +12 -12
  50. data/spec/support/shared_example_groups/request_hooks.rb +1 -1
  51. data/spec/support/sinatra_app.rb +9 -0
  52. data/spec/support/vcr_localhost_server.rb +4 -25
  53. data/spec/support/vcr_stub_helpers.rb +1 -1
  54. data/spec/vcr/cassette/http_interaction_list_spec.rb +41 -14
  55. data/spec/vcr/cassette/migrator_spec.rb +1 -1
  56. data/spec/vcr/cassette/persisters_spec.rb +2 -2
  57. data/spec/vcr/cassette/serializers_spec.rb +13 -4
  58. data/spec/vcr/cassette_spec.rb +107 -58
  59. data/spec/vcr/configuration_spec.rb +23 -23
  60. data/spec/vcr/deprecations_spec.rb +9 -9
  61. data/spec/vcr/errors_spec.rb +6 -6
  62. data/spec/vcr/library_hooks/excon_spec.rb +15 -10
  63. data/spec/vcr/library_hooks/fakeweb_spec.rb +8 -8
  64. data/spec/vcr/library_hooks/faraday_spec.rb +1 -1
  65. data/spec/vcr/library_hooks/typhoeus_0.4_spec.rb +2 -2
  66. data/spec/vcr/library_hooks/typhoeus_spec.rb +68 -9
  67. data/spec/vcr/library_hooks/webmock_spec.rb +6 -10
  68. data/spec/vcr/middleware/faraday_spec.rb +33 -5
  69. data/spec/vcr/middleware/rack_spec.rb +2 -2
  70. data/spec/vcr/request_matcher_registry_spec.rb +11 -6
  71. data/spec/vcr/structs_spec.rb +114 -47
  72. data/spec/vcr/test_frameworks/cucumber_spec.rb +4 -4
  73. data/spec/vcr/util/hooks_spec.rb +2 -2
  74. data/spec/vcr/util/internet_connection_spec.rb +3 -3
  75. data/spec/vcr/util/version_checker_spec.rb +4 -4
  76. data/spec/vcr_spec.rb +22 -16
  77. data/vcr.gemspec +2 -31
  78. metadata +9 -328
  79. data/features/test_frameworks/shoulda.feature +0 -64
@@ -2,7 +2,7 @@ module VCR
2
2
  class Cassette
3
3
  # @private
4
4
  class HTTPInteractionList
5
- include Logger
5
+ include Logger::Mixin
6
6
 
7
7
  # @private
8
8
  module NullList
@@ -59,9 +59,10 @@ module VCR
59
59
  # @raise [VCR::Errors::UnusedHTTPInteractionError] if not all interactions were played back.
60
60
  def assert_no_unused_interactions!
61
61
  return unless has_unused_interactions?
62
+ logger = Logger.new(nil)
62
63
 
63
64
  descriptions = @interactions.map do |i|
64
- " - #{request_summary(i.request)} => #{response_summary(i.response)}"
65
+ " - #{logger.request_summary(i.request, @request_matchers)} => #{logger.response_summary(i.response)}"
65
66
  end.join("\n")
66
67
 
67
68
  raise Errors::UnusedHTTPInteractionError, "There are unused HTTP interactions left in the cassette:\n#{descriptions}"
@@ -30,6 +30,7 @@ module VCR
30
30
  end
31
31
 
32
32
  http_interactions.map! do |interaction|
33
+ interaction.response.adapter_metadata = {}
33
34
  interaction.recorded_at = File.mtime(cassette)
34
35
  remove_unnecessary_standard_port(interaction)
35
36
  denormalize_http_header_keys(interaction.request)
@@ -13,7 +13,7 @@ module VCR
13
13
  extend EncodingErrorHandling
14
14
 
15
15
  # @private
16
- ENCODING_ERRORS = [MultiJson::DecodeError]
16
+ ENCODING_ERRORS = [MultiJson::DecodeError, ArgumentError]
17
17
  ENCODING_ERRORS << EncodingError if defined?(EncodingError)
18
18
 
19
19
  # The file extension to use for this serializer.
@@ -7,7 +7,7 @@ module VCR
7
7
  class Configuration
8
8
  include Hooks
9
9
  include VariableArgsBlockCaller
10
- include Logger
10
+ include Logger::Mixin
11
11
 
12
12
  # Gets the directory to read cassettes from and write cassettes to.
13
13
  #
@@ -430,7 +430,21 @@ module VCR
430
430
  # VCR.configure do |c|
431
431
  # c.debug_logger = File.open('vcr.log', 'w')
432
432
  # end
433
- attr_accessor :debug_logger
433
+ attr_reader :debug_logger
434
+ # @private (documented above)
435
+ def debug_logger=(value)
436
+ @debug_logger = value
437
+
438
+ if value
439
+ @logger = Logger.new(value)
440
+ else
441
+ @logger = Logger::Null
442
+ end
443
+ end
444
+
445
+ # @private
446
+ # Logger object that provides logging APIs and helper methods.
447
+ attr_reader :logger
434
448
 
435
449
  # Sets a callback that determines whether or not to base64 encode
436
450
  # the bytes of a request or response body during serialization in
@@ -479,7 +493,7 @@ module VCR
479
493
 
480
494
  self.uri_parser = URI
481
495
  self.query_parser = CGI.method(:parse)
482
- self.debug_logger = NullDebugLogger
496
+ self.debug_logger = nil
483
497
 
484
498
  register_built_in_hooks
485
499
  end
@@ -537,11 +551,5 @@ module VCR
537
551
  # @private
538
552
  define_hook :after_library_hooks_loaded
539
553
  end
540
-
541
- # @private
542
- module NullDebugLogger
543
- extend self
544
- def puts(*); end
545
- end
546
554
  end
547
555
 
@@ -50,7 +50,7 @@ else
50
50
  :status_message => stubbed_response.status.message,
51
51
  :headers => stubbed_response_headers,
52
52
  :body => stubbed_response.body,
53
- :effective_url => request.url,
53
+ :effective_url => stubbed_response.adapter_metadata.fetch('effective_url', request.url),
54
54
  :mock => true
55
55
  end
56
56
 
@@ -69,7 +69,8 @@ else
69
69
  VCR::ResponseStatus.new(response.code, response.status_message),
70
70
  response.headers,
71
71
  response.body,
72
- response.http_version
72
+ response.http_version,
73
+ { "effective_url" => response.effective_url }
73
74
  end
74
75
 
75
76
  ::Typhoeus.on_complete do |response|
@@ -2,7 +2,7 @@ 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.8.0', '1.11').check_version!
5
+ VCR::VersionChecker.new('WebMock', WebMock.version, '1.8.0', '1.13').check_version!
6
6
 
7
7
  module VCR
8
8
  class LibraryHooks
@@ -2,7 +2,7 @@ require 'excon'
2
2
  require 'vcr/request_handler'
3
3
  require 'vcr/util/version_checker'
4
4
 
5
- VCR::VersionChecker.new('Excon', Excon::VERSION, '0.22.0', '0.22').check_version!
5
+ VCR::VersionChecker.new('Excon', Excon::VERSION, '0.22.0', '0.26').check_version!
6
6
 
7
7
  module VCR
8
8
  # Contains middlewares for use with different libraries.
@@ -39,6 +39,7 @@ module VCR
39
39
 
40
40
  # @private
41
41
  def error_call(params)
42
+ @request_handler.ensure_response_body_can_be_read_for_error_case
42
43
  @request_handler.after_request(params)
43
44
  super
44
45
  end
@@ -84,10 +85,21 @@ module VCR
84
85
  invoke_after_request_hook(vcr_response)
85
86
  end
86
87
 
88
+ def ensure_response_body_can_be_read_for_error_case
89
+ # Excon does not invoke the `:response_block` when an error
90
+ # has occurred, so we need to be sure to use the non-streaming
91
+ # body reader.
92
+ @response_body_reader = NonStreamingResponseBodyReader
93
+ end
94
+
87
95
  attr_reader :request_params, :response_params, :response_body_reader
88
96
 
89
97
  private
90
98
 
99
+ def externally_stubbed?
100
+ !!::Excon.stub_for(request_params)
101
+ end
102
+
91
103
  def should_record?
92
104
  @should_record
93
105
  end
@@ -27,6 +27,7 @@ module VCR
27
27
  #
28
28
  # @param [Hash] env the Faraday request env hash
29
29
  def call(env)
30
+ return if VCR.library_hooks.disabled?(:faraday)
30
31
  RequestHandler.new(@app, env).handle
31
32
  end
32
33
 
@@ -1,7 +1,7 @@
1
1
  module VCR
2
2
  # @private
3
3
  class RequestHandler
4
- include Logger
4
+ include Logger::Mixin
5
5
 
6
6
  def handle
7
7
  log "Handling request: #{request_summary} (disabled: #{disabled?})"
data/lib/vcr/structs.rb CHANGED
@@ -76,6 +76,11 @@ module VCR
76
76
  private
77
77
 
78
78
  def serializable_body
79
+ # Ensure it's just a string, and not a string with some
80
+ # extra state, as such strings serialize to YAML with
81
+ # all the extra state.
82
+ body = String.new(self.body.to_s)
83
+
79
84
  if VCR.configuration.preserve_exact_body_bytes_for?(self)
80
85
  base_body_hash(body).merge('base64_string' => Base64.encode64(body))
81
86
  else
@@ -166,7 +171,7 @@ module VCR
166
171
  module OrderedHashSerializer
167
172
  def each
168
173
  @ordered_keys.each do |key|
169
- yield key, self[key]
174
+ yield key, self[key] if has_key?(key)
170
175
  end
171
176
  end
172
177
 
@@ -341,10 +346,16 @@ module VCR
341
346
  # @attr [Hash{String => Array<String>}] headers the response headers
342
347
  # @attr [String] body the response body
343
348
  # @attr [nil, String] http_version the HTTP version
344
- class Response < Struct.new(:status, :headers, :body, :http_version)
349
+ # @attr [Hash] adapter_metadata Additional metadata used by a specific VCR adapter.
350
+ class Response < Struct.new(:status, :headers, :body, :http_version, :adapter_metadata)
345
351
  include Normalizers::Header
346
352
  include Normalizers::Body
347
353
 
354
+ def initialize(*args)
355
+ super(*args)
356
+ self.adapter_metadata ||= {}
357
+ end
358
+
348
359
  # Builds a serializable hash from the response data.
349
360
  #
350
361
  # @return [Hash] hash that represents this response
@@ -356,7 +367,10 @@ module VCR
356
367
  'headers' => headers,
357
368
  'body' => serializable_body,
358
369
  'http_version' => http_version
359
- }.tap { |h| OrderedHashSerializer.apply_to(h, members) }
370
+ }.tap do |hash|
371
+ hash['adapter_metadata'] = adapter_metadata unless adapter_metadata.empty?
372
+ OrderedHashSerializer.apply_to(hash, members)
373
+ end
360
374
  end
361
375
 
362
376
  # Constructs a new instance from a hash.
@@ -367,7 +381,8 @@ module VCR
367
381
  new ResponseStatus.from_hash(hash.fetch('status', {})),
368
382
  hash['headers'],
369
383
  body_from(hash['body']),
370
- hash['http_version']
384
+ hash['http_version'],
385
+ hash['adapter_metadata']
371
386
  end
372
387
 
373
388
  # Updates the Content-Length response header so that it is
@@ -52,8 +52,8 @@ module VCR
52
52
  VCR.insert_cassette(cassette_name, options)
53
53
  end
54
54
 
55
- @main_object.After(tag_name) do
56
- VCR.eject_cassette
55
+ @main_object.After(tag_name) do |scenario|
56
+ VCR.eject_cassette(:skip_no_unused_interactions_assertion => scenario.failed?)
57
57
  end
58
58
 
59
59
  self.class.add_tag(tag_name)
@@ -17,14 +17,22 @@ module VCR
17
17
  end
18
18
  end
19
19
 
20
- config.around(:each, :vcr => lambda { |v| !!v }) do |example|
20
+ when_tagged_with_vcr = { :vcr => lambda { |v| !!v } }
21
+
22
+ config.before(:each, when_tagged_with_vcr) do |ex|
23
+ example = respond_to?(:example) ? self.example : ex
24
+
21
25
  options = example.metadata[:vcr]
22
26
  options = options.is_a?(Hash) ? options.dup : {} # in case it's just :vcr => true
23
27
 
24
28
  cassette_name = options.delete(:cassette_name) ||
25
29
  vcr_cassette_name_for[example.metadata]
30
+ VCR.insert_cassette(cassette_name, options)
31
+ end
26
32
 
27
- VCR.use_cassette(cassette_name, options, &example)
33
+ config.after(:each, when_tagged_with_vcr) do |ex|
34
+ example = respond_to?(:example) ? self.example : ex
35
+ VCR.eject_cassette(:skip_no_unused_interactions_assertion => !!example.exception)
28
36
  end
29
37
  end
30
38
  end
@@ -1,14 +1,15 @@
1
1
  module VCR
2
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
3
+ # Provides log message formatting helper methods.
4
+ class Logger
5
+ def initialize(stream)
6
+ @stream = stream
8
7
  end
9
8
 
10
- def log_prefix
11
- ''
9
+ def log(message, log_prefix, indentation_level = 0)
10
+ indentation = ' ' * indentation_level
11
+ log_message = indentation + log_prefix + message
12
+ @stream.puts log_message
12
13
  end
13
14
 
14
15
  def request_summary(request, request_matchers)
@@ -21,5 +22,38 @@ module VCR
21
22
  def response_summary(response)
22
23
  "[#{response.status.code} #{response.body[0, 80].inspect}]"
23
24
  end
25
+
26
+ # @private
27
+ # A null-object version of the Logger. Used when
28
+ # a `debug_logger` has not been set.
29
+ #
30
+ # @note We used to use a null object for the `debug_logger` itself,
31
+ # but some users noticed a negative perf impact from having the
32
+ # logger formatting logic still executing in that case, so we
33
+ # moved the null object interface up a layer to here.
34
+ module Null
35
+ module_function
36
+
37
+ def log(*); end
38
+ def request_summary(*); end
39
+ def response_summary(*); end
40
+ end
41
+
42
+ # @private
43
+ # Provides common logger helper methods that simply delegate to
44
+ # the underlying logger object.
45
+ module Mixin
46
+ def log(message, indentation_level = 0)
47
+ VCR.configuration.logger.log(message, log_prefix, indentation_level)
48
+ end
49
+
50
+ def request_summary(*args)
51
+ VCR.configuration.logger.request_summary(*args)
52
+ end
53
+
54
+ def response_summary(*args)
55
+ VCR.configuration.logger.response_summary(*args)
56
+ end
57
+ end
24
58
  end
25
59
  end
data/lib/vcr/version.rb CHANGED
@@ -10,7 +10,7 @@ module VCR
10
10
  # * `parts` [Array<Integer>] List of the version parts.
11
11
  def version
12
12
  @version ||= begin
13
- string = '2.5.0'
13
+ string = '2.6.0'
14
14
 
15
15
  def string.parts
16
16
  split('.').map { |p| p.to_i }
data/script/ci.sh CHANGED
@@ -1,6 +1,13 @@
1
1
  # Kill the whole script on error
2
2
  set -e -x
3
3
 
4
+ # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html
5
+ export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived
6
+
7
+ # force jRuby to use client mode JVM or a compilation mode thats as close as possible,
8
+ # idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time
9
+ export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1'
10
+
4
11
  echo "-------- Running Typhoeus 0.4 Specs ---------"
5
12
  bundle install --gemfile=gemfiles/typhoeus_old.gemfile --without extras
6
13
  BUNDLE_GEMFILE=gemfiles/typhoeus_old.gemfile bundle exec rspec spec/vcr/library_hooks/typhoeus_0.4_spec.rb --format progress --backtrace
@@ -10,7 +17,7 @@ git submodule init
10
17
  git submodule update
11
18
 
12
19
  echo "-------- Running Specs ---------"
13
- bundle exec ruby -w -I./spec -r./spec/capture_warnings -rspec_helper -S rspec spec --format progress --backtrace
20
+ bundle exec ruby -I./spec -r./spec/capture_warnings -rspec_helper -S rspec spec --format progress --backtrace
14
21
 
15
22
  echo "-------- Running Cukes ---------"
16
23
  bundle exec cucumber
@@ -2,6 +2,12 @@ require 'spec_helper'
2
2
 
3
3
  describe VCR do
4
4
  context 'when used in a multithreaded environment', :with_monkey_patches => :excon do
5
+ def preload_yaml_serializer_to_avoid_circular_require_warning_race_condition
6
+ VCR.cassette_serializers[:yaml]
7
+ end
8
+
9
+ before { preload_yaml_serializer_to_avoid_circular_require_warning_race_condition }
10
+
5
11
  def recorded_content_for(name)
6
12
  VCR.cassette_persisters[:file_system]["#{name}.yml"].to_s
7
13
  end
@@ -1,17 +1,25 @@
1
1
  require 'rubygems' if RUBY_VERSION =~ /^1\.8/
2
+ require 'bundler/setup'
2
3
  require 'rspec/core'
3
4
  require 'rspec/expectations'
4
5
  require 'tempfile'
6
+
5
7
  stderr_file = Tempfile.new("vcr.stderr")
6
- $stderr.reopen(stderr_file.path)
7
8
  current_dir = Dir.pwd
8
9
 
9
10
  RSpec.configure do |config|
11
+ config.before(:suite) do
12
+ $stderr.reopen(stderr_file.path)
13
+ $VERBOSE = true
14
+ end
15
+
10
16
  config.after(:suite) do
11
17
  stderr_file.rewind
12
18
  lines = stderr_file.read.split("\n").uniq
13
19
  stderr_file.close!
14
20
 
21
+ $stderr.reopen(STDERR)
22
+
15
23
  vcr_warnings, other_warnings = lines.partition { |line| line.include?(current_dir) }
16
24
 
17
25
  # After upgrading to curb 0.8.1, I started to get a circular require
data/spec/spec_helper.rb CHANGED
@@ -3,7 +3,7 @@ require 'rubygems'
3
3
  using_git = File.exist?(File.expand_path('../../.git/', __FILE__))
4
4
  require 'bundler/setup' if using_git
5
5
 
6
- if RUBY_VERSION =~ /1.9/ && RUBY_ENGINE == 'ruby'
6
+ if RUBY_VERSION.to_f >= 1.9 && RUBY_ENGINE == 'ruby'
7
7
  require 'simplecov'
8
8
 
9
9
  SimpleCov.start do
@@ -41,7 +41,7 @@ require 'monkey_patches'
41
41
  require "support/http_library_adapters"
42
42
 
43
43
  module VCR
44
- SPEC_ROOT = File.dirname(__FILE__)
44
+ SPEC_ROOT = File.dirname(File.expand_path('.', __FILE__))
45
45
 
46
46
  def reset!(hook = :fakeweb)
47
47
  instance_variables.each do |ivar|
@@ -60,6 +60,10 @@ RSpec.configure do |config|
60
60
  expectations.syntax = :expect
61
61
  end
62
62
 
63
+ config.mock_with :rspec do |mocks|
64
+ mocks.syntax = :expect
65
+ end
66
+
63
67
  tmp_dir = File.expand_path('../../tmp/cassette_library_dir', __FILE__)
64
68
  config.before(:each) do
65
69
  unless example.metadata[:skip_vcr_reset]
@@ -0,0 +1,8 @@
1
+ shared_context "configuration stubbing" do
2
+ let(:config) { double("VCR::Configuration") }
3
+
4
+ before do
5
+ allow(VCR).to receive(:configuration) { config }
6
+ end
7
+ end
8
+