vcr 2.0.0.rc1 → 2.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.limited_red +1 -0
- data/.travis.yml +10 -1
- data/.yardopts +9 -0
- data/CHANGELOG.md +51 -1
- data/Gemfile +5 -1
- data/LICENSE +1 -1
- data/README.md +23 -28
- data/Rakefile +63 -18
- data/Upgrade.md +200 -0
- data/features/.nav +2 -0
- data/features/cassettes/automatic_re_recording.feature +19 -15
- data/features/cassettes/dynamic_erb.feature +12 -4
- data/features/cassettes/exclusive.feature +31 -23
- data/features/cassettes/format.feature +54 -30
- data/features/cassettes/naming.feature +1 -1
- data/features/cassettes/update_content_length_header.feature +16 -12
- data/features/configuration/allow_http_connections_when_no_cassette.feature +1 -1
- data/features/configuration/debug_logging.feature +52 -0
- data/features/configuration/filter_sensitive_data.feature +4 -4
- data/features/configuration/hook_into.feature +5 -2
- data/features/configuration/ignore_request.feature +5 -3
- data/features/configuration/preserve_exact_body_bytes.feature +103 -0
- data/features/hooks/after_http_request.feature +17 -4
- data/features/hooks/around_http_request.feature +2 -1
- data/features/hooks/before_http_request.feature +25 -8
- data/features/hooks/before_playback.feature +16 -12
- data/features/hooks/before_record.feature +2 -2
- data/features/http_libraries/em_http_request.feature +82 -58
- data/features/http_libraries/net_http.feature +6 -6
- data/features/middleware/faraday.feature +2 -1
- data/features/middleware/rack.feature +2 -2
- data/features/record_modes/all.feature +19 -15
- data/features/record_modes/new_episodes.feature +17 -13
- data/features/record_modes/none.feature +15 -11
- data/features/record_modes/once.feature +16 -12
- data/features/request_matching/body.feature +28 -20
- data/features/request_matching/custom_matcher.feature +28 -20
- data/features/request_matching/headers.feature +34 -26
- data/features/request_matching/host.feature +28 -20
- data/features/request_matching/identical_request_sequence.feature +28 -20
- data/features/request_matching/method.feature +28 -20
- data/features/request_matching/path.feature +28 -20
- data/features/request_matching/playback_repeats.feature +28 -20
- data/features/request_matching/uri.feature +28 -20
- data/features/request_matching/uri_without_param.feature +28 -20
- data/features/support/env.rb +7 -6
- data/features/support/vcr_cucumber_helpers.rb +1 -0
- data/features/test_frameworks/cucumber.feature +8 -8
- data/features/test_frameworks/rspec_macro.feature +4 -4
- data/features/test_frameworks/rspec_metadata.feature +6 -6
- data/features/test_frameworks/shoulda.feature +1 -1
- data/features/test_frameworks/test_unit.feature +1 -1
- data/lib/vcr.rb +156 -5
- data/lib/vcr/cassette.rb +80 -30
- data/lib/vcr/cassette/http_interaction_list.rb +33 -4
- data/lib/vcr/cassette/migrator.rb +2 -3
- data/lib/vcr/cassette/reader.rb +1 -0
- data/lib/vcr/cassette/serializers.rb +22 -0
- data/lib/vcr/cassette/serializers/json.rb +27 -2
- data/lib/vcr/cassette/serializers/psych.rb +26 -2
- data/lib/vcr/cassette/serializers/syck.rb +28 -2
- data/lib/vcr/cassette/serializers/yaml.rb +28 -2
- data/lib/vcr/configuration.rb +348 -10
- data/lib/vcr/deprecations.rb +8 -0
- data/lib/vcr/errors.rb +40 -0
- data/lib/vcr/extensions/net_http_response.rb +12 -11
- data/lib/vcr/library_hooks.rb +1 -0
- data/lib/vcr/library_hooks/excon.rb +24 -3
- data/lib/vcr/library_hooks/fakeweb.rb +32 -16
- data/lib/vcr/library_hooks/faraday.rb +3 -0
- data/lib/vcr/library_hooks/typhoeus.rb +40 -37
- data/lib/vcr/library_hooks/webmock.rb +54 -34
- data/lib/vcr/middleware/faraday.rb +13 -0
- data/lib/vcr/middleware/rack.rb +35 -0
- data/lib/vcr/request_handler.rb +60 -8
- data/lib/vcr/request_ignorer.rb +1 -0
- data/lib/vcr/request_matcher_registry.rb +28 -0
- data/lib/vcr/structs.rb +245 -38
- data/lib/vcr/test_frameworks/cucumber.rb +10 -0
- data/lib/vcr/test_frameworks/rspec.rb +26 -1
- data/lib/vcr/util/hooks.rb +29 -27
- data/lib/vcr/util/internet_connection.rb +2 -0
- data/lib/vcr/util/logger.rb +25 -0
- data/lib/vcr/util/variable_args_block_caller.rb +1 -0
- data/lib/vcr/util/version_checker.rb +1 -0
- data/lib/vcr/version.rb +8 -1
- data/spec/capture_warnings.rb +3 -3
- data/spec/monkey_patches.rb +28 -13
- data/spec/spec_helper.rb +17 -0
- data/spec/support/http_library_adapters.rb +7 -4
- data/spec/support/shared_example_groups/hook_into_http_library.rb +96 -32
- data/spec/support/shared_example_groups/request_hooks.rb +9 -8
- data/spec/support/sinatra_app.rb +3 -1
- data/spec/support/vcr_localhost_server.rb +1 -0
- data/spec/vcr/cassette/http_interaction_list_spec.rb +119 -54
- data/spec/vcr/cassette/migrator_spec.rb +19 -6
- data/spec/vcr/cassette/serializers_spec.rb +51 -6
- data/spec/vcr/cassette_spec.rb +44 -19
- data/spec/vcr/configuration_spec.rb +91 -6
- data/spec/vcr/library_hooks/excon_spec.rb +54 -16
- data/spec/vcr/library_hooks/fakeweb_spec.rb +12 -21
- data/spec/vcr/library_hooks/typhoeus_spec.rb +2 -29
- data/spec/vcr/library_hooks/webmock_spec.rb +4 -18
- data/spec/vcr/middleware/faraday_spec.rb +1 -16
- data/spec/vcr/structs_spec.rb +194 -61
- data/spec/vcr/test_frameworks/rspec_spec.rb +10 -0
- data/spec/vcr/util/hooks_spec.rb +104 -56
- data/spec/vcr/util/version_checker_spec.rb +45 -0
- data/spec/vcr_spec.rb +11 -0
- data/vcr.gemspec +30 -34
- metadata +149 -95
- 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 =
|
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]
|
data/lib/vcr/util/hooks.rb
CHANGED
@@ -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
|
-
|
8
|
-
|
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
|
12
|
-
|
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
|
16
|
-
|
17
|
-
|
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 |
|
29
|
-
|
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
|
36
|
-
|
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(
|
49
|
+
def define_hook(hook_type, prepend = false)
|
44
50
|
placement_method = prepend ? :unshift : :<<
|
45
51
|
|
46
|
-
#
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
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.
|
13
|
+
string = '2.0.0.rc2'
|
7
14
|
|
8
15
|
def string.parts
|
9
16
|
split('.').map { |p| p.to_i }
|
data/spec/capture_warnings.rb
CHANGED
@@ -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
|
data/spec/monkey_patches.rb
CHANGED
@@ -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 = $
|
33
|
-
|
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
|
-
$
|
122
|
-
|
123
|
-
|
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('-', '_')]
|
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
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
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
|