vcr 3.0.3 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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