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