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
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe VCR::Configuration do
|
4
4
|
describe '#cassette_library_dir=' do
|
5
5
|
let(:tmp_dir) { VCR::SPEC_ROOT + '/../tmp/cassette_library_dir/new_dir' }
|
6
|
-
after(:each)
|
6
|
+
after(:each) { FileUtils.rm_rf tmp_dir }
|
7
7
|
|
8
8
|
it 'creates the directory if it does not exist' do
|
9
9
|
expect { subject.cassette_library_dir = tmp_dir }.to change { File.exist?(tmp_dir) }.from(false).to(true)
|
@@ -12,6 +12,13 @@ describe VCR::Configuration do
|
|
12
12
|
it 'does not raise an error if given nil' do
|
13
13
|
expect { subject.cassette_library_dir = nil }.to_not raise_error
|
14
14
|
end
|
15
|
+
|
16
|
+
it 'resolves the given directory to an absolute path, so VCR continues to work even if the current directory changes' do
|
17
|
+
relative_dir = 'tmp/cassette_library_dir/new_dir'
|
18
|
+
subject.cassette_library_dir = relative_dir
|
19
|
+
absolute_dir = File.join(VCR::SPEC_ROOT.sub(/\/spec\z/, ''), relative_dir)
|
20
|
+
subject.cassette_library_dir.should eq(absolute_dir)
|
21
|
+
end
|
15
22
|
end
|
16
23
|
|
17
24
|
describe '#default_cassette_options' do
|
@@ -110,21 +117,51 @@ describe VCR::Configuration do
|
|
110
117
|
end
|
111
118
|
end
|
112
119
|
|
120
|
+
describe "#before_record hook", :with_monkey_patches => :fakeweb do
|
121
|
+
specify 'the request on the yielded interaction is not typed even though the request given to before_http_request is' do
|
122
|
+
before_record_req = before_request_req = nil
|
123
|
+
VCR.configure do |c|
|
124
|
+
c.before_http_request { |r| before_request_req = r }
|
125
|
+
c.before_record { |i| before_record_req = i.request }
|
126
|
+
end
|
127
|
+
|
128
|
+
VCR.use_cassette("example") do
|
129
|
+
::Net::HTTP.get_response(URI("http://localhost:#{VCR::SinatraApp.port}/foo"))
|
130
|
+
end
|
131
|
+
|
132
|
+
before_record_req.should_not respond_to(:type)
|
133
|
+
before_request_req.should respond_to(:type)
|
134
|
+
end unless RUBY_VERSION =~ /^1\.8/
|
135
|
+
end
|
136
|
+
|
137
|
+
[:before_record, :before_playback].each do |hook_type|
|
138
|
+
describe "##{hook_type}" do
|
139
|
+
it 'sets up a tag filter' do
|
140
|
+
called = false
|
141
|
+
VCR.configuration.send(hook_type, :my_tag) { called = true }
|
142
|
+
VCR.configuration.invoke_hook(hook_type, stub, stub(:tags => []))
|
143
|
+
called.should be_false
|
144
|
+
VCR.configuration.invoke_hook(hook_type, stub, stub(:tags => [:my_tag]))
|
145
|
+
called.should be_true
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
113
150
|
%w[ filter_sensitive_data define_cassette_placeholder ].each do |method|
|
114
151
|
describe "##{method}" do
|
115
|
-
let(:interaction) { mock('interaction') }
|
152
|
+
let(:interaction) { mock('interaction').as_null_object }
|
116
153
|
before(:each) { interaction.stub(:filter!) }
|
117
154
|
|
118
155
|
it 'adds a before_record hook that replaces the string returned by the block with the given string' do
|
119
156
|
subject.send(method, 'foo', &lambda { 'bar' })
|
120
157
|
interaction.should_receive(:filter!).with('bar', 'foo')
|
121
|
-
subject.invoke_hook(:before_record, interaction)
|
158
|
+
subject.invoke_hook(:before_record, interaction, stub.as_null_object)
|
122
159
|
end
|
123
160
|
|
124
161
|
it 'adds a before_playback hook that replaces the given string with the string returned by the block' do
|
125
162
|
subject.send(method, 'foo', &lambda { 'bar' })
|
126
163
|
interaction.should_receive(:filter!).with('foo', 'bar')
|
127
|
-
subject.invoke_hook(:before_playback, interaction)
|
164
|
+
subject.invoke_hook(:before_playback, interaction, stub.as_null_object)
|
128
165
|
end
|
129
166
|
|
130
167
|
it 'tags the before_record hook when given a tag' do
|
@@ -140,14 +177,14 @@ describe VCR::Configuration do
|
|
140
177
|
it 'yields the interaction to the block for the before_record hook' do
|
141
178
|
yielded_interaction = nil
|
142
179
|
subject.send(method, 'foo', &lambda { |i| yielded_interaction = i; 'bar' })
|
143
|
-
subject.invoke_hook(:before_record, interaction)
|
180
|
+
subject.invoke_hook(:before_record, interaction, stub.as_null_object)
|
144
181
|
yielded_interaction.should equal(interaction)
|
145
182
|
end
|
146
183
|
|
147
184
|
it 'yields the interaction to the block for the before_playback hook' do
|
148
185
|
yielded_interaction = nil
|
149
186
|
subject.send(method, 'foo', &lambda { |i| yielded_interaction = i; 'bar' })
|
150
|
-
subject.invoke_hook(:before_playback, interaction)
|
187
|
+
subject.invoke_hook(:before_playback, interaction, stub.as_null_object)
|
151
188
|
yielded_interaction.should equal(interaction)
|
152
189
|
end
|
153
190
|
end
|
@@ -169,4 +206,52 @@ describe VCR::Configuration do
|
|
169
206
|
subject.cassette_serializers[:custom].should be(custom_serializer)
|
170
207
|
end
|
171
208
|
end
|
209
|
+
|
210
|
+
describe "#preserve_exact_body_bytes_for?" do
|
211
|
+
def message_for(body)
|
212
|
+
stub(:body => body)
|
213
|
+
end
|
214
|
+
|
215
|
+
context "default hook" do
|
216
|
+
it "returns false when there is no current cassette" do
|
217
|
+
subject.preserve_exact_body_bytes_for?(message_for "string").should be_false
|
218
|
+
end
|
219
|
+
|
220
|
+
it "returns false when the current cassette has been created without the :preserve_exact_body_bytes option" do
|
221
|
+
VCR.insert_cassette('foo')
|
222
|
+
subject.preserve_exact_body_bytes_for?(message_for "string").should be_false
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'returns true when the current cassette has been created with the :preserve_exact_body_bytes option' do
|
226
|
+
VCR.insert_cassette('foo', :preserve_exact_body_bytes => true)
|
227
|
+
subject.preserve_exact_body_bytes_for?(message_for "string").should be_true
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
it "returns true when the configured block returns true" do
|
232
|
+
subject.preserve_exact_body_bytes { |msg| msg.body == "a" }
|
233
|
+
subject.preserve_exact_body_bytes_for?(message_for "a").should be_true
|
234
|
+
subject.preserve_exact_body_bytes_for?(message_for "b").should be_false
|
235
|
+
end
|
236
|
+
|
237
|
+
it "returns true when any of the registered blocks returns true" do
|
238
|
+
called_hooks = []
|
239
|
+
subject.preserve_exact_body_bytes { called_hooks << :hook_1; false }
|
240
|
+
subject.preserve_exact_body_bytes { called_hooks << :hook_2; true }
|
241
|
+
subject.preserve_exact_body_bytes_for?(message_for "a").should be_true
|
242
|
+
called_hooks.should eq([:hook_1, :hook_2])
|
243
|
+
end
|
244
|
+
|
245
|
+
it "invokes the configured hook with the http message and the current cassette" do
|
246
|
+
VCR.use_cassette('example') do |cassette|
|
247
|
+
cassette.should be_a(VCR::Cassette)
|
248
|
+
message = stub(:message)
|
249
|
+
|
250
|
+
yielded_objects = nil
|
251
|
+
subject.preserve_exact_body_bytes { |a, b| yielded_objects = [a, b] }
|
252
|
+
subject.preserve_exact_body_bytes_for?(message)
|
253
|
+
yielded_objects.should eq([message, cassette])
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
172
257
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe "Excon hook" do
|
3
|
+
describe "Excon hook", :with_monkey_patches => :excon do
|
4
4
|
# TODO: figure out a way to get disabling VCR to work with Excon
|
5
5
|
# and allow dirct excon stubs to work.
|
6
6
|
# def directly_stub_request(method, url, response_body)
|
@@ -9,20 +9,6 @@ describe "Excon hook" do
|
|
9
9
|
|
10
10
|
it_behaves_like 'a hook into an HTTP library', :excon, 'excon', :status_message_not_exposed
|
11
11
|
|
12
|
-
it_performs('version checking', 'Excon',
|
13
|
-
:valid => %w[ 0.6.5 0.7.9 ],
|
14
|
-
:too_low => %w[ 0.5.99 0.6.4 ],
|
15
|
-
:too_high => %w[ 0.8.0 1.0.0 ]
|
16
|
-
) do
|
17
|
-
before(:each) { @orig_version = Excon::VERSION }
|
18
|
-
after(:each) { Excon::VERSION = @orig_version }
|
19
|
-
|
20
|
-
# Cannot be regular method def as that raises a "dynamic constant assignment" error
|
21
|
-
define_method :stub_version do |version|
|
22
|
-
Excon::VERSION = version
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
12
|
context "when the query is specified as a hash option" do
|
27
13
|
let(:excon) { ::Excon.new("http://localhost:#{VCR::SinatraApp.port}/search") }
|
28
14
|
|
@@ -74,7 +60,51 @@ describe "Excon hook" do
|
|
74
60
|
}.to raise_error(Excon::Errors::Error)
|
75
61
|
end
|
76
62
|
|
77
|
-
|
63
|
+
it 'performs the right number of retries' do
|
64
|
+
connection = Excon.new("http://localhost:#{VCR::SinatraApp.port}/not_found")
|
65
|
+
|
66
|
+
# Excon define's .stub so we can't use RSpec's here...
|
67
|
+
Excon.should_receive(:new).at_least(:once).and_return(connection)
|
68
|
+
|
69
|
+
connection.extend Module.new {
|
70
|
+
def request_kernel_call_counts
|
71
|
+
@request_kernel_call_counts ||= Hash.new(0)
|
72
|
+
end
|
73
|
+
|
74
|
+
def request_kernel(params, &block)
|
75
|
+
request_kernel_call_counts[params[:mock]] += 1
|
76
|
+
super
|
77
|
+
end
|
78
|
+
}
|
79
|
+
|
80
|
+
expect {
|
81
|
+
connection.get(:expects => 200, :idempotent => true, :retry_limit => 3)
|
82
|
+
}.to raise_error(Excon::Errors::Error)
|
83
|
+
|
84
|
+
connection.request_kernel_call_counts.should eq(true => 3, false => 3)
|
85
|
+
end
|
86
|
+
|
87
|
+
def error_raised_by
|
88
|
+
yield
|
89
|
+
rescue => e
|
90
|
+
return e
|
91
|
+
else
|
92
|
+
raise "No error was raised"
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'raises the same error class as excon itself raises' do
|
96
|
+
real_error, stubbed_error = 2.times.map do
|
97
|
+
error_raised_by do
|
98
|
+
VCR.use_cassette('excon_error', :record => :once) do
|
99
|
+
Excon.get("http://localhost:#{VCR::SinatraApp.port}/not_found", :expects => 200)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
stubbed_error.class.should be(real_error.class)
|
105
|
+
end
|
106
|
+
|
107
|
+
it_behaves_like "request hooks", :excon, :recordable do
|
78
108
|
undef make_request
|
79
109
|
def make_request(disabled = false)
|
80
110
|
expect {
|
@@ -83,5 +113,13 @@ describe "Excon hook" do
|
|
83
113
|
end
|
84
114
|
end
|
85
115
|
end
|
116
|
+
|
117
|
+
describe "VCR.configuration.after_library_hooks_loaded hook" do
|
118
|
+
it 'disables the webmock excon adapter so it does not conflict with our typhoeus hook' do
|
119
|
+
::WebMock::HttpLibAdapters::ExconAdapter.should respond_to(:disable!)
|
120
|
+
::WebMock::HttpLibAdapters::ExconAdapter.should_receive(:disable!)
|
121
|
+
$excon_after_loaded_hook.conditionally_invoke
|
122
|
+
end
|
123
|
+
end
|
86
124
|
end
|
87
125
|
|
@@ -29,25 +29,20 @@ describe "FakeWeb hook", :with_monkey_patches => :fakeweb do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
it_performs('version checking', 'FakeWeb',
|
33
|
-
:valid => %w[ 1.3.0 1.3.1 1.3.99 ],
|
34
|
-
:too_low => %w[ 1.2.8 1.1.30 0.30.30 ],
|
35
|
-
:too_high => %w[ 1.4.0 1.10.0 2.0.0 ]
|
36
|
-
) do
|
37
|
-
before(:each) { @orig_version = FakeWeb::VERSION }
|
38
|
-
after(:each) { FakeWeb::VERSION = @orig_version }
|
39
|
-
|
40
|
-
# Cannot be regular method def as that raises a "dynamic constant assignment" error
|
41
|
-
define_method :stub_version do |version|
|
42
|
-
FakeWeb::VERSION = version
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
32
|
describe "some specific Net::HTTP edge cases" do
|
47
33
|
before(:each) do
|
48
34
|
VCR.stub(:real_http_connections_allowed? => true)
|
49
35
|
end
|
50
36
|
|
37
|
+
it 'records the request body when using #post_form' do
|
38
|
+
VCR.should_receive(:record_http_interaction) do |interaction|
|
39
|
+
interaction.request.body.should eq("q=ruby")
|
40
|
+
end
|
41
|
+
|
42
|
+
uri = URI("http://localhost:#{VCR::SinatraApp.port}/foo")
|
43
|
+
Net::HTTP.post_form(uri, 'q' => 'ruby')
|
44
|
+
end
|
45
|
+
|
51
46
|
it "does not record headers for which Net::HTTP sets defaults near the end of the real request" do
|
52
47
|
VCR.should_receive(:record_http_interaction) do |interaction|
|
53
48
|
interaction.request.headers.should_not have_key('content-type')
|
@@ -102,12 +97,8 @@ describe "FakeWeb hook", :with_monkey_patches => :fakeweb do
|
|
102
97
|
end
|
103
98
|
end
|
104
99
|
|
105
|
-
describe "VCR.configuration.after_library_hooks_loaded hook"
|
106
|
-
|
107
|
-
load "vcr/library_hooks/fakeweb.rb" # to re-add the hook since it's cleared by each test
|
108
|
-
end
|
109
|
-
|
110
|
-
let(:run_hook) { VCR.configuration.invoke_hook(:after_library_hooks_loaded) }
|
100
|
+
describe "VCR.configuration.after_library_hooks_loaded hook" do
|
101
|
+
let(:run_hook) { $fakeweb_after_loaded_hook.conditionally_invoke }
|
111
102
|
|
112
103
|
context 'when WebMock has been loaded' do
|
113
104
|
before(:each) { defined?(WebMock).should be_true }
|
@@ -133,7 +124,7 @@ describe "FakeWeb hook", :with_monkey_patches => :fakeweb do
|
|
133
124
|
VCR.configuration.ignore_request { |r| true }
|
134
125
|
end
|
135
126
|
|
136
|
-
it_behaves_like "request hooks", :fakeweb do
|
127
|
+
it_behaves_like "request hooks", :fakeweb, :ignored do
|
137
128
|
undef assert_expected_response
|
138
129
|
def assert_expected_response(response)
|
139
130
|
response.should be_nil
|
@@ -21,37 +21,10 @@ describe "Typhoeus hook", :with_monkey_patches => :typhoeus do
|
|
21
21
|
|
22
22
|
it_behaves_like 'a hook into an HTTP library', :typhoeus, 'typhoeus'
|
23
23
|
|
24
|
-
|
25
|
-
# stub the callback registration methods so we don't get a second
|
26
|
-
# callback registered when we load the typhoeus file below.
|
27
|
-
# Note that we have to use `stub!`, not `stub` because
|
28
|
-
# Typhoeus::Hydra defines its own stub method...so to use RSpec's,
|
29
|
-
# we use stub!
|
30
|
-
::Typhoeus::Hydra.stub!(:after_request_before_on_complete)
|
31
|
-
::Typhoeus::Hydra.stub!(:register_stub_finder)
|
32
|
-
end
|
33
|
-
|
34
|
-
it_performs('version checking', 'Typhoeus',
|
35
|
-
:valid => %w[ 0.3.2 0.3.10 ],
|
36
|
-
:too_low => %w[ 0.2.0 0.2.31 0.3.1 ],
|
37
|
-
:too_high => %w[ 0.4.0 1.0.0 ]
|
38
|
-
) do
|
39
|
-
before(:each) { @orig_version = Typhoeus::VERSION }
|
40
|
-
after(:each) { Typhoeus::VERSION = @orig_version }
|
41
|
-
|
42
|
-
# Cannot be regular method def as that raises a "dynamic constant assignment" error
|
43
|
-
define_method :stub_version do |version|
|
44
|
-
Typhoeus::VERSION = version
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
describe "VCR.configuration.after_library_hooks_loaded hook", :disable_warnings do
|
49
|
-
before(:each) { stub_callback_registration }
|
50
|
-
|
24
|
+
describe "VCR.configuration.after_library_hooks_loaded hook" do
|
51
25
|
it 'disables the webmock typhoeus adapter so it does not conflict with our typhoeus hook' do
|
52
|
-
load "vcr/library_hooks/typhoeus.rb" # to re-add the hook since it's cleared by each test
|
53
26
|
::WebMock::HttpLibAdapters::TyphoeusAdapter.should_receive(:disable!)
|
54
|
-
|
27
|
+
$typhoeus_after_loaded_hook.conditionally_invoke
|
55
28
|
end
|
56
29
|
end
|
57
30
|
end unless RUBY_PLATFORM == 'java'
|
@@ -18,8 +18,10 @@ describe "WebMock hook", :with_monkey_patches => :webmock do
|
|
18
18
|
::WebMock.stub_request(method, url).to_return(:body => response_body)
|
19
19
|
end
|
20
20
|
|
21
|
-
%w[net/http patron httpclient em-http-request curb typhoeus].each do |lib|
|
22
|
-
|
21
|
+
%w[net/http patron httpclient em-http-request curb typhoeus excon].each do |lib|
|
22
|
+
other = []
|
23
|
+
other << :status_message_not_exposed if lib == 'excon'
|
24
|
+
it_behaves_like 'a hook into an HTTP library', :webmock, lib, *other do
|
23
25
|
if lib == 'net/http'
|
24
26
|
def normalize_request_headers(headers)
|
25
27
|
headers.merge(DEFAULT_REQUEST_HEADERS)
|
@@ -27,20 +29,4 @@ describe "WebMock hook", :with_monkey_patches => :webmock do
|
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
30
|
-
|
31
|
-
it_performs('version checking', 'WebMock',
|
32
|
-
:valid => %w[ 1.7.8 1.7.10 1.7.99 ],
|
33
|
-
:too_low => %w[ 0.9.9 0.9.10 0.1.30 1.0.30 1.7.7 ],
|
34
|
-
:too_high => %w[ 1.8.0 1.10.0 2.0.0 ]
|
35
|
-
) do
|
36
|
-
|
37
|
-
def stub_callback_registration
|
38
|
-
::WebMock.stub(:after_request)
|
39
|
-
::WebMock.stub(:globally_stub_request)
|
40
|
-
end
|
41
|
-
|
42
|
-
def stub_version(version)
|
43
|
-
WebMock.stub(:version).and_return(version)
|
44
|
-
end
|
45
|
-
end
|
46
32
|
end
|
@@ -9,21 +9,6 @@ describe VCR::Middleware::Faraday do
|
|
9
9
|
:not_disableable
|
10
10
|
end
|
11
11
|
|
12
|
-
it_performs('version checking', 'Faraday',
|
13
|
-
:valid => %w[ 0.7.0 0.7.10 ],
|
14
|
-
:too_low => %w[ 0.6.9 0.5.99 ],
|
15
|
-
:too_high => %w[ 0.8.0 1.0.0 ],
|
16
|
-
:file => 'vcr/middleware/faraday.rb'
|
17
|
-
) do
|
18
|
-
before(:each) { @orig_version = Faraday::VERSION }
|
19
|
-
after(:each) { Faraday::VERSION = @orig_version }
|
20
|
-
|
21
|
-
# Cannot be regular method def as that raises a "dynamic constant assignment" error
|
22
|
-
define_method :stub_version do |version|
|
23
|
-
::Faraday::VERSION = version
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
12
|
context 'when making parallel requests' do
|
28
13
|
include VCRStubHelpers
|
29
14
|
let(:parallel_manager) { ::Faraday::Adapter::Typhoeus.setup_parallel_manager }
|
@@ -81,7 +66,7 @@ describe VCR::Middleware::Faraday do
|
|
81
66
|
end
|
82
67
|
end
|
83
68
|
|
84
|
-
it_behaves_like "request hooks", :faraday do
|
69
|
+
it_behaves_like "request hooks", :faraday, :recordable do
|
85
70
|
let!(:inserted_cassette) { VCR.insert_cassette('new_cassette') }
|
86
71
|
|
87
72
|
undef make_request
|
data/spec/vcr/structs_spec.rb
CHANGED
@@ -49,23 +49,13 @@ end
|
|
49
49
|
|
50
50
|
module VCR
|
51
51
|
describe HTTPInteraction do
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
sig.should_receive(attr).and_return(:the_value)
|
56
|
-
instance = described_class.new(sig, nil)
|
57
|
-
instance.send(attr).should eq(:the_value)
|
52
|
+
if ''.respond_to?(:encoding)
|
53
|
+
def body_hash(key, value)
|
54
|
+
{ key => value, 'encoding' => 'UTF-8' }
|
58
55
|
end
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
it 'returns false by default' do
|
63
|
-
should_not be_ignored
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'returns true when #ignore! has been called' do
|
67
|
-
subject.ignore!
|
68
|
-
should be_ignored
|
56
|
+
else
|
57
|
+
def body_hash(key, value)
|
58
|
+
{ key => value }
|
69
59
|
end
|
70
60
|
end
|
71
61
|
|
@@ -90,7 +80,7 @@ module VCR
|
|
90
80
|
'request' => {
|
91
81
|
'method' => 'get',
|
92
82
|
'uri' => 'http://foo.com/',
|
93
|
-
'body' => 'req body',
|
83
|
+
'body' => body_hash('string', 'req body'),
|
94
84
|
'headers' => { "bar" => ["foo"] }
|
95
85
|
},
|
96
86
|
'response' => {
|
@@ -99,7 +89,7 @@ module VCR
|
|
99
89
|
'message' => 'OK'
|
100
90
|
},
|
101
91
|
'headers' => { "foo" => ["bar"] },
|
102
|
-
'body' => 'res body',
|
92
|
+
'body' => body_hash('string', 'res body'),
|
103
93
|
'http_version' => '1.1'
|
104
94
|
},
|
105
95
|
'recorded_at' => "Wed, 04 May 2011 12:30:00 GMT"
|
@@ -125,9 +115,86 @@ module VCR
|
|
125
115
|
i = HTTPInteraction.from_hash(hash)
|
126
116
|
i.response.should eq(Response.new(ResponseStatus.new))
|
127
117
|
end
|
118
|
+
|
119
|
+
it 'decodes the base64 body string' do
|
120
|
+
hash['request']['body'] = body_hash('base64_string', Base64.encode64('req body'))
|
121
|
+
hash['response']['body'] = body_hash('base64_string', Base64.encode64('res body'))
|
122
|
+
|
123
|
+
i = HTTPInteraction.from_hash(hash)
|
124
|
+
i.request.body.should eq('req body')
|
125
|
+
i.response.body.should eq('res body')
|
126
|
+
end
|
127
|
+
|
128
|
+
if ''.respond_to?(:encoding)
|
129
|
+
it 'force encodes the decoded base64 string as the original encoding' do
|
130
|
+
string = "café"
|
131
|
+
string.force_encoding("US-ASCII")
|
132
|
+
string.should_not be_valid_encoding
|
133
|
+
|
134
|
+
hash['request']['body'] = { 'base64_string' => Base64.encode64(string.dup), 'encoding' => 'US-ASCII' }
|
135
|
+
hash['response']['body'] = { 'base64_string' => Base64.encode64(string.dup), 'encoding' => 'US-ASCII' }
|
136
|
+
|
137
|
+
i = HTTPInteraction.from_hash(hash)
|
138
|
+
i.request.body.encoding.name.should eq("US-ASCII")
|
139
|
+
i.response.body.encoding.name.should eq("US-ASCII")
|
140
|
+
i.request.body.bytes.to_a.should eq(string.bytes.to_a)
|
141
|
+
i.response.body.bytes.to_a.should eq(string.bytes.to_a)
|
142
|
+
i.request.body.should_not be_valid_encoding
|
143
|
+
i.response.body.should_not be_valid_encoding
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'does not attempt to force encode the decoded base64 string when there is no encoding given (i.e. if the cassette was recorded on ruby 1.8)' do
|
147
|
+
hash['request']['body'] = { 'base64_string' => Base64.encode64('foo') }
|
148
|
+
|
149
|
+
i = HTTPInteraction.from_hash(hash)
|
150
|
+
i.request.body.should eq('foo')
|
151
|
+
i.request.body.encoding.name.should eq("ASCII-8BIT")
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'tries to encode strings to the original encoding' do
|
155
|
+
hash['request']['body'] = { 'string' => "abc", 'encoding' => 'ISO-8859-1' }
|
156
|
+
hash['response']['body'] = { 'string' => "abc", 'encoding' => 'ISO-8859-1' }
|
157
|
+
|
158
|
+
i = HTTPInteraction.from_hash(hash)
|
159
|
+
i.request.body.should eq("abc")
|
160
|
+
i.response.body.should eq("abc")
|
161
|
+
i.request.body.encoding.name.should eq("ISO-8859-1")
|
162
|
+
i.response.body.encoding.name.should eq("ISO-8859-1")
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'when the string cannot be encoded as the original encoding' do
|
166
|
+
before do
|
167
|
+
Request.stub(:warn)
|
168
|
+
Response.stub(:warn)
|
169
|
+
|
170
|
+
hash['request']['body'] = { 'string' => "\xFAbc", 'encoding' => 'ISO-8859-1' }
|
171
|
+
hash['response']['body'] = { 'string' => "\xFAbc", 'encoding' => 'ISO-8859-1' }
|
172
|
+
expect { "\xFAbc".encode("ISO-8859-1") }.to raise_error(EncodingError)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'does not force the encoding' do
|
176
|
+
i = HTTPInteraction.from_hash(hash)
|
177
|
+
i.request.body.should eq("\xFAbc")
|
178
|
+
i.response.body.should eq("\xFAbc")
|
179
|
+
i.request.body.encoding.name.should_not eq("ISO-8859-1")
|
180
|
+
i.response.body.encoding.name.should_not eq("ISO-8859-1")
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'prints a warning and informs users of the :preserve_exact_body_bytes option' do
|
184
|
+
Request.should_receive(:warn).with(/ISO-8859-1.*preserve_exact_body_bytes/)
|
185
|
+
Response.should_receive(:warn).with(/ISO-8859-1.*preserve_exact_body_bytes/)
|
186
|
+
|
187
|
+
HTTPInteraction.from_hash(hash)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
128
191
|
end
|
129
192
|
|
130
193
|
describe "#to_hash" do
|
194
|
+
before(:each) do
|
195
|
+
VCR.stub_chain(:configuration, :preserve_exact_body_bytes_for?).and_return(false)
|
196
|
+
end
|
197
|
+
|
131
198
|
let(:hash) { interaction.to_hash }
|
132
199
|
|
133
200
|
it 'returns a nested hash containing all of the pertinent details' do
|
@@ -138,7 +205,7 @@ module VCR
|
|
138
205
|
hash['request'].should eq({
|
139
206
|
'method' => 'get',
|
140
207
|
'uri' => 'http://foo.com/',
|
141
|
-
'body' => 'req body',
|
208
|
+
'body' => body_hash('string', 'req body'),
|
142
209
|
'headers' => { "bar" => ["foo"] }
|
143
210
|
})
|
144
211
|
|
@@ -148,11 +215,25 @@ module VCR
|
|
148
215
|
'message' => 'OK'
|
149
216
|
},
|
150
217
|
'headers' => { "foo" => ["bar"] },
|
151
|
-
'body' => 'res body',
|
218
|
+
'body' => body_hash('string', 'res body'),
|
152
219
|
'http_version' => '1.1'
|
153
220
|
})
|
154
221
|
end
|
155
222
|
|
223
|
+
it 'encodes the body as base64 when the configuration is so set' do
|
224
|
+
VCR.stub_chain(:configuration, :preserve_exact_body_bytes_for?).and_return(true)
|
225
|
+
hash['request']['body'].should eq(body_hash('base64_string', Base64.encode64('req body')))
|
226
|
+
hash['response']['body'].should eq(body_hash('base64_string', Base64.encode64('res body')))
|
227
|
+
end
|
228
|
+
|
229
|
+
it "sets the string's original encoding", :if => ''.respond_to?(:encoding) do
|
230
|
+
interaction.request.body.force_encoding('ISO-8859-10')
|
231
|
+
interaction.response.body.force_encoding('ASCII-8BIT')
|
232
|
+
|
233
|
+
hash['request']['body']['encoding'].should eq('ISO-8859-10')
|
234
|
+
hash['response']['body']['encoding'].should eq('ASCII-8BIT')
|
235
|
+
end
|
236
|
+
|
156
237
|
def assert_yielded_keys(hash, *keys)
|
157
238
|
yielded_keys = []
|
158
239
|
hash.each { |k, v| yielded_keys << k }
|
@@ -166,65 +247,120 @@ module VCR
|
|
166
247
|
assert_yielded_keys hash['response']['status'], 'code', 'message'
|
167
248
|
end
|
168
249
|
end
|
250
|
+
end
|
169
251
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
252
|
+
describe HTTPInteraction::HookAware do
|
253
|
+
let(:response_status) { VCR::ResponseStatus.new(200, "OK foo") }
|
254
|
+
let(:body) { "The body foo this is (foo-Foo)" }
|
255
|
+
let(:headers) do {
|
256
|
+
'x-http-foo' => ['bar23', '23foo'],
|
257
|
+
'x-http-bar' => ['foo23', '18']
|
258
|
+
} end
|
259
|
+
|
260
|
+
let(:response) do
|
261
|
+
VCR::Response.new(
|
262
|
+
response_status,
|
263
|
+
headers.dup,
|
264
|
+
body.dup,
|
265
|
+
'1.1'
|
266
|
+
)
|
267
|
+
end
|
177
268
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
269
|
+
let(:request) do
|
270
|
+
VCR::Request.new(
|
271
|
+
:get,
|
272
|
+
'http://example-foo.com:80/foo/',
|
273
|
+
body.dup,
|
274
|
+
headers.dup
|
275
|
+
)
|
276
|
+
end
|
277
|
+
|
278
|
+
let(:interaction) { VCR::HTTPInteraction.new(request, response) }
|
279
|
+
subject { HTTPInteraction::HookAware.new(interaction) }
|
186
280
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
'http://example-foo.com:80/foo/',
|
191
|
-
body.dup,
|
192
|
-
headers.dup
|
193
|
-
)
|
281
|
+
describe '#ignored?' do
|
282
|
+
it 'returns false by default' do
|
283
|
+
should_not be_ignored
|
194
284
|
end
|
195
285
|
|
196
|
-
|
286
|
+
it 'returns true when #ignore! has been called' do
|
287
|
+
subject.ignore!
|
288
|
+
should be_ignored
|
289
|
+
end
|
290
|
+
end
|
197
291
|
|
198
|
-
|
292
|
+
describe '#filter!' do
|
293
|
+
let(:filtered) { subject.filter!('foo', 'AAA') }
|
199
294
|
|
200
295
|
it 'does nothing when given a blank argument' do
|
201
296
|
expect {
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
297
|
+
subject.filter!(nil, 'AAA')
|
298
|
+
subject.filter!('foo', nil)
|
299
|
+
subject.filter!("", 'AAA')
|
300
|
+
subject.filter!('foo', "")
|
206
301
|
}.not_to change { interaction }
|
207
302
|
end
|
208
303
|
|
209
304
|
[:request, :response].each do |part|
|
210
305
|
it "replaces the sensitive text in the #{part} header keys and values" do
|
211
|
-
|
306
|
+
filtered.send(part).headers.should eq({
|
212
307
|
'x-http-AAA' => ['bar23', '23AAA'],
|
213
308
|
'x-http-bar' => ['AAA23', '18']
|
214
309
|
})
|
215
310
|
end
|
216
311
|
|
217
312
|
it "replaces the sensitive text in the #{part} body" do
|
218
|
-
|
313
|
+
filtered.send(part).body.should eq("The body AAA this is (AAA-Foo)")
|
219
314
|
end
|
220
315
|
end
|
221
316
|
|
222
317
|
it 'replaces the sensitive text in the response status' do
|
223
|
-
|
318
|
+
filtered.response.status.message.should eq('OK AAA')
|
224
319
|
end
|
225
320
|
|
226
321
|
it 'replaces sensitive text in the request URI' do
|
227
|
-
|
322
|
+
filtered.request.uri.should eq('http://example-AAA.com/AAA/')
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
describe Request::Typed do
|
328
|
+
[:uri, :method, :headers, :body].each do |method|
|
329
|
+
it "delegates ##{method} to the request" do
|
330
|
+
request = stub(method => "delegated value")
|
331
|
+
Request::Typed.new(request, :type).send(method).should eq("delegated value")
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
describe "#type" do
|
336
|
+
it 'returns the initialized type' do
|
337
|
+
Request::Typed.new(stub, :ignored).type.should be(:ignored)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
[:ignored, :stubbed, :recordable, :unhandled].each do |type|
|
342
|
+
describe "##{type}?" do
|
343
|
+
it "returns true if the type is set to :#{type}" do
|
344
|
+
Request::Typed.new(stub, type).send("#{type}?").should be_true
|
345
|
+
end
|
346
|
+
|
347
|
+
it "returns false if the type is set to :other" do
|
348
|
+
Request::Typed.new(stub, :other).send("#{type}?").should be_false
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
describe "#real?" do
|
354
|
+
[:ignored, :recordable].each do |type|
|
355
|
+
it "returns true if the type is set to :#{type}" do
|
356
|
+
Request::Typed.new(stub, type).should be_real
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
[:stubbed, :unhandled].each do |type|
|
361
|
+
it "returns false if the type is set to :#{type}" do
|
362
|
+
Request::Typed.new(stub, type).should_not be_real
|
363
|
+
end
|
228
364
|
end
|
229
365
|
end
|
230
366
|
end
|
@@ -277,25 +413,22 @@ module VCR
|
|
277
413
|
end
|
278
414
|
end
|
279
415
|
|
280
|
-
describe
|
416
|
+
describe Request::FiberAware do
|
417
|
+
subject { Request::FiberAware.new(Request.new) }
|
418
|
+
|
281
419
|
it 'adds a #proceed method that yields in a fiber' do
|
282
420
|
fiber = Fiber.new do |request|
|
283
421
|
request.proceed
|
284
422
|
:done
|
285
423
|
end
|
286
424
|
|
287
|
-
fiber.resume(subject
|
425
|
+
fiber.resume(subject).should be_nil
|
288
426
|
fiber.resume.should eq(:done)
|
289
427
|
end
|
290
428
|
|
291
429
|
it 'can be cast to a proc' do
|
292
|
-
|
293
|
-
|
294
|
-
lambda(&request).call
|
295
|
-
end
|
296
|
-
|
297
|
-
it 'returns the request' do
|
298
|
-
subject.fiber_aware.should be(subject)
|
430
|
+
Fiber.should_receive(:yield)
|
431
|
+
lambda(&subject).call
|
299
432
|
end
|
300
433
|
end if RUBY_VERSION > '1.9'
|
301
434
|
|