mustwin-vcr 2.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/features/about_these_examples.md +18 -0
  3. data/features/cassettes/allow_unused_http_interactions.feature +100 -0
  4. data/features/cassettes/automatic_re_recording.feature +72 -0
  5. data/features/cassettes/decompress.feature +74 -0
  6. data/features/cassettes/dynamic_erb.feature +100 -0
  7. data/features/cassettes/exclusive.feature +126 -0
  8. data/features/cassettes/format.feature +323 -0
  9. data/features/cassettes/freezing_time.feature +68 -0
  10. data/features/cassettes/naming.feature +28 -0
  11. data/features/cassettes/no_cassette.feature +152 -0
  12. data/features/cassettes/update_content_length_header.feature +112 -0
  13. data/features/configuration/allow_http_connections_when_no_cassette.feature +55 -0
  14. data/features/configuration/cassette_library_dir.feature +31 -0
  15. data/features/configuration/debug_logging.feature +59 -0
  16. data/features/configuration/default_cassette_options.feature +100 -0
  17. data/features/configuration/filter_sensitive_data.feature +153 -0
  18. data/features/configuration/hook_into.feature +172 -0
  19. data/features/configuration/ignore_request.feature +192 -0
  20. data/features/configuration/preserve_exact_body_bytes.feature +108 -0
  21. data/features/configuration/query_parser.feature +84 -0
  22. data/features/configuration/uri_parser.feature +89 -0
  23. data/features/getting_started.md +82 -0
  24. data/features/hooks/after_http_request.feature +58 -0
  25. data/features/hooks/around_http_request.feature +57 -0
  26. data/features/hooks/before_http_request.feature +63 -0
  27. data/features/hooks/before_playback.feature +184 -0
  28. data/features/hooks/before_record.feature +172 -0
  29. data/features/http_libraries/em_http_request.feature +250 -0
  30. data/features/http_libraries/net_http.feature +179 -0
  31. data/features/middleware/faraday.feature +56 -0
  32. data/features/middleware/rack.feature +92 -0
  33. data/features/record_modes/all.feature +82 -0
  34. data/features/record_modes/new_episodes.feature +79 -0
  35. data/features/record_modes/none.feature +72 -0
  36. data/features/record_modes/once.feature +95 -0
  37. data/features/request_matching/README.md +30 -0
  38. data/features/request_matching/body.feature +91 -0
  39. data/features/request_matching/body_as_json.feature +90 -0
  40. data/features/request_matching/custom_matcher.feature +135 -0
  41. data/features/request_matching/headers.feature +85 -0
  42. data/features/request_matching/host.feature +95 -0
  43. data/features/request_matching/identical_request_sequence.feature +89 -0
  44. data/features/request_matching/method.feature +96 -0
  45. data/features/request_matching/path.feature +96 -0
  46. data/features/request_matching/playback_repeats.feature +98 -0
  47. data/features/request_matching/query.feature +97 -0
  48. data/features/request_matching/uri.feature +94 -0
  49. data/features/request_matching/uri_without_param.feature +101 -0
  50. data/features/step_definitions/cli_steps.rb +193 -0
  51. data/features/support/env.rb +44 -0
  52. data/features/support/http_lib_filters.rb +53 -0
  53. data/features/test_frameworks/cucumber.feature +211 -0
  54. data/features/test_frameworks/rspec_macro.feature +81 -0
  55. data/features/test_frameworks/rspec_metadata.feature +150 -0
  56. data/features/test_frameworks/test_unit.feature +49 -0
  57. data/lib/vcr.rb +347 -0
  58. data/lib/vcr/cassette.rb +291 -0
  59. data/lib/vcr/cassette/erb_renderer.rb +55 -0
  60. data/lib/vcr/cassette/http_interaction_list.rb +108 -0
  61. data/lib/vcr/cassette/migrator.rb +118 -0
  62. data/lib/vcr/cassette/persisters.rb +42 -0
  63. data/lib/vcr/cassette/persisters/file_system.rb +64 -0
  64. data/lib/vcr/cassette/serializers.rb +57 -0
  65. data/lib/vcr/cassette/serializers/json.rb +48 -0
  66. data/lib/vcr/cassette/serializers/psych.rb +48 -0
  67. data/lib/vcr/cassette/serializers/syck.rb +61 -0
  68. data/lib/vcr/cassette/serializers/yaml.rb +50 -0
  69. data/lib/vcr/configuration.rb +555 -0
  70. data/lib/vcr/deprecations.rb +109 -0
  71. data/lib/vcr/errors.rb +266 -0
  72. data/lib/vcr/extensions/net_http_response.rb +36 -0
  73. data/lib/vcr/library_hooks.rb +18 -0
  74. data/lib/vcr/library_hooks/excon.rb +27 -0
  75. data/lib/vcr/library_hooks/fakeweb.rb +196 -0
  76. data/lib/vcr/library_hooks/faraday.rb +51 -0
  77. data/lib/vcr/library_hooks/typhoeus.rb +120 -0
  78. data/lib/vcr/library_hooks/typhoeus_0.4.rb +103 -0
  79. data/lib/vcr/library_hooks/webmock.rb +164 -0
  80. data/lib/vcr/middleware/excon.rb +221 -0
  81. data/lib/vcr/middleware/excon/legacy_methods.rb +33 -0
  82. data/lib/vcr/middleware/faraday.rb +118 -0
  83. data/lib/vcr/middleware/rack.rb +79 -0
  84. data/lib/vcr/request_handler.rb +114 -0
  85. data/lib/vcr/request_ignorer.rb +43 -0
  86. data/lib/vcr/request_matcher_registry.rb +149 -0
  87. data/lib/vcr/structs.rb +578 -0
  88. data/lib/vcr/tasks/vcr.rake +9 -0
  89. data/lib/vcr/test_frameworks/cucumber.rb +64 -0
  90. data/lib/vcr/test_frameworks/rspec.rb +47 -0
  91. data/lib/vcr/util/hooks.rb +61 -0
  92. data/lib/vcr/util/internet_connection.rb +43 -0
  93. data/lib/vcr/util/logger.rb +59 -0
  94. data/lib/vcr/util/variable_args_block_caller.rb +13 -0
  95. data/lib/vcr/util/version_checker.rb +48 -0
  96. data/lib/vcr/version.rb +34 -0
  97. data/spec/acceptance/threading_spec.rb +34 -0
  98. data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
  99. data/spec/fixtures/cassette_spec/empty.yml +0 -0
  100. data/spec/fixtures/cassette_spec/example.yml +111 -0
  101. data/spec/fixtures/cassette_spec/with_localhost_requests.yml +111 -0
  102. data/spec/fixtures/fake_example_responses.yml +110 -0
  103. data/spec/fixtures/match_requests_on.yml +187 -0
  104. data/spec/lib/vcr/cassette/erb_renderer_spec.rb +53 -0
  105. data/spec/lib/vcr/cassette/http_interaction_list_spec.rb +295 -0
  106. data/spec/lib/vcr/cassette/migrator_spec.rb +195 -0
  107. data/spec/lib/vcr/cassette/persisters/file_system_spec.rb +69 -0
  108. data/spec/lib/vcr/cassette/persisters_spec.rb +39 -0
  109. data/spec/lib/vcr/cassette/serializers_spec.rb +176 -0
  110. data/spec/lib/vcr/cassette_spec.rb +618 -0
  111. data/spec/lib/vcr/configuration_spec.rb +326 -0
  112. data/spec/lib/vcr/deprecations_spec.rb +85 -0
  113. data/spec/lib/vcr/errors_spec.rb +162 -0
  114. data/spec/lib/vcr/extensions/net_http_response_spec.rb +86 -0
  115. data/spec/lib/vcr/library_hooks/excon_spec.rb +104 -0
  116. data/spec/lib/vcr/library_hooks/fakeweb_spec.rb +169 -0
  117. data/spec/lib/vcr/library_hooks/faraday_spec.rb +68 -0
  118. data/spec/lib/vcr/library_hooks/typhoeus_0.4_spec.rb +36 -0
  119. data/spec/lib/vcr/library_hooks/typhoeus_spec.rb +162 -0
  120. data/spec/lib/vcr/library_hooks/webmock_spec.rb +118 -0
  121. data/spec/lib/vcr/library_hooks_spec.rb +51 -0
  122. data/spec/lib/vcr/middleware/faraday_spec.rb +182 -0
  123. data/spec/lib/vcr/middleware/rack_spec.rb +115 -0
  124. data/spec/lib/vcr/request_ignorer_spec.rb +70 -0
  125. data/spec/lib/vcr/request_matcher_registry_spec.rb +345 -0
  126. data/spec/lib/vcr/structs_spec.rb +732 -0
  127. data/spec/lib/vcr/test_frameworks/cucumber_spec.rb +107 -0
  128. data/spec/lib/vcr/test_frameworks/rspec_spec.rb +83 -0
  129. data/spec/lib/vcr/util/hooks_spec.rb +158 -0
  130. data/spec/lib/vcr/util/internet_connection_spec.rb +37 -0
  131. data/spec/lib/vcr/util/version_checker_spec.rb +31 -0
  132. data/spec/lib/vcr/version_spec.rb +27 -0
  133. data/spec/lib/vcr_spec.rb +349 -0
  134. data/spec/monkey_patches.rb +182 -0
  135. data/spec/spec_helper.rb +62 -0
  136. data/spec/support/configuration_stubbing.rb +8 -0
  137. data/spec/support/cucumber_helpers.rb +35 -0
  138. data/spec/support/fixnum_extension.rb +10 -0
  139. data/spec/support/http_library_adapters.rb +289 -0
  140. data/spec/support/limited_uri.rb +21 -0
  141. data/spec/support/ruby_interpreter.rb +7 -0
  142. data/spec/support/shared_example_groups/excon.rb +63 -0
  143. data/spec/support/shared_example_groups/hook_into_http_library.rb +594 -0
  144. data/spec/support/shared_example_groups/request_hooks.rb +59 -0
  145. data/spec/support/sinatra_app.rb +86 -0
  146. data/spec/support/vcr_localhost_server.rb +76 -0
  147. data/spec/support/vcr_stub_helpers.rb +17 -0
  148. metadata +677 -0
@@ -0,0 +1,732 @@
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
+