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