vcr 2.0.0.beta1 → 2.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +37 -2
- data/Gemfile +2 -2
- data/README.md +10 -1
- data/Rakefile +43 -7
- data/Upgrade.md +45 -0
- data/features/.nav +1 -0
- data/features/cassettes/automatic_re_recording.feature +19 -17
- data/features/cassettes/dynamic_erb.feature +32 -28
- data/features/cassettes/exclusive.feature +28 -24
- data/features/cassettes/format.feature +213 -31
- data/features/cassettes/update_content_length_header.feature +20 -18
- data/features/configuration/filter_sensitive_data.feature +4 -4
- data/features/configuration/hooks.feature +27 -23
- data/features/http_libraries/em_http_request.feature +79 -75
- data/features/record_modes/all.feature +14 -14
- data/features/record_modes/new_episodes.feature +15 -15
- data/features/record_modes/none.feature +15 -15
- data/features/record_modes/once.feature +15 -15
- data/features/request_matching/body.feature +25 -23
- data/features/request_matching/custom_matcher.feature +25 -23
- data/features/request_matching/headers.feature +32 -36
- data/features/request_matching/host.feature +27 -25
- data/features/request_matching/identical_request_sequence.feature +27 -25
- data/features/request_matching/method.feature +27 -25
- data/features/request_matching/path.feature +27 -25
- data/features/request_matching/playback_repeats.feature +27 -25
- data/features/request_matching/uri.feature +27 -25
- data/features/request_matching/uri_without_param.feature +28 -26
- data/features/step_definitions/cli_steps.rb +71 -17
- data/features/support/env.rb +3 -1
- data/features/support/http_lib_filters.rb +6 -3
- data/features/support/vcr_cucumber_helpers.rb +4 -2
- data/lib/vcr.rb +6 -2
- data/lib/vcr/cassette.rb +75 -51
- data/lib/vcr/cassette/migrator.rb +111 -0
- data/lib/vcr/cassette/serializers.rb +35 -0
- data/lib/vcr/cassette/serializers/json.rb +23 -0
- data/lib/vcr/cassette/serializers/psych.rb +24 -0
- data/lib/vcr/cassette/serializers/syck.rb +35 -0
- data/lib/vcr/cassette/serializers/yaml.rb +24 -0
- data/lib/vcr/configuration.rb +6 -1
- data/lib/vcr/errors.rb +1 -1
- data/lib/vcr/library_hooks/excon.rb +1 -7
- data/lib/vcr/library_hooks/typhoeus.rb +6 -22
- data/lib/vcr/library_hooks/webmock.rb +1 -1
- data/lib/vcr/middleware/faraday.rb +1 -1
- data/lib/vcr/request_matcher_registry.rb +43 -30
- data/lib/vcr/structs.rb +209 -0
- data/lib/vcr/tasks/vcr.rake +9 -0
- data/lib/vcr/version.rb +1 -1
- data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
- data/spec/fixtures/cassette_spec/example.yml +79 -78
- data/spec/fixtures/cassette_spec/with_localhost_requests.yml +79 -77
- data/spec/fixtures/fake_example.com_responses.yml +78 -76
- data/spec/fixtures/match_requests_on.yml +147 -145
- data/spec/monkey_patches.rb +5 -5
- data/spec/support/http_library_adapters.rb +48 -0
- data/spec/support/shared_example_groups/hook_into_http_library.rb +53 -20
- data/spec/support/sinatra_app.rb +12 -0
- data/spec/vcr/cassette/http_interaction_list_spec.rb +1 -1
- data/spec/vcr/cassette/migrator_spec.rb +183 -0
- data/spec/vcr/cassette/serializers_spec.rb +122 -0
- data/spec/vcr/cassette_spec.rb +147 -83
- data/spec/vcr/configuration_spec.rb +11 -1
- data/spec/vcr/library_hooks/typhoeus_spec.rb +3 -3
- data/spec/vcr/library_hooks/webmock_spec.rb +7 -1
- data/spec/vcr/request_ignorer_spec.rb +1 -1
- data/spec/vcr/request_matcher_registry_spec.rb +46 -4
- data/spec/vcr/structs_spec.rb +309 -0
- data/spec/vcr_spec.rb +7 -0
- data/vcr.gemspec +9 -12
- metadata +75 -61
- data/lib/vcr/structs/http_interaction.rb +0 -58
- data/lib/vcr/structs/normalizers/body.rb +0 -24
- data/lib/vcr/structs/normalizers/header.rb +0 -64
- data/lib/vcr/structs/normalizers/status_message.rb +0 -17
- data/lib/vcr/structs/normalizers/uri.rb +0 -34
- data/lib/vcr/structs/request.rb +0 -13
- data/lib/vcr/structs/response.rb +0 -13
- data/lib/vcr/structs/response_status.rb +0 -5
- data/lib/vcr/util/yaml.rb +0 -11
- data/spec/support/shared_example_groups/normalizers.rb +0 -94
- data/spec/vcr/structs/http_interaction_spec.rb +0 -89
- data/spec/vcr/structs/request_spec.rb +0 -39
- data/spec/vcr/structs/response_spec.rb +0 -44
- data/spec/vcr/structs/response_status_spec.rb +0 -9
@@ -1,24 +0,0 @@
|
|
1
|
-
module VCR
|
2
|
-
module Normalizers
|
3
|
-
module Body
|
4
|
-
def initialize(*args)
|
5
|
-
super
|
6
|
-
normalize_body
|
7
|
-
end
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
def normalize_body
|
12
|
-
# Ensure that the body is a raw string, in case the string instance
|
13
|
-
# has been subclassed or extended with additional instance variables
|
14
|
-
# or attributes, so that it is serialized to YAML as a raw string.
|
15
|
-
# This is needed for rest-client. See this ticket for more info:
|
16
|
-
# http://github.com/myronmarston/vcr/issues/4
|
17
|
-
self.body = case body
|
18
|
-
when nil, ''; nil
|
19
|
-
else String.new(body)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
module VCR
|
2
|
-
module Normalizers
|
3
|
-
module Header
|
4
|
-
# These headers get added by the various HTTP clients automatically,
|
5
|
-
# and we don't care about them. We store the headers for the purposes
|
6
|
-
# of request matching, and we only care to match on headers users
|
7
|
-
# explicitly set.
|
8
|
-
HEADERS_TO_SKIP = {
|
9
|
-
'connection' => %w[ close Keep-Alive ],
|
10
|
-
'accept' => %w[ */* ],
|
11
|
-
'expect' => [''],
|
12
|
-
'user-agent' => ["Typhoeus - http://github.com/dbalatero/typhoeus/tree/master", 'Ruby']
|
13
|
-
}
|
14
|
-
|
15
|
-
def initialize(*args)
|
16
|
-
super
|
17
|
-
normalize_headers
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def important_header_values(k, values)
|
23
|
-
skip_values = HEADERS_TO_SKIP[k] || []
|
24
|
-
values - skip_values
|
25
|
-
end
|
26
|
-
|
27
|
-
def normalize_headers
|
28
|
-
new_headers = {}
|
29
|
-
|
30
|
-
headers.each do |k, v|
|
31
|
-
k = k.downcase
|
32
|
-
|
33
|
-
val_array = case v
|
34
|
-
when Array then v
|
35
|
-
when nil then []
|
36
|
-
else [v]
|
37
|
-
end
|
38
|
-
|
39
|
-
important_vals = important_header_values(k, val_array)
|
40
|
-
next unless important_vals.size > 0
|
41
|
-
|
42
|
-
new_headers[k] = convert_to_raw_strings(important_vals)
|
43
|
-
end if headers
|
44
|
-
|
45
|
-
self.headers = new_headers.empty? ? nil : new_headers
|
46
|
-
end
|
47
|
-
|
48
|
-
def convert_to_raw_strings(array)
|
49
|
-
# Ensure the values are raw strings.
|
50
|
-
# Apparently for Paperclip uploads to S3, headers
|
51
|
-
# get serialized with some extra stuff which leads
|
52
|
-
# to a seg fault. See this issue for more info:
|
53
|
-
# https://github.com/myronmarston/vcr/issues#issue/39
|
54
|
-
array.map do |v|
|
55
|
-
case v
|
56
|
-
when String; String.new(v)
|
57
|
-
when Array; convert_to_raw_strings(v)
|
58
|
-
else v
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module VCR
|
2
|
-
module Normalizers
|
3
|
-
module StatusMessage
|
4
|
-
def initialize(*args)
|
5
|
-
super
|
6
|
-
normalize_status_message
|
7
|
-
end
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
def normalize_status_message
|
12
|
-
self.message = message.strip if message
|
13
|
-
self.message = nil if message == ''
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module VCR
|
2
|
-
module Normalizers
|
3
|
-
module URI
|
4
|
-
DEFAULT_PORTS = {
|
5
|
-
'http' => 80,
|
6
|
-
'https' => 443
|
7
|
-
}
|
8
|
-
|
9
|
-
def initialize(*args)
|
10
|
-
super
|
11
|
-
normalize_uri
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def normalize_uri
|
17
|
-
u = begin
|
18
|
-
::URI.parse(uri)
|
19
|
-
rescue ::URI::InvalidURIError
|
20
|
-
return
|
21
|
-
end
|
22
|
-
|
23
|
-
u.port ||= DEFAULT_PORTS[u.scheme]
|
24
|
-
|
25
|
-
# URI#to_s only includes the port if it's not the default
|
26
|
-
# but we want to always include it (since FakeWeb/WebMock
|
27
|
-
# urls have always included it). We force it to be included
|
28
|
-
# here by redefining default_port so that URI#to_s will include it.
|
29
|
-
def u.default_port; nil; end
|
30
|
-
self.uri = u.to_s
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/lib/vcr/structs/request.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
module VCR
|
2
|
-
class Request < Struct.new(:method, :uri, :body, :headers)
|
3
|
-
include Normalizers::Header
|
4
|
-
include Normalizers::URI
|
5
|
-
include Normalizers::Body
|
6
|
-
|
7
|
-
@@object_method = Object.instance_method(:method)
|
8
|
-
def method(*args)
|
9
|
-
return super if args.empty?
|
10
|
-
@@object_method.bind(self).call(*args)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
data/lib/vcr/structs/response.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'vcr/structs/response_status'
|
2
|
-
|
3
|
-
module VCR
|
4
|
-
class Response < Struct.new(:status, :headers, :body, :http_version)
|
5
|
-
include Normalizers::Header
|
6
|
-
include Normalizers::Body
|
7
|
-
|
8
|
-
def update_content_length_header
|
9
|
-
headers['content-length'] &&= [body ? body.length.to_s : '0']
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
data/lib/vcr/util/yaml.rb
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
shared_examples_for "header normalization" do
|
2
|
-
let(:instance) do
|
3
|
-
with_headers('Some_Header' => 'value1', 'aNother' => ['a', 'b'], 'third' => [], 'fourth' => nil)
|
4
|
-
end
|
5
|
-
|
6
|
-
it 'normalizes the hash to lower case keys and arrays of values' do
|
7
|
-
instance.headers['some_header'].should eq(['value1'])
|
8
|
-
instance.headers['another'].should eq(['a', 'b'])
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'removes empty headers' do
|
12
|
-
instance.headers.should_not have_key('third')
|
13
|
-
instance.headers.should_not have_key('fourth')
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'filters out unimportant default values set by the HTTP library' do
|
17
|
-
instance = with_headers('accept' => ['*/*'], 'connection' => 'close', 'http-user' => ['foo'], 'expect' => ['', 'bar'])
|
18
|
-
instance.headers.should eq({ 'http-user' => ['foo'], 'expect' => ['bar'] })
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'sets empty hash header to nil' do
|
22
|
-
with_headers({}).headers.should be_nil
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'ensures header keys are serialized to yaml as raw strings' do
|
26
|
-
key = 'my-key'
|
27
|
-
key.instance_variable_set(:@foo, 7)
|
28
|
-
instance = with_headers(key => ['value1'])
|
29
|
-
VCR::YAML.dump(instance.headers).should eq(VCR::YAML.dump('my-key' => ['value1']))
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'ensures header values are serialized to yaml as raw strings' do
|
33
|
-
value = 'my-value'
|
34
|
-
value.instance_variable_set(:@foo, 7)
|
35
|
-
instance = with_headers('my-key' => [value])
|
36
|
-
VCR::YAML.dump(instance.headers).should eq(VCR::YAML.dump('my-key' => ['my-value']))
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'handles nested arrays' do
|
40
|
-
accept_encoding = [["gzip", "1.0"], ["deflate", "1.0"], ["sdch", "1.0"]]
|
41
|
-
instance = with_headers('accept-encoding' => accept_encoding)
|
42
|
-
instance.headers['accept-encoding'].should eq(accept_encoding)
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'handles nested arrays with floats' do
|
46
|
-
accept_encoding = [["gzip", 1.0], ["deflate", 1.0], ["sdch", 1.0]]
|
47
|
-
instance = with_headers('accept-encoding' => accept_encoding)
|
48
|
-
instance.headers['accept-encoding'].should eq(accept_encoding)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
shared_examples_for "body normalization" do
|
53
|
-
it 'sets empty string to nil' do
|
54
|
-
instance('').body.should be_nil
|
55
|
-
end
|
56
|
-
|
57
|
-
it "ensures the body is serialized to yaml as a raw string" do
|
58
|
-
body = "My String"
|
59
|
-
body.instance_variable_set(:@foo, 7)
|
60
|
-
VCR::YAML.dump(instance(body).body).should eq(VCR::YAML.dump("My String"))
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
shared_examples_for 'uri normalization' do
|
65
|
-
it 'adds port 80 to an http URI that lacks a port' do
|
66
|
-
instance('http://example.com/foo').uri.should eq('http://example.com:80/foo')
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'keeps the existing port for an http URI' do
|
70
|
-
instance('http://example.com:8000/foo').uri.should eq('http://example.com:8000/foo')
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'adds port 443 to an https URI that lacks a port' do
|
74
|
-
instance('https://example.com/foo').uri.should eq('https://example.com:443/foo')
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'keeps the existing port for an https URI' do
|
78
|
-
instance('https://example.com:8000/foo').uri.should eq('https://example.com:8000/foo')
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
shared_examples_for 'status message normalization' do
|
83
|
-
it 'chomps leading and trailing spaces on the status message' do
|
84
|
-
instance(' OK ').message.should eq('OK')
|
85
|
-
end
|
86
|
-
|
87
|
-
it 'sets status message to nil when it is the empty string' do
|
88
|
-
instance('').message.should be_nil
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'sets status message to nil when it is a blank string' do
|
92
|
-
instance(' ').message.should be_nil
|
93
|
-
end
|
94
|
-
end
|
@@ -1,89 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe VCR::HTTPInteraction do
|
4
|
-
%w( uri method ).each do |attr|
|
5
|
-
it "delegates :#{attr} to the request signature" do
|
6
|
-
sig = mock('request signature')
|
7
|
-
sig.should_receive(attr).and_return(:the_value)
|
8
|
-
instance = described_class.new(sig, nil)
|
9
|
-
instance.send(attr).should eq(:the_value)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
describe '#ignored?' do
|
14
|
-
it 'returns false by default' do
|
15
|
-
should_not be_ignored
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'returns true when #ignore! has been called' do
|
19
|
-
subject.ignore!
|
20
|
-
should be_ignored
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'does not include `@ignored` in the serialized output' do
|
25
|
-
subject.ignore!
|
26
|
-
VCR::YAML.dump(subject).should_not include('ignored')
|
27
|
-
end
|
28
|
-
|
29
|
-
describe '#filter!' do
|
30
|
-
let(:response_status) { VCR::ResponseStatus.new(200, "OK foo") }
|
31
|
-
let(:body) { "The body foo this is (foo-Foo)" }
|
32
|
-
let(:headers) do {
|
33
|
-
'x-http-foo' => ['bar23', '23foo'],
|
34
|
-
'x-http-bar' => ['foo23', '18']
|
35
|
-
} end
|
36
|
-
|
37
|
-
let(:response) do
|
38
|
-
VCR::Response.new(
|
39
|
-
response_status,
|
40
|
-
headers.dup,
|
41
|
-
body.dup,
|
42
|
-
'1.1'
|
43
|
-
)
|
44
|
-
end
|
45
|
-
|
46
|
-
let(:request) do
|
47
|
-
VCR::Request.new(
|
48
|
-
:get,
|
49
|
-
'http://example-foo.com:80/foo/',
|
50
|
-
body.dup,
|
51
|
-
headers.dup
|
52
|
-
)
|
53
|
-
end
|
54
|
-
|
55
|
-
let(:interaction) { VCR::HTTPInteraction.new(request, response) }
|
56
|
-
|
57
|
-
subject { interaction.filter!('foo', 'AAA') }
|
58
|
-
|
59
|
-
it 'does nothing when given a blank argument' do
|
60
|
-
expect {
|
61
|
-
interaction.filter!(nil, 'AAA')
|
62
|
-
interaction.filter!('foo', nil)
|
63
|
-
interaction.filter!("", 'AAA')
|
64
|
-
interaction.filter!('foo', "")
|
65
|
-
}.not_to change { interaction }
|
66
|
-
end
|
67
|
-
|
68
|
-
[:request, :response].each do |part|
|
69
|
-
it "replaces the sensitive text in the #{part} header keys and values" do
|
70
|
-
subject.send(part).headers.should eq({
|
71
|
-
'x-http-AAA' => ['bar23', '23AAA'],
|
72
|
-
'x-http-bar' => ['AAA23', '18']
|
73
|
-
})
|
74
|
-
end
|
75
|
-
|
76
|
-
it "replaces the sensitive text in the #{part} body" do
|
77
|
-
subject.send(part).body.should eq("The body AAA this is (AAA-Foo)")
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'replaces the sensitive text in the response status' do
|
82
|
-
subject.response.status.message.should eq('OK AAA')
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'replaces sensitive text in the request URI' do
|
86
|
-
subject.request.uri.should eq('http://example-AAA.com:80/AAA/')
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe VCR::Request do
|
4
|
-
describe '#method' do
|
5
|
-
subject { VCR::Request.new(:get) }
|
6
|
-
|
7
|
-
context 'when given no arguments' do
|
8
|
-
it 'returns the HTTP method' do
|
9
|
-
subject.method.should eq(:get)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
context 'when given an argument' do
|
14
|
-
it 'returns the method object for the named method' do
|
15
|
-
m = subject.method(:class)
|
16
|
-
m.should be_a(Method)
|
17
|
-
m.call.should eq(described_class)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
it_performs 'uri normalization' do
|
23
|
-
def instance(uri)
|
24
|
-
VCR::Request.new(:get, uri, '', {})
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
it_performs 'header normalization' do
|
29
|
-
def with_headers(headers)
|
30
|
-
described_class.new(:get, 'http://example.com/', nil, headers)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
it_performs 'body normalization' do
|
35
|
-
def instance(body)
|
36
|
-
described_class.new(:get, 'http://example.com/', body, {})
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe VCR::Response do
|
4
|
-
it_performs 'header normalization' do
|
5
|
-
def with_headers(headers)
|
6
|
-
described_class.new(:status, headers, nil, '1.1')
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
it_performs 'body normalization' do
|
11
|
-
def instance(body)
|
12
|
-
described_class.new(:status, {}, body, '1.1')
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
describe '#update_content_length_header' do
|
17
|
-
def instance(body, content_length = nil)
|
18
|
-
headers = { 'content-type' => 'text' }
|
19
|
-
headers.merge!('content-length' => content_length) if content_length
|
20
|
-
described_class.new(VCR::ResponseStatus.new, headers, body)
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'does nothing when the response lacks a content_length header' do
|
24
|
-
inst = instance('the body')
|
25
|
-
expect {
|
26
|
-
inst.update_content_length_header
|
27
|
-
}.not_to change { inst.headers['content-length'] }
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'sets the content_length header to the response body length when the header is present' do
|
31
|
-
inst = instance('the body', '3')
|
32
|
-
expect {
|
33
|
-
inst.update_content_length_header
|
34
|
-
}.to change { inst.headers['content-length'] }.from(['3']).to(['8'])
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'sets the content_length header to 0 if the response body is nil' do
|
38
|
-
inst = instance(nil, '3')
|
39
|
-
expect {
|
40
|
-
inst.update_content_length_header
|
41
|
-
}.to change { inst.headers['content-length'] }.from(['3']).to(['0'])
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|