vcr 2.0.0.beta1 → 2.0.0.beta2
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/.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
|