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
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
shared_examples_for "request hooks" do |library_hook_name|
|
|
1
|
+
shared_examples_for "request hooks" do |library_hook_name, request_type|
|
|
2
2
|
let(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
|
|
3
3
|
|
|
4
|
-
before(:each) do
|
|
5
|
-
# ensure that all the other library hooks are disabled so that we don't
|
|
6
|
-
# get double-hookage (such as for WebMock and Typhoeus both invoking the
|
|
7
|
-
# hooks for a typhoeus request)
|
|
8
|
-
VCR.library_hooks.stub(:disabled?) { |lib_name| lib_name != library_hook_name }
|
|
9
|
-
end
|
|
10
|
-
|
|
11
4
|
def make_request(disabled = false)
|
|
12
5
|
make_http_request(:get, request_url)
|
|
13
6
|
end
|
|
@@ -45,6 +38,14 @@ shared_examples_for "request hooks" do |library_hook_name|
|
|
|
45
38
|
make_request(:disabled)
|
|
46
39
|
hook_called.should be_false
|
|
47
40
|
end
|
|
41
|
+
|
|
42
|
+
specify "the #type of the yielded request given to the #{hook} hook is #{request_type}" do
|
|
43
|
+
request = nil
|
|
44
|
+
VCR.configuration.send(hook) { |r| request = r }
|
|
45
|
+
|
|
46
|
+
make_request
|
|
47
|
+
request.type.should be(request_type)
|
|
48
|
+
end
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
specify "the after_http_request hook yields the response if there is one and the second block arg is given" do
|
data/spec/support/sinatra_app.rb
CHANGED
|
@@ -2,6 +2,8 @@ require 'sinatra'
|
|
|
2
2
|
|
|
3
3
|
module VCR
|
|
4
4
|
class SinatraApp < ::Sinatra::Base
|
|
5
|
+
disable :protection
|
|
6
|
+
|
|
5
7
|
get '/' do
|
|
6
8
|
"GET to root"
|
|
7
9
|
end
|
|
@@ -28,7 +30,7 @@ module VCR
|
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
get '/set-cookie-headers/2' do
|
|
31
|
-
headers 'Set-Cookie' => %w[ foo
|
|
33
|
+
headers 'Set-Cookie' => %w[ bar foo ]
|
|
32
34
|
'header set'
|
|
33
35
|
end
|
|
34
36
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'vcr/util/logger'
|
|
1
2
|
require 'vcr/cassette/http_interaction_list'
|
|
2
3
|
require 'vcr/request_matcher_registry'
|
|
3
4
|
require 'vcr/structs'
|
|
@@ -5,57 +6,14 @@ require 'uri'
|
|
|
5
6
|
|
|
6
7
|
module VCR
|
|
7
8
|
class Cassette
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
HTTPInteractionList.new([], [:method]).send(method, stub).should respond_with(nil)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
it 'returns nil when there is no matching interaction' do
|
|
15
|
-
HTTPInteractionList.new([
|
|
16
|
-
interaction('foo', :method => :post),
|
|
17
|
-
interaction('foo', :method => :put)
|
|
18
|
-
], [:method]).send(method,
|
|
19
|
-
request_with(:method => :get)
|
|
20
|
-
).should respond_with(nil)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it 'returns the first matching interaction' do
|
|
24
|
-
list = HTTPInteractionList.new([
|
|
25
|
-
interaction('put response', :method => :put),
|
|
26
|
-
interaction('post response 1', :method => :post),
|
|
27
|
-
interaction('post response 2', :method => :post)
|
|
28
|
-
], [:method])
|
|
29
|
-
|
|
30
|
-
list.send(method, request_with(:method => :post)).should respond_with("post response 1")
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it 'invokes each matcher block to find the matching interaction' do
|
|
34
|
-
VCR.request_matchers.register(:foo) { |r1, r2| true }
|
|
35
|
-
VCR.request_matchers.register(:bar) { |r1, r2| true }
|
|
36
|
-
|
|
37
|
-
calls = 0
|
|
38
|
-
VCR.request_matchers.register(:baz) { |r1, r2| calls += 1; calls == 2 }
|
|
39
|
-
|
|
40
|
-
list = HTTPInteractionList.new([
|
|
41
|
-
interaction('response', :method => :put)
|
|
42
|
-
], [:foo, :bar, :baz])
|
|
43
|
-
|
|
44
|
-
list.send(method, request_with(:method => :post)).should respond_with(nil)
|
|
45
|
-
list.send(method, request_with(:method => :post)).should respond_with('response')
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
it "delegates to the parent list when it can't find a matching interaction" do
|
|
49
|
-
parent_list = mock(method => response('parent'))
|
|
50
|
-
HTTPInteractionList.new(
|
|
51
|
-
[], [:method], false, parent_list
|
|
52
|
-
).send(method, stub).should respond_with('parent')
|
|
9
|
+
describe HTTPInteractionList do
|
|
10
|
+
::RSpec::Matchers.define :respond_with do |expected|
|
|
11
|
+
match { |a| expected.nil? ? a.nil? : a.body == expected }
|
|
53
12
|
end
|
|
54
|
-
end
|
|
55
13
|
|
|
56
|
-
describe HTTPInteractionList do
|
|
57
14
|
before(:each) do
|
|
58
15
|
VCR.stub(:request_matchers => VCR::RequestMatcherRegistry.new)
|
|
16
|
+
VCR.stub_chain(:configuration, :debug_logger).and_return(stub.as_null_object)
|
|
59
17
|
end
|
|
60
18
|
|
|
61
19
|
def request_with(values)
|
|
@@ -67,7 +25,10 @@ module VCR
|
|
|
67
25
|
end
|
|
68
26
|
|
|
69
27
|
def response(body)
|
|
70
|
-
VCR::Response.new.tap
|
|
28
|
+
VCR::Response.new.tap do |r|
|
|
29
|
+
r.body = body
|
|
30
|
+
r.status = VCR::ResponseStatus.new(200)
|
|
31
|
+
end
|
|
71
32
|
end
|
|
72
33
|
|
|
73
34
|
def interaction(body, request_values)
|
|
@@ -125,14 +86,118 @@ module VCR
|
|
|
125
86
|
end
|
|
126
87
|
end
|
|
127
88
|
|
|
128
|
-
describe "
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
89
|
+
describe "has_interaction_matching?" do
|
|
90
|
+
it 'returns false when the list is empty' do
|
|
91
|
+
HTTPInteractionList.new([], [:method]).should_not have_interaction_matching(stub)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'returns false when there is no matching interaction' do
|
|
95
|
+
list.should_not have_interaction_matching(request_with(:method => :get))
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'returns true when there is a matching interaction' do
|
|
99
|
+
list.should have_interaction_matching(request_with(:method => :post))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'does not consume the interactions when they match' do
|
|
103
|
+
list.should have_interaction_matching(request_with(:method => :post))
|
|
104
|
+
list.remaining_unused_interaction_count.should eq(3)
|
|
105
|
+
list.should have_interaction_matching(request_with(:method => :post))
|
|
106
|
+
list.remaining_unused_interaction_count.should eq(3)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'invokes each matcher block to find the matching interaction' do
|
|
110
|
+
VCR.request_matchers.register(:foo) { |r1, r2| true }
|
|
111
|
+
VCR.request_matchers.register(:bar) { |r1, r2| true }
|
|
112
|
+
|
|
113
|
+
calls = 0
|
|
114
|
+
VCR.request_matchers.register(:baz) { |r1, r2| calls += 1; calls == 2 }
|
|
115
|
+
|
|
116
|
+
list = HTTPInteractionList.new([
|
|
117
|
+
interaction('response', :method => :put)
|
|
118
|
+
], [:foo, :bar, :baz])
|
|
119
|
+
|
|
120
|
+
list.should_not have_interaction_matching(request_with(:method => :post))
|
|
121
|
+
list.should have_interaction_matching(request_with(:method => :post))
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "delegates to the parent list when it can't find a matching interaction" do
|
|
125
|
+
parent_list = mock(:has_interaction_matching? => true)
|
|
126
|
+
HTTPInteractionList.new( [], [:method], false, parent_list).should have_interaction_matching(stub)
|
|
127
|
+
parent_list = mock(:has_interaction_matching? => false)
|
|
128
|
+
HTTPInteractionList.new( [], [:method], false, parent_list).should_not have_interaction_matching(stub)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
context 'when allow_playback_repeats is set to true' do
|
|
132
|
+
let(:allow_playback_repeats) { true }
|
|
133
|
+
|
|
134
|
+
it 'considers used interactions' do
|
|
135
|
+
list.response_for(request_with(:method => :put))
|
|
136
|
+
|
|
137
|
+
10.times.map {
|
|
138
|
+
list.has_interaction_matching?(request_with(:method => :put))
|
|
139
|
+
}.should eq([true] * 10)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
context 'when allow_playback_repeats is set to false' do
|
|
144
|
+
let(:allow_playback_repeats) { false }
|
|
145
|
+
|
|
146
|
+
it 'does not consider used interactions' do
|
|
147
|
+
list.response_for(request_with(:method => :put))
|
|
148
|
+
|
|
149
|
+
10.times.map {
|
|
150
|
+
list.has_interaction_matching?(request_with(:method => :put))
|
|
151
|
+
}.should eq([false] * 10)
|
|
134
152
|
end
|
|
135
153
|
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
describe "#response_for" do
|
|
157
|
+
it 'returns nil when the list is empty' do
|
|
158
|
+
HTTPInteractionList.new([], [:method]).response_for(stub).should respond_with(nil)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it 'returns nil when there is no matching interaction' do
|
|
162
|
+
HTTPInteractionList.new([
|
|
163
|
+
interaction('foo', :method => :post),
|
|
164
|
+
interaction('foo', :method => :put)
|
|
165
|
+
], [:method]).response_for(
|
|
166
|
+
request_with(:method => :get)
|
|
167
|
+
).should respond_with(nil)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'returns the first matching interaction' do
|
|
171
|
+
list = HTTPInteractionList.new([
|
|
172
|
+
interaction('put response', :method => :put),
|
|
173
|
+
interaction('post response 1', :method => :post),
|
|
174
|
+
interaction('post response 2', :method => :post)
|
|
175
|
+
], [:method])
|
|
176
|
+
|
|
177
|
+
list.response_for(request_with(:method => :post)).should respond_with("post response 1")
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it 'invokes each matcher block to find the matching interaction' do
|
|
181
|
+
VCR.request_matchers.register(:foo) { |r1, r2| true }
|
|
182
|
+
VCR.request_matchers.register(:bar) { |r1, r2| true }
|
|
183
|
+
|
|
184
|
+
calls = 0
|
|
185
|
+
VCR.request_matchers.register(:baz) { |r1, r2| calls += 1; calls == 2 }
|
|
186
|
+
|
|
187
|
+
list = HTTPInteractionList.new([
|
|
188
|
+
interaction('response', :method => :put)
|
|
189
|
+
], [:foo, :bar, :baz])
|
|
190
|
+
|
|
191
|
+
list.response_for(request_with(:method => :post)).should respond_with(nil)
|
|
192
|
+
list.response_for(request_with(:method => :post)).should respond_with('response')
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it "delegates to the parent list when it can't find a matching interaction" do
|
|
196
|
+
parent_list = mock(:response_for => response('parent'))
|
|
197
|
+
HTTPInteractionList.new(
|
|
198
|
+
[], [:method], false, parent_list
|
|
199
|
+
).response_for(stub).should respond_with('parent')
|
|
200
|
+
end
|
|
136
201
|
|
|
137
202
|
it 'consumes the first matching interaction so that it will not be used again' do
|
|
138
203
|
list.response_for(request_with(:method => :post)).body.should eq("post response 1")
|
|
@@ -47,7 +47,9 @@ http_interactions:
|
|
|
47
47
|
- request:
|
|
48
48
|
method: get
|
|
49
49
|
uri: http://example.com/foo
|
|
50
|
-
body:
|
|
50
|
+
body:
|
|
51
|
+
encoding: US-ASCII
|
|
52
|
+
string: ""
|
|
51
53
|
headers: {}
|
|
52
54
|
|
|
53
55
|
response:
|
|
@@ -59,13 +61,17 @@ http_interactions:
|
|
|
59
61
|
- text/html;charset=utf-8
|
|
60
62
|
Content-Length:
|
|
61
63
|
- "9"
|
|
62
|
-
body:
|
|
64
|
+
body:
|
|
65
|
+
encoding: UTF-8
|
|
66
|
+
string: Hello foo
|
|
63
67
|
http_version: "1.1"
|
|
64
68
|
recorded_at: Wed, 04 May 2011 12:30:00 GMT
|
|
65
69
|
- request:
|
|
66
70
|
method: get
|
|
67
71
|
uri: http://localhost:7777/bar
|
|
68
|
-
body:
|
|
72
|
+
body:
|
|
73
|
+
encoding: US-ASCII
|
|
74
|
+
string: ""
|
|
69
75
|
headers: {}
|
|
70
76
|
|
|
71
77
|
response:
|
|
@@ -77,7 +83,9 @@ http_interactions:
|
|
|
77
83
|
- text/html;charset=utf-8
|
|
78
84
|
Content-Length:
|
|
79
85
|
- "9"
|
|
80
|
-
body:
|
|
86
|
+
body:
|
|
87
|
+
encoding: UTF-8
|
|
88
|
+
string: Hello bar
|
|
81
89
|
http_version: "1.1"
|
|
82
90
|
recorded_at: Wed, 04 May 2011 12:30:00 GMT
|
|
83
91
|
recorded_with: VCR #{VCR.version}
|
|
@@ -92,6 +100,11 @@ EOF
|
|
|
92
100
|
FileUtils.mkdir_p dir
|
|
93
101
|
end
|
|
94
102
|
|
|
103
|
+
before(:each) do
|
|
104
|
+
# the encoding won't be set on rubies that don't support it
|
|
105
|
+
updated_contents.gsub!(/^\s+encoding:.*$/, '')
|
|
106
|
+
end unless ''.respond_to?(:encoding)
|
|
107
|
+
|
|
95
108
|
# JRuby serializes YAML with some slightly different whitespace.
|
|
96
109
|
before(:each) do
|
|
97
110
|
[original_contents, updated_contents].each do |contents|
|
|
@@ -123,7 +136,7 @@ EOF
|
|
|
123
136
|
it 'migrates a cassette from the 1.x to 2.x format' do
|
|
124
137
|
File.open(file_name, 'w') { |f| f.write(original_contents) }
|
|
125
138
|
subject.migrate!
|
|
126
|
-
|
|
139
|
+
YAML.load_file(file_name).should eq(YAML.load(updated_contents))
|
|
127
140
|
output.should match(/Migrated example.yml/)
|
|
128
141
|
end
|
|
129
142
|
|
|
@@ -145,7 +158,7 @@ EOF
|
|
|
145
158
|
modified_contents = original_contents.gsub('example.com', '<HOST>')
|
|
146
159
|
File.open(file_name, 'w') { |f| f.write(modified_contents) }
|
|
147
160
|
subject.migrate!
|
|
148
|
-
|
|
161
|
+
YAML.load_file(file_name).should eq(YAML.load(updated_contents.gsub('example.com', '<HOST>:80')))
|
|
149
162
|
end
|
|
150
163
|
|
|
151
164
|
it 'ignores files that are empty' do
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'vcr/cassette/serializers'
|
|
2
|
+
require 'multi_json'
|
|
2
3
|
begin
|
|
3
4
|
require 'psych' # ensure psych is loaded for these tests if its available
|
|
4
5
|
rescue LoadError
|
|
@@ -7,11 +8,21 @@ end
|
|
|
7
8
|
module VCR
|
|
8
9
|
class Cassette
|
|
9
10
|
describe Serializers do
|
|
11
|
+
shared_examples_for "encoding error handling" do |name, error_class|
|
|
12
|
+
context "the #{name} serializer" do
|
|
13
|
+
it 'appends info about the :preserve_exact_body_bytes option to the error' do
|
|
14
|
+
expect {
|
|
15
|
+
result = serializer.serialize("a" => string)
|
|
16
|
+
serializer.deserialize(result)
|
|
17
|
+
}.to raise_error(error_class, /preserve_exact_body_bytes/)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
10
21
|
|
|
11
22
|
shared_examples_for "a serializer" do |name, file_extension, lazily_loaded|
|
|
12
|
-
|
|
13
|
-
let(:serializer) { subject[name] }
|
|
23
|
+
let(:serializer) { subject[name] }
|
|
14
24
|
|
|
25
|
+
context "the #{name} serializer" do
|
|
15
26
|
it 'lazily loads the serializer' do
|
|
16
27
|
serializers = subject.instance_variable_get(:@serializers)
|
|
17
28
|
serializers.should_not have_key(name)
|
|
@@ -34,10 +45,44 @@ module VCR
|
|
|
34
45
|
end
|
|
35
46
|
end
|
|
36
47
|
|
|
37
|
-
it_behaves_like "a serializer", :yaml, "yml", :lazily_loaded
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
it_behaves_like "a serializer", :yaml, "yml", :lazily_loaded do
|
|
49
|
+
it_behaves_like "encoding error handling", :yaml, ArgumentError do
|
|
50
|
+
let(:string) { "\xFA".force_encoding("UTF-8") }
|
|
51
|
+
before { ::YAML::ENGINE.yamler = 'psych' }
|
|
52
|
+
end if ''.respond_to?(:encoding)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it_behaves_like "a serializer", :syck, "yml", :lazily_loaded do
|
|
56
|
+
it_behaves_like "encoding error handling", :syck, ArgumentError do
|
|
57
|
+
let(:string) { "\xFA".force_encoding("UTF-8") }
|
|
58
|
+
end if ''.respond_to?(:encoding)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it_behaves_like "a serializer", :psych, "yml", :lazily_loaded do
|
|
62
|
+
it_behaves_like "encoding error handling", :psych, ArgumentError do
|
|
63
|
+
let(:string) { "\xFA".force_encoding("UTF-8") }
|
|
64
|
+
end if ''.respond_to?(:encoding)
|
|
65
|
+
end if RUBY_VERSION =~ /1.9/
|
|
66
|
+
|
|
67
|
+
it_behaves_like "a serializer", :json, "json", :lazily_loaded do
|
|
68
|
+
engines = {}
|
|
69
|
+
|
|
70
|
+
engines[:yajl] = ::MultiJson::DecodeError unless RUBY_INTERPRETER == :jruby
|
|
71
|
+
|
|
72
|
+
if RUBY_VERSION =~ /1.9/
|
|
73
|
+
engines[:json_gem] = EncodingError
|
|
74
|
+
engines[:json_pure] = EncodingError
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
engines.each do |engine, error|
|
|
78
|
+
context "when MultiJson is configured to use #{engine.inspect}" do
|
|
79
|
+
before { MultiJson.engine = engine }
|
|
80
|
+
it_behaves_like "encoding error handling", :json, error do
|
|
81
|
+
let(:string) { "\xFA" }
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
41
86
|
|
|
42
87
|
context "a custom :ruby serializer" do
|
|
43
88
|
let(:custom_serializer) do
|
data/spec/vcr/cassette_spec.rb
CHANGED
|
@@ -39,18 +39,36 @@ describe VCR::Cassette do
|
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
describe '#tags' do
|
|
43
|
+
it 'returns a blank array if no tag has been set' do
|
|
44
|
+
VCR::Cassette.new("name").tags.should eq([])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'converts a single :tag to an array' do
|
|
48
|
+
VCR::Cassette.new("name", :tag => :foo).tags.should eq([:foo])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'accepts an array as the :tags option' do
|
|
52
|
+
VCR::Cassette.new("name", :tags => [:foo]).tags.should eq([:foo])
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
42
56
|
describe '#record_http_interaction' do
|
|
57
|
+
let(:the_interaction) { stub(:request => stub(:method => :get).as_null_object).as_null_object }
|
|
58
|
+
|
|
43
59
|
it 'adds the interaction to #new_recorded_interactions' do
|
|
44
60
|
cassette = VCR::Cassette.new(:test_cassette)
|
|
45
61
|
cassette.new_recorded_interactions.should eq([])
|
|
46
|
-
cassette.record_http_interaction(
|
|
47
|
-
cassette.new_recorded_interactions.should eq([
|
|
62
|
+
cassette.record_http_interaction(the_interaction)
|
|
63
|
+
cassette.new_recorded_interactions.should eq([the_interaction])
|
|
48
64
|
end
|
|
49
65
|
end
|
|
50
66
|
|
|
51
67
|
describe "#serializable_hash" do
|
|
52
68
|
subject { VCR::Cassette.new("foo") }
|
|
53
|
-
let(:
|
|
69
|
+
let(:interaction_1) { http_interaction { |i| i.request.body = 'req body 1'; i.response.body = 'res body 1' } }
|
|
70
|
+
let(:interaction_2) { http_interaction { |i| i.request.body = 'req body 2'; i.response.body = 'res body 2' } }
|
|
71
|
+
let(:interactions) { [interaction_1, interaction_2] }
|
|
54
72
|
|
|
55
73
|
before(:each) do
|
|
56
74
|
interactions.each do |i|
|
|
@@ -61,7 +79,9 @@ describe VCR::Cassette do
|
|
|
61
79
|
let(:metadata) { subject.serializable_hash.reject { |k,v| k == "http_interactions" } }
|
|
62
80
|
|
|
63
81
|
it 'includes the hash form of all recorded interactions' do
|
|
64
|
-
|
|
82
|
+
interaction_1.stub(:to_hash => { "i" => 1, 'body' => '' })
|
|
83
|
+
interaction_2.stub(:to_hash => { "i" => 2, 'body' => '' })
|
|
84
|
+
subject.serializable_hash.should include('http_interactions' => [{ "i" => 1, 'body' => '' }, { "i" => 2, 'body' => '' }])
|
|
65
85
|
end
|
|
66
86
|
|
|
67
87
|
it 'includes additional metadata about the cassette' do
|
|
@@ -182,7 +202,7 @@ describe VCR::Cassette do
|
|
|
182
202
|
|
|
183
203
|
it 'does not raise an error in the case of an empty file' do
|
|
184
204
|
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
|
|
185
|
-
VCR::Cassette.new('empty', :record => :none).previously_recorded_interactions.should eq([])
|
|
205
|
+
VCR::Cassette.new('empty', :record => :none).send(:previously_recorded_interactions).should eq([])
|
|
186
206
|
end
|
|
187
207
|
|
|
188
208
|
VCR::Cassette::VALID_RECORD_MODES.each do |record_mode|
|
|
@@ -294,7 +314,7 @@ describe VCR::Cassette do
|
|
|
294
314
|
|
|
295
315
|
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
|
|
296
316
|
cassette = VCR::Cassette.new('with_localhost_requests', :record => record_mode)
|
|
297
|
-
cassette.previously_recorded_interactions.map { |i| URI.parse(i.uri).host }.should eq(%w[example.com])
|
|
317
|
+
cassette.send(:previously_recorded_interactions).map { |i| URI.parse(i.request.uri).host }.should eq(%w[example.com])
|
|
298
318
|
end
|
|
299
319
|
|
|
300
320
|
it "loads the recorded interactions from the library yml file" do
|
|
@@ -303,7 +323,7 @@ describe VCR::Cassette do
|
|
|
303
323
|
|
|
304
324
|
cassette.should have(3).previously_recorded_interactions
|
|
305
325
|
|
|
306
|
-
i1, i2, i3 = *cassette.previously_recorded_interactions
|
|
326
|
+
i1, i2, i3 = *cassette.send(:previously_recorded_interactions)
|
|
307
327
|
|
|
308
328
|
i1.request.method.should eq(:get)
|
|
309
329
|
i1.request.uri.should eq('http://example.com/')
|
|
@@ -341,15 +361,14 @@ describe VCR::Cassette do
|
|
|
341
361
|
end
|
|
342
362
|
|
|
343
363
|
if stub_requests
|
|
344
|
-
it 'invokes the
|
|
345
|
-
VCR.configuration.should_receive(:
|
|
364
|
+
it 'invokes the before_playback hooks' do
|
|
365
|
+
VCR.configuration.should_receive(:invoke_hook).with(
|
|
346
366
|
:before_playback,
|
|
347
|
-
|
|
348
|
-
an_instance_of(VCR::HTTPInteraction),
|
|
367
|
+
an_instance_of(VCR::HTTPInteraction::HookAware),
|
|
349
368
|
an_instance_of(VCR::Cassette)
|
|
350
369
|
).exactly(3).times
|
|
351
370
|
|
|
352
|
-
cassette = VCR::Cassette.new('example', :record => record_mode
|
|
371
|
+
cassette = VCR::Cassette.new('example', :record => record_mode)
|
|
353
372
|
cassette.should have(3).previously_recorded_interactions
|
|
354
373
|
end
|
|
355
374
|
|
|
@@ -367,14 +386,14 @@ describe VCR::Cassette do
|
|
|
367
386
|
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
|
|
368
387
|
cassette = VCR::Cassette.new('example', :record => record_mode, :match_requests_on => [:body, :headers])
|
|
369
388
|
cassette.http_interactions.interactions.should have(3).interactions
|
|
370
|
-
cassette.http_interactions.request_matchers.should eq([:body, :headers]
|
|
389
|
+
cassette.http_interactions.request_matchers.should eq([:body, :headers])
|
|
371
390
|
end
|
|
372
391
|
else
|
|
373
392
|
it 'instantiates the http_interactions with the no interactions and the request matchers' do
|
|
374
393
|
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
|
|
375
394
|
cassette = VCR::Cassette.new('example', :record => record_mode, :match_requests_on => [:body, :headers])
|
|
376
395
|
cassette.http_interactions.interactions.should have(0).interactions
|
|
377
|
-
cassette.http_interactions.request_matchers.should eq([:body, :headers]
|
|
396
|
+
cassette.http_interactions.request_matchers.should eq([:body, :headers])
|
|
378
397
|
end
|
|
379
398
|
end
|
|
380
399
|
end
|
|
@@ -402,11 +421,12 @@ describe VCR::Cassette do
|
|
|
402
421
|
cassette = VCR::Cassette.new('example', :tag => :foo)
|
|
403
422
|
cassette.stub!(:new_recorded_interactions).and_return(interactions)
|
|
404
423
|
|
|
424
|
+
VCR.configuration.stub(:invoke_hook).and_return([false])
|
|
425
|
+
|
|
405
426
|
interactions.each do |i|
|
|
406
|
-
VCR.configuration.should_receive(:
|
|
427
|
+
VCR.configuration.should_receive(:invoke_hook).with(
|
|
407
428
|
:before_record,
|
|
408
|
-
|
|
409
|
-
i,
|
|
429
|
+
an_instance_of(VCR::HTTPInteraction::HookAware),
|
|
410
430
|
cassette
|
|
411
431
|
).ordered
|
|
412
432
|
end
|
|
@@ -418,7 +438,9 @@ describe VCR::Cassette do
|
|
|
418
438
|
interaction_1 = http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' }
|
|
419
439
|
interaction_2 = http_interaction { |i| i.request.uri = 'http://bar.com/'; i.response.body = 'res 2' }
|
|
420
440
|
|
|
421
|
-
interaction_1.
|
|
441
|
+
hook_aware_interaction_1 = interaction_1.hook_aware
|
|
442
|
+
interaction_1.stub(:hook_aware => hook_aware_interaction_1)
|
|
443
|
+
hook_aware_interaction_1.ignore!
|
|
422
444
|
|
|
423
445
|
cassette = VCR::Cassette.new('test_cassette')
|
|
424
446
|
cassette.stub!(:new_recorded_interactions).and_return([interaction_1, interaction_2])
|
|
@@ -430,7 +452,10 @@ describe VCR::Cassette do
|
|
|
430
452
|
|
|
431
453
|
it 'does not write the cassette to disk if all interactions have been ignored' do
|
|
432
454
|
interaction_1 = http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' }
|
|
433
|
-
|
|
455
|
+
|
|
456
|
+
hook_aware_interaction_1 = interaction_1.hook_aware
|
|
457
|
+
interaction_1.stub(:hook_aware => hook_aware_interaction_1)
|
|
458
|
+
hook_aware_interaction_1.ignore!
|
|
434
459
|
|
|
435
460
|
cassette = VCR::Cassette.new('test_cassette')
|
|
436
461
|
cassette.stub!(:new_recorded_interactions).and_return([interaction_1])
|