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.
Files changed (113) hide show
  1. data/.gitignore +2 -0
  2. data/.limited_red +1 -0
  3. data/.travis.yml +10 -1
  4. data/.yardopts +9 -0
  5. data/CHANGELOG.md +51 -1
  6. data/Gemfile +5 -1
  7. data/LICENSE +1 -1
  8. data/README.md +23 -28
  9. data/Rakefile +63 -18
  10. data/Upgrade.md +200 -0
  11. data/features/.nav +2 -0
  12. data/features/cassettes/automatic_re_recording.feature +19 -15
  13. data/features/cassettes/dynamic_erb.feature +12 -4
  14. data/features/cassettes/exclusive.feature +31 -23
  15. data/features/cassettes/format.feature +54 -30
  16. data/features/cassettes/naming.feature +1 -1
  17. data/features/cassettes/update_content_length_header.feature +16 -12
  18. data/features/configuration/allow_http_connections_when_no_cassette.feature +1 -1
  19. data/features/configuration/debug_logging.feature +52 -0
  20. data/features/configuration/filter_sensitive_data.feature +4 -4
  21. data/features/configuration/hook_into.feature +5 -2
  22. data/features/configuration/ignore_request.feature +5 -3
  23. data/features/configuration/preserve_exact_body_bytes.feature +103 -0
  24. data/features/hooks/after_http_request.feature +17 -4
  25. data/features/hooks/around_http_request.feature +2 -1
  26. data/features/hooks/before_http_request.feature +25 -8
  27. data/features/hooks/before_playback.feature +16 -12
  28. data/features/hooks/before_record.feature +2 -2
  29. data/features/http_libraries/em_http_request.feature +82 -58
  30. data/features/http_libraries/net_http.feature +6 -6
  31. data/features/middleware/faraday.feature +2 -1
  32. data/features/middleware/rack.feature +2 -2
  33. data/features/record_modes/all.feature +19 -15
  34. data/features/record_modes/new_episodes.feature +17 -13
  35. data/features/record_modes/none.feature +15 -11
  36. data/features/record_modes/once.feature +16 -12
  37. data/features/request_matching/body.feature +28 -20
  38. data/features/request_matching/custom_matcher.feature +28 -20
  39. data/features/request_matching/headers.feature +34 -26
  40. data/features/request_matching/host.feature +28 -20
  41. data/features/request_matching/identical_request_sequence.feature +28 -20
  42. data/features/request_matching/method.feature +28 -20
  43. data/features/request_matching/path.feature +28 -20
  44. data/features/request_matching/playback_repeats.feature +28 -20
  45. data/features/request_matching/uri.feature +28 -20
  46. data/features/request_matching/uri_without_param.feature +28 -20
  47. data/features/support/env.rb +7 -6
  48. data/features/support/vcr_cucumber_helpers.rb +1 -0
  49. data/features/test_frameworks/cucumber.feature +8 -8
  50. data/features/test_frameworks/rspec_macro.feature +4 -4
  51. data/features/test_frameworks/rspec_metadata.feature +6 -6
  52. data/features/test_frameworks/shoulda.feature +1 -1
  53. data/features/test_frameworks/test_unit.feature +1 -1
  54. data/lib/vcr.rb +156 -5
  55. data/lib/vcr/cassette.rb +80 -30
  56. data/lib/vcr/cassette/http_interaction_list.rb +33 -4
  57. data/lib/vcr/cassette/migrator.rb +2 -3
  58. data/lib/vcr/cassette/reader.rb +1 -0
  59. data/lib/vcr/cassette/serializers.rb +22 -0
  60. data/lib/vcr/cassette/serializers/json.rb +27 -2
  61. data/lib/vcr/cassette/serializers/psych.rb +26 -2
  62. data/lib/vcr/cassette/serializers/syck.rb +28 -2
  63. data/lib/vcr/cassette/serializers/yaml.rb +28 -2
  64. data/lib/vcr/configuration.rb +348 -10
  65. data/lib/vcr/deprecations.rb +8 -0
  66. data/lib/vcr/errors.rb +40 -0
  67. data/lib/vcr/extensions/net_http_response.rb +12 -11
  68. data/lib/vcr/library_hooks.rb +1 -0
  69. data/lib/vcr/library_hooks/excon.rb +24 -3
  70. data/lib/vcr/library_hooks/fakeweb.rb +32 -16
  71. data/lib/vcr/library_hooks/faraday.rb +3 -0
  72. data/lib/vcr/library_hooks/typhoeus.rb +40 -37
  73. data/lib/vcr/library_hooks/webmock.rb +54 -34
  74. data/lib/vcr/middleware/faraday.rb +13 -0
  75. data/lib/vcr/middleware/rack.rb +35 -0
  76. data/lib/vcr/request_handler.rb +60 -8
  77. data/lib/vcr/request_ignorer.rb +1 -0
  78. data/lib/vcr/request_matcher_registry.rb +28 -0
  79. data/lib/vcr/structs.rb +245 -38
  80. data/lib/vcr/test_frameworks/cucumber.rb +10 -0
  81. data/lib/vcr/test_frameworks/rspec.rb +26 -1
  82. data/lib/vcr/util/hooks.rb +29 -27
  83. data/lib/vcr/util/internet_connection.rb +2 -0
  84. data/lib/vcr/util/logger.rb +25 -0
  85. data/lib/vcr/util/variable_args_block_caller.rb +1 -0
  86. data/lib/vcr/util/version_checker.rb +1 -0
  87. data/lib/vcr/version.rb +8 -1
  88. data/spec/capture_warnings.rb +3 -3
  89. data/spec/monkey_patches.rb +28 -13
  90. data/spec/spec_helper.rb +17 -0
  91. data/spec/support/http_library_adapters.rb +7 -4
  92. data/spec/support/shared_example_groups/hook_into_http_library.rb +96 -32
  93. data/spec/support/shared_example_groups/request_hooks.rb +9 -8
  94. data/spec/support/sinatra_app.rb +3 -1
  95. data/spec/support/vcr_localhost_server.rb +1 -0
  96. data/spec/vcr/cassette/http_interaction_list_spec.rb +119 -54
  97. data/spec/vcr/cassette/migrator_spec.rb +19 -6
  98. data/spec/vcr/cassette/serializers_spec.rb +51 -6
  99. data/spec/vcr/cassette_spec.rb +44 -19
  100. data/spec/vcr/configuration_spec.rb +91 -6
  101. data/spec/vcr/library_hooks/excon_spec.rb +54 -16
  102. data/spec/vcr/library_hooks/fakeweb_spec.rb +12 -21
  103. data/spec/vcr/library_hooks/typhoeus_spec.rb +2 -29
  104. data/spec/vcr/library_hooks/webmock_spec.rb +4 -18
  105. data/spec/vcr/middleware/faraday_spec.rb +1 -16
  106. data/spec/vcr/structs_spec.rb +194 -61
  107. data/spec/vcr/test_frameworks/rspec_spec.rb +10 -0
  108. data/spec/vcr/util/hooks_spec.rb +104 -56
  109. data/spec/vcr/util/version_checker_spec.rb +45 -0
  110. data/spec/vcr_spec.rb +11 -0
  111. data/vcr.gemspec +30 -34
  112. metadata +149 -95
  113. 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
@@ -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 bar ]
33
+ headers 'Set-Cookie' => %w[ bar foo ]
32
34
  'header set'
33
35
  end
34
36
 
@@ -59,6 +59,7 @@ module VCR
59
59
  def concurrently
60
60
  if should_use_subprocess?
61
61
  pid = Process.fork do
62
+ trap(:INT) { ::Rack::Handler::WEBrick.shutdown }
62
63
  yield
63
64
  exit # manually exit; otherwise this sub-process will re-run the specs that haven't run yet.
64
65
  end
@@ -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
- shared_examples_for "an HTTP interaction finding method" do |method|
10
- it 'returns nil when the list is empty' do
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 { |r| r.body = body }
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 "#response_for" do
129
- it_behaves_like "an HTTP interaction finding method", :response_for do
130
- def respond_with(value)
131
- ::RSpec::Matchers::Matcher.new :respond_with, value do |expected|
132
- match { |a| expected.nil? ? a.nil? : a.body == expected }
133
- end
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: Hello foo
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: Hello bar
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
- File.read(file_name).should eq(updated_contents)
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
- File.read(file_name).should eq(updated_contents.gsub('example.com', '<HOST>:80'))
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
- context "the #{name} serializer" do
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
- it_behaves_like "a serializer", :syck, "yml", :lazily_loaded
39
- it_behaves_like "a serializer", :psych, "yml", :lazily_loaded if RUBY_VERSION =~ /1.9/
40
- it_behaves_like "a serializer", :json, "json", :lazily_loaded
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
@@ -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(:the_interaction)
47
- cassette.new_recorded_interactions.should eq([:the_interaction])
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(:interactions) { [stub(:to_hash => { "i" => 1 }, :ignored? => false), stub(:to_hash => { "i" => 2 }, :ignored? => false)] }
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
- subject.serializable_hash.should include('http_interactions' => [{ "i" => 1 }, { "i" => 2 }])
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 appropriately tagged before_playback hooks' do
345
- VCR.configuration.should_receive(:invoke_tagged_hook).with(
364
+ it 'invokes the before_playback hooks' do
365
+ VCR.configuration.should_receive(:invoke_hook).with(
346
366
  :before_playback,
347
- :foo,
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, :tag => :foo)
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].map { |m| VCR.request_matchers[m] })
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].map { |m| VCR.request_matchers[m] })
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(:invoke_tagged_hook).with(
427
+ VCR.configuration.should_receive(:invoke_hook).with(
407
428
  :before_record,
408
- :foo,
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.ignore!
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
- interaction_1.ignore!
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])