vcr 1.6.0 → 1.7.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.
Files changed (82) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +5 -0
  3. data/.travis.yml +1 -0
  4. data/CHANGELOG.md +29 -2
  5. data/Gemfile +3 -3
  6. data/README.md +55 -11
  7. data/Rakefile +45 -4
  8. data/features/.nav +6 -0
  9. data/features/{README.md → about_the_cucumber_features.md} +1 -1
  10. data/features/cassettes/update_content_length_header.feature +106 -0
  11. data/features/configuration/default_cassette_options.feature +20 -2
  12. data/features/configuration/filter_sensitive_data.feature +154 -0
  13. data/features/getting_started.md +67 -0
  14. data/features/record_modes/all.feature +4 -2
  15. data/features/record_modes/new_episodes.feature +8 -2
  16. data/features/record_modes/none.feature +4 -2
  17. data/features/record_modes/once.feature +92 -0
  18. data/features/step_definitions/cli_steps.rb +18 -0
  19. data/lib/vcr.rb +13 -6
  20. data/lib/vcr/cassette.rb +36 -15
  21. data/lib/vcr/config.rb +14 -2
  22. data/lib/vcr/deprecations/cassette.rb +29 -0
  23. data/lib/vcr/deprecations/config.rb +18 -0
  24. data/lib/vcr/deprecations/http_stubbing_adapters/common.rb +9 -0
  25. data/lib/vcr/deprecations/http_stubbing_adapters/fakeweb.rb +11 -0
  26. data/lib/vcr/http_stubbing_adapters/common.rb +1 -1
  27. data/lib/vcr/http_stubbing_adapters/fakeweb.rb +2 -7
  28. data/lib/vcr/http_stubbing_adapters/multi_object_proxy.rb +1 -1
  29. data/lib/vcr/middleware/common.rb +3 -5
  30. data/lib/vcr/rspec.rb +1 -38
  31. data/lib/vcr/structs/http_interaction.rb +29 -0
  32. data/lib/vcr/structs/request.rb +6 -0
  33. data/lib/vcr/structs/response.rb +4 -0
  34. data/lib/vcr/{cucumber_tags.rb → test_frameworks/cucumber.rb} +10 -2
  35. data/lib/vcr/test_frameworks/rspec.rb +37 -0
  36. data/lib/vcr/util/basic_object.rb +32 -28
  37. data/lib/vcr/{hooks.rb → util/hooks.rb} +3 -6
  38. data/lib/vcr/util/internet_connection.rb +1 -1
  39. data/lib/vcr/util/ping.rb +21 -17
  40. data/lib/vcr/util/variable_args_block_caller.rb +12 -0
  41. data/lib/vcr/util/yaml.rb +11 -0
  42. data/lib/vcr/version.rb +1 -1
  43. data/script/FullBuildRakeFile +7 -0
  44. data/spec/monkey_patches.rb +0 -7
  45. data/spec/spec_helper.rb +40 -8
  46. data/spec/support/http_library_adapters.rb +0 -262
  47. data/spec/support/shared_example_groups/http_library.rb +256 -0
  48. data/spec/support/{http_stubbing_adapter.rb → shared_example_groups/http_stubbing_adapter.rb} +15 -3
  49. data/spec/support/shared_example_groups/ignore_localhost_deprecation.rb +28 -0
  50. data/spec/support/{normalizers.rb → shared_example_groups/normalizers.rb} +3 -3
  51. data/spec/support/{version_checker.rb → shared_example_groups/version_checking.rb} +1 -1
  52. data/spec/vcr/cassette_spec.rb +80 -28
  53. data/spec/vcr/config_spec.rb +55 -8
  54. data/spec/vcr/deprecations/cassette_spec.rb +57 -0
  55. data/spec/vcr/deprecations/config_spec.rb +30 -0
  56. data/spec/vcr/deprecations/http_stubbing_adapters/common_spec.rb +7 -0
  57. data/spec/vcr/deprecations/http_stubbing_adapters/fakeweb_spec.rb +16 -0
  58. data/spec/vcr/extensions/net_http_response_spec.rb +1 -3
  59. data/spec/vcr/extensions/net_http_spec.rb +1 -3
  60. data/spec/vcr/http_stubbing_adapters/fakeweb_spec.rb +1 -4
  61. data/spec/vcr/http_stubbing_adapters/faraday_spec.rb +1 -4
  62. data/spec/vcr/http_stubbing_adapters/typhoeus_spec.rb +1 -4
  63. data/spec/vcr/http_stubbing_adapters/webmock_spec.rb +1 -3
  64. data/spec/vcr/middleware/faraday_spec.rb +4 -4
  65. data/spec/vcr/middleware/rack_spec.rb +4 -4
  66. data/spec/vcr/structs/http_interaction_spec.rb +61 -0
  67. data/spec/vcr/structs/request_spec.rb +20 -2
  68. data/spec/vcr/structs/response_spec.rb +23 -1
  69. data/spec/vcr/structs/response_status_spec.rb +1 -1
  70. data/spec/vcr/{cucumber_tags_spec.rb → test_frameworks/cucumber_spec.rb} +12 -8
  71. data/spec/vcr/{rspec_spec.rb → test_frameworks/rspec_spec.rb} +0 -0
  72. data/spec/vcr/{hooks_spec.rb → util/hooks_spec.rb} +3 -3
  73. data/spec/vcr/util/internet_connection_spec.rb +3 -3
  74. data/spec/vcr_spec.rb +3 -3
  75. data/vcr.gemspec +5 -5
  76. metadata +149 -131
  77. data/Gemfile.lock +0 -155
  78. data/lib/vcr/deprecations.rb +0 -54
  79. data/spec/support/disable_warnings.rb +0 -12
  80. data/spec/support/temp_cassette_library_dir.rb +0 -16
  81. data/spec/support/webmock_macros.rb +0 -14
  82. data/spec/vcr/deprecations_spec.rb +0 -139
@@ -0,0 +1,67 @@
1
+ ## Getting Started
2
+
3
+ ### Install it
4
+
5
+ [sudo] gem install vcr
6
+ [sudo] gem install fakeweb
7
+
8
+ ### Configure it
9
+
10
+ Create a file named `vcr_setup.rb` with content like:
11
+
12
+ require 'vcr'
13
+
14
+ VCR.config do |c|
15
+ c.cassette_library_dir = 'vcr_cassettes'
16
+ c.stub_with :fakeweb
17
+ c.default_cassette_options = { :record => :once }
18
+ end
19
+
20
+ Ensure this file is required by your test suite before any
21
+ of the tests are run.
22
+
23
+ ### Use it
24
+
25
+ Run your tests. Any tests that make HTTP requests using Net::HTTP will
26
+ raise errors like:
27
+
28
+ FakeWeb::NetConnectNotAllowedError: Real HTTP connections are disabled.
29
+ Unregistered request: GET http://example.com/.
30
+ You can use VCR to automatically record this request and replay it later.
31
+ For more details, visit the VCR documentation at: http://relishapp.com/myronmarston/vcr
32
+
33
+ Find one of these tests (preferably one that uses the same HTTP method and
34
+ request URL every time--if not, you'll have to configure the request matcher).
35
+ Wrap the body of it (or at least the code that makes the HTTP request) in a
36
+ `VCR.use_cassette` block:
37
+
38
+ VCR.use_cassette('whatever cassette name you want') do
39
+ # the body of the test would go here...
40
+ end
41
+
42
+ Run this test. It will record the HTTP request to disk as a cassette (a
43
+ test fixture), with content like:
44
+
45
+ ---
46
+ - !ruby/struct:VCR::HTTPInteraction
47
+ request: !ruby/struct:VCR::Request
48
+ method: :get
49
+ uri: http://example.com:80/
50
+ body:
51
+ headers:
52
+ response: !ruby/struct:VCR::Response
53
+ status: !ruby/struct:VCR::ResponseStatus
54
+ code: 200
55
+ message: OK
56
+ headers:
57
+ content-type:
58
+ - text/html;charset=utf-8
59
+ content-length:
60
+ - "26"
61
+ body: This is the response body.
62
+ http_version: "1.1"
63
+
64
+ Disconnect your computer from the internet. Run the test again.
65
+ It should pass since VCR is automatically replaying the recorded
66
+ response when the request is made.
67
+
@@ -1,7 +1,9 @@
1
1
  Feature: :all
2
2
 
3
- The `:all` record mode records all requests and does not
4
- replay any previously recorded responds.
3
+ The `:all` record mode will:
4
+
5
+ - Record new interactions.
6
+ - Never replay previously recorded interactions.
5
7
 
6
8
  This can be temporarily used to force VCR to re-record
7
9
  a cassette (i.e. to ensure the responses are not out of date)
@@ -1,7 +1,13 @@
1
1
  Feature: :new_episodes
2
2
 
3
- The `:new_episodes` record mode replays previously recorded
4
- requests and records new ones.
3
+ The `:new_episodes` record mode will:
4
+
5
+ - Record new interactions.
6
+ - Replay previously recorded interactions.
7
+
8
+ It is similar to the `:once` record mode, but will _always_ record new
9
+ interactions, even if you have an existing recorded one that is similar
10
+ (but not identical, based on the `:match_request_on` option).
5
11
 
6
12
  Background:
7
13
  Given a file named "setup.rb" with:
@@ -1,7 +1,9 @@
1
1
  Feature: :none
2
2
 
3
- The `:none` record mode allows previously recorded responses
4
- to be replayed, but raises an error for any new requests.
3
+ The `:none` record mode will:
4
+
5
+ - Replay previously recorded interactions.
6
+ - Cause an error to be raised for any new requests.
5
7
 
6
8
  This is useful when your code makes potentially dangerous
7
9
  HTTP requests. The `:none` record mode guarantees that no
@@ -0,0 +1,92 @@
1
+ Feature: :once
2
+
3
+ The `:once` record mode will:
4
+
5
+ - Replay previously recorded interactions.
6
+ - Record new interactions if there is no cassette file.
7
+ - Cause an error to be raised for new requests if there is a cassette file.
8
+
9
+ It is similar to the `:new_episodes` record mode, but will prevent new,
10
+ unexpected requests from being made (i.e. because the request URI changed
11
+ or whatever).
12
+
13
+ `:once` is the default record mode, used when you do not set one.
14
+
15
+ Background:
16
+ Given a file named "setup.rb" with:
17
+ """
18
+ require 'vcr_cucumber_helpers'
19
+
20
+ start_sinatra_app(:port => 7777) do
21
+ get('/') { 'Hello' }
22
+ end
23
+
24
+ require 'vcr'
25
+
26
+ VCR.config do |c|
27
+ c.stub_with :fakeweb
28
+ c.cassette_library_dir = 'cassettes'
29
+ end
30
+ """
31
+ And a previously recorded cassette file "cassettes/example.yml" with:
32
+ """
33
+ ---
34
+ - !ruby/struct:VCR::HTTPInteraction
35
+ request: !ruby/struct:VCR::Request
36
+ method: :get
37
+ uri: http://example.com:80/foo
38
+ body:
39
+ headers:
40
+ response: !ruby/struct:VCR::Response
41
+ status: !ruby/struct:VCR::ResponseStatus
42
+ code: 200
43
+ message: OK
44
+ headers:
45
+ content-type:
46
+ - text/html;charset=utf-8
47
+ content-length:
48
+ - "20"
49
+ body: example.com response
50
+ http_version: "1.1"
51
+ """
52
+
53
+ Scenario: Previously recorded responses are replayed
54
+ Given a file named "replay_recorded_response.rb" with:
55
+ """
56
+ require 'setup'
57
+
58
+ VCR.use_cassette('example', :record => :once) do
59
+ response = Net::HTTP.get_response('example.com', '/foo')
60
+ puts "Response: #{response.body}"
61
+ end
62
+ """
63
+ When I run "ruby replay_recorded_response.rb"
64
+ Then it should pass with "Response: example.com response"
65
+
66
+ Scenario: New requests result in an error when the cassette file exists
67
+ Given a file named "error_for_new_requests_when_cassette_exists.rb" with:
68
+ """
69
+ require 'setup'
70
+
71
+ VCR.use_cassette('example', :record => :once) do
72
+ response = Net::HTTP.get_response('localhost', '/', 7777)
73
+ puts "Response: #{response.body}"
74
+ end
75
+ """
76
+ When I run "ruby error_for_new_requests_when_cassette_exists.rb"
77
+ Then it should fail with "Real HTTP connections are disabled"
78
+
79
+ Scenario: New requests get recorded when there is no cassette file
80
+ Given a file named "record_new_requests.rb" with:
81
+ """
82
+ require 'setup'
83
+
84
+ VCR.use_cassette('example', :record => :once) do
85
+ response = Net::HTTP.get_response('localhost', '/', 7777)
86
+ puts "Response: #{response.body}"
87
+ end
88
+ """
89
+ When I remove the file "cassettes/example.yml"
90
+ And I run "ruby record_new_requests.rb"
91
+ Then it should pass with "Response: Hello"
92
+ And the file "cassettes/example.yml" should contain "body: Hello"
@@ -95,6 +95,24 @@ Then /^the file "([^"]*)" should contain each of these:$/ do |file_name, table|
95
95
  end
96
96
  end
97
97
 
98
+ Then /^the file "([^"]*)" should contain:$/ do |file_name, expected_content|
99
+ check_file_content(file_name, expected_content, true)
100
+ end
101
+
102
+ Then /^the file "([^"]*)" should contain a YAML fragment like:$/ do |file_name, fragment|
103
+ if defined?(::Psych)
104
+ # psych serializes things slightly differently...
105
+ fragment = fragment.split("\n").map { |s| s.rstrip }.join("\n")
106
+ end
107
+
108
+ # JRuby serializes things a bit differently
109
+ if RUBY_PLATFORM == 'java'
110
+ fragment = fragment.gsub(/^(\s+\-)/,' \1')
111
+ end
112
+
113
+ check_file_content(file_name, fragment, true)
114
+ end
115
+
98
116
  Then /^the cassette "([^"]*)" should have the following response bodies:$/ do |file, table|
99
117
  interactions = in_current_dir { YAML.load_file(file) }
100
118
  actual_response_bodies = interactions.map { |i| i.response.body }
data/lib/vcr.rb CHANGED
@@ -1,11 +1,18 @@
1
+ require 'vcr/util/regexes'
2
+ require 'vcr/util/variable_args_block_caller'
3
+ require 'vcr/util/yaml'
4
+
1
5
  require 'vcr/cassette'
2
6
  require 'vcr/config'
3
- require 'vcr/deprecations'
4
7
  require 'vcr/request_matcher'
5
- require 'vcr/util/regexes'
6
8
  require 'vcr/version'
9
+
7
10
  require 'vcr/http_stubbing_adapters/common'
8
11
 
12
+ require 'vcr/deprecations/cassette'
13
+ require 'vcr/deprecations/config'
14
+ require 'vcr/deprecations/http_stubbing_adapters/common'
15
+
9
16
  require 'vcr/structs/normalizers/body'
10
17
  require 'vcr/structs/normalizers/header'
11
18
  require 'vcr/structs/normalizers/status_message'
@@ -16,12 +23,13 @@ require 'vcr/structs/response'
16
23
  require 'vcr/structs/response_status'
17
24
 
18
25
  module VCR
26
+ include VariableArgsBlockCaller
19
27
  extend self
20
28
 
21
29
  autoload :BasicObject, 'vcr/util/basic_object'
22
- autoload :CucumberTags, 'vcr/cucumber_tags'
30
+ autoload :CucumberTags, 'vcr/test_frameworks/cucumber'
23
31
  autoload :InternetConnection, 'vcr/util/internet_connection'
24
- autoload :RSpec, 'vcr/rspec'
32
+ autoload :RSpec, 'vcr/test_frameworks/rspec'
25
33
 
26
34
  LOCALHOST_ALIASES = %w( localhost 127.0.0.1 0.0.0.0 )
27
35
 
@@ -63,8 +71,7 @@ module VCR
63
71
  cassette = insert_cassette(*args)
64
72
 
65
73
  begin
66
- # yield the cassette if the block accepts an argument or variable args
67
- block.arity == 0 ? block.call : block.call(cassette)
74
+ call_block(block, cassette)
68
75
  ensure
69
76
  eject_cassette
70
77
  end
@@ -1,5 +1,4 @@
1
1
  require 'fileutils'
2
- require 'yaml'
3
2
  require 'erb'
4
3
  require 'set'
5
4
 
@@ -7,13 +6,21 @@ require 'vcr/cassette/reader'
7
6
 
8
7
  module VCR
9
8
  class Cassette
10
- VALID_RECORD_MODES = [:all, :none, :new_episodes]
9
+ VALID_RECORD_MODES = [:all, :none, :new_episodes, :once]
11
10
 
12
11
  attr_reader :name, :record_mode, :match_requests_on, :erb, :re_record_interval, :tag
13
12
 
14
13
  def initialize(name, options = {})
15
14
  options = VCR::Config.default_cassette_options.merge(options)
16
- invalid_options = options.keys - [:record, :erb, :allow_real_http, :match_requests_on, :re_record_interval, :tag]
15
+ invalid_options = options.keys - [
16
+ :record,
17
+ :erb,
18
+ :allow_real_http,
19
+ :match_requests_on,
20
+ :re_record_interval,
21
+ :tag,
22
+ :update_content_length_header
23
+ ]
17
24
 
18
25
  if invalid_options.size > 0
19
26
  raise ArgumentError.new("You passed the following invalid options to VCR::Cassette.new: #{invalid_options.inspect}.")
@@ -26,6 +33,7 @@ module VCR
26
33
  @re_record_interval = options[:re_record_interval]
27
34
  @tag = options[:tag]
28
35
  @record_mode = :all if should_re_record?
36
+ @update_content_length_header = options[:update_content_length_header]
29
37
 
30
38
  deprecate_old_cassette_options(options)
31
39
  raise_error_unless_valid_record_mode
@@ -57,6 +65,10 @@ module VCR
57
65
  File.join(VCR::Config.cassette_library_dir, "#{sanitized_name}.yml") if VCR::Config.cassette_library_dir
58
66
  end
59
67
 
68
+ def update_content_length_header?
69
+ @update_content_length_header
70
+ end
71
+
60
72
  private
61
73
 
62
74
  def sanitized_name
@@ -65,7 +77,7 @@ module VCR
65
77
 
66
78
  def raise_error_unless_valid_record_mode
67
79
  unless VALID_RECORD_MODES.include?(record_mode)
68
- raise ArgumentError.new("#{record_mode} is not a valid cassette record mode. Valid options are: #{VALID_RECORD_MODES.inspect}")
80
+ raise ArgumentError.new("#{record_mode} is not a valid cassette record mode. Valid modes are: #{VALID_RECORD_MODES.inspect}")
69
81
  end
70
82
  end
71
83
 
@@ -77,7 +89,11 @@ module VCR
77
89
  end
78
90
 
79
91
  def should_allow_http_connections?
80
- record_mode != :none
92
+ case record_mode
93
+ when :none; false
94
+ when :once; !File.size?(file)
95
+ else true
96
+ end
81
97
  end
82
98
 
83
99
  def should_stub_requests?
@@ -101,18 +117,23 @@ module VCR
101
117
  VCR.http_stubbing_adapter.create_stubs_checkpoint(self)
102
118
  if file && File.size?(file)
103
119
  begin
104
- interactions = YAML.load(raw_yaml_content)
105
- invoke_hook(:before_playback, interactions)
106
-
107
- interactions.reject! do |i|
108
- i.request.uri.is_a?(String) && VCR::Config.uri_should_be_ignored?(i.request.uri)
109
- end
110
-
111
- recorded_interactions.replace(interactions)
112
- rescue TypeError
120
+ interactions = VCR::YAML.load(raw_yaml_content)
121
+ rescue TypeError, ArgumentError # Syck raises TypeError, Psych raises ArgumentError
113
122
  raise unless raw_yaml_content =~ /VCR::RecordedResponse/
114
123
  raise "The VCR cassette #{sanitized_name}.yml uses an old format that is now deprecated. VCR provides a rake task to migrate your old cassettes to the new format. See http://github.com/myronmarston/vcr/blob/master/CHANGELOG.md for more info."
115
124
  end
125
+
126
+ invoke_hook(:before_playback, interactions)
127
+
128
+ interactions.reject! do |i|
129
+ i.request.uri.is_a?(String) && VCR::Config.uri_should_be_ignored?(i.request.uri)
130
+ end
131
+
132
+ if update_content_length_header?
133
+ interactions.each { |i| i.response.update_content_length_header }
134
+ end
135
+
136
+ recorded_interactions.replace(interactions)
116
137
  end
117
138
 
118
139
  if should_stub_requests?
@@ -152,7 +173,7 @@ module VCR
152
173
 
153
174
  directory = File.dirname(file)
154
175
  FileUtils.mkdir_p directory unless File.exist?(directory)
155
- File.open(file, 'w') { |f| f.write interactions.to_yaml }
176
+ File.open(file, 'w') { |f| f.write VCR::YAML.dump(interactions) }
156
177
  end
157
178
 
158
179
  def invoke_hook(type, interactions)
@@ -1,9 +1,10 @@
1
1
  require 'fileutils'
2
- require 'vcr/hooks'
2
+ require 'vcr/util/hooks'
3
3
 
4
4
  module VCR
5
5
  module Config
6
6
  include VCR::Hooks
7
+ include VCR::VariableArgsBlockCaller
7
8
  extend self
8
9
 
9
10
  define_hook :before_record
@@ -18,7 +19,8 @@ module VCR
18
19
  attr_writer :default_cassette_options
19
20
  def default_cassette_options
20
21
  @default_cassette_options ||= {}
21
- @default_cassette_options.merge!(:match_requests_on => RequestMatcher::DEFAULT_MATCH_ATTRIBUTES) unless @default_cassette_options.has_key?(:match_requests_on)
22
+ @default_cassette_options[:match_requests_on] ||= RequestMatcher::DEFAULT_MATCH_ATTRIBUTES
23
+ @default_cassette_options[:record] ||= :once
22
24
  @default_cassette_options
23
25
  end
24
26
 
@@ -57,6 +59,16 @@ module VCR
57
59
  !!@allow_http_connections_when_no_cassette
58
60
  end
59
61
 
62
+ def filter_sensitive_data(placeholder, tag = nil, &block)
63
+ before_record(tag) do |interaction|
64
+ interaction.filter!(call_block(block, interaction), placeholder)
65
+ end
66
+
67
+ before_playback(tag) do |interaction|
68
+ interaction.filter!(placeholder, call_block(block, interaction))
69
+ end
70
+ end
71
+
60
72
  def uri_should_be_ignored?(uri)
61
73
  uri = URI.parse(uri) unless uri.respond_to?(:host)
62
74
  ignored_hosts.include?(uri.host)
@@ -0,0 +1,29 @@
1
+ module VCR
2
+ class Cassette
3
+ def allow_real_http_requests_to?(uri)
4
+ warn "WARNING: VCR::Cassette#allow_real_http_requests_to? is deprecated and should no longer be used."
5
+ VCR::Config.uri_should_be_ignored?(uri.to_s)
6
+ end
7
+
8
+ private
9
+
10
+ def deprecate_old_cassette_options(options)
11
+ message = "VCR's :allow_real_http cassette option is deprecated. Instead, use the ignore_localhost configuration option."
12
+ if options[:allow_real_http] == :localhost
13
+ @original_ignored_hosts = VCR::Config.ignored_hosts.dup
14
+ VCR::Config.ignored_hosts.clear
15
+ VCR::Config.ignore_hosts *VCR::LOCALHOST_ALIASES
16
+ Kernel.warn "WARNING: #{message}"
17
+ elsif options[:allow_real_http]
18
+ raise ArgumentError.new(message)
19
+ end
20
+ end
21
+
22
+ def restore_ignore_localhost_for_deprecation
23
+ if defined?(@original_ignored_hosts)
24
+ VCR::Config.ignored_hosts.clear
25
+ VCR::Config.ignore_hosts *@original_ignored_hosts
26
+ end
27
+ end
28
+ end
29
+ end