vcr 2.0.0.beta1 → 2.0.0.beta2

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 (88) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +3 -0
  3. data/CHANGELOG.md +37 -2
  4. data/Gemfile +2 -2
  5. data/README.md +10 -1
  6. data/Rakefile +43 -7
  7. data/Upgrade.md +45 -0
  8. data/features/.nav +1 -0
  9. data/features/cassettes/automatic_re_recording.feature +19 -17
  10. data/features/cassettes/dynamic_erb.feature +32 -28
  11. data/features/cassettes/exclusive.feature +28 -24
  12. data/features/cassettes/format.feature +213 -31
  13. data/features/cassettes/update_content_length_header.feature +20 -18
  14. data/features/configuration/filter_sensitive_data.feature +4 -4
  15. data/features/configuration/hooks.feature +27 -23
  16. data/features/http_libraries/em_http_request.feature +79 -75
  17. data/features/record_modes/all.feature +14 -14
  18. data/features/record_modes/new_episodes.feature +15 -15
  19. data/features/record_modes/none.feature +15 -15
  20. data/features/record_modes/once.feature +15 -15
  21. data/features/request_matching/body.feature +25 -23
  22. data/features/request_matching/custom_matcher.feature +25 -23
  23. data/features/request_matching/headers.feature +32 -36
  24. data/features/request_matching/host.feature +27 -25
  25. data/features/request_matching/identical_request_sequence.feature +27 -25
  26. data/features/request_matching/method.feature +27 -25
  27. data/features/request_matching/path.feature +27 -25
  28. data/features/request_matching/playback_repeats.feature +27 -25
  29. data/features/request_matching/uri.feature +27 -25
  30. data/features/request_matching/uri_without_param.feature +28 -26
  31. data/features/step_definitions/cli_steps.rb +71 -17
  32. data/features/support/env.rb +3 -1
  33. data/features/support/http_lib_filters.rb +6 -3
  34. data/features/support/vcr_cucumber_helpers.rb +4 -2
  35. data/lib/vcr.rb +6 -2
  36. data/lib/vcr/cassette.rb +75 -51
  37. data/lib/vcr/cassette/migrator.rb +111 -0
  38. data/lib/vcr/cassette/serializers.rb +35 -0
  39. data/lib/vcr/cassette/serializers/json.rb +23 -0
  40. data/lib/vcr/cassette/serializers/psych.rb +24 -0
  41. data/lib/vcr/cassette/serializers/syck.rb +35 -0
  42. data/lib/vcr/cassette/serializers/yaml.rb +24 -0
  43. data/lib/vcr/configuration.rb +6 -1
  44. data/lib/vcr/errors.rb +1 -1
  45. data/lib/vcr/library_hooks/excon.rb +1 -7
  46. data/lib/vcr/library_hooks/typhoeus.rb +6 -22
  47. data/lib/vcr/library_hooks/webmock.rb +1 -1
  48. data/lib/vcr/middleware/faraday.rb +1 -1
  49. data/lib/vcr/request_matcher_registry.rb +43 -30
  50. data/lib/vcr/structs.rb +209 -0
  51. data/lib/vcr/tasks/vcr.rake +9 -0
  52. data/lib/vcr/version.rb +1 -1
  53. data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
  54. data/spec/fixtures/cassette_spec/example.yml +79 -78
  55. data/spec/fixtures/cassette_spec/with_localhost_requests.yml +79 -77
  56. data/spec/fixtures/fake_example.com_responses.yml +78 -76
  57. data/spec/fixtures/match_requests_on.yml +147 -145
  58. data/spec/monkey_patches.rb +5 -5
  59. data/spec/support/http_library_adapters.rb +48 -0
  60. data/spec/support/shared_example_groups/hook_into_http_library.rb +53 -20
  61. data/spec/support/sinatra_app.rb +12 -0
  62. data/spec/vcr/cassette/http_interaction_list_spec.rb +1 -1
  63. data/spec/vcr/cassette/migrator_spec.rb +183 -0
  64. data/spec/vcr/cassette/serializers_spec.rb +122 -0
  65. data/spec/vcr/cassette_spec.rb +147 -83
  66. data/spec/vcr/configuration_spec.rb +11 -1
  67. data/spec/vcr/library_hooks/typhoeus_spec.rb +3 -3
  68. data/spec/vcr/library_hooks/webmock_spec.rb +7 -1
  69. data/spec/vcr/request_ignorer_spec.rb +1 -1
  70. data/spec/vcr/request_matcher_registry_spec.rb +46 -4
  71. data/spec/vcr/structs_spec.rb +309 -0
  72. data/spec/vcr_spec.rb +7 -0
  73. data/vcr.gemspec +9 -12
  74. metadata +75 -61
  75. data/lib/vcr/structs/http_interaction.rb +0 -58
  76. data/lib/vcr/structs/normalizers/body.rb +0 -24
  77. data/lib/vcr/structs/normalizers/header.rb +0 -64
  78. data/lib/vcr/structs/normalizers/status_message.rb +0 -17
  79. data/lib/vcr/structs/normalizers/uri.rb +0 -34
  80. data/lib/vcr/structs/request.rb +0 -13
  81. data/lib/vcr/structs/response.rb +0 -13
  82. data/lib/vcr/structs/response_status.rb +0 -5
  83. data/lib/vcr/util/yaml.rb +0 -11
  84. data/spec/support/shared_example_groups/normalizers.rb +0 -94
  85. data/spec/vcr/structs/http_interaction_spec.rb +0 -89
  86. data/spec/vcr/structs/request_spec.rb +0 -39
  87. data/spec/vcr/structs/response_spec.rb +0 -44
  88. data/spec/vcr/structs/response_status_spec.rb +0 -9
@@ -0,0 +1,122 @@
1
+ require 'vcr/cassette/serializers'
2
+ begin
3
+ require 'psych' # ensure psych is loaded for these tests if its available
4
+ rescue LoadError
5
+ end
6
+
7
+ module VCR
8
+ class Cassette
9
+ describe Serializers do
10
+
11
+ shared_examples_for "a serializer" do |name, file_extension, lazily_loaded|
12
+ context "the #{name} serializer" do
13
+ let(:serializer) { subject[name] }
14
+
15
+ it 'lazily loads the serializer' do
16
+ serializers = subject.instance_variable_get(:@serializers)
17
+ serializers.should_not have_key(name)
18
+ subject[name].should_not be_nil
19
+ serializers.should have_key(name)
20
+ end if lazily_loaded
21
+
22
+ it "returns '#{file_extension}' as the file extension" do
23
+ serializer.file_extension.should eq(file_extension)
24
+ end
25
+
26
+ it "can serialize and deserialize a hash" do
27
+ hash = { "a" => 7, "nested" => { "hash" => [1, 2, 3] }}
28
+ serialized = serializer.serialize(hash)
29
+ serialized.should_not eq(hash)
30
+ serialized.should be_a(String)
31
+ deserialized = serializer.deserialize(serialized)
32
+ deserialized.should eq(hash)
33
+ end
34
+ end
35
+ end
36
+
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
41
+
42
+ context "a custom :ruby serializer" do
43
+ let(:custom_serializer) do
44
+ Object.new.tap do |obj|
45
+ def obj.file_extension
46
+ "rb"
47
+ end
48
+
49
+ def obj.serialize(hash)
50
+ hash.inspect
51
+ end
52
+
53
+ def obj.deserialize(string)
54
+ eval(string)
55
+ end
56
+ end
57
+ end
58
+
59
+ before(:each) do
60
+ subject[:ruby] = custom_serializer
61
+ end
62
+
63
+ it_behaves_like "a serializer", :ruby, "rb", false
64
+ end
65
+
66
+ describe "#[]=" do
67
+ context 'when there is already a serializer registered for the given name' do
68
+ before(:each) do
69
+ subject[:foo] = :old_serializer
70
+ subject.stub :warn
71
+ end
72
+
73
+ it 'overrides the existing serializer' do
74
+ subject[:foo] = :new_serializer
75
+ subject[:foo].should be(:new_serializer)
76
+ end
77
+
78
+ it 'warns that there is a name collision' do
79
+ subject.should_receive(:warn).with(
80
+ /WARNING: There is already a VCR cassette serializer registered for :foo\. Overriding it/
81
+ )
82
+ subject[:foo] = :new_serializer
83
+ end
84
+ end
85
+ end
86
+
87
+ describe "#[]" do
88
+ it 'raises an error when given an unrecognized serializer name' do
89
+ expect { subject[:foo] }.to raise_error(ArgumentError)
90
+ end
91
+
92
+ it 'returns the named serializer' do
93
+ subject[:yaml].should be(VCR::Cassette::Serializers::YAML)
94
+ end
95
+ end
96
+
97
+ # see https://gist.github.com/815769
98
+ problematic_syck_string = "1\n \n2"
99
+
100
+ describe "psych serializer" do
101
+ it 'serializes things using pysch even if syck is configured as the default YAML engine' do
102
+ ::YAML::ENGINE.yamler = 'syck'
103
+ serialized = subject[:psych].serialize(problematic_syck_string)
104
+ subject[:psych].deserialize(serialized).should eq(problematic_syck_string)
105
+ end if defined?(::Psych)
106
+
107
+ it 'raises an error if psych cannot be loaded' do
108
+ expect { subject[:psych] }.to raise_error(LoadError)
109
+ end unless defined?(::Psych)
110
+ end
111
+
112
+ describe "syck serializer" do
113
+ it 'forcibly serializes things using syck even if psych is the currently configured YAML engine' do
114
+ ::YAML::ENGINE.yamler = 'psych'
115
+ serialized = subject[:syck].serialize(problematic_syck_string)
116
+ subject[:syck].deserialize(serialized).should_not eq(problematic_syck_string)
117
+ end if defined?(::Psych)
118
+ end
119
+ end
120
+ end
121
+ end
122
+
@@ -1,12 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe VCR::Cassette do
4
+ def http_interaction
5
+ request = VCR::Request.new(:get)
6
+ response = VCR::Response.new
7
+ response.status = VCR::ResponseStatus.new
8
+ VCR::HTTPInteraction.new(request, response).tap { |i| yield i if block_given? }
9
+ end
10
+
4
11
  describe '#file' do
5
12
  it 'combines the cassette_library_dir with the cassette name' do
6
13
  cassette = VCR::Cassette.new('the_file')
7
14
  cassette.file.should eq(File.join(VCR.configuration.cassette_library_dir, 'the_file.yml'))
8
15
  end
9
16
 
17
+ it 'uses the file extension from the serializer' do
18
+ VCR.cassette_serializers[:custom] = stub(:file_extension => "custom")
19
+ cassette = VCR::Cassette.new('the_file', :serialize_with => :custom)
20
+ cassette.file.should =~ /\.custom$/
21
+ end
22
+
10
23
  it 'strips out disallowed characters so that it is a valid file name with no spaces' do
11
24
  cassette = VCR::Cassette.new("\nthis \t! is-the_13212_file name")
12
25
  cassette.file.should =~ /#{Regexp.escape('_this_is-the_13212_file_name.yml')}$/
@@ -35,6 +48,27 @@ describe VCR::Cassette do
35
48
  end
36
49
  end
37
50
 
51
+ describe "#serializable_hash" do
52
+ subject { VCR::Cassette.new("foo") }
53
+ let(:interactions) { [stub(:to_hash => { "i" => 1 }, :ignored? => false), stub(:to_hash => { "i" => 2 }, :ignored? => false)] }
54
+
55
+ before(:each) do
56
+ interactions.each do |i|
57
+ subject.record_http_interaction(i)
58
+ end
59
+ end
60
+
61
+ let(:metadata) { subject.serializable_hash.reject { |k,v| k == "http_interactions" } }
62
+
63
+ it 'includes the hash form of all recorded interactions' do
64
+ subject.serializable_hash.should include('http_interactions' => [{ "i" => 1 }, { "i" => 2 }])
65
+ end
66
+
67
+ it 'includes additional metadata about the cassette' do
68
+ metadata.should eq("recorded_with" => "VCR #{VCR.version}")
69
+ end
70
+ end
71
+
38
72
  describe "#recording?" do
39
73
  [:all, :new_episodes].each do |mode|
40
74
  it "returns true when the record mode is :#{mode}" do
@@ -89,57 +123,66 @@ describe VCR::Cassette do
89
123
  end
90
124
  end
91
125
 
92
- describe '.new' do
93
- it "raises an error if given an invalid record mode" do
94
- expect { VCR::Cassette.new(:test, :record => :not_a_record_mode) }.to raise_error(ArgumentError)
126
+ describe "reading the file from disk" do
127
+ before(:each) do
128
+ File.stub(:size? => true)
95
129
  end
96
130
 
97
- it 'raises an error if given invalid options' do
98
- expect {
99
- VCR::Cassette.new(:test, :invalid => :option)
100
- }.to raise_error(ArgumentError)
101
- end
131
+ let(:empty_cassette_yaml) { YAML.dump("http_interactions" => []) }
102
132
 
103
- it 'does not raise an error in the case of an empty file' do
104
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
105
- VCR::Cassette.new('empty', :record => :none).recorded_interactions.should eq([])
133
+ it 'reads the appropriate file from disk using a VCR::Cassette::Reader' do
134
+ VCR::Cassette::Reader.should_receive(:new).with(
135
+ "#{VCR.configuration.cassette_library_dir}/foo.yml", anything
136
+ ).and_return(mock('reader', :read => empty_cassette_yaml))
137
+
138
+ VCR::Cassette.new('foo', :record => :new_episodes).http_interactions
106
139
  end
107
140
 
108
- describe "reading the file from disk" do
109
- before(:each) do
110
- File.stub(:size? => true)
111
- end
141
+ [true, false, nil, { }].each do |erb|
142
+ it "passes #{erb.inspect} to the VCR::Cassette::Reader when given as the :erb option" do
143
+ # test that it overrides the default
144
+ VCR.configuration.default_cassette_options = { :erb => true }
112
145
 
113
- it 'reads the appropriate file from disk using a VCR::Cassette::Reader' do
114
146
  VCR::Cassette::Reader.should_receive(:new).with(
115
- "#{VCR.configuration.cassette_library_dir}/foo.yml", anything
116
- ).and_return(mock('reader', :read => VCR::YAML.dump([])))
147
+ anything, erb
148
+ ).and_return(mock('reader', :read => empty_cassette_yaml))
117
149
 
118
- VCR::Cassette.new('foo', :record => :new_episodes)
150
+ VCR::Cassette.new('foo', :record => :new_episodes, :erb => erb).http_interactions
119
151
  end
120
152
 
121
- [true, false, nil, { }].each do |erb|
122
- it "passes #{erb.inspect} to the VCR::Cassette::Reader when given as the :erb option" do
123
- # test that it overrides the default
124
- VCR.configuration.default_cassette_options = { :erb => true }
153
+ it "passes #{erb.inspect} to the VCR::Cassette::Reader when it is the default :erb option and none is given" do
154
+ VCR.configuration.default_cassette_options = { :erb => erb }
125
155
 
126
- VCR::Cassette::Reader.should_receive(:new).with(
127
- anything, erb
128
- ).and_return(mock('reader', :read => VCR::YAML.dump([])))
156
+ VCR::Cassette::Reader.should_receive(:new).with(
157
+ anything, erb
158
+ ).and_return(mock('reader', :read => empty_cassette_yaml))
129
159
 
130
- VCR::Cassette.new('foo', :record => :new_episodes, :erb => erb)
131
- end
160
+ VCR::Cassette.new('foo', :record => :new_episodes).http_interactions
161
+ end
162
+ end
132
163
 
133
- it "passes #{erb.inspect} to the VCR::Cassette::Reader when it is the default :erb option and none is given" do
134
- VCR.configuration.default_cassette_options = { :erb => erb }
164
+ it 'raises a friendly error when the cassette file is in the old VCR 1.x format' do
165
+ VCR.configuration.cassette_library_dir = 'spec/fixtures/cassette_spec'
166
+ expect {
167
+ VCR::Cassette.new('1_x_cassette').http_interactions
168
+ }.to raise_error(VCR::Errors::InvalidCassetteFormatError)
169
+ end
170
+ end
135
171
 
136
- VCR::Cassette::Reader.should_receive(:new).with(
137
- anything, erb
138
- ).and_return(mock('reader', :read => VCR::YAML.dump([])))
172
+ describe '.new' do
173
+ it "raises an error if given an invalid record mode" do
174
+ expect { VCR::Cassette.new(:test, :record => :not_a_record_mode) }.to raise_error(ArgumentError)
175
+ end
139
176
 
140
- VCR::Cassette.new('foo', :record => :new_episodes)
141
- end
142
- end
177
+ it 'raises an error if given invalid options' do
178
+ expect {
179
+ VCR::Cassette.new(:test, :invalid => :option)
180
+ }.to raise_error(ArgumentError)
181
+ end
182
+
183
+ it 'does not raise an error in the case of an empty file' do
184
+ VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
185
+ VCR::Cassette.new('empty', :record => :none).previously_recorded_interactions.should eq([])
143
186
  end
144
187
 
145
188
  VCR::Cassette::VALID_RECORD_MODES.each do |record_mode|
@@ -155,27 +198,35 @@ describe VCR::Cassette do
155
198
  end
156
199
 
157
200
  context "when :#{record_mode} is passed as the record option" do
201
+ def stub_old_interactions(interactions)
202
+ hashes = interactions.map(&:to_hash)
203
+ VCR.cassette_serializers[:yaml].stub(:deserialize => { 'http_interactions' => hashes })
204
+ VCR::HTTPInteraction.stub(:from_hash) do |hash|
205
+ interactions[hashes.index(hash)]
206
+ end
207
+ end
208
+
158
209
  unless record_mode == :all
159
- let(:interaction_1) { VCR::HTTPInteraction.new(VCR::Request.new(:get, 'http://example.com/'), VCR::Response.new(VCR::ResponseStatus.new)) }
160
- let(:interaction_2) { VCR::HTTPInteraction.new(VCR::Request.new(:get, 'http://example.com/'), VCR::Response.new(VCR::ResponseStatus.new)) }
210
+ let(:interaction_1) { http_interaction { |i| i.request.uri = 'http://example.com/foo' } }
211
+ let(:interaction_2) { http_interaction { |i| i.request.uri = 'http://example.com/bar' } }
161
212
  let(:interactions) { [interaction_1, interaction_2] }
162
213
  before(:each) { VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec" }
163
214
 
164
215
  it 'updates the content_length headers when given :update_content_length_header => true' do
165
- VCR::YAML.stub(:load => interactions)
216
+ stub_old_interactions(interactions)
166
217
  interaction_1.response.should_receive(:update_content_length_header)
167
218
  interaction_2.response.should_receive(:update_content_length_header)
168
219
 
169
- VCR::Cassette.new('example', :record => record_mode, :update_content_length_header => true)
220
+ VCR::Cassette.new('example', :record => record_mode, :update_content_length_header => true).http_interactions
170
221
  end
171
222
 
172
223
  [nil, false].each do |val|
173
224
  it "does not update the content_lenth headers when given :update_content_length_header => #{val.inspect}" do
174
- VCR::YAML.stub(:load => interactions)
225
+ stub_old_interactions(interactions)
175
226
  interaction_1.response.should_not_receive(:update_content_length_header)
176
227
  interaction_2.response.should_not_receive(:update_content_length_header)
177
228
 
178
- VCR::Cassette.new('example', :record => record_mode, :update_content_length_header => val)
229
+ VCR::Cassette.new('example', :record => record_mode, :update_content_length_header => val).http_interactions
179
230
  end
180
231
  end
181
232
 
@@ -193,20 +244,34 @@ describe VCR::Cassette do
193
244
 
194
245
  context 'when the cassette file does exist' do
195
246
  before(:each) do
247
+ interactions = timestamps.map do |ts|
248
+ http_interaction { |i| i.recorded_at = ts }.to_hash
249
+ end
250
+ yaml = YAML.dump("http_interactions" => interactions)
251
+
196
252
  File.stub(:exist?).with(file_name).and_return(true)
197
- File.stub(:read).with(file_name).and_return(VCR::YAML.dump([]))
253
+ File.stub(:size?).with(file_name).and_return(true)
254
+ File.stub(:read).with(file_name).and_return(yaml)
198
255
  end
199
256
 
200
- context 'and the file was last modified less than 7 days ago' do
201
- before(:each) { File.stub(:stat).with(file_name).and_return(stub(:mtime => Time.now - 7.days + 60)) }
257
+ context 'and the earliest recorded interaction was recorded less than 7 days ago' do
258
+ let(:timestamps) do [
259
+ Time.now - 6.days + 60,
260
+ Time.now - 7.days + 60,
261
+ Time.now - 5.days + 60
262
+ ] end
202
263
 
203
264
  it "has :#{record_mode} for the record mode" do
204
265
  subject.record_mode.should eq(record_mode)
205
266
  end
206
267
  end
207
268
 
208
- context 'and the file was last modified more than 7 days ago' do
209
- before(:each) { File.stub(:stat).with(file_name).and_return(stub(:mtime => Time.now - 7.days - 60)) }
269
+ context 'and the earliest recorded interaction was recorded more than 7 days ago' do
270
+ let(:timestamps) do [
271
+ Time.now - 6.days - 60,
272
+ Time.now - 7.days - 60,
273
+ Time.now - 5.days - 60
274
+ ] end
210
275
 
211
276
  it "has :all for the record mode when there is an internet connection available" do
212
277
  VCR::InternetConnection.stub(:available? => true)
@@ -229,27 +294,27 @@ describe VCR::Cassette do
229
294
 
230
295
  VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
231
296
  cassette = VCR::Cassette.new('with_localhost_requests', :record => record_mode)
232
- cassette.recorded_interactions.map { |i| URI.parse(i.uri).host }.should eq(%w[example.com])
297
+ cassette.previously_recorded_interactions.map { |i| URI.parse(i.uri).host }.should eq(%w[example.com])
233
298
  end
234
299
 
235
300
  it "loads the recorded interactions from the library yml file" do
236
301
  VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
237
302
  cassette = VCR::Cassette.new('example', :record => record_mode)
238
303
 
239
- cassette.should have(3).recorded_interactions
304
+ cassette.should have(3).previously_recorded_interactions
240
305
 
241
- i1, i2, i3 = *cassette.recorded_interactions
306
+ i1, i2, i3 = *cassette.previously_recorded_interactions
242
307
 
243
308
  i1.request.method.should eq(:get)
244
- i1.request.uri.should eq('http://example.com:80/')
309
+ i1.request.uri.should eq('http://example.com/')
245
310
  i1.response.body.should =~ /You have reached this web page by typing.+example\.com/
246
311
 
247
312
  i2.request.method.should eq(:get)
248
- i2.request.uri.should eq('http://example.com:80/foo')
313
+ i2.request.uri.should eq('http://example.com/foo')
249
314
  i2.response.body.should =~ /foo was not found on this server/
250
315
 
251
316
  i3.request.method.should eq(:get)
252
- i3.request.uri.should eq('http://example.com:80/')
317
+ i3.request.uri.should eq('http://example.com/')
253
318
  i3.response.body.should =~ /Another example\.com response/
254
319
  end
255
320
 
@@ -285,7 +350,7 @@ describe VCR::Cassette do
285
350
  ).exactly(3).times
286
351
 
287
352
  cassette = VCR::Cassette.new('example', :record => record_mode, :tag => :foo)
288
- cassette.should have(3).recorded_interactions
353
+ cassette.should have(3).previously_recorded_interactions
289
354
  end
290
355
 
291
356
  it 'does not playback any interactions that are ignored in a before_playback hook' do
@@ -295,7 +360,7 @@ describe VCR::Cassette do
295
360
 
296
361
  VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
297
362
  cassette = VCR::Cassette.new('example', :record => record_mode)
298
- cassette.should have(2).recorded_interactions
363
+ cassette.should have(2).previously_recorded_interactions
299
364
  end
300
365
 
301
366
  it 'instantiates the http_interactions with the loaded interactions and the request matchers' do
@@ -317,25 +382,21 @@ describe VCR::Cassette do
317
382
  end
318
383
 
319
384
  describe '#eject' do
320
- it "writes the recorded interactions to disk as yaml" do
321
- recorded_interactions = [
322
- VCR::HTTPInteraction.new(:req_sig_1, :response_1),
323
- VCR::HTTPInteraction.new(:req_sig_2, :response_2),
324
- VCR::HTTPInteraction.new(:req_sig_3, :response_3)
325
- ]
326
-
385
+ it "writes the serializable_hash to disk as yaml" do
327
386
  cassette = VCR::Cassette.new(:eject_test)
328
- cassette.stub!(:new_recorded_interactions).and_return(recorded_interactions)
387
+ cassette.record_http_interaction http_interaction # so it has one
388
+ cassette.should respond_to(:serializable_hash)
389
+ cassette.stub(:serializable_hash => { "http_interactions" => [1, 3, 5] })
329
390
 
330
391
  expect { cassette.eject }.to change { File.exist?(cassette.file) }.from(false).to(true)
331
- saved_recorded_interactions = VCR::YAML.load_file(cassette.file)
332
- saved_recorded_interactions.should eq(recorded_interactions)
392
+ saved_stuff = YAML.load_file(cassette.file)
393
+ saved_stuff.should eq("http_interactions" => [1, 3, 5])
333
394
  end
334
395
 
335
396
  it 'invokes the appropriately tagged before_record hooks' do
336
397
  interactions = [
337
- VCR::HTTPInteraction.new(:req_sig_1, :response_1),
338
- VCR::HTTPInteraction.new(:req_sig_2, :response_2)
398
+ http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' },
399
+ http_interaction { |i| i.request.uri = 'http://bar.com/'; i.response.body = 'res 2' }
339
400
  ]
340
401
 
341
402
  cassette = VCR::Cassette.new('example', :tag => :foo)
@@ -354,8 +415,8 @@ describe VCR::Cassette do
354
415
  end
355
416
 
356
417
  it 'does not record interactions that have been ignored' do
357
- interaction_1 = VCR::HTTPInteraction.new(:request_1, :response_1)
358
- interaction_2 = VCR::HTTPInteraction.new(:request_2, :response_2)
418
+ interaction_1 = http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' }
419
+ interaction_2 = http_interaction { |i| i.request.uri = 'http://bar.com/'; i.response.body = 'res 2' }
359
420
 
360
421
  interaction_1.ignore!
361
422
 
@@ -363,12 +424,12 @@ describe VCR::Cassette do
363
424
  cassette.stub!(:new_recorded_interactions).and_return([interaction_1, interaction_2])
364
425
  cassette.eject
365
426
 
366
- saved_recorded_interactions = VCR::YAML.load_file(cassette.file)
367
- saved_recorded_interactions.should eq([interaction_2])
427
+ saved_recorded_interactions = ::YAML.load_file(cassette.file)
428
+ saved_recorded_interactions["http_interactions"].should eq([interaction_2.to_hash])
368
429
  end
369
430
 
370
431
  it 'does not write the cassette to disk if all interactions have been ignored' do
371
- interaction_1 = VCR::HTTPInteraction.new(:request_1, :response_1)
432
+ interaction_1 = http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' }
372
433
  interaction_1.ignore!
373
434
 
374
435
  cassette = VCR::Cassette.new('test_cassette')
@@ -379,13 +440,13 @@ describe VCR::Cassette do
379
440
  end
380
441
 
381
442
  it "writes the recorded interactions to a subdirectory if the cassette name includes a directory" do
382
- recorded_interactions = [VCR::HTTPInteraction.new(:the_request, :the_response)]
443
+ recorded_interactions = [http_interaction { |i| i.response.body = "subdirectory response" }]
383
444
  cassette = VCR::Cassette.new('subdirectory/test_cassette')
384
- cassette.stub!(:new_recorded_interactions).and_return(recorded_interactions)
445
+ cassette.stub(:new_recorded_interactions => recorded_interactions)
385
446
 
386
447
  expect { cassette.eject }.to change { File.exist?(cassette.file) }.from(false).to(true)
387
- saved_recorded_interactions = VCR::YAML.load_file(cassette.file)
388
- saved_recorded_interactions.should eq(recorded_interactions)
448
+ saved_recorded_interactions = YAML.load_file(cassette.file)
449
+ saved_recorded_interactions["http_interactions"].should eq(recorded_interactions.map(&:to_hash))
389
450
  end
390
451
 
391
452
  [:all, :none, :new_episodes].each do |record_mode|
@@ -404,22 +465,25 @@ describe VCR::Cassette do
404
465
  end
405
466
 
406
467
  context 'when some new interactions have been recorded' do
407
- def interaction(response, request_attributes)
408
- request = VCR::Request.new
409
- request_attributes.each do |key, value|
410
- request.send("#{key}=", value)
468
+ def interaction(response_body, request_attributes)
469
+ http_interaction do |interaction|
470
+ interaction.response.body = response_body
471
+ request_attributes.each do |key, value|
472
+ interaction.request.send("#{key}=", value)
473
+ end
411
474
  end
412
- VCR::HTTPInteraction.new(request, response)
413
475
  end
414
476
 
415
477
  let(:interaction_foo_1) { interaction("foo 1", :uri => 'http://foo.com/') }
416
478
  let(:interaction_foo_2) { interaction("foo 2", :uri => 'http://foo.com/') }
417
479
  let(:interaction_bar) { interaction("bar", :uri => 'http://bar.com/') }
418
480
 
419
- let(:saved_recorded_interactions) { VCR::YAML.load_file(subject.file) }
481
+ let(:saved_recorded_interactions) { YAML.load_file(subject.file)['http_interactions'].map { |h| VCR::HTTPInteraction.from_hash(h) } }
482
+ let(:now) { Time.utc(2011, 6, 11, 12, 30) }
420
483
 
421
484
  before(:each) do
422
- subject.stub(:recorded_interactions => [interaction_foo_1])
485
+ Time.stub(:now => now)
486
+ subject.stub(:previously_recorded_interactions => [interaction_foo_1])
423
487
  subject.record_http_interaction(interaction_foo_2)
424
488
  subject.record_http_interaction(interaction_bar)
425
489
  subject.eject