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.
Files changed (88) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +3 -0
  3. data/CHANGELOG.md +37 -2
  4. data/Gemfile +2 -2
  5. data/README.md +10 -1
  6. data/Rakefile +43 -7
  7. data/Upgrade.md +45 -0
  8. data/features/.nav +1 -0
  9. data/features/cassettes/automatic_re_recording.feature +19 -17
  10. data/features/cassettes/dynamic_erb.feature +32 -28
  11. data/features/cassettes/exclusive.feature +28 -24
  12. data/features/cassettes/format.feature +213 -31
  13. data/features/cassettes/update_content_length_header.feature +20 -18
  14. data/features/configuration/filter_sensitive_data.feature +4 -4
  15. data/features/configuration/hooks.feature +27 -23
  16. data/features/http_libraries/em_http_request.feature +79 -75
  17. data/features/record_modes/all.feature +14 -14
  18. data/features/record_modes/new_episodes.feature +15 -15
  19. data/features/record_modes/none.feature +15 -15
  20. data/features/record_modes/once.feature +15 -15
  21. data/features/request_matching/body.feature +25 -23
  22. data/features/request_matching/custom_matcher.feature +25 -23
  23. data/features/request_matching/headers.feature +32 -36
  24. data/features/request_matching/host.feature +27 -25
  25. data/features/request_matching/identical_request_sequence.feature +27 -25
  26. data/features/request_matching/method.feature +27 -25
  27. data/features/request_matching/path.feature +27 -25
  28. data/features/request_matching/playback_repeats.feature +27 -25
  29. data/features/request_matching/uri.feature +27 -25
  30. data/features/request_matching/uri_without_param.feature +28 -26
  31. data/features/step_definitions/cli_steps.rb +71 -17
  32. data/features/support/env.rb +3 -1
  33. data/features/support/http_lib_filters.rb +6 -3
  34. data/features/support/vcr_cucumber_helpers.rb +4 -2
  35. data/lib/vcr.rb +6 -2
  36. data/lib/vcr/cassette.rb +75 -51
  37. data/lib/vcr/cassette/migrator.rb +111 -0
  38. data/lib/vcr/cassette/serializers.rb +35 -0
  39. data/lib/vcr/cassette/serializers/json.rb +23 -0
  40. data/lib/vcr/cassette/serializers/psych.rb +24 -0
  41. data/lib/vcr/cassette/serializers/syck.rb +35 -0
  42. data/lib/vcr/cassette/serializers/yaml.rb +24 -0
  43. data/lib/vcr/configuration.rb +6 -1
  44. data/lib/vcr/errors.rb +1 -1
  45. data/lib/vcr/library_hooks/excon.rb +1 -7
  46. data/lib/vcr/library_hooks/typhoeus.rb +6 -22
  47. data/lib/vcr/library_hooks/webmock.rb +1 -1
  48. data/lib/vcr/middleware/faraday.rb +1 -1
  49. data/lib/vcr/request_matcher_registry.rb +43 -30
  50. data/lib/vcr/structs.rb +209 -0
  51. data/lib/vcr/tasks/vcr.rake +9 -0
  52. data/lib/vcr/version.rb +1 -1
  53. data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
  54. data/spec/fixtures/cassette_spec/example.yml +79 -78
  55. data/spec/fixtures/cassette_spec/with_localhost_requests.yml +79 -77
  56. data/spec/fixtures/fake_example.com_responses.yml +78 -76
  57. data/spec/fixtures/match_requests_on.yml +147 -145
  58. data/spec/monkey_patches.rb +5 -5
  59. data/spec/support/http_library_adapters.rb +48 -0
  60. data/spec/support/shared_example_groups/hook_into_http_library.rb +53 -20
  61. data/spec/support/sinatra_app.rb +12 -0
  62. data/spec/vcr/cassette/http_interaction_list_spec.rb +1 -1
  63. data/spec/vcr/cassette/migrator_spec.rb +183 -0
  64. data/spec/vcr/cassette/serializers_spec.rb +122 -0
  65. data/spec/vcr/cassette_spec.rb +147 -83
  66. data/spec/vcr/configuration_spec.rb +11 -1
  67. data/spec/vcr/library_hooks/typhoeus_spec.rb +3 -3
  68. data/spec/vcr/library_hooks/webmock_spec.rb +7 -1
  69. data/spec/vcr/request_ignorer_spec.rb +1 -1
  70. data/spec/vcr/request_matcher_registry_spec.rb +46 -4
  71. data/spec/vcr/structs_spec.rb +309 -0
  72. data/spec/vcr_spec.rb +7 -0
  73. data/vcr.gemspec +9 -12
  74. metadata +75 -61
  75. data/lib/vcr/structs/http_interaction.rb +0 -58
  76. data/lib/vcr/structs/normalizers/body.rb +0 -24
  77. data/lib/vcr/structs/normalizers/header.rb +0 -64
  78. data/lib/vcr/structs/normalizers/status_message.rb +0 -17
  79. data/lib/vcr/structs/normalizers/uri.rb +0 -34
  80. data/lib/vcr/structs/request.rb +0 -13
  81. data/lib/vcr/structs/response.rb +0 -13
  82. data/lib/vcr/structs/response_status.rb +0 -5
  83. data/lib/vcr/util/yaml.rb +0 -11
  84. data/spec/support/shared_example_groups/normalizers.rb +0 -94
  85. data/spec/vcr/structs/http_interaction_spec.rb +0 -89
  86. data/spec/vcr/structs/request_spec.rb +0 -39
  87. data/spec/vcr/structs/response_spec.rb +0 -44
  88. 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
@@ -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
@@ -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
-
@@ -1,5 +0,0 @@
1
- module VCR
2
- class ResponseStatus < Struct.new(:code, :message)
3
- include Normalizers::StatusMessage
4
- end
5
- end
data/lib/vcr/util/yaml.rb DELETED
@@ -1,11 +0,0 @@
1
- require 'yaml'
2
-
3
- module VCR
4
- # Attempt to use psych if it is available.
5
- YAML = begin
6
- require 'psych'
7
- Psych
8
- rescue LoadError
9
- ::YAML
10
- end
11
- end
@@ -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