vcr 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|