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.
- 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
|