vcr 2.0.0.beta1 → 2.0.0.beta2

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