vcr 2.0.0.beta2 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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