vcr 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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