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.
- 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
|