vcr 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.travis.yml +0 -3
- data/CHANGELOG.md +32 -3
- data/Gemfile +33 -0
- data/Gemfile.lock +99 -119
- data/README.md +19 -7
- data/Rakefile +5 -9
- data/benchmarks/null_logging.rb +62 -0
- data/features/.nav +0 -1
- data/features/about_these_examples.md +1 -2
- data/features/cassettes/allow_unused_http_interactions.feature +15 -1
- data/features/cassettes/decompress.feature +6 -2
- data/features/cassettes/format.feature +20 -12
- data/features/cassettes/freezing_time.feature +68 -0
- data/features/configuration/cassette_library_dir.feature +5 -0
- data/features/configuration/preserve_exact_body_bytes.feature +5 -0
- data/features/configuration/uri_parser.feature +2 -4
- data/features/http_libraries/net_http.feature +1 -1
- data/features/request_matching/headers.feature +0 -1
- data/features/step_definitions/cli_steps.rb +1 -4
- data/features/test_frameworks/cucumber.feature +59 -0
- data/features/test_frameworks/rspec_metadata.feature +59 -1
- data/gemfiles/typhoeus_old.gemfile +19 -0
- data/gemfiles/typhoeus_old.gemfile.lock +84 -86
- data/lib/vcr.rb +12 -3
- data/lib/vcr/cassette.rb +32 -11
- data/lib/vcr/cassette/http_interaction_list.rb +3 -2
- data/lib/vcr/cassette/migrator.rb +1 -0
- data/lib/vcr/cassette/serializers/json.rb +1 -1
- data/lib/vcr/configuration.rb +17 -9
- data/lib/vcr/library_hooks/typhoeus.rb +3 -2
- data/lib/vcr/library_hooks/webmock.rb +1 -1
- data/lib/vcr/middleware/excon.rb +13 -1
- data/lib/vcr/middleware/faraday.rb +1 -0
- data/lib/vcr/request_handler.rb +1 -1
- data/lib/vcr/structs.rb +19 -4
- data/lib/vcr/test_frameworks/cucumber.rb +2 -2
- data/lib/vcr/test_frameworks/rspec.rb +10 -2
- data/lib/vcr/util/logger.rb +41 -7
- data/lib/vcr/version.rb +1 -1
- data/script/ci.sh +8 -1
- data/spec/acceptance/threading_spec.rb +6 -0
- data/spec/capture_warnings.rb +9 -1
- data/spec/spec_helper.rb +6 -2
- data/spec/support/configuration_stubbing.rb +8 -0
- data/spec/support/http_library_adapters.rb +1 -1
- data/spec/support/limited_uri.rb +1 -0
- data/spec/support/shared_example_groups/excon.rb +23 -1
- data/spec/support/shared_example_groups/hook_into_http_library.rb +12 -12
- data/spec/support/shared_example_groups/request_hooks.rb +1 -1
- data/spec/support/sinatra_app.rb +9 -0
- data/spec/support/vcr_localhost_server.rb +4 -25
- data/spec/support/vcr_stub_helpers.rb +1 -1
- data/spec/vcr/cassette/http_interaction_list_spec.rb +41 -14
- data/spec/vcr/cassette/migrator_spec.rb +1 -1
- data/spec/vcr/cassette/persisters_spec.rb +2 -2
- data/spec/vcr/cassette/serializers_spec.rb +13 -4
- data/spec/vcr/cassette_spec.rb +107 -58
- data/spec/vcr/configuration_spec.rb +23 -23
- data/spec/vcr/deprecations_spec.rb +9 -9
- data/spec/vcr/errors_spec.rb +6 -6
- data/spec/vcr/library_hooks/excon_spec.rb +15 -10
- data/spec/vcr/library_hooks/fakeweb_spec.rb +8 -8
- data/spec/vcr/library_hooks/faraday_spec.rb +1 -1
- data/spec/vcr/library_hooks/typhoeus_0.4_spec.rb +2 -2
- data/spec/vcr/library_hooks/typhoeus_spec.rb +68 -9
- data/spec/vcr/library_hooks/webmock_spec.rb +6 -10
- data/spec/vcr/middleware/faraday_spec.rb +33 -5
- data/spec/vcr/middleware/rack_spec.rb +2 -2
- data/spec/vcr/request_matcher_registry_spec.rb +11 -6
- data/spec/vcr/structs_spec.rb +114 -47
- data/spec/vcr/test_frameworks/cucumber_spec.rb +4 -4
- data/spec/vcr/util/hooks_spec.rb +2 -2
- data/spec/vcr/util/internet_connection_spec.rb +3 -3
- data/spec/vcr/util/version_checker_spec.rb +4 -4
- data/spec/vcr_spec.rb +22 -16
- data/vcr.gemspec +2 -31
- metadata +9 -328
- data/features/test_frameworks/shoulda.feature +0 -64
data/spec/support/limited_uri.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
shared_examples "Excon streaming" do
|
2
2
|
context "when Excon's streaming API is used" do
|
3
3
|
it 'properly records and plays back the response' do
|
4
|
-
VCR.
|
4
|
+
allow(VCR).to receive(:real_http_connections_allowed?).and_return(true)
|
5
5
|
recorded, played_back = [1, 2].map do
|
6
6
|
chunks = []
|
7
7
|
|
@@ -17,6 +17,28 @@ shared_examples "Excon streaming" do
|
|
17
17
|
expect(recorded).to eq(played_back)
|
18
18
|
expect(recorded).to eq("FOO!")
|
19
19
|
end
|
20
|
+
|
21
|
+
it 'properly records and plays back the response for unexpected status' do
|
22
|
+
allow(VCR).to receive(:real_http_connections_allowed?).and_return(true)
|
23
|
+
recorded, played_back = [1, 2].map do
|
24
|
+
chunks = []
|
25
|
+
|
26
|
+
VCR.use_cassette('excon_streaming_error', :record => :once) do
|
27
|
+
begin
|
28
|
+
Excon.get "http://localhost:#{VCR::SinatraApp.port}/404_not_200", :expects => 200, :response_block => lambda { |chunk, remaining_bytes, total_bytes|
|
29
|
+
chunks << chunk
|
30
|
+
}
|
31
|
+
rescue Excon::Errors::Error => e
|
32
|
+
chunks << e.response.body
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
chunks.join
|
37
|
+
end
|
38
|
+
|
39
|
+
expect(recorded).to eq(played_back)
|
40
|
+
expect(recorded).to eq('404 not 200')
|
41
|
+
end
|
20
42
|
end
|
21
43
|
end
|
22
44
|
|
@@ -109,7 +109,7 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
|
|
109
109
|
call_count = 0
|
110
110
|
[:has_interaction_matching?, :response_for].each do |method_name|
|
111
111
|
orig_meth = VCR.http_interactions.method(method_name)
|
112
|
-
VCR.http_interactions.
|
112
|
+
allow(VCR.http_interactions).to receive(method_name) do |*args|
|
113
113
|
call_count += 1
|
114
114
|
orig_meth.call(*args)
|
115
115
|
end
|
@@ -135,7 +135,7 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
|
|
135
135
|
VCR.insert_cassette('example')
|
136
136
|
disable_real_connections
|
137
137
|
|
138
|
-
VCR.
|
138
|
+
expect(VCR).to receive(:record_http_interaction) do |interaction|
|
139
139
|
expect(interaction.request.uri).to eq(request_url)
|
140
140
|
end
|
141
141
|
|
@@ -173,7 +173,7 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
|
|
173
173
|
|
174
174
|
it 'does not record requests that are directly stubbed' do
|
175
175
|
expect(VCR).to respond_to(:record_http_interaction)
|
176
|
-
VCR.
|
176
|
+
expect(VCR).not_to receive(:record_http_interaction)
|
177
177
|
|
178
178
|
VCR.use_cassette("temp") do
|
179
179
|
directly_stub_request(:get, request_url, "stubbed response")
|
@@ -220,7 +220,7 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
|
|
220
220
|
VCR.use_cassette('new_cassette', &request)
|
221
221
|
end
|
222
222
|
|
223
|
-
VCR.
|
223
|
+
expect(VCR).to receive(:record_http_interaction) do
|
224
224
|
expect(VCR.current_cassette.name).to eq('new_cassette')
|
225
225
|
end
|
226
226
|
|
@@ -370,7 +370,7 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
|
|
370
370
|
specify 'the after_http_request hook can be used to eject a cassette after the request is recorded' do
|
371
371
|
VCR.configuration.after_http_request { |request| VCR.eject_cassette }
|
372
372
|
|
373
|
-
VCR.
|
373
|
+
expect(VCR).to receive(:record_http_interaction) do |interaction|
|
374
374
|
expect(VCR.current_cassette).to be(inserted_cassette)
|
375
375
|
end
|
376
376
|
|
@@ -408,7 +408,7 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
|
|
408
408
|
end
|
409
409
|
|
410
410
|
describe '.stub_requests using specific match_attributes' do
|
411
|
-
before(:each) { VCR.
|
411
|
+
before(:each) { allow(VCR).to receive(:real_http_connections_allowed?).and_return(false) }
|
412
412
|
let(:interactions) { interactions_from('match_requests_on.yml') }
|
413
413
|
|
414
414
|
let(:normalized_interactions) do
|
@@ -486,17 +486,17 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
|
|
486
486
|
describe 'recording new http requests' do
|
487
487
|
let(:recorded_interaction) do
|
488
488
|
interaction = nil
|
489
|
-
VCR.
|
489
|
+
expect(VCR).to receive(:record_http_interaction) { |i| interaction = i }
|
490
490
|
make_http_request(:post, url, "the body", { 'X-Http-Foo' => 'bar' })
|
491
491
|
interaction
|
492
492
|
end
|
493
493
|
|
494
494
|
it 'does not record the request if the hook is disabled' do
|
495
495
|
VCR.library_hooks.exclusively_enabled :something_else do
|
496
|
-
VCR.
|
496
|
+
expect(VCR).not_to receive(:record_http_interaction)
|
497
497
|
make_http_request(:get, url)
|
498
498
|
end
|
499
|
-
end
|
499
|
+
end
|
500
500
|
|
501
501
|
it 'records the request uri' do
|
502
502
|
expect(recorded_interaction.request.uri).to eq(url)
|
@@ -534,7 +534,7 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
|
|
534
534
|
end
|
535
535
|
else
|
536
536
|
it 'does not allow real HTTP requests or record them' do
|
537
|
-
VCR.
|
537
|
+
expect(VCR).to receive(:record_http_interaction).never
|
538
538
|
expect { make_http_request(:get, url) }.to raise_error(NET_CONNECT_NOT_ALLOWED_ERROR)
|
539
539
|
end
|
540
540
|
end
|
@@ -542,7 +542,7 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
|
|
542
542
|
|
543
543
|
[true, false].each do |http_allowed|
|
544
544
|
context "when VCR.real_http_connections_allowed? is returning #{http_allowed}" do
|
545
|
-
before(:each) { VCR.
|
545
|
+
before(:each) { allow(VCR).to receive(:real_http_connections_allowed?).and_return(http_allowed) }
|
546
546
|
|
547
547
|
test_real_http_request(http_allowed, *other)
|
548
548
|
|
@@ -573,7 +573,7 @@ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library
|
|
573
573
|
end
|
574
574
|
|
575
575
|
it 'gets the stubbed responses when requests are made to http://example.com/foo, and does not record them' do
|
576
|
-
VCR.
|
576
|
+
expect(VCR).to receive(:record_http_interaction).never
|
577
577
|
expect(get_body_string(make_http_request(:get, 'http://example.com/foo'))).to match(/example\.com get response \d with path=foo/)
|
578
578
|
end
|
579
579
|
|
@@ -30,7 +30,7 @@ shared_examples_for "request hooks" do |library_hook_name, request_type|
|
|
30
30
|
|
31
31
|
specify "the #{hook} hook is not called if the library hook is disabled" do
|
32
32
|
expect(VCR.library_hooks).to respond_to(:disabled?)
|
33
|
-
VCR.library_hooks.
|
33
|
+
allow(VCR.library_hooks).to receive(:disabled?).and_return(true)
|
34
34
|
|
35
35
|
hook_called = false
|
36
36
|
VCR.configuration.send(hook) { |r| hook_called = true }
|
data/spec/support/sinatra_app.rb
CHANGED
@@ -20,6 +20,10 @@ module VCR
|
|
20
20
|
"FOO!"
|
21
21
|
end
|
22
22
|
|
23
|
+
get '/redirect-to-root' do
|
24
|
+
redirect to('/')
|
25
|
+
end
|
26
|
+
|
23
27
|
post '/foo' do
|
24
28
|
"FOO!"
|
25
29
|
end
|
@@ -38,6 +42,11 @@ module VCR
|
|
38
42
|
status 204
|
39
43
|
end
|
40
44
|
|
45
|
+
get '/404_not_200' do
|
46
|
+
status 404
|
47
|
+
'404 not 200'
|
48
|
+
end
|
49
|
+
|
41
50
|
# we use a global counter so that every response is different;
|
42
51
|
# this ensures that the test demonstrates that the response
|
43
52
|
# is being played back (and not running a 2nd real request)
|
@@ -57,31 +57,10 @@ module VCR
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def concurrently
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
exit # manually exit; otherwise this sub-process will re-run the specs that haven't run yet.
|
65
|
-
end
|
66
|
-
|
67
|
-
at_exit do
|
68
|
-
Process.kill('INT', pid)
|
69
|
-
begin
|
70
|
-
Process.wait(pid)
|
71
|
-
rescue Errno::ECHILD
|
72
|
-
# ignore this error...I think it means the child process has already exited.
|
73
|
-
end
|
74
|
-
end
|
75
|
-
else
|
76
|
-
# JRuby doesn't support forking.
|
77
|
-
# Rubinius does, but there's a weird issue with the booted? check not working,
|
78
|
-
# so we're just using a thread for now.
|
79
|
-
Thread.new { yield }
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def should_use_subprocess?
|
84
|
-
false
|
60
|
+
# JRuby doesn't support forking.
|
61
|
+
# Rubinius does, but there's a weird issue with the booted? check not working,
|
62
|
+
# so we're just using a thread for now.
|
63
|
+
Thread.new { yield }
|
85
64
|
end
|
86
65
|
|
87
66
|
def wait_until(timeout, error_message, &block)
|
@@ -5,7 +5,7 @@ module VCRStubHelpers
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def stub_requests(*args)
|
8
|
-
VCR.
|
8
|
+
allow(VCR).to receive(:http_interactions).and_return(VCR::Cassette::HTTPInteractionList.new(*args))
|
9
9
|
end
|
10
10
|
|
11
11
|
def http_interaction(url, response_body = "FOO!", status_code = 200)
|
@@ -2,17 +2,20 @@ require 'vcr/util/logger'
|
|
2
2
|
require 'vcr/cassette/http_interaction_list'
|
3
3
|
require 'vcr/request_matcher_registry'
|
4
4
|
require 'vcr/structs'
|
5
|
+
require 'support/configuration_stubbing'
|
5
6
|
|
6
7
|
module VCR
|
7
8
|
class Cassette
|
8
9
|
describe HTTPInteractionList do
|
10
|
+
include_context "configuration stubbing"
|
11
|
+
|
9
12
|
::RSpec::Matchers.define :respond_with do |expected|
|
10
13
|
match { |a| expected.nil? ? a.nil? : a.body == expected }
|
11
14
|
end
|
12
15
|
|
13
16
|
before(:each) do
|
14
|
-
VCR.
|
15
|
-
|
17
|
+
allow(VCR).to receive(:request_matchers).and_return(VCR::RequestMatcherRegistry.new)
|
18
|
+
allow(config).to receive(:logger).and_return(double.as_null_object)
|
16
19
|
end
|
17
20
|
|
18
21
|
def request_with(values)
|
@@ -87,22 +90,46 @@ module VCR
|
|
87
90
|
|
88
91
|
describe "#assert_no_unused_interactions?" do
|
89
92
|
it 'should raise a SkippedHTTPRequestError when there are unused interactions left' do
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
+
expect {
|
94
|
+
list.assert_no_unused_interactions!
|
95
|
+
}.to raise_error(Errors::UnusedHTTPInteractionError)
|
96
|
+
|
97
|
+
list.response_for(request_with(:method => :put))
|
98
|
+
expect {
|
99
|
+
list.assert_no_unused_interactions!
|
100
|
+
}.to raise_error(Errors::UnusedHTTPInteractionError)
|
93
101
|
end
|
94
102
|
|
95
103
|
it 'should raise nothing when there are no unused interactions left' do
|
96
104
|
[:put, :post, :post].each do |method|
|
97
105
|
list.response_for(request_with(:method => method))
|
98
106
|
end
|
99
|
-
|
107
|
+
|
108
|
+
expect {
|
109
|
+
list.assert_no_unused_interactions!
|
110
|
+
}.not_to raise_error
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when the null logger is in use' do
|
114
|
+
before { allow(config).to receive(:logger).and_return(Logger::Null) }
|
115
|
+
|
116
|
+
it 'includes formatted request details in the error message' do
|
117
|
+
expect {
|
118
|
+
list.assert_no_unused_interactions!
|
119
|
+
}.to raise_error(/\[put/)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'includes formatted response details in the error message' do
|
123
|
+
expect {
|
124
|
+
list.assert_no_unused_interactions!
|
125
|
+
}.to raise_error(/\[200 "put response"\]/)
|
126
|
+
end
|
100
127
|
end
|
101
128
|
end
|
102
129
|
|
103
130
|
describe "has_interaction_matching?" do
|
104
131
|
it 'returns false when the list is empty' do
|
105
|
-
expect(HTTPInteractionList.new([], [:method])).not_to have_interaction_matching(
|
132
|
+
expect(HTTPInteractionList.new([], [:method])).not_to have_interaction_matching(double)
|
106
133
|
end
|
107
134
|
|
108
135
|
it 'returns false when there is no matching interaction' do
|
@@ -136,10 +163,10 @@ module VCR
|
|
136
163
|
end
|
137
164
|
|
138
165
|
it "delegates to the parent list when it can't find a matching interaction" do
|
139
|
-
parent_list =
|
140
|
-
expect(HTTPInteractionList.new( [], [:method], false, parent_list)).to have_interaction_matching(
|
141
|
-
parent_list =
|
142
|
-
expect(HTTPInteractionList.new( [], [:method], false, parent_list)).not_to have_interaction_matching(
|
166
|
+
parent_list = double(:has_interaction_matching? => true)
|
167
|
+
expect(HTTPInteractionList.new( [], [:method], false, parent_list)).to have_interaction_matching(double)
|
168
|
+
parent_list = double(:has_interaction_matching? => false)
|
169
|
+
expect(HTTPInteractionList.new( [], [:method], false, parent_list)).not_to have_interaction_matching(double)
|
143
170
|
end
|
144
171
|
|
145
172
|
context 'when allow_playback_repeats is set to true' do
|
@@ -173,7 +200,7 @@ module VCR
|
|
173
200
|
|
174
201
|
describe "#response_for" do
|
175
202
|
it 'returns nil when the list is empty' do
|
176
|
-
expect(HTTPInteractionList.new([], [:method]).response_for(
|
203
|
+
expect(HTTPInteractionList.new([], [:method]).response_for(double)).to respond_with(nil)
|
177
204
|
end
|
178
205
|
|
179
206
|
it 'returns nil when there is no matching interaction' do
|
@@ -213,10 +240,10 @@ module VCR
|
|
213
240
|
end
|
214
241
|
|
215
242
|
it "delegates to the parent list when it can't find a matching interaction" do
|
216
|
-
parent_list =
|
243
|
+
parent_list = double(:response_for => response('parent'))
|
217
244
|
result = HTTPInteractionList.new(
|
218
245
|
[], [:method], false, parent_list
|
219
|
-
).response_for(
|
246
|
+
).response_for(double)
|
220
247
|
|
221
248
|
expect(result).to respond_with('parent')
|
222
249
|
end
|
@@ -130,7 +130,7 @@ EOF
|
|
130
130
|
subject { described_class.new(dir, out_io) }
|
131
131
|
|
132
132
|
before(:each) do
|
133
|
-
File.
|
133
|
+
allow(File).to receive(:mtime).with(file_name).and_return(filemtime)
|
134
134
|
end
|
135
135
|
|
136
136
|
it 'migrates a cassette from the 1.x to 2.x format' do
|
@@ -7,7 +7,7 @@ module VCR
|
|
7
7
|
context 'when there is already a persister registered for the given name' do
|
8
8
|
before(:each) do
|
9
9
|
subject[:foo] = :old_persister
|
10
|
-
subject.
|
10
|
+
allow(subject).to receive :warn
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'overrides the existing persister' do
|
@@ -16,7 +16,7 @@ module VCR
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'warns that there is a name collision' do
|
19
|
-
subject.
|
19
|
+
expect(subject).to receive(:warn).with(
|
20
20
|
/WARNING: There is already a VCR cassette persister registered for :foo\. Overriding it/
|
21
21
|
)
|
22
22
|
subject[:foo] = :new_persister
|
@@ -69,11 +69,20 @@ module VCR
|
|
69
69
|
it_behaves_like "a serializer", :json, "json", :lazily_loaded do
|
70
70
|
engines = {}
|
71
71
|
|
72
|
-
|
72
|
+
if RUBY_INTERPRETER == :jruby
|
73
|
+
# don't test yajl on jruby
|
74
|
+
elsif RUBY_VERSION.to_f < 1.9
|
75
|
+
engines[:yajl] = MultiJson::LoadError
|
76
|
+
else
|
77
|
+
engines[:yajl] = ArgumentError
|
78
|
+
end
|
73
79
|
|
74
80
|
if RUBY_VERSION =~ /1.9/
|
75
81
|
engines[:json_gem] = EncodingError
|
76
|
-
|
82
|
+
|
83
|
+
# Disable json_pure for now due to this bug:
|
84
|
+
# https://github.com/flori/json/issues/186
|
85
|
+
# engines[:json_pure] = EncodingError
|
77
86
|
end
|
78
87
|
|
79
88
|
engines.each do |engine, error|
|
@@ -114,7 +123,7 @@ module VCR
|
|
114
123
|
context 'when there is already a serializer registered for the given name' do
|
115
124
|
before(:each) do
|
116
125
|
subject[:foo] = :old_serializer
|
117
|
-
subject.
|
126
|
+
allow(subject).to receive :warn
|
118
127
|
end
|
119
128
|
|
120
129
|
it 'overrides the existing serializer' do
|
@@ -123,7 +132,7 @@ module VCR
|
|
123
132
|
end
|
124
133
|
|
125
134
|
it 'warns that there is a name collision' do
|
126
|
-
subject.
|
135
|
+
expect(subject).to receive(:warn).with(
|
127
136
|
/WARNING: There is already a VCR cassette serializer registered for :foo\. Overriding it/
|
128
137
|
)
|
129
138
|
subject[:foo] = :new_serializer
|
data/spec/vcr/cassette_spec.rb
CHANGED
@@ -8,16 +8,26 @@ describe VCR::Cassette do
|
|
8
8
|
VCR::HTTPInteraction.new(request, response).tap { |i| yield i if block_given? }
|
9
9
|
end
|
10
10
|
|
11
|
+
def stub_old_interactions(interactions)
|
12
|
+
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
|
13
|
+
|
14
|
+
hashes = interactions.map(&:to_hash)
|
15
|
+
allow(VCR.cassette_serializers[:yaml]).to receive(:deserialize).and_return({ 'http_interactions' => hashes })
|
16
|
+
allow(VCR::HTTPInteraction).to receive(:from_hash) do |hash|
|
17
|
+
interactions[hashes.index(hash)]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
11
21
|
describe '#file' do
|
12
22
|
it 'delegates the file resolution to the FileSystem persister' do
|
13
23
|
fs = VCR::Cassette::Persisters::FileSystem
|
14
24
|
expect(fs).to respond_to(:absolute_path_to_file).with(1).argument
|
15
|
-
fs.
|
25
|
+
expect(fs).to receive(:absolute_path_to_file).with("cassette name.yml") { "f.yml" }
|
16
26
|
expect(VCR::Cassette.new("cassette name").file).to eq("f.yml")
|
17
27
|
end
|
18
28
|
|
19
29
|
it 'raises a NotImplementedError when a different persister is used' do
|
20
|
-
VCR.cassette_persisters[:a] =
|
30
|
+
VCR.cassette_persisters[:a] = double
|
21
31
|
cassette = VCR::Cassette.new("f", :persist_with => :a)
|
22
32
|
expect { cassette.file }.to raise_error(NotImplementedError)
|
23
33
|
end
|
@@ -38,7 +48,7 @@ describe VCR::Cassette do
|
|
38
48
|
end
|
39
49
|
|
40
50
|
describe '#record_http_interaction' do
|
41
|
-
let(:the_interaction) {
|
51
|
+
let(:the_interaction) { double(:request => double(:method => :get).as_null_object).as_null_object }
|
42
52
|
|
43
53
|
it 'adds the interaction to #new_recorded_interactions' do
|
44
54
|
cassette = VCR::Cassette.new(:test_cassette)
|
@@ -63,8 +73,8 @@ describe VCR::Cassette do
|
|
63
73
|
let(:metadata) { subject.serializable_hash.reject { |k,v| k == "http_interactions" } }
|
64
74
|
|
65
75
|
it 'includes the hash form of all recorded interactions' do
|
66
|
-
interaction_1.
|
67
|
-
interaction_2.
|
76
|
+
allow(interaction_1).to receive(:to_hash).and_return({ "i" => 1, 'body' => '' })
|
77
|
+
allow(interaction_2).to receive(:to_hash).and_return({ "i" => 2, 'body' => '' })
|
68
78
|
expect(subject.serializable_hash).to include('http_interactions' => [{ "i" => 1, 'body' => '' }, { "i" => 2, 'body' => '' }])
|
69
79
|
end
|
70
80
|
|
@@ -131,11 +141,11 @@ describe VCR::Cassette do
|
|
131
141
|
let(:empty_cassette_yaml) { YAML.dump("http_interactions" => []) }
|
132
142
|
|
133
143
|
it 'optionally renders the file as ERB using the ERBRenderer' do
|
134
|
-
VCR::Cassette::Persisters::FileSystem.
|
144
|
+
allow(VCR::Cassette::Persisters::FileSystem).to receive(:[]).and_return(empty_cassette_yaml)
|
135
145
|
|
136
|
-
VCR::Cassette::ERBRenderer.
|
146
|
+
expect(VCR::Cassette::ERBRenderer).to receive(:new).with(
|
137
147
|
empty_cassette_yaml, anything, "foo"
|
138
|
-
).and_return(
|
148
|
+
).and_return(double('renderer', :render => empty_cassette_yaml))
|
139
149
|
|
140
150
|
VCR::Cassette.new('foo', :record => :new_episodes).http_interactions
|
141
151
|
end
|
@@ -145,9 +155,9 @@ describe VCR::Cassette do
|
|
145
155
|
# test that it overrides the default
|
146
156
|
VCR.configuration.default_cassette_options = { :erb => true }
|
147
157
|
|
148
|
-
VCR::Cassette::ERBRenderer.
|
158
|
+
expect(VCR::Cassette::ERBRenderer).to receive(:new).with(
|
149
159
|
anything, erb, anything
|
150
|
-
).and_return(
|
160
|
+
).and_return(double('renderer', :render => empty_cassette_yaml))
|
151
161
|
|
152
162
|
VCR::Cassette.new('foo', :record => :new_episodes, :erb => erb).http_interactions
|
153
163
|
end
|
@@ -155,9 +165,9 @@ describe VCR::Cassette do
|
|
155
165
|
it "passes #{erb.inspect} to the VCR::Cassette::ERBRenderer when it is the default :erb option and none is given" do
|
156
166
|
VCR.configuration.default_cassette_options = { :erb => erb }
|
157
167
|
|
158
|
-
VCR::Cassette::ERBRenderer.
|
168
|
+
expect(VCR::Cassette::ERBRenderer).to receive(:new).with(
|
159
169
|
anything, erb, anything
|
160
|
-
).and_return(
|
170
|
+
).and_return(double('renderer', :render => empty_cassette_yaml))
|
161
171
|
|
162
172
|
VCR::Cassette.new('foo', :record => :new_episodes).http_interactions
|
163
173
|
end
|
@@ -187,12 +197,12 @@ describe VCR::Cassette do
|
|
187
197
|
expect(VCR::Cassette.new('empty', :record => :none).send(:previously_recorded_interactions)).to eq([])
|
188
198
|
end
|
189
199
|
|
190
|
-
let(:custom_persister) {
|
200
|
+
let(:custom_persister) { double("custom persister") }
|
191
201
|
|
192
202
|
it 'reads from the configured persister' do
|
193
203
|
VCR.configuration.cassette_library_dir = nil
|
194
204
|
VCR.cassette_persisters[:foo] = custom_persister
|
195
|
-
custom_persister.
|
205
|
+
expect(custom_persister).to receive(:[]).with("abc.yml") { "" }
|
196
206
|
VCR::Cassette.new("abc", :persist_with => :foo).http_interactions
|
197
207
|
end
|
198
208
|
|
@@ -209,24 +219,15 @@ describe VCR::Cassette do
|
|
209
219
|
end
|
210
220
|
|
211
221
|
context "when :#{record_mode} is passed as the record option" do
|
212
|
-
def stub_old_interactions(interactions)
|
213
|
-
hashes = interactions.map(&:to_hash)
|
214
|
-
VCR.cassette_serializers[:yaml].stub(:deserialize => { 'http_interactions' => hashes })
|
215
|
-
VCR::HTTPInteraction.stub(:from_hash) do |hash|
|
216
|
-
interactions[hashes.index(hash)]
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
222
|
unless record_mode == :all
|
221
223
|
let(:interaction_1) { http_interaction { |i| i.request.uri = 'http://example.com/foo' } }
|
222
224
|
let(:interaction_2) { http_interaction { |i| i.request.uri = 'http://example.com/bar' } }
|
223
225
|
let(:interactions) { [interaction_1, interaction_2] }
|
224
|
-
before(:each) { VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec" }
|
225
226
|
|
226
227
|
it 'updates the content_length headers when given :update_content_length_header => true' do
|
227
228
|
stub_old_interactions(interactions)
|
228
|
-
interaction_1.response.
|
229
|
-
interaction_2.response.
|
229
|
+
expect(interaction_1.response).to receive(:update_content_length_header)
|
230
|
+
expect(interaction_2.response).to receive(:update_content_length_header)
|
230
231
|
|
231
232
|
VCR::Cassette.new('example', :record => record_mode, :update_content_length_header => true).http_interactions
|
232
233
|
end
|
@@ -234,8 +235,8 @@ describe VCR::Cassette do
|
|
234
235
|
[nil, false].each do |val|
|
235
236
|
it "does not update the content_lenth headers when given :update_content_length_header => #{val.inspect}" do
|
236
237
|
stub_old_interactions(interactions)
|
237
|
-
interaction_1.response.
|
238
|
-
interaction_2.response.
|
238
|
+
expect(interaction_1.response).not_to receive(:update_content_length_header)
|
239
|
+
expect(interaction_2.response).not_to receive(:update_content_length_header)
|
239
240
|
|
240
241
|
VCR::Cassette.new('example', :record => record_mode, :update_content_length_header => val).http_interactions
|
241
242
|
end
|
@@ -246,7 +247,7 @@ describe VCR::Cassette do
|
|
246
247
|
subject { VCR::Cassette.new(File.basename(file_name).gsub('.yml', ''), :record => record_mode, :re_record_interval => 7.days) }
|
247
248
|
|
248
249
|
context 'when the cassette file does not exist' do
|
249
|
-
before(:each) { File.
|
250
|
+
before(:each) { allow(File).to receive(:exist?).with(file_name).and_return(false) }
|
250
251
|
|
251
252
|
it "has :#{record_mode} for the record mode" do
|
252
253
|
expect(subject.record_mode).to eq(record_mode)
|
@@ -260,9 +261,9 @@ describe VCR::Cassette do
|
|
260
261
|
end
|
261
262
|
yaml = YAML.dump("http_interactions" => interactions)
|
262
263
|
|
263
|
-
File.
|
264
|
-
File.
|
265
|
-
File.
|
264
|
+
allow(File).to receive(:exist?).with(file_name).and_return(true)
|
265
|
+
allow(File).to receive(:size?).with(file_name).and_return(true)
|
266
|
+
allow(File).to receive(:read).with(file_name).and_return(yaml)
|
266
267
|
end
|
267
268
|
|
268
269
|
context 'and the earliest recorded interaction was recorded less than 7 days ago' do
|
@@ -285,12 +286,12 @@ describe VCR::Cassette do
|
|
285
286
|
] end
|
286
287
|
|
287
288
|
it "has :all for the record mode when there is an internet connection available" do
|
288
|
-
VCR::InternetConnection.
|
289
|
+
allow(VCR::InternetConnection).to receive(:available?).and_return(true)
|
289
290
|
expect(subject.record_mode).to eq(:all)
|
290
291
|
end
|
291
292
|
|
292
293
|
it "has :#{record_mode} for the record mode when there is no internet connection available" do
|
293
|
-
VCR::InternetConnection.
|
294
|
+
allow(VCR::InternetConnection).to receive(:available?).and_return(false)
|
294
295
|
expect(subject.record_mode).to eq(record_mode)
|
295
296
|
end
|
296
297
|
end
|
@@ -299,7 +300,7 @@ describe VCR::Cassette do
|
|
299
300
|
end
|
300
301
|
|
301
302
|
it 'does not load ignored interactions' do
|
302
|
-
VCR.request_ignorer.
|
303
|
+
allow(VCR.request_ignorer).to receive(:ignore?) do |request|
|
303
304
|
request.uri !~ /example\.com/
|
304
305
|
end
|
305
306
|
|
@@ -338,14 +339,14 @@ describe VCR::Cassette do
|
|
338
339
|
end
|
339
340
|
|
340
341
|
it "instantiates the http_interactions with parent_list set to a null list if given :exclusive => true" do
|
341
|
-
VCR.
|
342
|
+
allow(VCR).to receive(:http_interactions).and_return(double)
|
342
343
|
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
|
343
344
|
cassette = VCR::Cassette.new('example', :record => record_mode, :exclusive => true)
|
344
345
|
expect(cassette.http_interactions.parent_list).to be(VCR::Cassette::HTTPInteractionList::NullList)
|
345
346
|
end
|
346
347
|
|
347
348
|
it "instantiates the http_interactions with parent_list set to VCR.http_interactions if given :exclusive => false" do
|
348
|
-
VCR.
|
349
|
+
allow(VCR).to receive(:http_interactions).and_return(double)
|
349
350
|
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
|
350
351
|
cassette = VCR::Cassette.new('example', :record => record_mode, :exclusive => false)
|
351
352
|
expect(cassette.http_interactions.parent_list).to be(VCR.http_interactions)
|
@@ -353,7 +354,9 @@ describe VCR::Cassette do
|
|
353
354
|
|
354
355
|
if stub_requests
|
355
356
|
it 'invokes the before_playback hooks' do
|
356
|
-
VCR.configuration.
|
357
|
+
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
|
358
|
+
|
359
|
+
expect(VCR.configuration).to receive(:invoke_hook).with(
|
357
360
|
:before_playback,
|
358
361
|
an_instance_of(VCR::HTTPInteraction::HookAware),
|
359
362
|
an_instance_of(VCR::Cassette)
|
@@ -391,17 +394,63 @@ describe VCR::Cassette do
|
|
391
394
|
end
|
392
395
|
end
|
393
396
|
|
397
|
+
describe ".originally_recorded_at" do
|
398
|
+
it 'returns the earliest `recorded_at` timestamp' do
|
399
|
+
i1 = http_interaction { |i| i.recorded_at = Time.now - 1000 }
|
400
|
+
i2 = http_interaction { |i| i.recorded_at = Time.now - 10000 }
|
401
|
+
i3 = http_interaction { |i| i.recorded_at = Time.now - 100 }
|
402
|
+
|
403
|
+
stub_old_interactions([i1, i2, i3])
|
404
|
+
|
405
|
+
cassette = VCR::Cassette.new("example")
|
406
|
+
expect(cassette.originally_recorded_at).to eq(i2.recorded_at)
|
407
|
+
end
|
408
|
+
|
409
|
+
it 'records nil for a cassette that has no prior recorded interactions' do
|
410
|
+
stub_old_interactions([])
|
411
|
+
cassette = VCR::Cassette.new("example")
|
412
|
+
expect(cassette.originally_recorded_at).to be_nil
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
394
416
|
describe '#eject' do
|
395
|
-
let(:custom_persister) {
|
417
|
+
let(:custom_persister) { double("custom persister", :[] => nil) }
|
396
418
|
|
397
|
-
|
398
|
-
|
419
|
+
context "when :allow_unused_http_interactions is set to false" do
|
420
|
+
it 'asserts that there are no unused interactions' do
|
421
|
+
cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => false)
|
399
422
|
|
400
|
-
|
401
|
-
|
402
|
-
|
423
|
+
interaction_list = cassette.http_interactions
|
424
|
+
expect(interaction_list).to respond_to(:assert_no_unused_interactions!).with(0).arguments
|
425
|
+
expect(interaction_list).to receive(:assert_no_unused_interactions!)
|
403
426
|
|
404
|
-
|
427
|
+
cassette.eject
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'does not assert no unused interactions if there is an existing error' do
|
431
|
+
cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => false)
|
432
|
+
interaction_list = cassette.http_interactions
|
433
|
+
allow(interaction_list).to receive(:assert_no_unused_interactions!)
|
434
|
+
|
435
|
+
expect {
|
436
|
+
begin
|
437
|
+
raise "boom"
|
438
|
+
ensure
|
439
|
+
cassette.eject
|
440
|
+
end
|
441
|
+
}.to raise_error(/boom/)
|
442
|
+
|
443
|
+
expect(interaction_list).not_to have_received(:assert_no_unused_interactions!)
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'does not assert no unused interactions if :skip_no_unused_interactions_assertion is passed' do
|
447
|
+
cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => false)
|
448
|
+
|
449
|
+
interaction_list = cassette.http_interactions
|
450
|
+
expect(interaction_list).not_to receive(:assert_no_unused_interactions!)
|
451
|
+
|
452
|
+
cassette.eject(:skip_no_unused_interactions_assertion => true)
|
453
|
+
end
|
405
454
|
end
|
406
455
|
|
407
456
|
it 'does not assert that there are no unused interactions if allow_unused_http_interactions is set to true' do
|
@@ -409,7 +458,7 @@ describe VCR::Cassette do
|
|
409
458
|
|
410
459
|
interaction_list = cassette.http_interactions
|
411
460
|
expect(interaction_list).to respond_to(:assert_no_unused_interactions!)
|
412
|
-
interaction_list.
|
461
|
+
expect(interaction_list).not_to receive(:assert_no_unused_interactions!)
|
413
462
|
|
414
463
|
cassette.eject
|
415
464
|
end
|
@@ -420,7 +469,7 @@ describe VCR::Cassette do
|
|
420
469
|
cassette = VCR.insert_cassette("foo", :persist_with => :foo)
|
421
470
|
cassette.record_http_interaction http_interaction
|
422
471
|
|
423
|
-
custom_persister.
|
472
|
+
expect(custom_persister).to receive(:[]=).with("foo.yml", /http_interactions/)
|
424
473
|
|
425
474
|
cassette.eject
|
426
475
|
end
|
@@ -429,7 +478,7 @@ describe VCR::Cassette do
|
|
429
478
|
cassette = VCR::Cassette.new(:eject_test)
|
430
479
|
cassette.record_http_interaction http_interaction # so it has one
|
431
480
|
expect(cassette).to respond_to(:serializable_hash)
|
432
|
-
cassette.
|
481
|
+
allow(cassette).to receive(:serializable_hash).and_return({ "http_interactions" => [1, 3, 5] })
|
433
482
|
|
434
483
|
expect { cassette.eject }.to change { File.exist?(cassette.file) }.from(false).to(true)
|
435
484
|
saved_stuff = YAML.load_file(cassette.file)
|
@@ -443,12 +492,12 @@ describe VCR::Cassette do
|
|
443
492
|
]
|
444
493
|
|
445
494
|
cassette = VCR::Cassette.new('example', :tag => :foo)
|
446
|
-
cassette.
|
495
|
+
allow(cassette).to receive(:new_recorded_interactions).and_return(interactions)
|
447
496
|
|
448
|
-
VCR.configuration.
|
497
|
+
allow(VCR.configuration).to receive(:invoke_hook).and_return([false])
|
449
498
|
|
450
499
|
interactions.each do |i|
|
451
|
-
VCR.configuration.
|
500
|
+
expect(VCR.configuration).to receive(:invoke_hook).with(
|
452
501
|
:before_record,
|
453
502
|
an_instance_of(VCR::HTTPInteraction::HookAware),
|
454
503
|
cassette
|
@@ -463,11 +512,11 @@ describe VCR::Cassette do
|
|
463
512
|
interaction_2 = http_interaction { |i| i.request.uri = 'http://bar.com/'; i.response.body = 'res 2' }
|
464
513
|
|
465
514
|
hook_aware_interaction_1 = interaction_1.hook_aware
|
466
|
-
interaction_1.
|
515
|
+
allow(interaction_1).to receive(:hook_aware).and_return(hook_aware_interaction_1)
|
467
516
|
hook_aware_interaction_1.ignore!
|
468
517
|
|
469
518
|
cassette = VCR::Cassette.new('test_cassette')
|
470
|
-
cassette.
|
519
|
+
allow(cassette).to receive(:new_recorded_interactions).and_return([interaction_1, interaction_2])
|
471
520
|
cassette.eject
|
472
521
|
|
473
522
|
saved_recorded_interactions = ::YAML.load_file(cassette.file)
|
@@ -478,11 +527,11 @@ describe VCR::Cassette do
|
|
478
527
|
interaction_1 = http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' }
|
479
528
|
|
480
529
|
hook_aware_interaction_1 = interaction_1.hook_aware
|
481
|
-
interaction_1.
|
530
|
+
allow(interaction_1).to receive(:hook_aware).and_return(hook_aware_interaction_1)
|
482
531
|
hook_aware_interaction_1.ignore!
|
483
532
|
|
484
533
|
cassette = VCR::Cassette.new('test_cassette')
|
485
|
-
cassette.
|
534
|
+
allow(cassette).to receive(:new_recorded_interactions).and_return([interaction_1])
|
486
535
|
cassette.eject
|
487
536
|
|
488
537
|
expect(File).not_to exist(cassette.file)
|
@@ -491,7 +540,7 @@ describe VCR::Cassette do
|
|
491
540
|
it "writes the recorded interactions to a subdirectory if the cassette name includes a directory" do
|
492
541
|
recorded_interactions = [http_interaction { |i| i.response.body = "subdirectory response" }]
|
493
542
|
cassette = VCR::Cassette.new('subdirectory/test_cassette')
|
494
|
-
cassette.
|
543
|
+
allow(cassette).to receive(:new_recorded_interactions).and_return(recorded_interactions)
|
495
544
|
|
496
545
|
expect { cassette.eject }.to change { File.exist?(cassette.file) }.from(false).to(true)
|
497
546
|
saved_recorded_interactions = YAML.load_file(cassette.file)
|
@@ -509,7 +558,7 @@ describe VCR::Cassette do
|
|
509
558
|
|
510
559
|
it "does not re-write to disk the previously recorded interactions if there are no new ones" do
|
511
560
|
yaml_file = subject.file
|
512
|
-
File.
|
561
|
+
expect(File).not_to receive(:open).with(subject.file, 'w')
|
513
562
|
expect { subject.eject }.to_not change { File.mtime(yaml_file) }
|
514
563
|
end
|
515
564
|
|
@@ -531,8 +580,8 @@ describe VCR::Cassette do
|
|
531
580
|
let(:now) { Time.utc(2011, 6, 11, 12, 30) }
|
532
581
|
|
533
582
|
before(:each) do
|
534
|
-
Time.
|
535
|
-
subject.
|
583
|
+
allow(Time).to receive(:now).and_return(now)
|
584
|
+
allow(subject).to receive(:previously_recorded_interactions).and_return([interaction_foo_1])
|
536
585
|
subject.record_http_interaction(interaction_foo_2)
|
537
586
|
subject.record_http_interaction(interaction_bar)
|
538
587
|
subject.eject
|