vcr 3.0.3 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/lib/vcr.rb +18 -1
  3. data/lib/vcr/cassette.rb +11 -3
  4. data/lib/vcr/cassette/persisters/file_system.rb +1 -1
  5. data/lib/vcr/configuration.rb +3 -5
  6. data/lib/vcr/deprecations.rb +0 -62
  7. data/lib/vcr/errors.rb +16 -0
  8. data/lib/vcr/library_hooks/typhoeus.rb +37 -8
  9. data/lib/vcr/middleware/faraday.rb +5 -1
  10. data/lib/vcr/structs.rb +1 -1
  11. data/lib/vcr/util/hooks.rb +1 -0
  12. data/lib/vcr/version.rb +1 -1
  13. metadata +9 -249
  14. data/features/CHANGELOG.md +0 -710
  15. data/features/CONTRIBUTING.md +0 -26
  16. data/features/LICENSE.md +0 -20
  17. data/features/README.md +0 -339
  18. data/features/Upgrade.md +0 -289
  19. data/features/about_these_examples.md +0 -18
  20. data/features/cassettes/allow_unused_http_interactions.feature +0 -100
  21. data/features/cassettes/automatic_re_recording.feature +0 -72
  22. data/features/cassettes/decompress.feature +0 -74
  23. data/features/cassettes/dynamic_erb.feature +0 -100
  24. data/features/cassettes/exclusive.feature +0 -126
  25. data/features/cassettes/format.feature +0 -411
  26. data/features/cassettes/freezing_time.feature +0 -68
  27. data/features/cassettes/naming.feature +0 -28
  28. data/features/cassettes/no_cassette.feature +0 -152
  29. data/features/cassettes/update_content_length_header.feature +0 -112
  30. data/features/configuration/allow_http_connections_when_no_cassette.feature +0 -55
  31. data/features/configuration/cassette_library_dir.feature +0 -31
  32. data/features/configuration/debug_logging.feature +0 -58
  33. data/features/configuration/default_cassette_options.feature +0 -100
  34. data/features/configuration/filter_sensitive_data.feature +0 -153
  35. data/features/configuration/hook_into.feature +0 -172
  36. data/features/configuration/ignore_request.feature +0 -192
  37. data/features/configuration/preserve_exact_body_bytes.feature +0 -108
  38. data/features/configuration/query_parser.feature +0 -84
  39. data/features/configuration/uri_parser.feature +0 -93
  40. data/features/getting_started.md +0 -82
  41. data/features/hooks/after_http_request.feature +0 -58
  42. data/features/hooks/around_http_request.feature +0 -57
  43. data/features/hooks/before_http_request.feature +0 -63
  44. data/features/hooks/before_playback.feature +0 -184
  45. data/features/hooks/before_record.feature +0 -172
  46. data/features/http_libraries/em_http_request.feature +0 -250
  47. data/features/http_libraries/net_http.feature +0 -179
  48. data/features/middleware/faraday.feature +0 -56
  49. data/features/middleware/rack.feature +0 -92
  50. data/features/record_modes/all.feature +0 -82
  51. data/features/record_modes/new_episodes.feature +0 -79
  52. data/features/record_modes/none.feature +0 -72
  53. data/features/record_modes/once.feature +0 -95
  54. data/features/request_matching/README.md +0 -30
  55. data/features/request_matching/body.feature +0 -91
  56. data/features/request_matching/body_as_json.feature +0 -90
  57. data/features/request_matching/custom_matcher.feature +0 -135
  58. data/features/request_matching/headers.feature +0 -85
  59. data/features/request_matching/host.feature +0 -95
  60. data/features/request_matching/identical_request_sequence.feature +0 -89
  61. data/features/request_matching/method.feature +0 -96
  62. data/features/request_matching/path.feature +0 -96
  63. data/features/request_matching/playback_repeats.feature +0 -98
  64. data/features/request_matching/query.feature +0 -97
  65. data/features/request_matching/uri.feature +0 -94
  66. data/features/request_matching/uri_without_param.feature +0 -101
  67. data/features/step_definitions/cli_steps.rb +0 -199
  68. data/features/support/env.rb +0 -46
  69. data/features/support/http_lib_filters.rb +0 -46
  70. data/features/test_frameworks/cucumber.feature +0 -211
  71. data/features/test_frameworks/rspec_macro.feature +0 -81
  72. data/features/test_frameworks/rspec_metadata.feature +0 -150
  73. data/features/test_frameworks/test_unit.feature +0 -49
  74. data/lib/vcr/extensions/net_http_response.rb +0 -36
  75. data/lib/vcr/library_hooks/fakeweb.rb +0 -197
  76. data/spec/acceptance/concurrency_spec.rb +0 -51
  77. data/spec/acceptance/threading_spec.rb +0 -34
  78. data/spec/fixtures/cassette_spec/1_x_cassette.yml +0 -110
  79. data/spec/fixtures/cassette_spec/empty.yml +0 -0
  80. data/spec/fixtures/cassette_spec/example.yml +0 -111
  81. data/spec/fixtures/cassette_spec/with_localhost_requests.yml +0 -111
  82. data/spec/fixtures/fake_example_responses.yml +0 -110
  83. data/spec/fixtures/match_requests_on.yml +0 -187
  84. data/spec/lib/vcr/cassette/erb_renderer_spec.rb +0 -53
  85. data/spec/lib/vcr/cassette/http_interaction_list_spec.rb +0 -295
  86. data/spec/lib/vcr/cassette/migrator_spec.rb +0 -196
  87. data/spec/lib/vcr/cassette/persisters/file_system_spec.rb +0 -75
  88. data/spec/lib/vcr/cassette/persisters_spec.rb +0 -39
  89. data/spec/lib/vcr/cassette/serializers_spec.rb +0 -182
  90. data/spec/lib/vcr/cassette_spec.rb +0 -618
  91. data/spec/lib/vcr/configuration_spec.rb +0 -326
  92. data/spec/lib/vcr/deprecations_spec.rb +0 -85
  93. data/spec/lib/vcr/errors_spec.rb +0 -178
  94. data/spec/lib/vcr/extensions/net_http_response_spec.rb +0 -86
  95. data/spec/lib/vcr/library_hooks/excon_spec.rb +0 -104
  96. data/spec/lib/vcr/library_hooks/fakeweb_spec.rb +0 -169
  97. data/spec/lib/vcr/library_hooks/faraday_spec.rb +0 -68
  98. data/spec/lib/vcr/library_hooks/typhoeus_0.4_spec.rb +0 -36
  99. data/spec/lib/vcr/library_hooks/typhoeus_spec.rb +0 -162
  100. data/spec/lib/vcr/library_hooks/webmock_spec.rb +0 -117
  101. data/spec/lib/vcr/library_hooks_spec.rb +0 -51
  102. data/spec/lib/vcr/middleware/faraday_spec.rb +0 -181
  103. data/spec/lib/vcr/middleware/rack_spec.rb +0 -115
  104. data/spec/lib/vcr/request_ignorer_spec.rb +0 -70
  105. data/spec/lib/vcr/request_matcher_registry_spec.rb +0 -345
  106. data/spec/lib/vcr/structs_spec.rb +0 -732
  107. data/spec/lib/vcr/test_frameworks/cucumber_spec.rb +0 -107
  108. data/spec/lib/vcr/test_frameworks/rspec_spec.rb +0 -94
  109. data/spec/lib/vcr/util/hooks_spec.rb +0 -158
  110. data/spec/lib/vcr/util/internet_connection_spec.rb +0 -37
  111. data/spec/lib/vcr/util/version_checker_spec.rb +0 -31
  112. data/spec/lib/vcr/version_spec.rb +0 -27
  113. data/spec/lib/vcr_spec.rb +0 -354
  114. data/spec/monkey_patches.rb +0 -186
  115. data/spec/spec_helper.rb +0 -63
  116. data/spec/support/configuration_stubbing.rb +0 -8
  117. data/spec/support/cucumber_helpers.rb +0 -39
  118. data/spec/support/fixnum_extension.rb +0 -10
  119. data/spec/support/http_library_adapters.rb +0 -289
  120. data/spec/support/limited_uri.rb +0 -21
  121. data/spec/support/ruby_interpreter.rb +0 -7
  122. data/spec/support/shared_example_groups/excon.rb +0 -63
  123. data/spec/support/shared_example_groups/hook_into_http_library.rb +0 -594
  124. data/spec/support/shared_example_groups/request_hooks.rb +0 -59
  125. data/spec/support/sinatra_app.rb +0 -86
  126. data/spec/support/vcr_localhost_server.rb +0 -76
  127. data/spec/support/vcr_stub_helpers.rb +0 -17
@@ -1,75 +0,0 @@
1
- require 'spec_helper'
2
- require 'vcr/cassette/persisters/file_system'
3
-
4
- module VCR
5
- class Cassette
6
- class Persisters
7
- describe FileSystem do
8
- before { FileSystem.storage_location = VCR.configuration.cassette_library_dir }
9
-
10
- describe "#[]" do
11
- it 'reads from the given file, relative to the configured storage location' do
12
- File.open(FileSystem.storage_location + '/foo.txt', 'w') { |f| f.write('1234') }
13
- expect(FileSystem["foo.txt"]).to eq("1234")
14
- end
15
-
16
- it 'handles directories in the given file name' do
17
- FileUtils.mkdir_p FileSystem.storage_location + '/a'
18
- File.open(FileSystem.storage_location + '/a/b', 'w') { |f| f.write('1234') }
19
- expect(FileSystem["a/b"]).to eq("1234")
20
- end
21
-
22
- it 'returns nil if the file does not exist' do
23
- expect(FileSystem["non_existant_file"]).to be_nil
24
- end
25
- end
26
-
27
- describe "#[]=" do
28
- context 'with a simple file_name and binary content' do
29
- let(:file_name) { 'foo.txt' }
30
- let(:content) { SecureRandom.random_bytes(20) }
31
- let(:location) { FileSystem.storage_location + '/' + file_name }
32
-
33
- it 'writes the given file contents to the given file name' do
34
- expect(File.exist?(location)).to be false
35
- FileSystem[file_name] = content
36
- expect(File.binread(location)).to eq(content)
37
- end
38
- end
39
-
40
- it 'creates any needed intermediary directories' do
41
- expect(File.exist?(FileSystem.storage_location + '/a')).to be false
42
- FileSystem["a/b"] = "bar"
43
- expect(File.read(FileSystem.storage_location + '/a/b')).to eq("bar")
44
- end
45
- end
46
-
47
- describe "#absolute_path_to_file" do
48
- it "returns the absolute path to the given relative file based on the storage location" do
49
- expected = File.join(FileSystem.storage_location, "bar/bazz.json")
50
- expect(FileSystem.absolute_path_to_file("bar/bazz.json")).to eq(expected)
51
- end
52
-
53
- it "returns nil if the storage_location is not set" do
54
- FileSystem.storage_location = nil
55
- expect(FileSystem.absolute_path_to_file("bar/bazz.json")).to be_nil
56
- end
57
-
58
- it "sanitizes the file name" do
59
- expected = File.join(FileSystem.storage_location, "_t_i-t_1_2_f_n.json")
60
- expect(FileSystem.absolute_path_to_file("\nt \t! i-t_1.2_f n.json")).to eq(expected)
61
-
62
- expected = File.join(FileSystem.storage_location, "a_1/b")
63
- expect(FileSystem.absolute_path_to_file("a 1/b")).to eq(expected)
64
- end
65
-
66
- it 'handles files with no extensions (even when there is a dot in the path)' do
67
- expected = File.join(FileSystem.storage_location, "/foo_bar/baz_qux")
68
- expect(FileSystem.absolute_path_to_file("/foo.bar/baz qux")).to eq(expected)
69
- end
70
- end
71
- end
72
- end
73
- end
74
- end
75
-
@@ -1,39 +0,0 @@
1
- require 'vcr/cassette/persisters'
2
-
3
- module VCR
4
- class Cassette
5
- describe Persisters do
6
- describe "#[]=" do
7
- context 'when there is already a persister registered for the given name' do
8
- before(:each) do
9
- subject[:foo] = :old_persister
10
- allow(subject).to receive :warn
11
- end
12
-
13
- it 'overrides the existing persister' do
14
- subject[:foo] = :new_persister
15
- expect(subject[:foo]).to be(:new_persister)
16
- end
17
-
18
- it 'warns that there is a name collision' do
19
- expect(subject).to receive(:warn).with(
20
- /WARNING: There is already a VCR cassette persister registered for :foo\. Overriding it/
21
- )
22
- subject[:foo] = :new_persister
23
- end
24
- end
25
- end
26
-
27
- describe "#[]" do
28
- it 'raises an error when given an unrecognized persister name' do
29
- expect { subject[:foo] }.to raise_error(ArgumentError)
30
- end
31
-
32
- it 'returns the named persister' do
33
- expect(subject[:file_system]).to be(VCR::Cassette::Persisters::FileSystem)
34
- end
35
- end
36
- end
37
- end
38
- end
39
-
@@ -1,182 +0,0 @@
1
- require 'support/ruby_interpreter'
2
- require 'vcr/cassette/serializers'
3
- require 'multi_json'
4
-
5
- begin
6
- require 'psych' # ensure psych is loaded for these tests if its available
7
- rescue LoadError
8
- end
9
-
10
- module VCR
11
- class Cassette
12
- describe Serializers do
13
- shared_examples_for "encoding error handling" do |name, error_class|
14
- context "the #{name} serializer" do
15
- it 'appends info about the :preserve_exact_body_bytes option to the error' do
16
- expect {
17
- result = serializer.serialize("a" => string)
18
- serializer.deserialize(result)
19
- }.to raise_error(error_class, /preserve_exact_body_bytes/)
20
- end unless (RUBY_INTERPRETER == :rubinius && RUBY_VERSION =~ /^1.9/)
21
- end
22
- end
23
-
24
- shared_examples_for "a serializer" do |name, file_extension, lazily_loaded|
25
- let(:serializer) { subject[name] }
26
-
27
- context "the #{name} serializer" do
28
- it 'lazily loads the serializer' do
29
- serializers = subject.instance_variable_get(:@serializers)
30
- expect(serializers).not_to have_key(name)
31
- expect(subject[name]).not_to be_nil
32
- expect(serializers).to have_key(name)
33
- end if lazily_loaded
34
-
35
- it "returns '#{file_extension}' as the file extension" do
36
- expect(serializer.file_extension).to eq(file_extension)
37
- end
38
-
39
- it "can serialize and deserialize a hash" do
40
- hash = { "a" => 7, "nested" => { "hash" => [1, 2, 3] }}
41
- serialized = serializer.serialize(hash)
42
- expect(serialized).not_to eq(hash)
43
- expect(serialized).to be_a(String)
44
- deserialized = serializer.deserialize(serialized)
45
- expect(deserialized).to eq(hash)
46
- end
47
- end
48
- end
49
-
50
- it_behaves_like "a serializer", :yaml, "yml", :lazily_loaded do
51
- it_behaves_like "encoding error handling", :yaml, ArgumentError do
52
- let(:string) { "\xFA".force_encoding("UTF-8") }
53
- before { ::YAML::ENGINE.yamler = 'psych' if defined?(::YAML::ENGINE) }
54
- end if ''.respond_to?(:encoding)
55
- end
56
-
57
- it_behaves_like "a serializer", :syck, "yml", :lazily_loaded do
58
- it_behaves_like "encoding error handling", :syck, ArgumentError do
59
- let(:string) { "\xFA".force_encoding("UTF-8") }
60
- end if ''.respond_to?(:encoding)
61
- end
62
-
63
- it_behaves_like "a serializer", :psych, "yml", :lazily_loaded do
64
- it_behaves_like "encoding error handling", :psych, ArgumentError do
65
- let(:string) { "\xFA".force_encoding("UTF-8") }
66
- end if ''.respond_to?(:encoding)
67
- end if RUBY_VERSION =~ /1.9/
68
-
69
- it_behaves_like "a serializer", :compressed, "gz", :lazily_loaded do
70
- it_behaves_like "encoding error handling", :compressed, ArgumentError do
71
- let(:string) { "\xFA".force_encoding("UTF-8") }
72
- end if ''.respond_to?(:encoding)
73
- end
74
-
75
- it_behaves_like "a serializer", :json, "json", :lazily_loaded do
76
- engines = {}
77
-
78
- if RUBY_INTERPRETER == :jruby
79
- # don't test yajl on jruby
80
- else
81
- engines[:yajl] = MultiJson::LoadError
82
- end
83
-
84
- if RUBY_VERSION =~ /1.9/
85
- engines[:json_gem] = EncodingError
86
-
87
- # Disable json_pure for now due to this bug:
88
- # https://github.com/flori/json/issues/186
89
- # engines[:json_pure] = EncodingError
90
- end
91
-
92
- engines.each do |engine, error|
93
- context "when MultiJson is configured to use #{engine.inspect}", :unless => (RUBY_INTERPRETER == :jruby) do
94
- before { MultiJson.engine = engine }
95
- it_behaves_like "encoding error handling", :json, error do
96
- let(:string) { "\xFA" }
97
- end
98
- end
99
- end
100
- end
101
-
102
- context "a custom :ruby serializer" do
103
- let(:custom_serializer) do
104
- Object.new.tap do |obj|
105
- def obj.file_extension
106
- "rb"
107
- end
108
-
109
- def obj.serialize(hash)
110
- hash.inspect
111
- end
112
-
113
- def obj.deserialize(string)
114
- eval(string)
115
- end
116
- end
117
- end
118
-
119
- before(:each) do
120
- subject[:ruby] = custom_serializer
121
- end
122
-
123
- it_behaves_like "a serializer", :ruby, "rb", false
124
- end
125
-
126
- describe "#[]=" do
127
- context 'when there is already a serializer registered for the given name' do
128
- before(:each) do
129
- subject[:foo] = :old_serializer
130
- allow(subject).to receive :warn
131
- end
132
-
133
- it 'overrides the existing serializer' do
134
- subject[:foo] = :new_serializer
135
- expect(subject[:foo]).to be(:new_serializer)
136
- end
137
-
138
- it 'warns that there is a name collision' do
139
- expect(subject).to receive(:warn).with(
140
- /WARNING: There is already a VCR cassette serializer registered for :foo\. Overriding it/
141
- )
142
- subject[:foo] = :new_serializer
143
- end
144
- end
145
- end
146
-
147
- describe "#[]" do
148
- it 'raises an error when given an unrecognized serializer name' do
149
- expect { subject[:foo] }.to raise_error(ArgumentError)
150
- end
151
-
152
- it 'returns the named serializer' do
153
- expect(subject[:yaml]).to be(VCR::Cassette::Serializers::YAML)
154
- end
155
- end
156
-
157
- # see https://gist.github.com/815769
158
- problematic_syck_string = "1\n \n2"
159
-
160
- describe "psych serializer" do
161
- it 'serializes things using pysch even if syck is configured as the default YAML engine' do
162
- ::YAML::ENGINE.yamler = 'syck'
163
- serialized = subject[:psych].serialize(problematic_syck_string)
164
- expect(subject[:psych].deserialize(serialized)).to eq(problematic_syck_string)
165
- end if defined?(::Psych) && RUBY_VERSION.to_f < 2.0
166
-
167
- it 'raises an error if psych cannot be loaded' do
168
- expect { subject[:psych] }.to raise_error(LoadError)
169
- end unless defined?(::Psych)
170
- end
171
-
172
- describe "syck serializer" do
173
- it 'forcibly serializes things using syck even if psych is the currently configured YAML engine' do
174
- ::YAML::ENGINE.yamler = 'psych'
175
- serialized = subject[:syck].serialize(problematic_syck_string)
176
- expect(subject[:syck].deserialize(serialized)).not_to eq(problematic_syck_string)
177
- end if defined?(::Psych) && (RUBY_INTERPRETER != :jruby) && (RUBY_VERSION.to_f < 2.0)
178
- end
179
- end
180
- end
181
- end
182
-
@@ -1,618 +0,0 @@
1
- require 'spec_helper'
2
-
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
-
11
- def stub_old_interactions(interactions)
12
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
13
-
14
- hashes = interactions.map(&:to_hash)
15
- allow(VCR.cassette_serializers[:yaml]).to receive(:deserialize).and_return({ 'http_interactions' => hashes })
16
- allow(VCR::HTTPInteraction).to receive(:from_hash) do |hash|
17
- interactions[hashes.index(hash)]
18
- end
19
- end
20
-
21
- describe '#file' do
22
- it 'delegates the file resolution to the FileSystem persister' do
23
- fs = VCR::Cassette::Persisters::FileSystem
24
- expect(fs).to respond_to(:absolute_path_to_file).with(1).argument
25
- expect(fs).to receive(:absolute_path_to_file).with("cassette name.yml") { "f.yml" }
26
- expect(VCR::Cassette.new("cassette name").file).to eq("f.yml")
27
- end
28
-
29
- it 'raises a NotImplementedError when a different persister is used' do
30
- VCR.cassette_persisters[:a] = double
31
- cassette = VCR::Cassette.new("f", :persist_with => :a)
32
- expect { cassette.file }.to raise_error(NotImplementedError)
33
- end
34
- end
35
-
36
- describe '#tags' do
37
- it 'returns a blank array if no tag has been set' do
38
- expect(VCR::Cassette.new("name").tags).to eq([])
39
- end
40
-
41
- it 'converts a single :tag to an array' do
42
- expect(VCR::Cassette.new("name", :tag => :foo).tags).to eq([:foo])
43
- end
44
-
45
- it 'accepts an array as the :tags option' do
46
- expect(VCR::Cassette.new("name", :tags => [:foo]).tags).to eq([:foo])
47
- end
48
- end
49
-
50
- describe '#record_http_interaction' do
51
- let(:the_interaction) { double(:request => double(:method => :get).as_null_object).as_null_object }
52
-
53
- it 'adds the interaction to #new_recorded_interactions' do
54
- cassette = VCR::Cassette.new(:test_cassette)
55
- expect(cassette.new_recorded_interactions).to eq([])
56
- cassette.record_http_interaction(the_interaction)
57
- expect(cassette.new_recorded_interactions).to eq([the_interaction])
58
- end
59
- end
60
-
61
- describe "#serializable_hash" do
62
- subject { VCR::Cassette.new("foo") }
63
- let(:interaction_1) { http_interaction { |i| i.request.body = 'req body 1'; i.response.body = 'res body 1' } }
64
- let(:interaction_2) { http_interaction { |i| i.request.body = 'req body 2'; i.response.body = 'res body 2' } }
65
- let(:interactions) { [interaction_1, interaction_2] }
66
-
67
- before(:each) do
68
- interactions.each do |i|
69
- subject.record_http_interaction(i)
70
- end
71
- end
72
-
73
- let(:metadata) { subject.serializable_hash.reject { |k,v| k == "http_interactions" } }
74
-
75
- it 'includes the hash form of all recorded interactions' do
76
- hash_1 = interaction_1.to_hash
77
- hash_2 = interaction_2.to_hash
78
- expect(subject.serializable_hash).to include('http_interactions' => [hash_1, hash_2])
79
- end
80
-
81
- it 'includes additional metadata about the cassette' do
82
- expect(metadata).to eq("recorded_with" => "VCR #{VCR.version}")
83
- end
84
-
85
- it 'does not allow the interactions to be mutated by configured hooks' do
86
- VCR.configure do |c|
87
- c.define_cassette_placeholder('<BODY>') { 'body' }
88
- end
89
-
90
- expect {
91
- subject.serializable_hash
92
- }.not_to change { interaction_1.response.body }
93
- end
94
- end
95
-
96
- describe "#recording?" do
97
- [:all, :new_episodes].each do |mode|
98
- it "returns true when the record mode is :#{mode}" do
99
- cassette = VCR::Cassette.new("foo", :record => mode)
100
- expect(cassette).to be_recording
101
- end
102
- end
103
-
104
- it "returns false when the record mode is :none" do
105
- cassette = VCR::Cassette.new("foo", :record => :none)
106
- expect(cassette).not_to be_recording
107
- end
108
-
109
- context 'when the record mode is :once' do
110
- before(:each) do
111
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
112
- end
113
-
114
- it 'returns false when there is an existing cassette file with content' do
115
- cassette = VCR::Cassette.new("example", :record => :once)
116
- expect(::File).to exist(cassette.file)
117
- expect(::File.size?(cassette.file)).to be_truthy
118
- expect(cassette).not_to be_recording
119
- end
120
-
121
- it 'returns true when there is an empty existing cassette file' do
122
- cassette = VCR::Cassette.new("empty", :record => :once)
123
- expect(::File).to exist(cassette.file)
124
- expect(::File.size?(cassette.file)).to be_falsey
125
- expect(cassette).to be_recording
126
- end
127
-
128
- it 'returns true when there is no existing cassette file' do
129
- cassette = VCR::Cassette.new("non_existant_file", :record => :once)
130
- expect(::File).not_to exist(cassette.file)
131
- expect(cassette).to be_recording
132
- end
133
- end
134
- end
135
-
136
- describe '#match_requests_on' do
137
- before(:each) { VCR.configuration.default_cassette_options.merge!(:match_requests_on => [:uri, :method]) }
138
-
139
- it "returns the provided options" do
140
- c = VCR::Cassette.new('example', :match_requests_on => [:uri])
141
- expect(c.match_requests_on).to eq([:uri])
142
- end
143
-
144
- it "returns a the default #match_requests_on when it has not been specified for the cassette" do
145
- c = VCR::Cassette.new('example')
146
- expect(c.match_requests_on).to eq([:uri, :method])
147
- end
148
- end
149
-
150
- describe "reading the file from disk" do
151
- let(:empty_cassette_yaml) { YAML.dump("http_interactions" => []) }
152
-
153
- it 'optionally renders the file as ERB using the ERBRenderer' do
154
- allow(VCR::Cassette::Persisters::FileSystem).to receive(:[]).and_return(empty_cassette_yaml)
155
-
156
- expect(VCR::Cassette::ERBRenderer).to receive(:new).with(
157
- empty_cassette_yaml, anything, "foo"
158
- ).and_return(double('renderer', :render => empty_cassette_yaml))
159
-
160
- VCR::Cassette.new('foo', :record => :new_episodes).http_interactions
161
- end
162
-
163
- [true, false, nil, { }].each do |erb|
164
- it "passes #{erb.inspect} to the VCR::Cassette::ERBRenderer when given as the :erb option" do
165
- # test that it overrides the default
166
- VCR.configuration.default_cassette_options = { :erb => true }
167
-
168
- expect(VCR::Cassette::ERBRenderer).to receive(:new).with(
169
- anything, erb, anything
170
- ).and_return(double('renderer', :render => empty_cassette_yaml))
171
-
172
- VCR::Cassette.new('foo', :record => :new_episodes, :erb => erb).http_interactions
173
- end
174
-
175
- it "passes #{erb.inspect} to the VCR::Cassette::ERBRenderer when it is the default :erb option and none is given" do
176
- VCR.configuration.default_cassette_options = { :erb => erb }
177
-
178
- expect(VCR::Cassette::ERBRenderer).to receive(:new).with(
179
- anything, erb, anything
180
- ).and_return(double('renderer', :render => empty_cassette_yaml))
181
-
182
- VCR::Cassette.new('foo', :record => :new_episodes).http_interactions
183
- end
184
- end
185
-
186
- it 'raises a friendly error when the cassette file is in the old VCR 1.x format' do
187
- VCR.configuration.cassette_library_dir = 'spec/fixtures/cassette_spec'
188
- expect {
189
- VCR::Cassette.new('1_x_cassette').http_interactions
190
- }.to raise_error(VCR::Errors::InvalidCassetteFormatError)
191
- end
192
- end
193
-
194
- describe '.new' do
195
- it "raises an error if given an invalid record mode" do
196
- expect { VCR::Cassette.new(:test, :record => :not_a_record_mode) }.to raise_error(ArgumentError)
197
- end
198
-
199
- it 'raises an error if given invalid options' do
200
- expect {
201
- VCR::Cassette.new(:test, :invalid => :option)
202
- }.to raise_error(ArgumentError)
203
- end
204
-
205
- it 'does not raise an error in the case of an empty file' do
206
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
207
- expect(VCR::Cassette.new('empty', :record => :none).send(:previously_recorded_interactions)).to eq([])
208
- end
209
-
210
- let(:custom_persister) { double("custom persister") }
211
-
212
- it 'reads from the configured persister' do
213
- VCR.configuration.cassette_library_dir = nil
214
- VCR.cassette_persisters[:foo] = custom_persister
215
- expect(custom_persister).to receive(:[]).with("abc.yml") { "" }
216
- VCR::Cassette.new("abc", :persist_with => :foo).http_interactions
217
- end
218
-
219
- VCR::Cassette::VALID_RECORD_MODES.each do |record_mode|
220
- stub_requests = (record_mode != :all)
221
-
222
- context "when VCR.configuration.default_cassette_options[:record] is :#{record_mode}" do
223
- before(:each) { VCR.configuration.default_cassette_options = { :record => record_mode } }
224
-
225
- it "defaults the record mode to #{record_mode} when VCR.configuration.default_cassette_options[:record] is #{record_mode}" do
226
- cassette = VCR::Cassette.new(:test)
227
- expect(cassette.record_mode).to eq(record_mode)
228
- end
229
- end
230
-
231
- context "when :#{record_mode} is passed as the record option" do
232
- unless record_mode == :all
233
- let(:interaction_1) { http_interaction { |i| i.request.uri = 'http://example.com/foo' } }
234
- let(:interaction_2) { http_interaction { |i| i.request.uri = 'http://example.com/bar' } }
235
- let(:interactions) { [interaction_1, interaction_2] }
236
-
237
- it 'updates the content_length headers when given :update_content_length_header => true' do
238
- stub_old_interactions(interactions)
239
- expect(interaction_1.response).to receive(:update_content_length_header)
240
- expect(interaction_2.response).to receive(:update_content_length_header)
241
-
242
- VCR::Cassette.new('example', :record => record_mode, :update_content_length_header => true).http_interactions
243
- end
244
-
245
- [nil, false].each do |val|
246
- it "does not update the content_lenth headers when given :update_content_length_header => #{val.inspect}" do
247
- stub_old_interactions(interactions)
248
- expect(interaction_1.response).not_to receive(:update_content_length_header)
249
- expect(interaction_2.response).not_to receive(:update_content_length_header)
250
-
251
- VCR::Cassette.new('example', :record => record_mode, :update_content_length_header => val).http_interactions
252
- end
253
- end
254
-
255
- context "and re_record_interval is 7.days" do
256
- let(:file_name) { ::File.join(VCR.configuration.cassette_library_dir, "cassette_name.yml") }
257
- subject { VCR::Cassette.new(::File.basename(file_name).gsub('.yml', ''), :record => record_mode, :re_record_interval => 7.days) }
258
-
259
- context 'when the cassette file does not exist' do
260
- before(:each) { allow(::File).to receive(:exist?).with(file_name).and_return(false) }
261
-
262
- it "has :#{record_mode} for the record mode" do
263
- expect(subject.record_mode).to eq(record_mode)
264
- end
265
- end
266
-
267
- context 'when the cassette file does exist' do
268
- before(:each) do
269
- interactions = timestamps.map do |ts|
270
- http_interaction { |i| i.recorded_at = ts }.to_hash
271
- end
272
- yaml = YAML.dump("http_interactions" => interactions)
273
-
274
- allow(::File).to receive(:exist?).with(file_name).and_return(true)
275
- allow(::File).to receive(:size?).with(file_name).and_return(true)
276
- allow(::File).to receive(:binread).with(file_name).and_return(yaml)
277
- end
278
-
279
- context 'and the earliest recorded interaction was recorded less than 7 days ago' do
280
- let(:timestamps) do [
281
- Time.now - 6.days + 60,
282
- Time.now - 7.days + 60,
283
- Time.now - 5.days + 60
284
- ] end
285
-
286
- it "has :#{record_mode} for the record mode" do
287
- expect(subject.record_mode).to eq(record_mode)
288
- end
289
- end
290
-
291
- context 'and the earliest recorded interaction was recorded more than 7 days ago' do
292
- let(:timestamps) do [
293
- Time.now - 6.days - 60,
294
- Time.now - 7.days - 60,
295
- Time.now - 5.days - 60
296
- ] end
297
-
298
- it "has :all for the record mode when there is an internet connection available" do
299
- allow(VCR::InternetConnection).to receive(:available?).and_return(true)
300
- expect(subject.record_mode).to eq(:all)
301
- end
302
-
303
- it "has :#{record_mode} for the record mode when there is no internet connection available" do
304
- allow(VCR::InternetConnection).to receive(:available?).and_return(false)
305
- expect(subject.record_mode).to eq(record_mode)
306
- end
307
- end
308
- end
309
- end
310
- end
311
-
312
- it 'does not load ignored interactions' do
313
- allow(VCR.request_ignorer).to receive(:ignore?) do |request|
314
- request.uri !~ /example\.com/
315
- end
316
-
317
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
318
- cassette = VCR::Cassette.new('with_localhost_requests', :record => record_mode)
319
- expect(cassette.send(:previously_recorded_interactions).map { |i| URI.parse(i.request.uri).host }).to eq(%w[example.com])
320
- end
321
-
322
- it "loads the recorded interactions from the library yml file" do
323
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
324
- cassette = VCR::Cassette.new('example', :record => record_mode)
325
-
326
- expect(cassette.send(:previously_recorded_interactions).size).to eq(3)
327
-
328
- i1, i2, i3 = *cassette.send(:previously_recorded_interactions)
329
-
330
- expect(i1.request.method).to eq(:get)
331
- expect(i1.request.uri).to eq('http://example.com/')
332
- expect(i1.response.body).to match(/You have reached this web page by typing.+example\.com/)
333
-
334
- expect(i2.request.method).to eq(:get)
335
- expect(i2.request.uri).to eq('http://example.com/foo')
336
- expect(i2.response.body).to match(/foo was not found on this server/)
337
-
338
- expect(i3.request.method).to eq(:get)
339
- expect(i3.request.uri).to eq('http://example.com/')
340
- expect(i3.response.body).to match(/Another example\.com response/)
341
- end
342
-
343
- [true, false].each do |value|
344
- it "instantiates the http_interactions with allow_playback_repeats = #{value} if given :allow_playback_repeats => #{value}" do
345
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
346
- cassette = VCR::Cassette.new('example', :record => record_mode, :allow_playback_repeats => value)
347
- expect(cassette.http_interactions.allow_playback_repeats).to eq(value)
348
- end
349
- end
350
-
351
- it "instantiates the http_interactions with parent_list set to a null list if given :exclusive => true" do
352
- allow(VCR).to receive(:http_interactions).and_return(double)
353
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
354
- cassette = VCR::Cassette.new('example', :record => record_mode, :exclusive => true)
355
- expect(cassette.http_interactions.parent_list).to be(VCR::Cassette::HTTPInteractionList::NullList)
356
- end
357
-
358
- it "instantiates the http_interactions with parent_list set to VCR.http_interactions if given :exclusive => false" do
359
- allow(VCR).to receive(:http_interactions).and_return(double)
360
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
361
- cassette = VCR::Cassette.new('example', :record => record_mode, :exclusive => false)
362
- expect(cassette.http_interactions.parent_list).to be(VCR.http_interactions)
363
- end
364
-
365
- if stub_requests
366
- it 'invokes the before_playback hooks' do
367
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
368
-
369
- expect(VCR.configuration).to receive(:invoke_hook).with(
370
- :before_playback,
371
- an_instance_of(VCR::HTTPInteraction::HookAware),
372
- an_instance_of(VCR::Cassette)
373
- ).exactly(3).times
374
-
375
- cassette = VCR::Cassette.new('example', :record => record_mode)
376
- expect(cassette.send(:previously_recorded_interactions).size).to eq(3)
377
- end
378
-
379
- it 'does not playback any interactions that are ignored in a before_playback hook' do
380
- VCR.configure do |c|
381
- c.before_playback { |i| i.ignore! if i.request.uri =~ /foo/ }
382
- end
383
-
384
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
385
- cassette = VCR::Cassette.new('example', :record => record_mode)
386
- expect(cassette.send(:previously_recorded_interactions).size).to eq(2)
387
- end
388
-
389
- it 'instantiates the http_interactions with the loaded interactions and the request matchers' do
390
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
391
- cassette = VCR::Cassette.new('example', :record => record_mode, :match_requests_on => [:body, :headers])
392
- expect(cassette.http_interactions.interactions.size).to eq(3)
393
- expect(cassette.http_interactions.request_matchers).to eq([:body, :headers])
394
- end
395
- else
396
- it 'instantiates the http_interactions with the no interactions and the request matchers' do
397
- VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
398
- cassette = VCR::Cassette.new('example', :record => record_mode, :match_requests_on => [:body, :headers])
399
- expect(cassette.http_interactions.interactions.size).to eq(0)
400
- expect(cassette.http_interactions.request_matchers).to eq([:body, :headers])
401
- end
402
- end
403
- end
404
- end
405
- end
406
-
407
- describe ".originally_recorded_at" do
408
- it 'returns the earliest `recorded_at` timestamp' do
409
- i1 = http_interaction { |i| i.recorded_at = Time.now - 1000 }
410
- i2 = http_interaction { |i| i.recorded_at = Time.now - 10000 }
411
- i3 = http_interaction { |i| i.recorded_at = Time.now - 100 }
412
-
413
- stub_old_interactions([i1, i2, i3])
414
-
415
- cassette = VCR::Cassette.new("example")
416
- expect(cassette.originally_recorded_at).to eq(i2.recorded_at)
417
- end
418
-
419
- it 'records nil for a cassette that has no prior recorded interactions' do
420
- stub_old_interactions([])
421
- cassette = VCR::Cassette.new("example")
422
- expect(cassette.originally_recorded_at).to be_nil
423
- end
424
- end
425
-
426
- describe '#eject' do
427
- let(:custom_persister) { double("custom persister", :[] => nil) }
428
-
429
- context "when :allow_unused_http_interactions is set to false" do
430
- it 'asserts that there are no unused interactions' do
431
- cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => false)
432
-
433
- interaction_list = cassette.http_interactions
434
- expect(interaction_list).to respond_to(:assert_no_unused_interactions!).with(0).arguments
435
- expect(interaction_list).to receive(:assert_no_unused_interactions!)
436
-
437
- cassette.eject
438
- end
439
-
440
- it 'does not assert no unused interactions if there is an existing error' do
441
- cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => false)
442
- interaction_list = cassette.http_interactions
443
- allow(interaction_list).to receive(:assert_no_unused_interactions!)
444
-
445
- expect {
446
- begin
447
- raise "boom"
448
- ensure
449
- cassette.eject
450
- end
451
- }.to raise_error(/boom/)
452
-
453
- expect(interaction_list).not_to have_received(:assert_no_unused_interactions!)
454
- end
455
-
456
- it 'does not assert no unused interactions if :skip_no_unused_interactions_assertion is passed' do
457
- cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => false)
458
-
459
- interaction_list = cassette.http_interactions
460
- expect(interaction_list).not_to receive(:assert_no_unused_interactions!)
461
-
462
- cassette.eject(:skip_no_unused_interactions_assertion => true)
463
- end
464
- end
465
-
466
- it 'does not assert that there are no unused interactions if allow_unused_http_interactions is set to true' do
467
- cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => true)
468
-
469
- interaction_list = cassette.http_interactions
470
- expect(interaction_list).to respond_to(:assert_no_unused_interactions!)
471
- expect(interaction_list).not_to receive(:assert_no_unused_interactions!)
472
-
473
- cassette.eject
474
- end
475
-
476
- it 'stores the cassette content using the configured persister' do
477
- VCR.configuration.cassette_library_dir = nil
478
- VCR.cassette_persisters[:foo] = custom_persister
479
- cassette = VCR.insert_cassette("foo", :persist_with => :foo)
480
- cassette.record_http_interaction http_interaction
481
-
482
- expect(custom_persister).to receive(:[]=).with("foo.yml", /http_interactions/)
483
-
484
- cassette.eject
485
- end
486
-
487
- it "writes the serializable_hash to disk as yaml" do
488
- cassette = VCR::Cassette.new(:eject_test)
489
- cassette.record_http_interaction http_interaction # so it has one
490
- expect(cassette).to respond_to(:serializable_hash)
491
- allow(cassette).to receive(:serializable_hash).and_return({ "http_interactions" => [1, 3, 5] })
492
-
493
- expect { cassette.eject }.to change { ::File.exist?(cassette.file) }.from(false).to(true)
494
- saved_stuff = YAML.load_file(cassette.file)
495
- expect(saved_stuff).to eq("http_interactions" => [1, 3, 5])
496
- end
497
-
498
- it 'invokes the appropriately tagged before_record hooks' do
499
- interactions = [
500
- http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' },
501
- http_interaction { |i| i.request.uri = 'http://bar.com/'; i.response.body = 'res 2' }
502
- ]
503
-
504
- cassette = VCR::Cassette.new('example', :tag => :foo)
505
- allow(cassette).to receive(:new_recorded_interactions).and_return(interactions)
506
-
507
- allow(VCR.configuration).to receive(:invoke_hook).and_return([false])
508
-
509
- interactions.each do |i|
510
- expect(VCR.configuration).to receive(:invoke_hook).with(
511
- :before_record,
512
- an_instance_of(VCR::HTTPInteraction::HookAware),
513
- cassette
514
- ).ordered
515
- end
516
-
517
- cassette.eject
518
- end
519
-
520
- it 'does not record interactions that have been ignored' do
521
- VCR.configure do |c|
522
- c.before_record { |i| i.ignore! if i.request.uri =~ /foo/ }
523
- end
524
-
525
- interaction_1 = http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' }
526
- interaction_2 = http_interaction { |i| i.request.uri = 'http://bar.com/'; i.response.body = 'res 2' }
527
-
528
- cassette = VCR::Cassette.new('test_cassette')
529
- allow(cassette).to receive(:new_recorded_interactions).and_return([interaction_1, interaction_2])
530
- cassette.eject
531
-
532
- saved_recorded_interactions = ::YAML.load_file(cassette.file)
533
- expect(saved_recorded_interactions["http_interactions"]).to eq([interaction_2.to_hash])
534
- end
535
-
536
- it 'does not write the cassette to disk if all interactions have been ignored' do
537
- VCR.configure do |c|
538
- c.before_record { |i| i.ignore! }
539
- end
540
-
541
- interaction_1 = http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' }
542
-
543
- cassette = VCR::Cassette.new('test_cassette')
544
- allow(cassette).to receive(:new_recorded_interactions).and_return([interaction_1])
545
- cassette.eject
546
-
547
- expect(::File).not_to exist(cassette.file)
548
- end
549
-
550
- it "writes the recorded interactions to a subdirectory if the cassette name includes a directory" do
551
- recorded_interactions = [http_interaction { |i| i.response.body = "subdirectory response" }]
552
- cassette = VCR::Cassette.new('subdirectory/test_cassette')
553
- allow(cassette).to receive(:new_recorded_interactions).and_return(recorded_interactions)
554
-
555
- expect { cassette.eject }.to change { ::File.exist?(cassette.file) }.from(false).to(true)
556
- saved_recorded_interactions = YAML.load_file(cassette.file)
557
- expect(saved_recorded_interactions["http_interactions"]).to eq(recorded_interactions.map(&:to_hash))
558
- end
559
-
560
- [:all, :none, :new_episodes].each do |record_mode|
561
- context "for a :record => :#{record_mode} cassette with previously recorded interactions" do
562
- subject { VCR::Cassette.new('example', :record => record_mode, :match_requests_on => [:uri]) }
563
-
564
- before(:each) do
565
- base_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
566
- FileUtils.cp(base_dir + "/example.yml", VCR.configuration.cassette_library_dir + "/example.yml")
567
- end
568
-
569
- it "does not re-write to disk the previously recorded interactions if there are no new ones" do
570
- yaml_file = subject.file
571
- expect(::File).not_to receive(:open).with(subject.file, 'w')
572
- expect { subject.eject }.to_not change { ::File.mtime(yaml_file) }
573
- end
574
-
575
- context 'when some new interactions have been recorded' do
576
- def interaction(response_body, request_attributes)
577
- http_interaction do |interaction|
578
- interaction.response.body = response_body
579
- request_attributes.each do |key, value|
580
- interaction.request.send("#{key}=", value)
581
- end
582
- end
583
- end
584
-
585
- let(:interaction_foo_1) { interaction("foo 1", :uri => 'http://foo.com/') }
586
- let(:interaction_foo_2) { interaction("foo 2", :uri => 'http://foo.com/') }
587
- let(:interaction_bar) { interaction("bar", :uri => 'http://bar.com/') }
588
-
589
- let(:saved_recorded_interactions) { YAML.load_file(subject.file)['http_interactions'].map { |h| VCR::HTTPInteraction.from_hash(h) } }
590
- let(:now) { Time.utc(2011, 6, 11, 12, 30) }
591
-
592
- before(:each) do
593
- allow(Time).to receive(:now).and_return(now)
594
- allow(subject).to receive(:previously_recorded_interactions).and_return([interaction_foo_1])
595
- subject.record_http_interaction(interaction_foo_2)
596
- subject.record_http_interaction(interaction_bar)
597
- subject.eject
598
- end
599
-
600
- if record_mode == :all
601
- it 'replaces previously recorded interactions with new ones when the requests match' do
602
- expect(saved_recorded_interactions.first).to eq(interaction_foo_2)
603
- expect(saved_recorded_interactions).not_to include(interaction_foo_1)
604
- end
605
-
606
- it 'appends new recorded interactions that do not match existing ones' do
607
- expect(saved_recorded_interactions.last).to eq(interaction_bar)
608
- end
609
- else
610
- it 'appends new recorded interactions after existing ones' do
611
- expect(saved_recorded_interactions).to eq([interaction_foo_1, interaction_foo_2, interaction_bar])
612
- end
613
- end
614
- end
615
- end
616
- end
617
- end
618
- end