vcr 1.5.1 → 1.6.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 (49) hide show
  1. data/CHANGELOG.md +11 -1
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +4 -3
  4. data/benchmarks/http_stubbing_libraries.rb +4 -4
  5. data/features/.nav +1 -0
  6. data/features/configuration/ignore_hosts.feature +61 -0
  7. data/features/http_libraries/net_http.feature +34 -0
  8. data/features/step_definitions/cli_steps.rb +16 -1
  9. data/lib/vcr.rb +23 -14
  10. data/lib/vcr/cassette.rb +2 -4
  11. data/lib/vcr/config.rb +20 -5
  12. data/lib/vcr/deprecations.rb +27 -14
  13. data/lib/vcr/http_stubbing_adapters/fakeweb.rb +14 -9
  14. data/lib/vcr/http_stubbing_adapters/faraday.rb +12 -3
  15. data/lib/vcr/http_stubbing_adapters/typhoeus.rb +3 -7
  16. data/lib/vcr/http_stubbing_adapters/webmock.rb +17 -7
  17. data/lib/vcr/middleware/faraday.rb +1 -1
  18. data/lib/vcr/request_matcher.rb +12 -8
  19. data/lib/vcr/structs/http_interaction.rb +16 -0
  20. data/lib/vcr/structs/normalizers/body.rb +24 -0
  21. data/lib/vcr/structs/normalizers/header.rb +56 -0
  22. data/lib/vcr/structs/normalizers/status_message.rb +17 -0
  23. data/lib/vcr/structs/normalizers/uri.rb +34 -0
  24. data/lib/vcr/structs/request.rb +20 -0
  25. data/lib/vcr/structs/response.rb +16 -0
  26. data/lib/vcr/structs/response_status.rb +9 -0
  27. data/lib/vcr/util/regexes.rb +37 -0
  28. data/lib/vcr/version.rb +16 -5
  29. data/spec/spec_helper.rb +26 -3
  30. data/spec/support/http_library_adapters.rb +11 -12
  31. data/spec/support/http_stubbing_adapter.rb +2 -16
  32. data/spec/support/normalizers.rb +84 -0
  33. data/spec/support/version_checker.rb +1 -1
  34. data/spec/vcr/cassette_spec.rb +8 -10
  35. data/spec/vcr/config_spec.rb +63 -17
  36. data/spec/vcr/deprecations_spec.rb +83 -24
  37. data/spec/vcr/http_stubbing_adapters/multi_object_proxy_spec.rb +1 -1
  38. data/spec/vcr/http_stubbing_adapters/typhoeus_spec.rb +2 -2
  39. data/spec/vcr/middleware/faraday_spec.rb +1 -1
  40. data/spec/vcr/structs/http_interaction_spec.rb +23 -0
  41. data/spec/vcr/structs/request_spec.rb +54 -0
  42. data/spec/vcr/structs/response_spec.rb +39 -0
  43. data/spec/vcr/structs/response_status_spec.rb +18 -0
  44. data/spec/vcr_spec.rb +26 -54
  45. data/vcr.gemspec +1 -1
  46. metadata +48 -31
  47. data/TODO.md +0 -5
  48. data/lib/vcr/structs.rb +0 -176
  49. data/spec/vcr/structs_spec.rb +0 -201
data/TODO.md DELETED
@@ -1,5 +0,0 @@
1
- * Use fakefs gem instead of using temp_dir in specs.
2
- * Fix #method vs .method in specs.
3
- * Cuke for Net::HTTP HTTPS request
4
- * Cuke for multiple simultaneous em-http-request
5
- * Ensure the checkpoints are properly deep-duped
@@ -1,176 +0,0 @@
1
- require 'forwardable'
2
-
3
- module VCR
4
- module BodyNormalizer
5
- def initialize(*args)
6
- super
7
- normalize_body
8
- end
9
-
10
- private
11
-
12
- def normalize_body
13
- # Ensure that the body is a raw string, in case the string instance
14
- # has been subclassed or extended with additional instance variables
15
- # or attributes, so that it is serialized to YAML as a raw string.
16
- # This is needed for rest-client. See this ticket for more info:
17
- # http://github.com/myronmarston/vcr/issues/4
18
- self.body = case body
19
- when nil, ''; nil
20
- else String.new(body)
21
- end
22
- end
23
- end
24
-
25
- module HeaderNormalizer
26
- # These headers get added by the various HTTP clients automatically,
27
- # and we don't care about them. We store the headers for the purposes
28
- # of request matching, and we only care to match on headers users
29
- # explicitly set.
30
- HEADERS_TO_SKIP = {
31
- 'connection' => %w[ close Keep-Alive ],
32
- 'accept' => %w[ */* ],
33
- 'expect' => [''],
34
- 'user-agent' => ["Typhoeus - http://github.com/pauldix/typhoeus/tree/master", 'Ruby']
35
- }
36
-
37
- def initialize(*args)
38
- super
39
- normalize_headers
40
- end
41
-
42
- private
43
-
44
- def important_header_values(k, values)
45
- skip_values = HEADERS_TO_SKIP[k] || []
46
- values - skip_values
47
- end
48
-
49
- def normalize_headers
50
- new_headers = {}
51
-
52
- headers.each do |k, v|
53
- k = k.downcase
54
-
55
- val_array = case v
56
- when Array then v
57
- when nil then []
58
- else [v]
59
- end
60
-
61
- important_vals = important_header_values(k, val_array)
62
- next unless important_vals.size > 0
63
-
64
- # Ensure the values are raw strings.
65
- # Apparently for Paperclip uploads to S3, headers
66
- # get serialized with some extra stuff which leads
67
- # to a seg fault. See this issue for more info:
68
- # https://github.com/myronmarston/vcr/issues#issue/39
69
- string_vals = important_vals.map { |v| String.new(v) }
70
-
71
- new_headers[k] = string_vals
72
- end if headers
73
-
74
- self.headers = new_headers.empty? ? nil : new_headers
75
- end
76
- end
77
-
78
- module URINormalizer
79
- DEFAULT_PORTS = {
80
- 'http' => 80,
81
- 'https' => 443
82
- }
83
-
84
- def initialize(*args)
85
- super
86
- normalize_uri
87
- end
88
-
89
- private
90
-
91
- def normalize_uri
92
- u = begin
93
- URI.parse(uri)
94
- rescue URI::InvalidURIError
95
- return
96
- end
97
-
98
- u.port ||= DEFAULT_PORTS[u.scheme]
99
-
100
- # URI#to_s only includes the port if it's not the default
101
- # but we want to always include it (since FakeWeb/WebMock
102
- # urls have always included it). We force it to be included
103
- # here by redefining default_port so that URI#to_s will include it.
104
- def u.default_port; nil; end
105
- self.uri = u.to_s
106
- end
107
- end
108
-
109
- module StatusMessageNormalizer
110
- def initialize(*args)
111
- super
112
- normalize_status_message
113
- end
114
-
115
- private
116
-
117
- def normalize_status_message
118
- self.message = message.strip if message
119
- self.message = nil if message == ''
120
- end
121
- end
122
-
123
- class Request < Struct.new(:method, :uri, :body, :headers)
124
- include HeaderNormalizer
125
- include URINormalizer
126
- include BodyNormalizer
127
-
128
- def self.from_net_http_request(net_http, request)
129
- new(
130
- request.method.downcase.to_sym,
131
- VCR.http_stubbing_adapter.request_uri(net_http, request),
132
- request.body,
133
- request.to_hash
134
- )
135
- end
136
-
137
- def matcher(match_attributes)
138
- RequestMatcher.new(self, match_attributes)
139
- end
140
- end
141
-
142
- class ResponseStatus < Struct.new(:code, :message)
143
- include StatusMessageNormalizer
144
-
145
- def self.from_net_http_response(response)
146
- new(response.code.to_i, response.message)
147
- end
148
- end
149
-
150
- class Response < Struct.new(:status, :headers, :body, :http_version)
151
- include HeaderNormalizer
152
- include BodyNormalizer
153
-
154
- def self.from_net_http_response(response)
155
- new(
156
- ResponseStatus.from_net_http_response(response),
157
- response.to_hash,
158
- response.body,
159
- response.http_version
160
- )
161
- end
162
- end
163
-
164
- class HTTPInteraction < Struct.new(:request, :response)
165
- extend ::Forwardable
166
- def_delegators :request, :uri, :method
167
-
168
- def ignore!
169
- @ignored = true
170
- end
171
-
172
- def ignored?
173
- @ignored
174
- end
175
- end
176
- end
@@ -1,201 +0,0 @@
1
- require 'spec_helper'
2
-
3
- shared_examples_for "a header normalizer" do
4
- let(:instance) do
5
- with_headers('Some_Header' => 'value1', 'aNother' => ['a', 'b'], 'third' => [], 'fourth' => nil)
6
- end
7
-
8
- it 'normalizes the hash to lower case keys and arrays of values' do
9
- instance.headers['some_header'].should == ['value1']
10
- instance.headers['another'].should == ['a', 'b']
11
- end
12
-
13
- it 'removes empty headers' do
14
- instance.headers.should_not have_key('third')
15
- instance.headers.should_not have_key('fourth')
16
- end
17
-
18
- it 'filters out unimportant default values set by the HTTP library' do
19
- instance = with_headers('accept' => ['*/*'], 'connection' => 'close', 'http-user' => ['foo'], 'expect' => ['', 'bar'])
20
- instance.headers.should == { 'http-user' => ['foo'], 'expect' => ['bar'] }
21
- end
22
-
23
- it 'sets empty hash header to nil' do
24
- with_headers({}).headers.should be_nil
25
- end
26
-
27
- it 'ensures header keys are serialized to yaml as raw strings' do
28
- key = 'my-key'
29
- key.instance_variable_set(:@foo, 7)
30
- instance = with_headers(key => ['value1'])
31
- instance.headers.to_yaml.should == { 'my-key' => ['value1'] }.to_yaml
32
- end
33
-
34
- it 'ensures header values are serialized to yaml as raw strings' do
35
- value = 'my-value'
36
- value.instance_variable_set(:@foo, 7)
37
- instance = with_headers('my-key' => [value])
38
- instance.headers.to_yaml.should == { 'my-key' => ['my-value'] }.to_yaml
39
- end
40
- end
41
-
42
- shared_examples_for "a body normalizer" do
43
- it 'sets empty string to nil' do
44
- instance('').body.should be_nil
45
- end
46
-
47
- it "ensures the body is serialized to yaml as a raw string" do
48
- body = "My String"
49
- body.instance_variable_set(:@foo, 7)
50
- instance(body).body.to_yaml.should == "My String".to_yaml
51
- end
52
- end
53
-
54
- describe VCR::Request do
55
- describe '#matcher' do
56
- it 'returns a matcher with the given request' do
57
- req = VCR::Request.new
58
- req.matcher([:uri]).request.should == req
59
- end
60
-
61
- it 'returns a matcher with the given match_attributes' do
62
- req = VCR::Request.new
63
- req.matcher([:uri, :headers]).match_attributes.to_a.should =~ [:uri, :headers]
64
- end
65
- end
66
-
67
- describe 'uri normalization' do
68
- def request(uri)
69
- VCR::Request.new(:get, uri, '', {})
70
- end
71
-
72
- it 'adds port 80 to an http URI that lacks a port' do
73
- request('http://example.com/foo').uri.should == 'http://example.com:80/foo'
74
- end
75
-
76
- it 'keeps the existing port for an http URI' do
77
- request('http://example.com:8000/foo').uri.should == 'http://example.com:8000/foo'
78
- end
79
-
80
- it 'adds port 443 to an https URI that lacks a port' do
81
- request('https://example.com/foo').uri.should == 'https://example.com:443/foo'
82
- end
83
-
84
- it 'keeps the existing port for an https URI' do
85
- request('https://example.com:8000/foo').uri.should == 'https://example.com:8000/foo'
86
- end
87
- end
88
-
89
- describe '.from_net_http_request' do
90
- let(:net_http) { YAML.load(File.read("#{VCR::SPEC_ROOT}/fixtures/#{YAML_SERIALIZATION_VERSION}/example_net_http.yml")) }
91
- let(:request) { YAML.load(File.read("#{VCR::SPEC_ROOT}/fixtures/#{YAML_SERIALIZATION_VERSION}/example_net_http_request.yml")) }
92
- subject { described_class.from_net_http_request(net_http, request) }
93
-
94
- before(:each) do
95
- VCR.http_stubbing_adapter.should respond_to(:request_uri)
96
- VCR.http_stubbing_adapter.stub!(:request_uri)
97
- end
98
-
99
- it { should be_instance_of(VCR::Request) }
100
- its(:method) { should == :post }
101
- its(:body) { should == 'id=7' }
102
- its(:headers) { should == { "content-type" => ["application/x-www-form-urlencoded"] } }
103
-
104
- it 'sets the uri using the http_stubbing_adapter.request_uri' do
105
- VCR.http_stubbing_adapter.should_receive(:request_uri).with(net_http, request).and_return('foo/bar')
106
- subject.uri.should == 'foo/bar'
107
- end
108
- end
109
-
110
- it_behaves_like 'a header normalizer' do
111
- def with_headers(headers)
112
- described_class.new(:get, 'http://example.com/', nil, headers)
113
- end
114
- end
115
-
116
- it_behaves_like 'a body normalizer' do
117
- def instance(body)
118
- described_class.new(:get, 'http://example.com/', body, {})
119
- end
120
- end
121
- end
122
-
123
- describe VCR::ResponseStatus do
124
- describe '.from_net_http_response' do
125
- let(:response) { YAML.load(File.read("#{VCR::SPEC_ROOT}/fixtures/#{YAML_SERIALIZATION_VERSION}/example_net_http_response.yml")) }
126
- subject { described_class.from_net_http_response(response) }
127
-
128
- it { should be_instance_of(described_class) }
129
- its(:code) { should == 200 }
130
- its(:message) { should == 'OK' }
131
- end
132
-
133
- it 'chomps leading and trailing spaces on the status message' do
134
- described_class.new(200, ' OK ').message.should == 'OK'
135
- end
136
-
137
- it 'sets status message to nil when it is the empty string' do
138
- described_class.new(200, '').message.should be_nil
139
- described_class.new(200, ' ').message.should be_nil
140
- end
141
- end
142
-
143
- describe VCR::Response do
144
- describe '.from_net_http_response' do
145
- let(:response) { YAML.load(File.read("#{VCR::SPEC_ROOT}/fixtures/#{YAML_SERIALIZATION_VERSION}/example_net_http_response.yml")) }
146
- subject { described_class.from_net_http_response(response) }
147
-
148
- it { should be_instance_of(described_class) }
149
- its(:body) { should == 'The response from example.com' }
150
- its(:http_version) { should == '1.1' }
151
- its(:headers) { should == {
152
- "last-modified" => ['Tue, 15 Nov 2005 13:24:10 GMT'],
153
- "etag" => ["\"24ec5-1b6-4059a80bfd280\""],
154
- "content-type" => ["text/html; charset=UTF-8"],
155
- "date" => ['Wed, 31 Mar 2010 02:43:26 GMT'],
156
- "server" => ['Apache/2.2.3 (CentOS)'],
157
- "content-length" => ['438'],
158
- "accept-ranges" => ['bytes']
159
- } }
160
-
161
- it 'assigns the status using VCR::ResponseStatus.from_net_http_response' do
162
- VCR::ResponseStatus.should respond_to(:from_net_http_response)
163
- VCR::ResponseStatus.should_receive(:from_net_http_response).with(response).and_return(:the_status)
164
- subject.status.should == :the_status
165
- end
166
- end
167
-
168
- it_behaves_like 'a header normalizer' do
169
- def with_headers(headers)
170
- described_class.new(:status, headers, nil, '1.1')
171
- end
172
- end
173
-
174
- it_behaves_like 'a body normalizer' do
175
- def instance(body)
176
- described_class.new(:status, {}, body, '1.1')
177
- end
178
- end
179
- end
180
-
181
- describe VCR::HTTPInteraction do
182
- %w( uri method ).each do |attr|
183
- it "delegates :#{attr} to the request signature" do
184
- sig = mock('request signature')
185
- sig.should_receive(attr).and_return(:the_value)
186
- instance = described_class.new(sig, nil)
187
- instance.send(attr).should == :the_value
188
- end
189
- end
190
-
191
- describe '#ignored?' do
192
- it 'returns false by default' do
193
- should_not be_ignored
194
- end
195
-
196
- it 'returns true when #ignore! has been called' do
197
- subject.ignore!
198
- should be_ignored
199
- end
200
- end
201
- end