vcr 2.0.0.beta2 → 2.0.0.rc1

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.
Files changed (91) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +0 -2
  3. data/CHANGELOG.md +22 -1
  4. data/CONTRIBUTING.md +46 -0
  5. data/Gemfile +1 -9
  6. data/README.md +8 -2
  7. data/Rakefile +12 -1
  8. data/cucumber.yml +8 -9
  9. data/features/.nav +10 -4
  10. data/features/cassettes/format.feature +1 -1
  11. data/features/cassettes/no_cassette.feature +8 -11
  12. data/features/configuration/allow_http_connections_when_no_cassette.feature +4 -4
  13. data/features/configuration/filter_sensitive_data.feature +3 -0
  14. data/features/configuration/hook_into.feature +4 -8
  15. data/features/configuration/ignore_request.feature +191 -0
  16. data/features/getting_started.md +38 -21
  17. data/features/hooks/after_http_request.feature +44 -0
  18. data/features/hooks/around_http_request.feature +56 -0
  19. data/features/hooks/before_http_request.feature +44 -0
  20. data/features/hooks/before_playback.feature +181 -0
  21. data/features/hooks/before_record.feature +172 -0
  22. data/features/middleware/faraday.feature +7 -3
  23. data/features/record_modes/none.feature +2 -1
  24. data/features/record_modes/once.feature +2 -1
  25. data/features/request_matching/body.feature +2 -2
  26. data/features/request_matching/custom_matcher.feature +2 -2
  27. data/features/request_matching/headers.feature +2 -2
  28. data/features/request_matching/host.feature +2 -2
  29. data/features/request_matching/identical_request_sequence.feature +2 -2
  30. data/features/request_matching/method.feature +2 -2
  31. data/features/request_matching/path.feature +2 -2
  32. data/features/request_matching/playback_repeats.feature +2 -1
  33. data/features/request_matching/uri.feature +2 -2
  34. data/features/support/env.rb +21 -12
  35. data/features/test_frameworks/cucumber.feature +9 -4
  36. data/features/test_frameworks/{rspec.feature → rspec_macro.feature} +7 -7
  37. data/features/test_frameworks/rspec_metadata.feature +90 -0
  38. data/lib/vcr.rb +1 -1
  39. data/lib/vcr/cassette.rb +3 -3
  40. data/lib/vcr/cassette/http_interaction_list.rb +13 -9
  41. data/lib/vcr/cassette/migrator.rb +1 -1
  42. data/lib/vcr/configuration.rb +37 -0
  43. data/lib/vcr/errors.rb +172 -6
  44. data/lib/vcr/library_hooks.rb +4 -6
  45. data/lib/vcr/library_hooks/excon.rb +23 -11
  46. data/lib/vcr/library_hooks/fakeweb.rb +85 -24
  47. data/lib/vcr/library_hooks/faraday.rb +30 -2
  48. data/lib/vcr/library_hooks/typhoeus.rb +25 -3
  49. data/lib/vcr/library_hooks/webmock.rb +25 -36
  50. data/lib/vcr/middleware/faraday.rb +23 -5
  51. data/lib/vcr/request_handler.rb +12 -1
  52. data/lib/vcr/request_ignorer.rb +12 -1
  53. data/lib/vcr/request_matcher_registry.rb +1 -9
  54. data/lib/vcr/structs.rb +32 -2
  55. data/lib/vcr/test_frameworks/rspec.rb +28 -0
  56. data/lib/vcr/util/hooks.rb +12 -4
  57. data/lib/vcr/util/version_checker.rb +2 -0
  58. data/lib/vcr/version.rb +1 -1
  59. data/spec/fixtures/cassette_spec/example.yml +1 -1
  60. data/spec/fixtures/{fake_example.com_responses.yml → fake_example_responses.yml} +0 -0
  61. data/spec/monkey_patches.rb +1 -1
  62. data/spec/spec_helper.rb +3 -1
  63. data/spec/support/http_library_adapters.rb +4 -3
  64. data/spec/support/shared_example_groups/hook_into_http_library.rb +194 -12
  65. data/spec/support/shared_example_groups/request_hooks.rb +58 -0
  66. data/spec/support/shared_example_groups/version_checking.rb +5 -0
  67. data/spec/support/sinatra_app.rb +17 -9
  68. data/spec/support/vcr_stub_helpers.rb +17 -0
  69. data/spec/vcr/cassette/http_interaction_list_spec.rb +28 -29
  70. data/spec/vcr/cassette/migrator_spec.rb +6 -7
  71. data/spec/vcr/cassette_spec.rb +5 -5
  72. data/spec/vcr/configuration_spec.rb +51 -32
  73. data/spec/vcr/deprecations_spec.rb +0 -8
  74. data/spec/vcr/errors_spec.rb +129 -0
  75. data/spec/vcr/library_hooks/excon_spec.rb +21 -4
  76. data/spec/vcr/library_hooks/fakeweb_spec.rb +71 -3
  77. data/spec/vcr/library_hooks/faraday_spec.rb +45 -0
  78. data/spec/vcr/library_hooks/typhoeus_spec.rb +31 -1
  79. data/spec/vcr/library_hooks/webmock_spec.rb +26 -3
  80. data/spec/vcr/middleware/faraday_spec.rb +84 -1
  81. data/spec/vcr/request_ignorer_spec.rb +16 -0
  82. data/spec/vcr/request_matcher_registry_spec.rb +0 -26
  83. data/spec/vcr/structs_spec.rb +61 -1
  84. data/spec/vcr/test_frameworks/rspec_spec.rb +32 -0
  85. data/spec/vcr/util/hooks_spec.rb +73 -63
  86. data/spec/vcr_spec.rb +2 -2
  87. data/vcr.gemspec +5 -5
  88. metadata +51 -31
  89. data/features/configuration/hooks.feature +0 -270
  90. data/features/configuration/ignore_hosts.feature +0 -61
  91. data/features/configuration/ignore_localhost.feature +0 -97
@@ -42,8 +42,10 @@ module VCR
42
42
  case
43
43
  when @major < @min_major then :too_low
44
44
  when @major > @max_major then :too_high
45
+ when @major > @min_major then :ok
45
46
  when @minor < @min_minor then :too_low
46
47
  when @minor > @max_minor then :too_high
48
+ when @minor > @min_minor then :ok
47
49
  when @patch < @min_patch then :too_low
48
50
  end
49
51
  end
data/lib/vcr/version.rb CHANGED
@@ -3,7 +3,7 @@ module VCR
3
3
 
4
4
  def version
5
5
  @version ||= begin
6
- string = '2.0.0.beta2'
6
+ string = '2.0.0.rc1'
7
7
 
8
8
  def string.parts
9
9
  split('.').map { |p| p.to_i }
@@ -108,4 +108,4 @@ http_interactions:
108
108
  body: Another example.com response
109
109
  http_version: '1.1'
110
110
  recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
111
- recorded_with: VCR 1.11.3
111
+ recorded_with: VCR 2.0.0
@@ -40,7 +40,7 @@ module MonkeyPatches
40
40
  def disable_all!
41
41
  realias_all :without_monkeypatches
42
42
 
43
- if defined?(::WebMock)
43
+ if defined?(::WebMock::HttpLibAdapters)
44
44
  ::WebMock::HttpLibAdapters::NetHttpAdapter.disable!
45
45
  ::WebMock::HttpLibAdapters::TyphoeusAdapter.disable! if defined?(::Typhoeus)
46
46
  ::WebMock::CallbackRegistry.reset
data/spec/spec_helper.rb CHANGED
@@ -25,12 +25,13 @@ module VCR
25
25
  end
26
26
 
27
27
  RSpec.configure do |config|
28
+ config.order = :rand
28
29
  config.color_enabled = true
29
30
  config.debug = (using_git && RUBY_INTERPRETER == :mri && !%w[ 1.9.3 ].include?(RUBY_VERSION) && !ENV['CI'])
30
31
  config.treat_symbols_as_metadata_keys_with_true_values = true
31
32
 
32
33
  tmp_dir = File.expand_path('../../tmp/cassette_library_dir', __FILE__)
33
- config.before(:each) do
34
+ config.before(:each, :skip_vcr_reset => lambda { |v| v != true }) do
34
35
  VCR.reset!
35
36
  VCR.configuration.cassette_library_dir = tmp_dir
36
37
  end
@@ -54,3 +55,4 @@ RSpec.configure do |config|
54
55
  config.alias_it_should_behave_like_to :it_performs, 'it performs'
55
56
  end
56
57
 
58
+ VCR::SinatraApp.boot
@@ -156,7 +156,7 @@ HTTP_LIBRARY_ADAPTERS['typhoeus'] = Module.new do
156
156
  end
157
157
 
158
158
  def normalize_request_headers(headers)
159
- headers.merge("User-Agent" => ["Typhoeus - http://github.com/dbalatero/typhoeus/tree/master"])
159
+ headers
160
160
  end
161
161
  end
162
162
 
@@ -172,7 +172,9 @@ HTTP_LIBRARY_ADAPTERS['excon'] = Module.new do
172
172
  end
173
173
 
174
174
  def make_http_request(method, url, body = nil, headers = {})
175
- Excon.send(method, url, :body => body, :headers => headers)
175
+ # There are multiple ways to use Excon but this is how fog (the main user of excon) uses it:
176
+ # https://github.com/fog/fog/blob/v1.1.1/lib/fog/aws/rds.rb#L139-147
177
+ Excon::Connection.new(url).request(:method => method.to_s.upcase, :body => body, :headers => headers)
176
178
  end
177
179
 
178
180
  def normalize_request_headers(headers)
@@ -219,7 +221,6 @@ end
219
221
 
220
222
  def faraday_connection(url_root)
221
223
  Faraday::Connection.new(:url => url_root) do |builder|
222
- builder.use VCR::Middleware::Faraday
223
224
  builder.adapter faraday_adapter
224
225
  end
225
226
  end
@@ -1,14 +1,10 @@
1
1
  require 'cgi'
2
2
 
3
- NET_CONNECT_NOT_ALLOWED_ERROR = /You can use VCR to automatically record this request and replay it later/
3
+ NET_CONNECT_NOT_ALLOWED_ERROR = /An HTTP request has been made that VCR does not know how to handle/
4
4
 
5
- shared_examples_for "a hook into an HTTP library" do |library, *other|
5
+ shared_examples_for "a hook into an HTTP library" do |library_hook_name, library, *other|
6
6
  include HeaderDowncaser
7
-
8
- def interactions_from(file)
9
- hashes = YAML.load_file(File.join(VCR::SPEC_ROOT, 'fixtures', file))['http_interactions']
10
- hashes.map { |h| VCR::HTTPInteraction.from_hash(h) }
11
- end
7
+ include VCRStubHelpers
12
8
 
13
9
  unless adapter_module = HTTP_LIBRARY_ADAPTERS[library]
14
10
  raise ArgumentError.new("No http library adapter module could be found for #{library}")
@@ -19,10 +15,6 @@ shared_examples_for "a hook into an HTTP library" do |library, *other|
19
15
  describe "using #{adapter_module.http_library_name}", :unless => http_lib_unsupported do
20
16
  include adapter_module
21
17
 
22
- def stub_requests(*args)
23
- VCR.stub(:http_interactions => VCR::Cassette::HTTPInteractionList.new(*args))
24
- end
25
-
26
18
  # Necessary for ruby 1.9.2. On 1.9.2 we get an error when we use super,
27
19
  # so this gives us another alias we can use for the original method.
28
20
  alias make_request make_http_request
@@ -117,6 +109,196 @@ shared_examples_for "a hook into an HTTP library" do |library, *other|
117
109
  test_playback "with an encoded ampersand", "http://example.com:80/search?q=#{CGI.escape("Q&A")}"
118
110
  end
119
111
 
112
+ describe "using the library's stubbing/disconnection APIs" do
113
+ let!(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
114
+
115
+ if method_defined?(:disable_real_connections)
116
+ it 'can make a real request when VCR is turned off' do
117
+ enable_real_connections
118
+ VCR.turn_off!
119
+ get_body_string(make_http_request(:get, request_url)).should eq("FOO!")
120
+ end
121
+
122
+ it 'does not mess with VCR when real connections are disabled' do
123
+ VCR.insert_cassette('example')
124
+ disable_real_connections
125
+
126
+ VCR.should_receive(:record_http_interaction) do |interaction|
127
+ interaction.request.uri.should eq(request_url)
128
+ end
129
+
130
+ make_http_request(:get, request_url)
131
+ end
132
+
133
+ it 'can disable real connections when VCR is turned off' do
134
+ VCR.turn_off!
135
+ expected_error = disable_real_connections
136
+
137
+ expect {
138
+ make_http_request(:get, request_url)
139
+ }.to raise_error(expected_error)
140
+ end
141
+ end
142
+
143
+ if method_defined?(:directly_stub_request)
144
+ it 'can directly stub the request when VCR is turned off' do
145
+ VCR.turn_off!
146
+ directly_stub_request(:get, request_url, "stubbed response")
147
+ get_body_string(make_http_request(:get, request_url)).should eq("stubbed response")
148
+ end
149
+ end
150
+ end
151
+
152
+ describe "request hooks" do
153
+ context 'when there is an around_http_request hook' do
154
+ before(:each) do
155
+ # ensure that all the other library hooks are disabled so that we don't
156
+ # get double-hookage (such as for WebMock and Typhoeus both invoking the
157
+ # hooks for a typhoeus request)
158
+ VCR.library_hooks.stub(:disabled?) { |lib_name| lib_name != library_hook_name }
159
+ end
160
+
161
+ let(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
162
+
163
+ it 'yields the request to the block' do
164
+ yielded_request = nil
165
+ VCR.configuration.around_http_request do |request|
166
+ yielded_request = request
167
+ request.proceed
168
+ end
169
+
170
+ VCR.use_cassette('new_cassette') do
171
+ make_http_request(:get, request_url)
172
+ end
173
+
174
+ yielded_request.method.should eq(:get)
175
+ yielded_request.uri.should eq(request_url)
176
+ end
177
+
178
+ it 'can be used to use a cassette for a request' do
179
+ VCR.configuration.around_http_request do |request|
180
+ VCR.use_cassette('new_cassette', &request)
181
+ end
182
+
183
+ VCR.should_receive(:record_http_interaction) do
184
+ VCR.current_cassette.name.should eq('new_cassette')
185
+ end
186
+
187
+ VCR.current_cassette.should be_nil
188
+ make_http_request(:get, request_url)
189
+ VCR.current_cassette.should be_nil
190
+ end
191
+
192
+ it 'nests them inside each other, making the first declared hook the outermost' do
193
+ order = []
194
+
195
+ VCR.configure do |c|
196
+ c.ignore_request { |r| true }
197
+ c.around_http_request do |request|
198
+ order << :before_1
199
+ request.proceed
200
+ order << :after_1
201
+ end
202
+
203
+ c.around_http_request do |request|
204
+ order << :before_2
205
+ request.proceed
206
+ order << :after_2
207
+ end
208
+ end
209
+
210
+ make_http_request(:get, request_url)
211
+
212
+ order.should eq([:before_1, :before_2, :after_2, :after_1])
213
+ end
214
+
215
+ it 'raises an appropriate error if the hook does not call request.proceed' do
216
+ VCR.configuration.ignore_request { |r| true }
217
+ hook_declaration = "#{__FILE__}:#{__LINE__ + 1}"
218
+ VCR.configuration.around_http_request { |r| }
219
+
220
+ expect {
221
+ make_http_request(:get, request_url)
222
+ }.to raise_error { |error|
223
+ error.message.should include('must call #proceed on the yielded request')
224
+ error.message.should include(hook_declaration)
225
+ }
226
+ end
227
+
228
+ it 'does not get a dead fiber error when multiple requests are made' do
229
+ VCR.configuration.around_http_request do |request|
230
+ VCR.use_cassette('new_cassette', &request)
231
+ end
232
+
233
+ 3.times { make_http_request(:get, request_url) }
234
+ end
235
+ end if RUBY_VERSION >= '1.9'
236
+
237
+ context "when the request is ignored" do
238
+ before(:each) do
239
+ VCR.configuration.ignore_request { |r| true }
240
+ end
241
+
242
+ it_behaves_like "request hooks", library_hook_name
243
+ end
244
+
245
+ context 'when the request is recorded' do
246
+ let!(:inserted_cassette) { VCR.insert_cassette('new_cassette') }
247
+
248
+ it_behaves_like "request hooks", library_hook_name do
249
+ let(:string_in_cassette) { 'example.com get response 1 with path=foo' }
250
+
251
+ it 'plays back the cassette when a request is made' do
252
+ VCR.eject_cassette
253
+ VCR.configure do |c|
254
+ c.cassette_library_dir = File.join(VCR::SPEC_ROOT, 'fixtures')
255
+ c.before_http_request do |request|
256
+ VCR.insert_cassette('fake_example_responses', :record => :none)
257
+ end
258
+ end
259
+ get_body_string(make_http_request(:get, 'http://example.com/foo')).should eq(string_in_cassette)
260
+ end
261
+
262
+ specify 'the after_http_request hook can be used to eject a cassette after the request is recorded' do
263
+ VCR.configuration.after_http_request { |request| VCR.eject_cassette }
264
+
265
+ VCR.should_receive(:record_http_interaction) do |interaction|
266
+ VCR.current_cassette.should be(inserted_cassette)
267
+ end
268
+
269
+ make_request
270
+ VCR.current_cassette.should be_nil
271
+ end
272
+ end
273
+ end
274
+
275
+ context 'when a stubbed response is played back for the request' do
276
+ before(:each) do
277
+ stub_requests([http_interaction(request_url)], [:method, :uri])
278
+ end
279
+
280
+ it_behaves_like "request hooks", library_hook_name
281
+ end
282
+
283
+ context 'when the request is not allowed' do
284
+ it_behaves_like "request hooks", library_hook_name do
285
+ undef assert_expected_response
286
+ def assert_expected_response(response)
287
+ response.should be_nil
288
+ end
289
+
290
+ undef make_request
291
+ def make_request(disabled = false)
292
+ if disabled
293
+ make_http_request(:get, request_url)
294
+ else
295
+ expect { make_http_request(:get, request_url) }.to raise_error(NET_CONNECT_NOT_ALLOWED_ERROR)
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end
301
+
120
302
  describe '.stub_requests using specific match_attributes' do
121
303
  before(:each) { VCR.stub(:real_http_connections_allowed? => false) }
122
304
  let(:interactions) { interactions_from('match_requests_on.yml') }
@@ -277,7 +459,7 @@ shared_examples_for "a hook into an HTTP library" do |library, *other|
277
459
  end
278
460
 
279
461
  context 'when some requests are stubbed' do
280
- let(:interactions) { interactions_from('fake_example.com_responses.yml') }
462
+ let(:interactions) { interactions_from('fake_example_responses.yml') }
281
463
  before(:each) do
282
464
  stub_requests(interactions, VCR::RequestMatcherRegistry::DEFAULT_MATCHERS)
283
465
  end
@@ -0,0 +1,58 @@
1
+ shared_examples_for "request hooks" do |library_hook_name|
2
+ let(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
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
+ def make_request(disabled = false)
12
+ make_http_request(:get, request_url)
13
+ end
14
+
15
+ def assert_expected_response(response)
16
+ response.status.code.should eq(200)
17
+ response.body.should eq('FOO!')
18
+ end
19
+
20
+ [:before_http_request, :after_http_request].each do |hook|
21
+ specify "the #{hook} hook is only called once per request" do
22
+ call_count = 0
23
+ VCR.configuration.send(hook) { |r| call_count += 1 }
24
+
25
+ make_request
26
+ call_count.should eq(1)
27
+ end
28
+
29
+ specify "the #{hook} hook yields the request" do
30
+ request = nil
31
+ VCR.configuration.send(hook) { |r| request = r }
32
+
33
+ make_request
34
+ request.method.should be(:get)
35
+ request.uri.should eq(request_url)
36
+ end
37
+
38
+ specify "the #{hook} hook is not called if the library hook is disabled" do
39
+ VCR.library_hooks.should respond_to(:disabled?)
40
+ VCR.library_hooks.stub(:disabled? => true)
41
+
42
+ hook_called = false
43
+ VCR.configuration.send(hook) { |r| hook_called = true }
44
+
45
+ make_request(:disabled)
46
+ hook_called.should be_false
47
+ end
48
+ end
49
+
50
+ specify "the after_http_request hook yields the response if there is one and the second block arg is given" do
51
+ response = nil
52
+ VCR.configuration.after_http_request { |req, res| response = res }
53
+
54
+ make_request
55
+ assert_expected_response(response)
56
+ end
57
+ end
58
+
@@ -1,6 +1,11 @@
1
1
  shared_examples_for "version checking" do |library, options|
2
2
  file = options[:file] || "vcr/library_hooks/#{library.downcase}.rb"
3
3
 
4
+ before(:each) do
5
+ # ensure we don't get double callback registration by reloading the file...
6
+ stub_callback_registration if respond_to?(:stub_callback_registration)
7
+ end
8
+
4
9
  context 'when loading the library hook file', :disable_warnings => true do
5
10
  options[:valid].each do |version|
6
11
  it "does nothing when #{library}'s version is #{version}" do
@@ -40,20 +40,28 @@ module VCR
40
40
  "Response #{$record_and_playback_response_count += 1}"
41
41
  end
42
42
 
43
- def self.port
44
- server.port
43
+ post '/record-and-playback' do
44
+ "Response #{$record_and_playback_response_count += 1}"
45
45
  end
46
46
 
47
47
  @_boot_failed = false
48
48
 
49
- def self.server
50
- raise "Sinatra app failed to boot." if @_boot_failed
51
- @server ||= begin
52
- VCR::LocalhostServer.new(new)
53
- rescue
54
- @_boot_failed = true
55
- raise
49
+ class << self
50
+ def port
51
+ server.port
52
+ end
53
+
54
+ def server
55
+ raise "Sinatra app failed to boot." if @_boot_failed
56
+ @server ||= begin
57
+ VCR::LocalhostServer.new(new)
58
+ rescue
59
+ @_boot_failed = true
60
+ raise
61
+ end
56
62
  end
63
+
64
+ alias boot server
57
65
  end
58
66
  end
59
67
  end
@@ -0,0 +1,17 @@
1
+ module VCRStubHelpers
2
+ def interactions_from(file)
3
+ hashes = YAML.load_file(File.join(VCR::SPEC_ROOT, 'fixtures', file))['http_interactions']
4
+ hashes.map { |h| VCR::HTTPInteraction.from_hash(h) }
5
+ end
6
+
7
+ def stub_requests(*args)
8
+ VCR.stub(:http_interactions => VCR::Cassette::HTTPInteractionList.new(*args))
9
+ end
10
+
11
+ def http_interaction(url, response_body = "FOO!", status_code = 200)
12
+ request = VCR::Request.new(:get, request_url)
13
+ response_status = VCR::ResponseStatus.new(status_code)
14
+ response = VCR::Response.new(response_status, nil, response_body, '1.1')
15
+ VCR::HTTPInteraction.new(request, response)
16
+ end
17
+ end