vcr 3.0.3 → 6.2.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.
- checksums.yaml +5 -5
- data/lib/vcr/cassette/erb_renderer.rb +4 -2
- data/lib/vcr/cassette/http_interaction_list.rb +14 -9
- data/lib/vcr/cassette/migrator.rb +5 -6
- data/lib/vcr/cassette/persisters/file_system.rb +9 -1
- data/lib/vcr/cassette/serializers/compressed.rb +2 -2
- data/lib/vcr/cassette/serializers/json.rb +14 -8
- data/lib/vcr/cassette/serializers/psych.rb +10 -2
- data/lib/vcr/cassette/serializers/syck.rb +7 -1
- data/lib/vcr/cassette/serializers/yaml.rb +14 -2
- data/lib/vcr/cassette/serializers.rb +10 -0
- data/lib/vcr/cassette.rb +63 -16
- data/lib/vcr/configuration.rb +21 -8
- data/lib/vcr/deprecations.rb +0 -62
- data/lib/vcr/errors.rb +17 -12
- data/lib/vcr/library_hooks/excon.rb +8 -0
- data/lib/vcr/library_hooks/typhoeus.rb +91 -79
- data/lib/vcr/library_hooks/webmock.rb +2 -11
- data/lib/vcr/linked_cassette.rb +4 -4
- data/lib/vcr/middleware/excon.rb +1 -1
- data/lib/vcr/middleware/faraday.rb +29 -2
- data/lib/vcr/request_ignorer.rb +8 -1
- data/lib/vcr/request_matcher_registry.rb +3 -3
- data/lib/vcr/structs.rb +48 -32
- data/lib/vcr/test_frameworks/cucumber.rb +16 -5
- data/lib/vcr/test_frameworks/rspec.rb +34 -22
- data/lib/vcr/util/hooks.rb +1 -0
- data/lib/vcr/util/internet_connection.rb +15 -21
- data/lib/vcr/version.rb +2 -2
- data/lib/vcr.rb +52 -2
- metadata +45 -272
- data/features/CHANGELOG.md +0 -710
- data/features/CONTRIBUTING.md +0 -26
- data/features/LICENSE.md +0 -20
- data/features/README.md +0 -339
- data/features/Upgrade.md +0 -289
- data/features/about_these_examples.md +0 -18
- data/features/cassettes/allow_unused_http_interactions.feature +0 -100
- data/features/cassettes/automatic_re_recording.feature +0 -72
- data/features/cassettes/decompress.feature +0 -74
- data/features/cassettes/dynamic_erb.feature +0 -100
- data/features/cassettes/exclusive.feature +0 -126
- data/features/cassettes/format.feature +0 -411
- data/features/cassettes/freezing_time.feature +0 -68
- data/features/cassettes/naming.feature +0 -28
- data/features/cassettes/no_cassette.feature +0 -152
- data/features/cassettes/update_content_length_header.feature +0 -112
- data/features/configuration/allow_http_connections_when_no_cassette.feature +0 -55
- data/features/configuration/cassette_library_dir.feature +0 -31
- data/features/configuration/debug_logging.feature +0 -58
- data/features/configuration/default_cassette_options.feature +0 -100
- data/features/configuration/filter_sensitive_data.feature +0 -153
- data/features/configuration/hook_into.feature +0 -172
- data/features/configuration/ignore_request.feature +0 -192
- data/features/configuration/preserve_exact_body_bytes.feature +0 -108
- data/features/configuration/query_parser.feature +0 -84
- data/features/configuration/uri_parser.feature +0 -93
- data/features/getting_started.md +0 -82
- data/features/hooks/after_http_request.feature +0 -58
- data/features/hooks/around_http_request.feature +0 -57
- data/features/hooks/before_http_request.feature +0 -63
- data/features/hooks/before_playback.feature +0 -184
- data/features/hooks/before_record.feature +0 -172
- data/features/http_libraries/em_http_request.feature +0 -250
- data/features/http_libraries/net_http.feature +0 -179
- data/features/middleware/faraday.feature +0 -56
- data/features/middleware/rack.feature +0 -92
- data/features/record_modes/all.feature +0 -82
- data/features/record_modes/new_episodes.feature +0 -79
- data/features/record_modes/none.feature +0 -72
- data/features/record_modes/once.feature +0 -95
- data/features/request_matching/README.md +0 -30
- data/features/request_matching/body.feature +0 -91
- data/features/request_matching/body_as_json.feature +0 -90
- data/features/request_matching/custom_matcher.feature +0 -135
- data/features/request_matching/headers.feature +0 -85
- data/features/request_matching/host.feature +0 -95
- data/features/request_matching/identical_request_sequence.feature +0 -89
- data/features/request_matching/method.feature +0 -96
- data/features/request_matching/path.feature +0 -96
- data/features/request_matching/playback_repeats.feature +0 -98
- data/features/request_matching/query.feature +0 -97
- data/features/request_matching/uri.feature +0 -94
- data/features/request_matching/uri_without_param.feature +0 -101
- data/features/step_definitions/cli_steps.rb +0 -199
- data/features/support/env.rb +0 -46
- data/features/support/http_lib_filters.rb +0 -46
- data/features/test_frameworks/cucumber.feature +0 -211
- data/features/test_frameworks/rspec_macro.feature +0 -81
- data/features/test_frameworks/rspec_metadata.feature +0 -150
- data/features/test_frameworks/test_unit.feature +0 -49
- data/lib/vcr/extensions/net_http_response.rb +0 -36
- data/lib/vcr/library_hooks/fakeweb.rb +0 -197
- data/lib/vcr/library_hooks/typhoeus_0.4.rb +0 -103
- data/spec/acceptance/concurrency_spec.rb +0 -51
- data/spec/acceptance/threading_spec.rb +0 -34
- data/spec/fixtures/cassette_spec/1_x_cassette.yml +0 -110
- data/spec/fixtures/cassette_spec/empty.yml +0 -0
- data/spec/fixtures/cassette_spec/example.yml +0 -111
- data/spec/fixtures/cassette_spec/with_localhost_requests.yml +0 -111
- data/spec/fixtures/fake_example_responses.yml +0 -110
- data/spec/fixtures/match_requests_on.yml +0 -187
- data/spec/lib/vcr/cassette/erb_renderer_spec.rb +0 -53
- data/spec/lib/vcr/cassette/http_interaction_list_spec.rb +0 -295
- data/spec/lib/vcr/cassette/migrator_spec.rb +0 -196
- data/spec/lib/vcr/cassette/persisters/file_system_spec.rb +0 -75
- data/spec/lib/vcr/cassette/persisters_spec.rb +0 -39
- data/spec/lib/vcr/cassette/serializers_spec.rb +0 -182
- data/spec/lib/vcr/cassette_spec.rb +0 -618
- data/spec/lib/vcr/configuration_spec.rb +0 -326
- data/spec/lib/vcr/deprecations_spec.rb +0 -85
- data/spec/lib/vcr/errors_spec.rb +0 -178
- data/spec/lib/vcr/extensions/net_http_response_spec.rb +0 -86
- data/spec/lib/vcr/library_hooks/excon_spec.rb +0 -104
- data/spec/lib/vcr/library_hooks/fakeweb_spec.rb +0 -169
- data/spec/lib/vcr/library_hooks/faraday_spec.rb +0 -68
- data/spec/lib/vcr/library_hooks/typhoeus_0.4_spec.rb +0 -36
- data/spec/lib/vcr/library_hooks/typhoeus_spec.rb +0 -162
- data/spec/lib/vcr/library_hooks/webmock_spec.rb +0 -117
- data/spec/lib/vcr/library_hooks_spec.rb +0 -51
- data/spec/lib/vcr/middleware/faraday_spec.rb +0 -181
- data/spec/lib/vcr/middleware/rack_spec.rb +0 -115
- data/spec/lib/vcr/request_ignorer_spec.rb +0 -70
- data/spec/lib/vcr/request_matcher_registry_spec.rb +0 -345
- data/spec/lib/vcr/structs_spec.rb +0 -732
- data/spec/lib/vcr/test_frameworks/cucumber_spec.rb +0 -107
- data/spec/lib/vcr/test_frameworks/rspec_spec.rb +0 -94
- data/spec/lib/vcr/util/hooks_spec.rb +0 -158
- data/spec/lib/vcr/util/internet_connection_spec.rb +0 -37
- data/spec/lib/vcr/util/version_checker_spec.rb +0 -31
- data/spec/lib/vcr/version_spec.rb +0 -27
- data/spec/lib/vcr_spec.rb +0 -354
- data/spec/monkey_patches.rb +0 -186
- data/spec/spec_helper.rb +0 -63
- data/spec/support/configuration_stubbing.rb +0 -8
- data/spec/support/cucumber_helpers.rb +0 -39
- data/spec/support/fixnum_extension.rb +0 -10
- data/spec/support/http_library_adapters.rb +0 -289
- data/spec/support/limited_uri.rb +0 -21
- data/spec/support/ruby_interpreter.rb +0 -7
- data/spec/support/shared_example_groups/excon.rb +0 -63
- data/spec/support/shared_example_groups/hook_into_http_library.rb +0 -594
- data/spec/support/shared_example_groups/request_hooks.rb +0 -59
- data/spec/support/sinatra_app.rb +0 -86
- data/spec/support/vcr_localhost_server.rb +0 -76
- data/spec/support/vcr_stub_helpers.rb +0 -17
|
@@ -22,6 +22,14 @@ VCR.configuration.after_library_hooks_loaded do
|
|
|
22
22
|
# (i.e. to double record requests or whatever).
|
|
23
23
|
if defined?(WebMock::HttpLibAdapters::ExconAdapter)
|
|
24
24
|
WebMock::HttpLibAdapters::ExconAdapter.disable!
|
|
25
|
+
|
|
26
|
+
if defined?(::RSpec)
|
|
27
|
+
::RSpec.configure do |config|
|
|
28
|
+
config.before(:suite) do
|
|
29
|
+
WebMock::HttpLibAdapters::ExconAdapter.disable!
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
25
33
|
end
|
|
26
34
|
end
|
|
27
35
|
|
|
@@ -2,79 +2,72 @@ require 'vcr/util/version_checker'
|
|
|
2
2
|
require 'vcr/request_handler'
|
|
3
3
|
require 'typhoeus'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
module VCR
|
|
11
|
-
class LibraryHooks
|
|
5
|
+
module VCR
|
|
6
|
+
class LibraryHooks
|
|
7
|
+
# @private
|
|
8
|
+
module Typhoeus
|
|
12
9
|
# @private
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
request.block_connection = false if VCR.turned_on?
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def vcr_request
|
|
23
|
-
@vcr_request ||= VCR::Request.new \
|
|
24
|
-
request.options.fetch(:method, :get),
|
|
25
|
-
request.url,
|
|
26
|
-
request_body,
|
|
27
|
-
request.options.fetch(:headers, {})
|
|
28
|
-
end
|
|
10
|
+
class RequestHandler < ::VCR::RequestHandler
|
|
11
|
+
attr_reader :request
|
|
12
|
+
def initialize(request)
|
|
13
|
+
@request = request
|
|
14
|
+
request.block_connection = false if VCR.turned_on?
|
|
15
|
+
end
|
|
29
16
|
|
|
30
|
-
|
|
17
|
+
def vcr_request
|
|
18
|
+
@vcr_request ||= VCR::Request.new \
|
|
19
|
+
request.options.fetch(:method, :get),
|
|
20
|
+
request.url,
|
|
21
|
+
request.encoded_body,
|
|
22
|
+
request.options.fetch(:headers, {})
|
|
23
|
+
end
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
::Typhoeus::Expectation.find_by(request)
|
|
34
|
-
end
|
|
25
|
+
private
|
|
35
26
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
end
|
|
27
|
+
def externally_stubbed?
|
|
28
|
+
::Typhoeus::Expectation.find_by(request)
|
|
29
|
+
end
|
|
40
30
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
def set_typed_request_for_after_hook(*args)
|
|
32
|
+
super
|
|
33
|
+
request.instance_variable_set(:@__typed_vcr_request, @after_hook_typed_request)
|
|
34
|
+
end
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
:status_message => stubbed_response.status.message,
|
|
51
|
-
:headers => stubbed_response_headers,
|
|
52
|
-
:body => stubbed_response.body,
|
|
53
|
-
:effective_url => stubbed_response.adapter_metadata.fetch('effective_url', request.url),
|
|
54
|
-
:mock => true
|
|
55
|
-
end
|
|
36
|
+
def on_unhandled_request
|
|
37
|
+
invoke_after_request_hook(nil)
|
|
38
|
+
super
|
|
39
|
+
end
|
|
56
40
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
41
|
+
def on_stubbed_by_vcr_request
|
|
42
|
+
response = ::Typhoeus::Response.new \
|
|
43
|
+
:http_version => stubbed_response.http_version,
|
|
44
|
+
:code => stubbed_response.status.code,
|
|
45
|
+
:status_message => stubbed_response.status.message,
|
|
46
|
+
:headers => stubbed_response_headers,
|
|
47
|
+
:body => stubbed_response.body,
|
|
48
|
+
:effective_url => stubbed_response.adapter_metadata.fetch('effective_url', request.url),
|
|
49
|
+
:mock => true
|
|
50
|
+
|
|
51
|
+
first_header_line = "HTTP/#{stubbed_response.http_version} #{response.code} #{response.status_message}\r\n"
|
|
52
|
+
response.instance_variable_set(:@first_header_line, first_header_line)
|
|
53
|
+
response.instance_variable_get(:@options)[:response_headers] =
|
|
54
|
+
first_header_line + response.headers.map { |k,v| "#{k}: #{v}"}.join("\r\n")
|
|
55
|
+
|
|
56
|
+
response
|
|
57
|
+
end
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def request_body
|
|
71
|
-
request.options.fetch(:body, "")
|
|
72
|
-
end
|
|
59
|
+
def stubbed_response_headers
|
|
60
|
+
@stubbed_response_headers ||= {}.tap do |hash|
|
|
61
|
+
stubbed_response.headers.each do |key, values|
|
|
62
|
+
hash[key] = values.size == 1 ? values.first : values
|
|
63
|
+
end if stubbed_response.headers
|
|
73
64
|
end
|
|
74
65
|
end
|
|
66
|
+
end
|
|
75
67
|
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
# @private
|
|
69
|
+
class << self
|
|
70
|
+
def vcr_response_from(response)
|
|
78
71
|
VCR::Response.new \
|
|
79
72
|
VCR::ResponseStatus.new(response.code, response.status_message),
|
|
80
73
|
response.headers,
|
|
@@ -83,27 +76,47 @@ else
|
|
|
83
76
|
{ "effective_url" => response.effective_url }
|
|
84
77
|
end
|
|
85
78
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
unless request.response.mock
|
|
93
|
-
http_interaction = VCR::HTTPInteraction.new(typed_vcr_request, vcr_response)
|
|
94
|
-
VCR.record_http_interaction(http_interaction)
|
|
79
|
+
def collect_chunks(request)
|
|
80
|
+
chunks = ''
|
|
81
|
+
request.on_body.unshift(
|
|
82
|
+
Proc.new do |body, response|
|
|
83
|
+
chunks += body
|
|
84
|
+
request.instance_variable_set(:@chunked_body, chunks)
|
|
95
85
|
end
|
|
86
|
+
)
|
|
87
|
+
end
|
|
96
88
|
|
|
97
|
-
|
|
98
|
-
|
|
89
|
+
def restore_body_from_chunks(response, request)
|
|
90
|
+
response.options[:response_body] = request.instance_variable_get(:@chunked_body)
|
|
99
91
|
end
|
|
92
|
+
end
|
|
100
93
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
94
|
+
::Typhoeus.on_complete do |response|
|
|
95
|
+
request = response.request
|
|
96
|
+
|
|
97
|
+
restore_body_from_chunks(response, request) if request.streaming?
|
|
98
|
+
|
|
99
|
+
unless VCR.library_hooks.disabled?(:typhoeus)
|
|
100
|
+
vcr_response = vcr_response_from(response)
|
|
101
|
+
typed_vcr_request = request.send(:remove_instance_variable, :@__typed_vcr_request)
|
|
102
|
+
|
|
103
|
+
unless request.response.mock
|
|
104
|
+
http_interaction = VCR::HTTPInteraction.new(typed_vcr_request, vcr_response)
|
|
105
|
+
VCR.record_http_interaction(http_interaction)
|
|
106
106
|
end
|
|
107
|
+
|
|
108
|
+
VCR.configuration.invoke_hook(:after_http_request, typed_vcr_request, vcr_response)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
::Typhoeus.before do |request|
|
|
113
|
+
collect_chunks(request) if request.streaming?
|
|
114
|
+
if response = VCR::LibraryHooks::Typhoeus::RequestHandler.new(request).handle
|
|
115
|
+
request.on_headers.each { |cb| cb.call(response) }
|
|
116
|
+
request.on_body.each { |cb| cb.call(response.body, response) }
|
|
117
|
+
request.finish(response)
|
|
118
|
+
else
|
|
119
|
+
true
|
|
107
120
|
end
|
|
108
121
|
end
|
|
109
122
|
end
|
|
@@ -117,4 +130,3 @@ VCR.configuration.after_library_hooks_loaded do
|
|
|
117
130
|
WebMock::HttpLibAdapters::TyphoeusAdapter.disable!
|
|
118
131
|
end
|
|
119
132
|
end
|
|
120
|
-
|
|
@@ -12,8 +12,6 @@ module VCR
|
|
|
12
12
|
module WebMock
|
|
13
13
|
extend self
|
|
14
14
|
|
|
15
|
-
@global_hook_disabled_requests = {}
|
|
16
|
-
|
|
17
15
|
def with_global_hook_disabled(request)
|
|
18
16
|
global_hook_disabled_requests << request
|
|
19
17
|
|
|
@@ -25,19 +23,12 @@ module VCR
|
|
|
25
23
|
end
|
|
26
24
|
|
|
27
25
|
def global_hook_disabled?(request)
|
|
28
|
-
requests =
|
|
26
|
+
requests = Thread.current[:_vcr_webmock_disabled_requests]
|
|
29
27
|
requests && requests.include?(request)
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
def global_hook_disabled_requests
|
|
33
|
-
|
|
34
|
-
return requests if requests
|
|
35
|
-
|
|
36
|
-
ObjectSpace.define_finalizer(Thread.current, lambda {
|
|
37
|
-
@global_hook_disabled_requests.delete(Thread.current.object_id)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
@global_hook_disabled_requests[Thread.current.object_id] = []
|
|
31
|
+
Thread.current[:_vcr_webmock_disabled_requests] ||= []
|
|
41
32
|
end
|
|
42
33
|
|
|
43
34
|
# @private
|
data/lib/vcr/linked_cassette.rb
CHANGED
|
@@ -9,8 +9,8 @@ module VCR
|
|
|
9
9
|
include Enumerable
|
|
10
10
|
|
|
11
11
|
# Creates a new list of context-owned cassettes and linked cassettes
|
|
12
|
-
# @param [Array] context-owned cassettes
|
|
13
|
-
# @param [Array] context-unowned (linked) cassettes
|
|
12
|
+
# @param cassettes [Array] context-owned cassettes
|
|
13
|
+
# @param linked_cassettes [Array] context-unowned (linked) cassettes
|
|
14
14
|
def initialize(cassettes, linked_cassettes)
|
|
15
15
|
@cassettes = cassettes
|
|
16
16
|
@linked_cassettes = linked_cassettes
|
|
@@ -52,8 +52,8 @@ module VCR
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# Create a new CassetteList
|
|
55
|
-
# @param [Array] context-owned cassettes
|
|
56
|
-
# @param [Array] context-unowned (linked) cassettes
|
|
55
|
+
# @param cassettes [Array] context-owned cassettes
|
|
56
|
+
# @param linked_cassettes [Array] context-unowned (linked) cassettes
|
|
57
57
|
def self.list(cassettes, linked_cassettes)
|
|
58
58
|
CassetteList.new(cassettes, linked_cassettes)
|
|
59
59
|
end
|
data/lib/vcr/middleware/excon.rb
CHANGED
|
@@ -31,6 +31,11 @@ module VCR
|
|
|
31
31
|
RequestHandler.new(@app, env).handle
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
# Close any persistent connections.
|
|
35
|
+
def close
|
|
36
|
+
@app.close if @app.respond_to?(:close)
|
|
37
|
+
end
|
|
38
|
+
|
|
34
39
|
# @private
|
|
35
40
|
class RequestHandler < ::VCR::RequestHandler
|
|
36
41
|
attr_reader :app, :env
|
|
@@ -40,9 +45,11 @@ module VCR
|
|
|
40
45
|
end
|
|
41
46
|
|
|
42
47
|
def handle
|
|
43
|
-
# Faraday must be
|
|
48
|
+
# Faraday must be exclusive here in case another library hook is being used.
|
|
44
49
|
# We don't want double recording/double playback.
|
|
45
50
|
VCR.library_hooks.exclusive_hook = :faraday
|
|
51
|
+
collect_chunks if env.request.stream_response?
|
|
52
|
+
|
|
46
53
|
super
|
|
47
54
|
ensure
|
|
48
55
|
response = defined?(@vcr_response) ? @vcr_response : nil
|
|
@@ -72,8 +79,12 @@ module VCR
|
|
|
72
79
|
end
|
|
73
80
|
|
|
74
81
|
def response_for(response)
|
|
82
|
+
# reason_phrase is a new addition to Faraday::Response,
|
|
83
|
+
# so maintain backward compatibility
|
|
84
|
+
reason = response.respond_to?(:reason_phrase) ? response.reason_phrase : nil
|
|
85
|
+
|
|
75
86
|
VCR::Response.new(
|
|
76
|
-
VCR::ResponseStatus.new(response.status,
|
|
87
|
+
VCR::ResponseStatus.new(response.status, reason),
|
|
77
88
|
response.headers,
|
|
78
89
|
raw_body_from(response.body),
|
|
79
90
|
nil
|
|
@@ -94,6 +105,7 @@ module VCR
|
|
|
94
105
|
@vcr_response = stubbed_response
|
|
95
106
|
|
|
96
107
|
faraday_response = ::Faraday::Response.new
|
|
108
|
+
env.request.on_data.call(stubbed_response.body, stubbed_response.body.length) if env.request.stream_response?
|
|
97
109
|
faraday_response.finish(env)
|
|
98
110
|
env[:response] = faraday_response
|
|
99
111
|
end
|
|
@@ -102,6 +114,7 @@ module VCR
|
|
|
102
114
|
@has_on_complete_hook = true
|
|
103
115
|
response = app.call(env)
|
|
104
116
|
response.on_complete do
|
|
117
|
+
restore_body_from_chunks(env.request) if env.request.stream_response?
|
|
105
118
|
@vcr_response = response_for(response)
|
|
106
119
|
VCR.record_http_interaction(VCR::HTTPInteraction.new(vcr_request, @vcr_response))
|
|
107
120
|
invoke_after_request_hook(@vcr_response) if delay_finishing?
|
|
@@ -112,6 +125,20 @@ module VCR
|
|
|
112
125
|
super
|
|
113
126
|
VCR.library_hooks.exclusive_hook = nil
|
|
114
127
|
end
|
|
128
|
+
|
|
129
|
+
def collect_chunks
|
|
130
|
+
caller_on_data = env.request.on_data
|
|
131
|
+
chunks = ''
|
|
132
|
+
env.request.on_data = Proc.new do |chunk, overall_received_bytes|
|
|
133
|
+
chunks += chunk
|
|
134
|
+
env.request.instance_variable_set(:@chunked_body, chunks)
|
|
135
|
+
caller_on_data.call(chunk, overall_received_bytes)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def restore_body_from_chunks(request)
|
|
140
|
+
env[:body] = request.instance_variable_get(:@chunked_body)
|
|
141
|
+
end
|
|
115
142
|
end
|
|
116
143
|
end
|
|
117
144
|
end
|
data/lib/vcr/request_ignorer.rb
CHANGED
|
@@ -25,10 +25,18 @@ module VCR
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
def localhost_ignored?
|
|
29
|
+
(LOCALHOST_ALIASES & ignore_hosts.to_a).any?
|
|
30
|
+
end
|
|
31
|
+
|
|
28
32
|
def ignore_hosts(*hosts)
|
|
29
33
|
ignored_hosts.merge(hosts)
|
|
30
34
|
end
|
|
31
35
|
|
|
36
|
+
def unignore_hosts(*hosts)
|
|
37
|
+
ignored_hosts.subtract(hosts)
|
|
38
|
+
end
|
|
39
|
+
|
|
32
40
|
def ignore?(request)
|
|
33
41
|
invoke_hook(:ignore_request, request).any?
|
|
34
42
|
end
|
|
@@ -40,4 +48,3 @@ module VCR
|
|
|
40
48
|
end
|
|
41
49
|
end
|
|
42
50
|
end
|
|
43
|
-
|
|
@@ -110,12 +110,12 @@ module VCR
|
|
|
110
110
|
|
|
111
111
|
def register_built_ins
|
|
112
112
|
register(:method) { |r1, r2| r1.method == r2.method }
|
|
113
|
-
register(:uri) { |r1, r2| r1.
|
|
113
|
+
register(:uri) { |r1, r2| r1.parsed_uri == r2.parsed_uri }
|
|
114
114
|
register(:body) { |r1, r2| r1.body == r2.body }
|
|
115
115
|
register(:headers) { |r1, r2| r1.headers == r2.headers }
|
|
116
116
|
|
|
117
117
|
register(:host) do |r1, r2|
|
|
118
|
-
r1.parsed_uri.host == r2.parsed_uri.host
|
|
118
|
+
r1.parsed_uri.host.chomp('.') == r2.parsed_uri.host.chomp('.')
|
|
119
119
|
end
|
|
120
120
|
register(:path) do |r1, r2|
|
|
121
121
|
r1.parsed_uri.path == r2.parsed_uri.path
|
|
@@ -138,7 +138,7 @@ module VCR
|
|
|
138
138
|
|
|
139
139
|
register(:body_as_json) do |r1, r2|
|
|
140
140
|
begin
|
|
141
|
-
JSON.parse(r1.body) == JSON.parse(r2.body)
|
|
141
|
+
r1.body == r2.body || JSON.parse(r1.body) == JSON.parse(r2.body)
|
|
142
142
|
rescue JSON::ParserError
|
|
143
143
|
false
|
|
144
144
|
end
|
data/lib/vcr/structs.rb
CHANGED
|
@@ -62,7 +62,7 @@ module VCR
|
|
|
62
62
|
super
|
|
63
63
|
|
|
64
64
|
if body && !body.is_a?(String)
|
|
65
|
-
raise ArgumentError, "#{self.class} initialized with an invalid body: #{body.inspect}."
|
|
65
|
+
raise ArgumentError, "#{self.class} initialized with an invalid (non-String) body of class #{body.class}: #{body.inspect}."
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
# Ensure that the body is a raw string, in case the string instance
|
|
@@ -167,25 +167,6 @@ module VCR
|
|
|
167
167
|
end
|
|
168
168
|
end
|
|
169
169
|
|
|
170
|
-
# @private
|
|
171
|
-
module OrderedHashSerializer
|
|
172
|
-
def each
|
|
173
|
-
@ordered_keys.each do |key|
|
|
174
|
-
yield key, self[key] if has_key?(key)
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
if RUBY_VERSION.to_f > 1.8
|
|
179
|
-
# 1.9+ hashes are already ordered.
|
|
180
|
-
def self.apply_to(*args); end
|
|
181
|
-
else
|
|
182
|
-
def self.apply_to(hash, keys)
|
|
183
|
-
hash.instance_variable_set(:@ordered_keys, keys)
|
|
184
|
-
hash.extend self
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
|
|
189
170
|
# The request of an {HTTPInteraction}.
|
|
190
171
|
#
|
|
191
172
|
# @attr [Symbol] method the HTTP method (i.e. :head, :options, :get, :post, :put, :patch or :delete)
|
|
@@ -219,7 +200,7 @@ module VCR
|
|
|
219
200
|
'uri' => uri,
|
|
220
201
|
'body' => serializable_body,
|
|
221
202
|
'headers' => headers
|
|
222
|
-
}
|
|
203
|
+
}
|
|
223
204
|
end
|
|
224
205
|
|
|
225
206
|
# Constructs a new instance from a hash.
|
|
@@ -274,7 +255,7 @@ module VCR
|
|
|
274
255
|
end
|
|
275
256
|
|
|
276
257
|
# @return [Boolean] whether or not this request is being stubbed by an
|
|
277
|
-
# external library (such as WebMock
|
|
258
|
+
# external library (such as WebMock).
|
|
278
259
|
# @see #stubbed_by_vcr?
|
|
279
260
|
# @see #stubbed?
|
|
280
261
|
def externally_stubbed?
|
|
@@ -365,11 +346,10 @@ module VCR
|
|
|
365
346
|
{
|
|
366
347
|
'status' => status.to_hash,
|
|
367
348
|
'headers' => headers,
|
|
368
|
-
'body' => serializable_body
|
|
369
|
-
'http_version' => http_version
|
|
349
|
+
'body' => serializable_body
|
|
370
350
|
}.tap do |hash|
|
|
351
|
+
hash['http_version'] = http_version if http_version
|
|
371
352
|
hash['adapter_metadata'] = adapter_metadata unless adapter_metadata.empty?
|
|
372
|
-
OrderedHashSerializer.apply_to(hash, members)
|
|
373
353
|
end
|
|
374
354
|
end
|
|
375
355
|
|
|
@@ -403,6 +383,11 @@ module VCR
|
|
|
403
383
|
%w[ gzip deflate ].include? content_encoding
|
|
404
384
|
end
|
|
405
385
|
|
|
386
|
+
# Checks if VCR decompressed the response body
|
|
387
|
+
def vcr_decompressed?
|
|
388
|
+
adapter_metadata['vcr_decompressed']
|
|
389
|
+
end
|
|
390
|
+
|
|
406
391
|
# Decodes the compressed body and deletes evidence that it was ever compressed.
|
|
407
392
|
#
|
|
408
393
|
# @return self
|
|
@@ -412,11 +397,43 @@ module VCR
|
|
|
412
397
|
self.class.decompress(body, content_encoding) { |new_body|
|
|
413
398
|
self.body = new_body
|
|
414
399
|
update_content_length_header
|
|
400
|
+
adapter_metadata['vcr_decompressed'] = content_encoding
|
|
415
401
|
delete_header('Content-Encoding')
|
|
416
402
|
}
|
|
417
403
|
return self
|
|
418
404
|
end
|
|
419
405
|
|
|
406
|
+
# Recompresses the decompressed body according to adapter metadata.
|
|
407
|
+
#
|
|
408
|
+
# @raise [VCR::Errors::UnknownContentEncodingError] if the content encoding
|
|
409
|
+
# stored in the adapter metadata is unknown
|
|
410
|
+
def recompress
|
|
411
|
+
type = adapter_metadata['vcr_decompressed']
|
|
412
|
+
new_body = begin
|
|
413
|
+
case type
|
|
414
|
+
when 'gzip'
|
|
415
|
+
body_str = ''
|
|
416
|
+
args = [StringIO.new(body_str)]
|
|
417
|
+
args << { :encoding => 'ASCII-8BIT' } if ''.respond_to?(:encoding)
|
|
418
|
+
writer = Zlib::GzipWriter.new(*args)
|
|
419
|
+
writer.write(body)
|
|
420
|
+
writer.close
|
|
421
|
+
body_str
|
|
422
|
+
when 'deflate'
|
|
423
|
+
Zlib::Deflate.inflate(body)
|
|
424
|
+
when 'identity', NilClass
|
|
425
|
+
nil
|
|
426
|
+
else
|
|
427
|
+
raise Errors::UnknownContentEncodingError, "unknown content encoding: #{type}"
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
if new_body
|
|
431
|
+
self.body = new_body
|
|
432
|
+
update_content_length_header
|
|
433
|
+
headers['Content-Encoding'] = type
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
420
437
|
begin
|
|
421
438
|
require 'zlib'
|
|
422
439
|
require 'stringio'
|
|
@@ -437,9 +454,10 @@ module VCR
|
|
|
437
454
|
|
|
438
455
|
case type
|
|
439
456
|
when 'gzip'
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
yield Zlib::GzipReader.new(
|
|
457
|
+
gzip_reader_options = {}
|
|
458
|
+
gzip_reader_options[:encoding] = 'ASCII-8BIT' if ''.respond_to?(:encoding)
|
|
459
|
+
yield Zlib::GzipReader.new(StringIO.new(body),
|
|
460
|
+
**gzip_reader_options).read
|
|
443
461
|
when 'deflate'
|
|
444
462
|
yield Zlib::Inflate.inflate(body)
|
|
445
463
|
when 'identity', NilClass
|
|
@@ -463,7 +481,7 @@ module VCR
|
|
|
463
481
|
def to_hash
|
|
464
482
|
{
|
|
465
483
|
'code' => code, 'message' => message
|
|
466
|
-
}
|
|
484
|
+
}
|
|
467
485
|
end
|
|
468
486
|
|
|
469
487
|
# Constructs a new instance from a hash.
|
|
@@ -496,9 +514,7 @@ module VCR
|
|
|
496
514
|
'request' => request.to_hash,
|
|
497
515
|
'response' => response.to_hash,
|
|
498
516
|
'recorded_at' => recorded_at.httpdate
|
|
499
|
-
}
|
|
500
|
-
OrderedHashSerializer.apply_to(hash, members)
|
|
501
|
-
end
|
|
517
|
+
}
|
|
502
518
|
end
|
|
503
519
|
|
|
504
520
|
# Constructs a new instance from a hash.
|
|
@@ -23,10 +23,10 @@ module VCR
|
|
|
23
23
|
# Adds `Before` and `After` cucumber hooks for the named tags that
|
|
24
24
|
# will cause a VCR cassette to be used for scenarios with matching tags.
|
|
25
25
|
#
|
|
26
|
-
# @param [Array<String>]
|
|
27
|
-
#
|
|
28
|
-
# `:use_scenario_name => true` to automatically name the
|
|
29
|
-
#
|
|
26
|
+
# @param tag_names [Array<String,Hash>] the cucumber scenario tags. If
|
|
27
|
+
# the last argument is a hash it is treated as cassette options.
|
|
28
|
+
# - `:use_scenario_name => true` to automatically name the
|
|
29
|
+
# cassette according to the scenario name.
|
|
30
30
|
def tags(*tag_names)
|
|
31
31
|
original_options = tag_names.last.is_a?(::Hash) ? tag_names.pop : {}
|
|
32
32
|
tag_names.each do |tag_name|
|
|
@@ -47,10 +47,21 @@ module VCR
|
|
|
47
47
|
scenario.scenario_outline.name,
|
|
48
48
|
scenario.name.split("\n").first
|
|
49
49
|
].join("/")
|
|
50
|
-
|
|
50
|
+
elsif scenario.respond_to?(:feature)
|
|
51
51
|
[ scenario.feature.name.split("\n").first,
|
|
52
52
|
scenario.name.split("\n").first
|
|
53
53
|
].join("/")
|
|
54
|
+
elsif scenario.location.lines.min == scenario.location.lines.max
|
|
55
|
+
# test case from a regular scenario in cucumber version 4
|
|
56
|
+
[ scenario.location.file.split("/").last.split(".").first,
|
|
57
|
+
scenario.name.split("\n").first
|
|
58
|
+
].join("/")
|
|
59
|
+
else
|
|
60
|
+
# test case from a scenario with examples ("scenario outline") in cucumber version 4
|
|
61
|
+
[ scenario.location.file.split("/").last.split(".").first,
|
|
62
|
+
scenario.name.split("\n").first,
|
|
63
|
+
"Example at line #{scenario.location.lines.max}"
|
|
64
|
+
].join("/")
|
|
54
65
|
end
|
|
55
66
|
else
|
|
56
67
|
"cucumber_tags/#{tag_name.gsub(/\A@/, '')}"
|
|
@@ -5,38 +5,50 @@ module VCR
|
|
|
5
5
|
module Metadata
|
|
6
6
|
extend self
|
|
7
7
|
|
|
8
|
+
def vcr_cassette_name_for(metadata)
|
|
9
|
+
description =
|
|
10
|
+
if metadata[:description].empty?
|
|
11
|
+
# we have an "it { is_expected.to be something }" block
|
|
12
|
+
metadata[:scoped_id]
|
|
13
|
+
else
|
|
14
|
+
metadata[:description]
|
|
15
|
+
end
|
|
16
|
+
example_group =
|
|
17
|
+
if metadata.key?(:example_group)
|
|
18
|
+
metadata[:example_group]
|
|
19
|
+
else
|
|
20
|
+
metadata[:parent_example_group]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if example_group
|
|
24
|
+
[vcr_cassette_name_for(example_group), description].join('/')
|
|
25
|
+
else
|
|
26
|
+
description
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
8
30
|
def configure!
|
|
9
31
|
::RSpec.configure do |config|
|
|
10
|
-
vcr_cassette_name_for = lambda do |metadata|
|
|
11
|
-
description = if metadata[:description].empty?
|
|
12
|
-
# we have an "it { is_expected.to be something }" block
|
|
13
|
-
metadata[:scoped_id]
|
|
14
|
-
else
|
|
15
|
-
metadata[:description]
|
|
16
|
-
end
|
|
17
|
-
example_group = if metadata.key?(:example_group)
|
|
18
|
-
metadata[:example_group]
|
|
19
|
-
else
|
|
20
|
-
metadata[:parent_example_group]
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
if example_group
|
|
24
|
-
[vcr_cassette_name_for[example_group], description].join('/')
|
|
25
|
-
else
|
|
26
|
-
description
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
32
|
|
|
30
33
|
when_tagged_with_vcr = { :vcr => lambda { |v| !!v } }
|
|
31
34
|
|
|
32
35
|
config.before(:each, when_tagged_with_vcr) do |ex|
|
|
33
36
|
example = ex.respond_to?(:metadata) ? ex : ex.example
|
|
34
37
|
|
|
38
|
+
cassette_name = nil
|
|
35
39
|
options = example.metadata[:vcr]
|
|
36
|
-
options =
|
|
40
|
+
options = case options
|
|
41
|
+
when Hash #=> vcr: { cassette_name: 'foo' }
|
|
42
|
+
options.dup
|
|
43
|
+
when String #=> vcr: 'bar'
|
|
44
|
+
cassette_name = options.dup
|
|
45
|
+
{}
|
|
46
|
+
else #=> :vcr or vcr: true
|
|
47
|
+
{}
|
|
48
|
+
end
|
|
37
49
|
|
|
38
|
-
cassette_name
|
|
39
|
-
|
|
50
|
+
cassette_name ||= options.delete(:cassette_name) ||
|
|
51
|
+
VCR::RSpec::Metadata.vcr_cassette_name_for(example.metadata)
|
|
40
52
|
VCR.insert_cassette(cassette_name, options)
|
|
41
53
|
end
|
|
42
54
|
|