vcr 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +11 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +4 -3
- data/benchmarks/http_stubbing_libraries.rb +4 -4
- data/features/.nav +1 -0
- data/features/configuration/ignore_hosts.feature +61 -0
- data/features/http_libraries/net_http.feature +34 -0
- data/features/step_definitions/cli_steps.rb +16 -1
- data/lib/vcr.rb +23 -14
- data/lib/vcr/cassette.rb +2 -4
- data/lib/vcr/config.rb +20 -5
- data/lib/vcr/deprecations.rb +27 -14
- data/lib/vcr/http_stubbing_adapters/fakeweb.rb +14 -9
- data/lib/vcr/http_stubbing_adapters/faraday.rb +12 -3
- data/lib/vcr/http_stubbing_adapters/typhoeus.rb +3 -7
- data/lib/vcr/http_stubbing_adapters/webmock.rb +17 -7
- data/lib/vcr/middleware/faraday.rb +1 -1
- data/lib/vcr/request_matcher.rb +12 -8
- data/lib/vcr/structs/http_interaction.rb +16 -0
- data/lib/vcr/structs/normalizers/body.rb +24 -0
- data/lib/vcr/structs/normalizers/header.rb +56 -0
- data/lib/vcr/structs/normalizers/status_message.rb +17 -0
- data/lib/vcr/structs/normalizers/uri.rb +34 -0
- data/lib/vcr/structs/request.rb +20 -0
- data/lib/vcr/structs/response.rb +16 -0
- data/lib/vcr/structs/response_status.rb +9 -0
- data/lib/vcr/util/regexes.rb +37 -0
- data/lib/vcr/version.rb +16 -5
- data/spec/spec_helper.rb +26 -3
- data/spec/support/http_library_adapters.rb +11 -12
- data/spec/support/http_stubbing_adapter.rb +2 -16
- data/spec/support/normalizers.rb +84 -0
- data/spec/support/version_checker.rb +1 -1
- data/spec/vcr/cassette_spec.rb +8 -10
- data/spec/vcr/config_spec.rb +63 -17
- data/spec/vcr/deprecations_spec.rb +83 -24
- data/spec/vcr/http_stubbing_adapters/multi_object_proxy_spec.rb +1 -1
- data/spec/vcr/http_stubbing_adapters/typhoeus_spec.rb +2 -2
- data/spec/vcr/middleware/faraday_spec.rb +1 -1
- data/spec/vcr/structs/http_interaction_spec.rb +23 -0
- data/spec/vcr/structs/request_spec.rb +54 -0
- data/spec/vcr/structs/response_spec.rb +39 -0
- data/spec/vcr/structs/response_status_spec.rb +18 -0
- data/spec/vcr_spec.rb +26 -54
- data/vcr.gemspec +1 -1
- metadata +48 -31
- data/TODO.md +0 -5
- data/lib/vcr/structs.rb +0 -176
- data/spec/vcr/structs_spec.rb +0 -201
data/TODO.md
DELETED
data/lib/vcr/structs.rb
DELETED
@@ -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
|
data/spec/vcr/structs_spec.rb
DELETED
@@ -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
|