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.
- checksums.yaml +4 -4
- data/lib/vcr.rb +18 -1
- data/lib/vcr/cassette.rb +11 -3
- data/lib/vcr/cassette/persisters/file_system.rb +1 -1
- data/lib/vcr/configuration.rb +3 -5
- data/lib/vcr/deprecations.rb +0 -62
- data/lib/vcr/errors.rb +16 -0
- data/lib/vcr/library_hooks/typhoeus.rb +37 -8
- data/lib/vcr/middleware/faraday.rb +5 -1
- data/lib/vcr/structs.rb +1 -1
- data/lib/vcr/util/hooks.rb +1 -0
- data/lib/vcr/version.rb +1 -1
- metadata +9 -249
- data/features/CHANGELOG.md +0 -710
- data/features/CONTRIBUTING.md +0 -26
- data/features/LICENSE.md +0 -20
- data/features/README.md +0 -339
- data/features/Upgrade.md +0 -289
- data/features/about_these_examples.md +0 -18
- data/features/cassettes/allow_unused_http_interactions.feature +0 -100
- data/features/cassettes/automatic_re_recording.feature +0 -72
- data/features/cassettes/decompress.feature +0 -74
- data/features/cassettes/dynamic_erb.feature +0 -100
- data/features/cassettes/exclusive.feature +0 -126
- data/features/cassettes/format.feature +0 -411
- data/features/cassettes/freezing_time.feature +0 -68
- data/features/cassettes/naming.feature +0 -28
- data/features/cassettes/no_cassette.feature +0 -152
- data/features/cassettes/update_content_length_header.feature +0 -112
- data/features/configuration/allow_http_connections_when_no_cassette.feature +0 -55
- data/features/configuration/cassette_library_dir.feature +0 -31
- data/features/configuration/debug_logging.feature +0 -58
- data/features/configuration/default_cassette_options.feature +0 -100
- data/features/configuration/filter_sensitive_data.feature +0 -153
- data/features/configuration/hook_into.feature +0 -172
- data/features/configuration/ignore_request.feature +0 -192
- data/features/configuration/preserve_exact_body_bytes.feature +0 -108
- data/features/configuration/query_parser.feature +0 -84
- data/features/configuration/uri_parser.feature +0 -93
- data/features/getting_started.md +0 -82
- data/features/hooks/after_http_request.feature +0 -58
- data/features/hooks/around_http_request.feature +0 -57
- data/features/hooks/before_http_request.feature +0 -63
- data/features/hooks/before_playback.feature +0 -184
- data/features/hooks/before_record.feature +0 -172
- data/features/http_libraries/em_http_request.feature +0 -250
- data/features/http_libraries/net_http.feature +0 -179
- data/features/middleware/faraday.feature +0 -56
- data/features/middleware/rack.feature +0 -92
- data/features/record_modes/all.feature +0 -82
- data/features/record_modes/new_episodes.feature +0 -79
- data/features/record_modes/none.feature +0 -72
- data/features/record_modes/once.feature +0 -95
- data/features/request_matching/README.md +0 -30
- data/features/request_matching/body.feature +0 -91
- data/features/request_matching/body_as_json.feature +0 -90
- data/features/request_matching/custom_matcher.feature +0 -135
- data/features/request_matching/headers.feature +0 -85
- data/features/request_matching/host.feature +0 -95
- data/features/request_matching/identical_request_sequence.feature +0 -89
- data/features/request_matching/method.feature +0 -96
- data/features/request_matching/path.feature +0 -96
- data/features/request_matching/playback_repeats.feature +0 -98
- data/features/request_matching/query.feature +0 -97
- data/features/request_matching/uri.feature +0 -94
- data/features/request_matching/uri_without_param.feature +0 -101
- data/features/step_definitions/cli_steps.rb +0 -199
- data/features/support/env.rb +0 -46
- data/features/support/http_lib_filters.rb +0 -46
- data/features/test_frameworks/cucumber.feature +0 -211
- data/features/test_frameworks/rspec_macro.feature +0 -81
- data/features/test_frameworks/rspec_metadata.feature +0 -150
- data/features/test_frameworks/test_unit.feature +0 -49
- data/lib/vcr/extensions/net_http_response.rb +0 -36
- data/lib/vcr/library_hooks/fakeweb.rb +0 -197
- data/spec/acceptance/concurrency_spec.rb +0 -51
- data/spec/acceptance/threading_spec.rb +0 -34
- data/spec/fixtures/cassette_spec/1_x_cassette.yml +0 -110
- data/spec/fixtures/cassette_spec/empty.yml +0 -0
- data/spec/fixtures/cassette_spec/example.yml +0 -111
- data/spec/fixtures/cassette_spec/with_localhost_requests.yml +0 -111
- data/spec/fixtures/fake_example_responses.yml +0 -110
- data/spec/fixtures/match_requests_on.yml +0 -187
- data/spec/lib/vcr/cassette/erb_renderer_spec.rb +0 -53
- data/spec/lib/vcr/cassette/http_interaction_list_spec.rb +0 -295
- data/spec/lib/vcr/cassette/migrator_spec.rb +0 -196
- data/spec/lib/vcr/cassette/persisters/file_system_spec.rb +0 -75
- data/spec/lib/vcr/cassette/persisters_spec.rb +0 -39
- data/spec/lib/vcr/cassette/serializers_spec.rb +0 -182
- data/spec/lib/vcr/cassette_spec.rb +0 -618
- data/spec/lib/vcr/configuration_spec.rb +0 -326
- data/spec/lib/vcr/deprecations_spec.rb +0 -85
- data/spec/lib/vcr/errors_spec.rb +0 -178
- data/spec/lib/vcr/extensions/net_http_response_spec.rb +0 -86
- data/spec/lib/vcr/library_hooks/excon_spec.rb +0 -104
- data/spec/lib/vcr/library_hooks/fakeweb_spec.rb +0 -169
- data/spec/lib/vcr/library_hooks/faraday_spec.rb +0 -68
- data/spec/lib/vcr/library_hooks/typhoeus_0.4_spec.rb +0 -36
- data/spec/lib/vcr/library_hooks/typhoeus_spec.rb +0 -162
- data/spec/lib/vcr/library_hooks/webmock_spec.rb +0 -117
- data/spec/lib/vcr/library_hooks_spec.rb +0 -51
- data/spec/lib/vcr/middleware/faraday_spec.rb +0 -181
- data/spec/lib/vcr/middleware/rack_spec.rb +0 -115
- data/spec/lib/vcr/request_ignorer_spec.rb +0 -70
- data/spec/lib/vcr/request_matcher_registry_spec.rb +0 -345
- data/spec/lib/vcr/structs_spec.rb +0 -732
- data/spec/lib/vcr/test_frameworks/cucumber_spec.rb +0 -107
- data/spec/lib/vcr/test_frameworks/rspec_spec.rb +0 -94
- data/spec/lib/vcr/util/hooks_spec.rb +0 -158
- data/spec/lib/vcr/util/internet_connection_spec.rb +0 -37
- data/spec/lib/vcr/util/version_checker_spec.rb +0 -31
- data/spec/lib/vcr/version_spec.rb +0 -27
- data/spec/lib/vcr_spec.rb +0 -354
- data/spec/monkey_patches.rb +0 -186
- data/spec/spec_helper.rb +0 -63
- data/spec/support/configuration_stubbing.rb +0 -8
- data/spec/support/cucumber_helpers.rb +0 -39
- data/spec/support/fixnum_extension.rb +0 -10
- data/spec/support/http_library_adapters.rb +0 -289
- data/spec/support/limited_uri.rb +0 -21
- data/spec/support/ruby_interpreter.rb +0 -7
- data/spec/support/shared_example_groups/excon.rb +0 -63
- data/spec/support/shared_example_groups/hook_into_http_library.rb +0 -594
- data/spec/support/shared_example_groups/request_hooks.rb +0 -59
- data/spec/support/sinatra_app.rb +0 -86
- data/spec/support/vcr_localhost_server.rb +0 -76
- data/spec/support/vcr_stub_helpers.rb +0 -17
@@ -1,732 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require 'support/ruby_interpreter'
|
4
|
-
|
5
|
-
require 'yaml'
|
6
|
-
require 'vcr/structs'
|
7
|
-
require 'vcr/errors'
|
8
|
-
require 'zlib'
|
9
|
-
require 'stringio'
|
10
|
-
require 'support/limited_uri'
|
11
|
-
require 'support/configuration_stubbing'
|
12
|
-
|
13
|
-
shared_examples_for "a header normalizer" do
|
14
|
-
let(:instance) do
|
15
|
-
with_headers('Some_Header' => 'value1', 'aNother' => ['a', 'b'], 'third' => [], 'fourth' => nil)
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'ensures header keys are serialized to yaml as raw strings' do
|
19
|
-
key = 'my-key'
|
20
|
-
key.instance_variable_set(:@foo, 7)
|
21
|
-
instance = with_headers(key => ['value1'])
|
22
|
-
expect(YAML.dump(instance.headers)).to eq(YAML.dump('my-key' => ['value1']))
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'ensures header values are serialized to yaml as raw strings' do
|
26
|
-
value = 'my-value'
|
27
|
-
value.instance_variable_set(:@foo, 7)
|
28
|
-
instance = with_headers('my-key' => [value])
|
29
|
-
expect(YAML.dump(instance.headers)).to eq(YAML.dump('my-key' => ['my-value']))
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'handles nested arrays' do
|
33
|
-
accept_encoding = [["gzip", "1.0"], ["deflate", "1.0"], ["sdch", "1.0"]]
|
34
|
-
instance = with_headers('accept-encoding' => accept_encoding)
|
35
|
-
expect(instance.headers['accept-encoding']).to eq(accept_encoding)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'handles nested arrays with floats' do
|
39
|
-
accept_encoding = [["gzip", 1.0], ["deflate", 1.0], ["sdch", 1.0]]
|
40
|
-
instance = with_headers('accept-encoding' => accept_encoding)
|
41
|
-
expect(instance.headers['accept-encoding']).to eq(accept_encoding)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
shared_examples_for "a body normalizer" do
|
46
|
-
it "ensures the body is serialized to yaml as a raw string" do
|
47
|
-
body = "My String"
|
48
|
-
body.instance_variable_set(:@foo, 7)
|
49
|
-
expect(YAML.dump(instance(body).body)).to eq(YAML.dump("My String"))
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'converts nil to a blank string' do
|
53
|
-
expect(instance(nil).body).to eq("")
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'raises an error if given another type of object as the body' do
|
57
|
-
expect {
|
58
|
-
instance(:a => "hash")
|
59
|
-
}.to raise_error(ArgumentError)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
module VCR
|
64
|
-
describe HTTPInteraction do
|
65
|
-
include_context "configuration stubbing"
|
66
|
-
before { allow(config).to receive(:uri_parser) { LimitedURI } }
|
67
|
-
|
68
|
-
if ''.respond_to?(:encoding)
|
69
|
-
def body_hash(key, value)
|
70
|
-
{ key => value, 'encoding' => 'UTF-8' }
|
71
|
-
end
|
72
|
-
else
|
73
|
-
def body_hash(key, value)
|
74
|
-
{ key => value }
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
describe "#recorded_at" do
|
79
|
-
let(:now) { Time.now }
|
80
|
-
|
81
|
-
it 'is initialized to the current time' do
|
82
|
-
allow(Time).to receive(:now).and_return(now)
|
83
|
-
expect(VCR::HTTPInteraction.new.recorded_at).to eq(now)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
let(:status) { ResponseStatus.new(200, "OK") }
|
88
|
-
let(:response) { Response.new(status, { "foo" => ["bar"] }, "res body", "1.1") }
|
89
|
-
let(:request) { Request.new(:get, "http://foo.com/", "req body", { "bar" => ["foo"] }) }
|
90
|
-
let(:recorded_at) { Time.utc(2011, 5, 4, 12, 30) }
|
91
|
-
let(:interaction) { HTTPInteraction.new(request, response, recorded_at) }
|
92
|
-
|
93
|
-
describe ".from_hash" do
|
94
|
-
let(:hash) do
|
95
|
-
{
|
96
|
-
'request' => {
|
97
|
-
'method' => 'get',
|
98
|
-
'uri' => 'http://foo.com/',
|
99
|
-
'body' => body_hash('string', 'req body'),
|
100
|
-
'headers' => { "bar" => ["foo"] }
|
101
|
-
},
|
102
|
-
'response' => {
|
103
|
-
'status' => {
|
104
|
-
'code' => 200,
|
105
|
-
'message' => 'OK'
|
106
|
-
},
|
107
|
-
'headers' => { "foo" => ["bar"] },
|
108
|
-
'body' => body_hash('string', 'res body'),
|
109
|
-
'http_version' => '1.1'
|
110
|
-
},
|
111
|
-
'recorded_at' => "Wed, 04 May 2011 12:30:00 GMT"
|
112
|
-
}
|
113
|
-
end
|
114
|
-
|
115
|
-
it 'constructs an HTTP interaction from the given hash' do
|
116
|
-
expect(HTTPInteraction.from_hash(hash)).to eq(interaction)
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'initializes the recorded_at timestamp from the hash' do
|
120
|
-
expect(HTTPInteraction.from_hash(hash).recorded_at).to eq(recorded_at)
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'initializes the response adapter_metadata from the hash if it is included' do
|
124
|
-
hash['response']['adapter_metadata'] = { 'foo' => 12 }
|
125
|
-
interaction = HTTPInteraction.from_hash(hash)
|
126
|
-
expect(interaction.response.adapter_metadata).to eq("foo" => 12)
|
127
|
-
end
|
128
|
-
|
129
|
-
it 'works when the response adapter_metadata is missing' do
|
130
|
-
expect(hash['response'].keys).not_to include('adapter_metadata')
|
131
|
-
interaction = HTTPInteraction.from_hash(hash)
|
132
|
-
expect(interaction.response.adapter_metadata).to eq({})
|
133
|
-
end
|
134
|
-
|
135
|
-
it 'uses a blank request when the hash lacks one' do
|
136
|
-
hash.delete('request')
|
137
|
-
i = HTTPInteraction.from_hash(hash)
|
138
|
-
expect(i.request).to eq(Request.new)
|
139
|
-
end
|
140
|
-
|
141
|
-
it 'uses a blank response when the hash lacks one' do
|
142
|
-
hash.delete('response')
|
143
|
-
i = HTTPInteraction.from_hash(hash)
|
144
|
-
expect(i.response).to eq(Response.new(ResponseStatus.new))
|
145
|
-
end
|
146
|
-
|
147
|
-
it 'decodes the base64 body string' do
|
148
|
-
hash['request']['body'] = body_hash('base64_string', Base64.encode64('req body'))
|
149
|
-
hash['response']['body'] = body_hash('base64_string', Base64.encode64('res body'))
|
150
|
-
|
151
|
-
i = HTTPInteraction.from_hash(hash)
|
152
|
-
expect(i.request.body).to eq('req body')
|
153
|
-
expect(i.response.body).to eq('res body')
|
154
|
-
end
|
155
|
-
|
156
|
-
if ''.respond_to?(:encoding)
|
157
|
-
it 'force encodes the decoded base64 string as the original encoding' do
|
158
|
-
string = "café"
|
159
|
-
string.force_encoding("US-ASCII")
|
160
|
-
expect(string).not_to be_valid_encoding
|
161
|
-
|
162
|
-
hash['request']['body'] = { 'base64_string' => Base64.encode64(string.dup), 'encoding' => 'US-ASCII' }
|
163
|
-
hash['response']['body'] = { 'base64_string' => Base64.encode64(string.dup), 'encoding' => 'US-ASCII' }
|
164
|
-
|
165
|
-
i = HTTPInteraction.from_hash(hash)
|
166
|
-
expect(i.request.body.encoding.name).to eq("US-ASCII")
|
167
|
-
expect(i.response.body.encoding.name).to eq("US-ASCII")
|
168
|
-
expect(i.request.body.bytes.to_a).to eq(string.bytes.to_a)
|
169
|
-
expect(i.response.body.bytes.to_a).to eq(string.bytes.to_a)
|
170
|
-
expect(i.request.body).not_to be_valid_encoding
|
171
|
-
expect(i.response.body).not_to be_valid_encoding
|
172
|
-
end
|
173
|
-
|
174
|
-
it 'does not attempt to force encode the decoded base64 string when there is no encoding given (i.e. if the cassette was recorded on ruby 1.8)' do
|
175
|
-
hash['request']['body'] = { 'base64_string' => Base64.encode64('foo') }
|
176
|
-
|
177
|
-
i = HTTPInteraction.from_hash(hash)
|
178
|
-
expect(i.request.body).to eq('foo')
|
179
|
-
expect(i.request.body.encoding.name).to eq("ASCII-8BIT")
|
180
|
-
end
|
181
|
-
|
182
|
-
it 'tries to encode strings to the original encoding' do
|
183
|
-
hash['request']['body'] = { 'string' => "abc", 'encoding' => 'ISO-8859-1' }
|
184
|
-
hash['response']['body'] = { 'string' => "abc", 'encoding' => 'ISO-8859-1' }
|
185
|
-
|
186
|
-
i = HTTPInteraction.from_hash(hash)
|
187
|
-
expect(i.request.body).to eq("abc")
|
188
|
-
expect(i.response.body).to eq("abc")
|
189
|
-
expect(i.request.body.encoding.name).to eq("ISO-8859-1")
|
190
|
-
expect(i.response.body.encoding.name).to eq("ISO-8859-1")
|
191
|
-
end
|
192
|
-
|
193
|
-
it 'does not attempt to encode the string when there is no encoding given (i.e. if the cassette was recorded on ruby 1.8)' do
|
194
|
-
string = 'foo'
|
195
|
-
string.force_encoding("ISO-8859-1")
|
196
|
-
hash['request']['body'] = { 'string' => string }
|
197
|
-
|
198
|
-
i = HTTPInteraction.from_hash(hash)
|
199
|
-
expect(i.request.body).to eq('foo')
|
200
|
-
expect(i.request.body.encoding.name).to eq("ISO-8859-1")
|
201
|
-
end
|
202
|
-
|
203
|
-
it 'force encodes to ASCII-8BIT (since it just means "no encoding" or binary)' do
|
204
|
-
string = "\u00f6"
|
205
|
-
string.encode("UTF-8")
|
206
|
-
expect(string).to be_valid_encoding
|
207
|
-
hash['request']['body'] = { 'string' => string, 'encoding' => 'ASCII-8BIT' }
|
208
|
-
|
209
|
-
expect(Request).not_to receive(:warn)
|
210
|
-
i = HTTPInteraction.from_hash(hash)
|
211
|
-
expect(i.request.body).to eq(string)
|
212
|
-
expect(i.request.body.bytes.to_a).to eq(string.bytes.to_a)
|
213
|
-
expect(i.request.body.encoding.name).to eq("ASCII-8BIT")
|
214
|
-
end
|
215
|
-
|
216
|
-
context 'when the string cannot be encoded as the original encoding' do
|
217
|
-
def verify_encoding_error
|
218
|
-
expect { "\xFAbc".encode("ISO-8859-1") }.to raise_error(EncodingError)
|
219
|
-
end
|
220
|
-
|
221
|
-
before do
|
222
|
-
allow(Request).to receive(:warn)
|
223
|
-
allow(Response).to receive(:warn)
|
224
|
-
|
225
|
-
hash['request']['body'] = { 'string' => "\xFAbc", 'encoding' => 'ISO-8859-1' }
|
226
|
-
hash['response']['body'] = { 'string' => "\xFAbc", 'encoding' => 'ISO-8859-1' }
|
227
|
-
|
228
|
-
verify_encoding_error
|
229
|
-
end
|
230
|
-
|
231
|
-
it 'does not force the encoding' do
|
232
|
-
i = HTTPInteraction.from_hash(hash)
|
233
|
-
expect(i.request.body).to eq("\xFAbc")
|
234
|
-
expect(i.response.body).to eq("\xFAbc")
|
235
|
-
expect(i.request.body.encoding.name).not_to eq("ISO-8859-1")
|
236
|
-
expect(i.response.body.encoding.name).not_to eq("ISO-8859-1")
|
237
|
-
end
|
238
|
-
|
239
|
-
it 'prints a warning and informs users of the :preserve_exact_body_bytes option' do
|
240
|
-
expect(Request).to receive(:warn).with(/ISO-8859-1.*preserve_exact_body_bytes/)
|
241
|
-
expect(Response).to receive(:warn).with(/ISO-8859-1.*preserve_exact_body_bytes/)
|
242
|
-
|
243
|
-
HTTPInteraction.from_hash(hash)
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
describe "#to_hash" do
|
250
|
-
include_context "configuration stubbing"
|
251
|
-
|
252
|
-
before(:each) do
|
253
|
-
allow(config).to receive(:preserve_exact_body_bytes_for?).and_return(false)
|
254
|
-
allow(config).to receive(:uri_parser).and_return(URI)
|
255
|
-
end
|
256
|
-
|
257
|
-
let(:hash) { interaction.to_hash }
|
258
|
-
|
259
|
-
it 'returns a nested hash containing all of the pertinent details' do
|
260
|
-
expect(hash.keys).to match_array %w[ request response recorded_at ]
|
261
|
-
|
262
|
-
expect(hash['recorded_at']).to eq(interaction.recorded_at.httpdate)
|
263
|
-
|
264
|
-
expect(hash['request']).to eq({
|
265
|
-
'method' => 'get',
|
266
|
-
'uri' => 'http://foo.com/',
|
267
|
-
'body' => body_hash('string', 'req body'),
|
268
|
-
'headers' => { "bar" => ["foo"] }
|
269
|
-
})
|
270
|
-
|
271
|
-
expect(hash['response']).to eq({
|
272
|
-
'status' => {
|
273
|
-
'code' => 200,
|
274
|
-
'message' => 'OK'
|
275
|
-
},
|
276
|
-
'headers' => { "foo" => ["bar"] },
|
277
|
-
'body' => body_hash('string', 'res body'),
|
278
|
-
'http_version' => '1.1'
|
279
|
-
})
|
280
|
-
end
|
281
|
-
|
282
|
-
it 'includes the response adapter metadata when it is not empty' do
|
283
|
-
interaction.response.adapter_metadata['foo'] = 17
|
284
|
-
expect(hash['response']['adapter_metadata']).to eq('foo' => 17)
|
285
|
-
end
|
286
|
-
|
287
|
-
it 'does not include the response adapter metadata when it is empty' do
|
288
|
-
expect(interaction.response.adapter_metadata).to eq({})
|
289
|
-
expect(hash['response'].keys).not_to include('adapter_metadata')
|
290
|
-
end
|
291
|
-
|
292
|
-
context "when the body is extended with a module and some state" do
|
293
|
-
it 'serializes to YAML w/o the extra state' do
|
294
|
-
interaction.request.body.extend Module.new { attr_accessor :foo }
|
295
|
-
interaction.response.body.extend Module.new { attr_accessor :foo }
|
296
|
-
interaction.request.body.foo = 98765
|
297
|
-
interaction.response.body.foo = 98765
|
298
|
-
|
299
|
-
expect(YAML.dump(interaction.to_hash)).not_to include("98765")
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
it 'encodes the body as base64 when the configuration is so set' do
|
304
|
-
allow(config).to receive(:preserve_exact_body_bytes_for?).and_return(true)
|
305
|
-
expect(hash['request']['body']).to eq(body_hash('base64_string', Base64.encode64('req body')))
|
306
|
-
expect(hash['response']['body']).to eq(body_hash('base64_string', Base64.encode64('res body')))
|
307
|
-
end
|
308
|
-
|
309
|
-
it "sets the string's original encoding", :if => ''.respond_to?(:encoding) do
|
310
|
-
interaction.request.body.force_encoding('ISO-8859-10')
|
311
|
-
interaction.response.body.force_encoding('ASCII-8BIT')
|
312
|
-
|
313
|
-
expect(hash['request']['body']['encoding']).to eq('ISO-8859-10')
|
314
|
-
expect(hash['response']['body']['encoding']).to eq('ASCII-8BIT')
|
315
|
-
end
|
316
|
-
|
317
|
-
def assert_yielded_keys(hash, *keys)
|
318
|
-
yielded_keys = []
|
319
|
-
hash.each { |k, v| yielded_keys << k }
|
320
|
-
expect(yielded_keys).to eq(keys)
|
321
|
-
end
|
322
|
-
|
323
|
-
it 'yields the entries in the expected order so the hash can be serialized in that order' do
|
324
|
-
assert_yielded_keys hash, 'request', 'response', 'recorded_at'
|
325
|
-
assert_yielded_keys hash['request'], 'method', 'uri', 'body', 'headers'
|
326
|
-
assert_yielded_keys hash['response'], 'status', 'headers', 'body', 'http_version'
|
327
|
-
assert_yielded_keys hash['response']['status'], 'code', 'message'
|
328
|
-
end
|
329
|
-
|
330
|
-
it 'yields `adapter_metadata` if it has any data' do
|
331
|
-
interaction.response.adapter_metadata['foo'] = 17
|
332
|
-
assert_yielded_keys hash['response'], 'status', 'headers', 'body', 'http_version', 'adapter_metadata'
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
describe "#parsed_uri" do
|
337
|
-
before :each do
|
338
|
-
allow(uri_parser).to receive(:parse).and_return(uri)
|
339
|
-
allow(config).to receive(:uri_parser).and_return(uri_parser)
|
340
|
-
end
|
341
|
-
|
342
|
-
let(:uri_parser){ double('parser') }
|
343
|
-
let(:uri){ double('uri').as_null_object }
|
344
|
-
|
345
|
-
it "parses the uri using the current uri_parser" do
|
346
|
-
expect(uri_parser).to receive(:parse).with(request.uri)
|
347
|
-
request.parsed_uri
|
348
|
-
end
|
349
|
-
|
350
|
-
it "returns the parsed uri" do
|
351
|
-
expect(request.parsed_uri).to eq uri
|
352
|
-
end
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
describe HTTPInteraction::HookAware do
|
357
|
-
include_context "configuration stubbing"
|
358
|
-
|
359
|
-
before do
|
360
|
-
allow(config).to receive(:uri_parser) { LimitedURI }
|
361
|
-
end
|
362
|
-
|
363
|
-
let(:response_status) { VCR::ResponseStatus.new(200, "OK foo") }
|
364
|
-
let(:body) { "The body foo this is (foo-Foo)" }
|
365
|
-
let(:headers) do {
|
366
|
-
'x-http-foo' => ['bar23', '23foo'],
|
367
|
-
'x-http-bar' => ['foo23', '18']
|
368
|
-
} end
|
369
|
-
|
370
|
-
let(:response) do
|
371
|
-
VCR::Response.new(
|
372
|
-
response_status,
|
373
|
-
headers.dup,
|
374
|
-
body.dup,
|
375
|
-
'1.1'
|
376
|
-
)
|
377
|
-
end
|
378
|
-
|
379
|
-
let(:request) do
|
380
|
-
VCR::Request.new(
|
381
|
-
:get,
|
382
|
-
'http://example-foo.com:80/foo/',
|
383
|
-
body.dup,
|
384
|
-
headers.dup
|
385
|
-
)
|
386
|
-
end
|
387
|
-
|
388
|
-
let(:interaction) { VCR::HTTPInteraction.new(request, response) }
|
389
|
-
subject { HTTPInteraction::HookAware.new(interaction) }
|
390
|
-
|
391
|
-
describe '#ignored?' do
|
392
|
-
it 'returns false by default' do
|
393
|
-
should_not be_ignored
|
394
|
-
end
|
395
|
-
|
396
|
-
it 'returns true when #ignore! has been called' do
|
397
|
-
subject.ignore!
|
398
|
-
should be_ignored
|
399
|
-
end
|
400
|
-
end
|
401
|
-
|
402
|
-
describe '#filter!' do
|
403
|
-
let(:filtered) { subject.filter!('foo', 'AAA') }
|
404
|
-
|
405
|
-
it 'does nothing when given a blank argument' do
|
406
|
-
expect {
|
407
|
-
subject.filter!(nil, 'AAA')
|
408
|
-
subject.filter!('foo', nil)
|
409
|
-
subject.filter!("", 'AAA')
|
410
|
-
subject.filter!('foo', "")
|
411
|
-
}.not_to change { interaction }
|
412
|
-
end
|
413
|
-
|
414
|
-
[:request, :response].each do |part|
|
415
|
-
it "replaces the sensitive text in the #{part} header keys and values" do
|
416
|
-
expect(filtered.send(part).headers).to eq({
|
417
|
-
'x-http-AAA' => ['bar23', '23AAA'],
|
418
|
-
'x-http-bar' => ['AAA23', '18']
|
419
|
-
})
|
420
|
-
end
|
421
|
-
|
422
|
-
it "replaces the sensitive text in the #{part} body" do
|
423
|
-
expect(filtered.send(part).body).to eq("The body AAA this is (AAA-Foo)")
|
424
|
-
end
|
425
|
-
end
|
426
|
-
|
427
|
-
it 'replaces the sensitive text in the response status' do
|
428
|
-
expect(filtered.response.status.message).to eq('OK AAA')
|
429
|
-
end
|
430
|
-
|
431
|
-
it 'replaces sensitive text in the request URI' do
|
432
|
-
expect(filtered.request.uri).to eq('http://example-AAA.com/AAA/')
|
433
|
-
end
|
434
|
-
|
435
|
-
it 'handles numbers (such as the port) properly' do
|
436
|
-
request.uri = "http://foo.com:9000/bar"
|
437
|
-
subject.filter!(9000, "<PORT>")
|
438
|
-
expect(request.uri).to eq("http://foo.com:<PORT>/bar")
|
439
|
-
end
|
440
|
-
end
|
441
|
-
end
|
442
|
-
|
443
|
-
describe Request::Typed do
|
444
|
-
[:uri, :method, :headers, :body].each do |method|
|
445
|
-
it "delegates ##{method} to the request" do
|
446
|
-
request = double(method => "delegated value")
|
447
|
-
expect(Request::Typed.new(request, :type).send(method)).to eq("delegated value")
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
describe "#type" do
|
452
|
-
it 'returns the initialized type' do
|
453
|
-
expect(Request::Typed.new(double, :ignored).type).to be(:ignored)
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
457
|
-
valid_types = [:ignored, :stubbed_by_vcr, :externally_stubbed, :recordable, :unhandled]
|
458
|
-
valid_types.each do |type|
|
459
|
-
describe "##{type}?" do
|
460
|
-
it "returns true if the type is set to :#{type}" do
|
461
|
-
expect(Request::Typed.new(double, type).send("#{type}?")).to be true
|
462
|
-
end
|
463
|
-
|
464
|
-
it "returns false if the type is set to :other" do
|
465
|
-
expect(Request::Typed.new(double, :other).send("#{type}?")).to be false
|
466
|
-
end
|
467
|
-
end
|
468
|
-
end
|
469
|
-
|
470
|
-
describe "#real?" do
|
471
|
-
real_types = [:ignored, :recordable]
|
472
|
-
real_types.each do |type|
|
473
|
-
it "returns true if the type is set to :#{type}" do
|
474
|
-
expect(Request::Typed.new(double, type)).to be_real
|
475
|
-
end
|
476
|
-
end
|
477
|
-
|
478
|
-
(valid_types - real_types).each do |type|
|
479
|
-
it "returns false if the type is set to :#{type}" do
|
480
|
-
expect(Request::Typed.new(double, type)).not_to be_real
|
481
|
-
end
|
482
|
-
end
|
483
|
-
end
|
484
|
-
|
485
|
-
describe "#stubbed?" do
|
486
|
-
stubbed_types = [:externally_stubbed, :stubbed_by_vcr]
|
487
|
-
stubbed_types.each do |type|
|
488
|
-
it "returns true if the type is set to :#{type}" do
|
489
|
-
expect(Request::Typed.new(double, type)).to be_stubbed
|
490
|
-
end
|
491
|
-
end
|
492
|
-
|
493
|
-
(valid_types - stubbed_types).each do |type|
|
494
|
-
it "returns false if the type is set to :#{type}" do
|
495
|
-
expect(Request::Typed.new(double, type)).not_to be_stubbed
|
496
|
-
end
|
497
|
-
end
|
498
|
-
end
|
499
|
-
end
|
500
|
-
|
501
|
-
describe Request do
|
502
|
-
include_context "configuration stubbing"
|
503
|
-
|
504
|
-
before do
|
505
|
-
allow(config).to receive(:uri_parser) { LimitedURI }
|
506
|
-
end
|
507
|
-
|
508
|
-
describe '#method' do
|
509
|
-
subject { VCR::Request.new(:get) }
|
510
|
-
|
511
|
-
context 'when given no arguments' do
|
512
|
-
it 'returns the HTTP method' do
|
513
|
-
expect(subject.method).to eq(:get)
|
514
|
-
end
|
515
|
-
end
|
516
|
-
|
517
|
-
context 'when given an argument' do
|
518
|
-
it 'returns the method object for the named method' do
|
519
|
-
m = subject.method(:class)
|
520
|
-
expect(m).to be_a(Method)
|
521
|
-
expect(m.call).to eq(described_class)
|
522
|
-
end
|
523
|
-
end
|
524
|
-
|
525
|
-
it 'gets normalized to a lowercase symbol' do
|
526
|
-
expect(VCR::Request.new("GET").method).to eq(:get)
|
527
|
-
expect(VCR::Request.new(:GET).method).to eq(:get)
|
528
|
-
expect(VCR::Request.new(:get).method).to eq(:get)
|
529
|
-
expect(VCR::Request.new("get").method).to eq(:get)
|
530
|
-
end
|
531
|
-
end
|
532
|
-
|
533
|
-
describe "#uri" do
|
534
|
-
def uri_for(uri)
|
535
|
-
VCR::Request.new(:get, uri).uri
|
536
|
-
end
|
537
|
-
|
538
|
-
it 'removes the default http port' do
|
539
|
-
expect(uri_for("http://foo.com:80/bar")).to eq("http://foo.com/bar")
|
540
|
-
end
|
541
|
-
|
542
|
-
it 'removes the default https port' do
|
543
|
-
expect(uri_for("https://foo.com:443/bar")).to eq("https://foo.com/bar")
|
544
|
-
end
|
545
|
-
|
546
|
-
it 'does not remove a non-standard http port' do
|
547
|
-
expect(uri_for("http://foo.com:81/bar")).to eq("http://foo.com:81/bar")
|
548
|
-
end
|
549
|
-
|
550
|
-
it 'does not remove a non-standard https port' do
|
551
|
-
expect(uri_for("https://foo.com:442/bar")).to eq("https://foo.com:442/bar")
|
552
|
-
end
|
553
|
-
end
|
554
|
-
|
555
|
-
describe Request::FiberAware do
|
556
|
-
subject { Request::FiberAware.new(Request.new) }
|
557
|
-
|
558
|
-
it 'adds a #proceed method that yields in a fiber' do
|
559
|
-
fiber = Fiber.new do |request|
|
560
|
-
request.proceed
|
561
|
-
:done
|
562
|
-
end
|
563
|
-
|
564
|
-
expect(fiber.resume(subject)).to be_nil
|
565
|
-
expect(fiber.resume).to eq(:done)
|
566
|
-
end
|
567
|
-
|
568
|
-
it 'can be cast to a proc' do
|
569
|
-
expect(Fiber).to receive(:yield)
|
570
|
-
lambda(&subject).call
|
571
|
-
end
|
572
|
-
end if RUBY_VERSION > '1.9'
|
573
|
-
|
574
|
-
it_behaves_like 'a header normalizer' do
|
575
|
-
def with_headers(headers)
|
576
|
-
described_class.new(:get, 'http://example.com/', nil, headers)
|
577
|
-
end
|
578
|
-
end
|
579
|
-
|
580
|
-
it_behaves_like 'a body normalizer' do
|
581
|
-
def instance(body)
|
582
|
-
described_class.new(:get, 'http://example.com/', body, {})
|
583
|
-
end
|
584
|
-
end
|
585
|
-
end
|
586
|
-
|
587
|
-
describe Response do
|
588
|
-
it_behaves_like 'a header normalizer' do
|
589
|
-
def with_headers(headers)
|
590
|
-
described_class.new(:status, headers, nil, '1.1')
|
591
|
-
end
|
592
|
-
end
|
593
|
-
|
594
|
-
it_behaves_like 'a body normalizer' do
|
595
|
-
def instance(body)
|
596
|
-
described_class.new(:status, {}, body, '1.1')
|
597
|
-
end
|
598
|
-
end
|
599
|
-
|
600
|
-
describe "#adapter_metadata" do
|
601
|
-
it 'returns the hash given as the last #initialize argument' do
|
602
|
-
response = Response.new(
|
603
|
-
ResponseStatus.new(200, "OK"),
|
604
|
-
{}, "the body", "1.1",
|
605
|
-
{ "meta" => "value" }
|
606
|
-
)
|
607
|
-
|
608
|
-
expect(response.adapter_metadata).to eq("meta" => "value")
|
609
|
-
end
|
610
|
-
|
611
|
-
it 'returns a blank hash when nil is passed to #initialize' do
|
612
|
-
response = Response.new(
|
613
|
-
ResponseStatus.new(200, "OK"),
|
614
|
-
{}, "the body", "1.1", nil
|
615
|
-
)
|
616
|
-
|
617
|
-
expect(response.adapter_metadata).to eq({})
|
618
|
-
end
|
619
|
-
end
|
620
|
-
|
621
|
-
describe '#update_content_length_header' do
|
622
|
-
%w[ content-length Content-Length ].each do |header|
|
623
|
-
context "for the #{header} header" do
|
624
|
-
define_method :instance do |body, content_length|
|
625
|
-
headers = { 'content-type' => 'text' }
|
626
|
-
headers.merge!(header => content_length) if content_length
|
627
|
-
described_class.new(VCR::ResponseStatus.new, headers, body)
|
628
|
-
end
|
629
|
-
|
630
|
-
it 'does nothing when the response lacks a content_length header' do
|
631
|
-
inst = instance('the body', nil)
|
632
|
-
expect {
|
633
|
-
inst.update_content_length_header
|
634
|
-
}.not_to change { inst.headers[header] }
|
635
|
-
end
|
636
|
-
|
637
|
-
it 'sets the content_length header to the response body length when the header is present' do
|
638
|
-
inst = instance('the body', '3')
|
639
|
-
expect {
|
640
|
-
inst.update_content_length_header
|
641
|
-
}.to change { inst.headers[header] }.from(['3']).to(['8'])
|
642
|
-
end
|
643
|
-
|
644
|
-
it 'sets the content_length header to 0 if the response body is nil' do
|
645
|
-
inst = instance(nil, '3')
|
646
|
-
expect {
|
647
|
-
inst.update_content_length_header
|
648
|
-
}.to change { inst.headers[header] }.from(['3']).to(['0'])
|
649
|
-
end
|
650
|
-
|
651
|
-
it 'sets the header according to RFC 2616 based on the number of bytes (not the number of characters)' do
|
652
|
-
inst = instance('aؼ', '2') # the second char is a double byte char
|
653
|
-
expect {
|
654
|
-
inst.update_content_length_header
|
655
|
-
}.to change { inst.headers[header] }.from(['2']).to(['3'])
|
656
|
-
end
|
657
|
-
end
|
658
|
-
end
|
659
|
-
end
|
660
|
-
|
661
|
-
describe '#decompress' do
|
662
|
-
%w[ content-encoding Content-Encoding ].each do |header|
|
663
|
-
context "for the #{header} header" do
|
664
|
-
define_method :instance do |body, content_encoding|
|
665
|
-
headers = { 'content-type' => 'text',
|
666
|
-
'content-length' => body.bytesize.to_s }
|
667
|
-
headers[header] = content_encoding if content_encoding
|
668
|
-
described_class.new(VCR::ResponseStatus.new, headers, body)
|
669
|
-
end
|
670
|
-
|
671
|
-
let(:content) { 'The quick brown fox jumps over the lazy dog' }
|
672
|
-
|
673
|
-
it "does nothing when no compression" do
|
674
|
-
resp = instance('Hello', nil)
|
675
|
-
expect(resp).not_to be_compressed
|
676
|
-
expect {
|
677
|
-
expect(resp.decompress).to equal(resp)
|
678
|
-
}.to_not change { resp.headers['content-length'] }
|
679
|
-
end
|
680
|
-
|
681
|
-
it "does nothing when encoding is 'identity'" do
|
682
|
-
resp = instance('Hello', 'identity')
|
683
|
-
expect(resp).not_to be_compressed
|
684
|
-
expect {
|
685
|
-
expect(resp.decompress).to equal(resp)
|
686
|
-
}.to_not change { resp.headers['content-length'] }
|
687
|
-
end
|
688
|
-
|
689
|
-
it "raises error for unrecognized encoding" do
|
690
|
-
resp = instance('Hello', 'flabbergaster')
|
691
|
-
expect(resp).not_to be_compressed
|
692
|
-
expect { resp.decompress }.
|
693
|
-
to raise_error(Errors::UnknownContentEncodingError, 'unknown content encoding: flabbergaster')
|
694
|
-
end
|
695
|
-
|
696
|
-
it "unzips gzipped response" do
|
697
|
-
io = StringIO.new
|
698
|
-
|
699
|
-
writer = Zlib::GzipWriter.new(io)
|
700
|
-
writer << content
|
701
|
-
writer.close
|
702
|
-
|
703
|
-
gzipped = io.string
|
704
|
-
resp = instance(gzipped, 'gzip')
|
705
|
-
expect(resp).to be_compressed
|
706
|
-
expect {
|
707
|
-
expect(resp.decompress).to equal(resp)
|
708
|
-
expect(resp).not_to be_compressed
|
709
|
-
expect(resp.body).to eq(content)
|
710
|
-
}.to change { resp.headers['content-length'] }.
|
711
|
-
from([gzipped.bytesize.to_s]).
|
712
|
-
to([content.bytesize.to_s])
|
713
|
-
end
|
714
|
-
|
715
|
-
it "inflates deflated response" do
|
716
|
-
deflated = Zlib::Deflate.deflate(content)
|
717
|
-
resp = instance(deflated, 'deflate')
|
718
|
-
expect(resp).to be_compressed
|
719
|
-
expect {
|
720
|
-
expect(resp.decompress).to equal(resp)
|
721
|
-
expect(resp).not_to be_compressed
|
722
|
-
expect(resp.body).to eq(content)
|
723
|
-
}.to change { resp.headers['content-length'] }.
|
724
|
-
from([deflated.bytesize.to_s]).
|
725
|
-
to([content.bytesize.to_s])
|
726
|
-
end
|
727
|
-
end
|
728
|
-
end
|
729
|
-
end
|
730
|
-
end
|
731
|
-
end
|
732
|
-
|