vcr 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.travis.yml +0 -3
- data/CHANGELOG.md +32 -3
- data/Gemfile +33 -0
- data/Gemfile.lock +99 -119
- data/README.md +19 -7
- data/Rakefile +5 -9
- data/benchmarks/null_logging.rb +62 -0
- data/features/.nav +0 -1
- data/features/about_these_examples.md +1 -2
- data/features/cassettes/allow_unused_http_interactions.feature +15 -1
- data/features/cassettes/decompress.feature +6 -2
- data/features/cassettes/format.feature +20 -12
- data/features/cassettes/freezing_time.feature +68 -0
- data/features/configuration/cassette_library_dir.feature +5 -0
- data/features/configuration/preserve_exact_body_bytes.feature +5 -0
- data/features/configuration/uri_parser.feature +2 -4
- data/features/http_libraries/net_http.feature +1 -1
- data/features/request_matching/headers.feature +0 -1
- data/features/step_definitions/cli_steps.rb +1 -4
- data/features/test_frameworks/cucumber.feature +59 -0
- data/features/test_frameworks/rspec_metadata.feature +59 -1
- data/gemfiles/typhoeus_old.gemfile +19 -0
- data/gemfiles/typhoeus_old.gemfile.lock +84 -86
- data/lib/vcr.rb +12 -3
- data/lib/vcr/cassette.rb +32 -11
- data/lib/vcr/cassette/http_interaction_list.rb +3 -2
- data/lib/vcr/cassette/migrator.rb +1 -0
- data/lib/vcr/cassette/serializers/json.rb +1 -1
- data/lib/vcr/configuration.rb +17 -9
- data/lib/vcr/library_hooks/typhoeus.rb +3 -2
- data/lib/vcr/library_hooks/webmock.rb +1 -1
- data/lib/vcr/middleware/excon.rb +13 -1
- data/lib/vcr/middleware/faraday.rb +1 -0
- data/lib/vcr/request_handler.rb +1 -1
- data/lib/vcr/structs.rb +19 -4
- data/lib/vcr/test_frameworks/cucumber.rb +2 -2
- data/lib/vcr/test_frameworks/rspec.rb +10 -2
- data/lib/vcr/util/logger.rb +41 -7
- data/lib/vcr/version.rb +1 -1
- data/script/ci.sh +8 -1
- data/spec/acceptance/threading_spec.rb +6 -0
- data/spec/capture_warnings.rb +9 -1
- data/spec/spec_helper.rb +6 -2
- data/spec/support/configuration_stubbing.rb +8 -0
- data/spec/support/http_library_adapters.rb +1 -1
- data/spec/support/limited_uri.rb +1 -0
- data/spec/support/shared_example_groups/excon.rb +23 -1
- data/spec/support/shared_example_groups/hook_into_http_library.rb +12 -12
- data/spec/support/shared_example_groups/request_hooks.rb +1 -1
- data/spec/support/sinatra_app.rb +9 -0
- data/spec/support/vcr_localhost_server.rb +4 -25
- data/spec/support/vcr_stub_helpers.rb +1 -1
- data/spec/vcr/cassette/http_interaction_list_spec.rb +41 -14
- data/spec/vcr/cassette/migrator_spec.rb +1 -1
- data/spec/vcr/cassette/persisters_spec.rb +2 -2
- data/spec/vcr/cassette/serializers_spec.rb +13 -4
- data/spec/vcr/cassette_spec.rb +107 -58
- data/spec/vcr/configuration_spec.rb +23 -23
- data/spec/vcr/deprecations_spec.rb +9 -9
- data/spec/vcr/errors_spec.rb +6 -6
- data/spec/vcr/library_hooks/excon_spec.rb +15 -10
- data/spec/vcr/library_hooks/fakeweb_spec.rb +8 -8
- data/spec/vcr/library_hooks/faraday_spec.rb +1 -1
- data/spec/vcr/library_hooks/typhoeus_0.4_spec.rb +2 -2
- data/spec/vcr/library_hooks/typhoeus_spec.rb +68 -9
- data/spec/vcr/library_hooks/webmock_spec.rb +6 -10
- data/spec/vcr/middleware/faraday_spec.rb +33 -5
- data/spec/vcr/middleware/rack_spec.rb +2 -2
- data/spec/vcr/request_matcher_registry_spec.rb +11 -6
- data/spec/vcr/structs_spec.rb +114 -47
- data/spec/vcr/test_frameworks/cucumber_spec.rb +4 -4
- data/spec/vcr/util/hooks_spec.rb +2 -2
- data/spec/vcr/util/internet_connection_spec.rb +3 -3
- data/spec/vcr/util/version_checker_spec.rb +4 -4
- data/spec/vcr_spec.rb +22 -16
- data/vcr.gemspec +2 -31
- metadata +9 -328
- 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}"
|
@@ -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.
|
data/lib/vcr/configuration.rb
CHANGED
@@ -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
|
-
|
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 =
|
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.
|
5
|
+
VCR::VersionChecker.new('WebMock', WebMock.version, '1.8.0', '1.13').check_version!
|
6
6
|
|
7
7
|
module VCR
|
8
8
|
class LibraryHooks
|
data/lib/vcr/middleware/excon.rb
CHANGED
@@ -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.
|
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
|
data/lib/vcr/request_handler.rb
CHANGED
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
data/lib/vcr/util/logger.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
module VCR
|
2
2
|
# @private
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
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 -
|
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
|
data/spec/capture_warnings.rb
CHANGED
@@ -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
|
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]
|