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,6 +1,6 @@
1
1
  Feature: Cassette format
2
2
 
3
- VCR Cassettes are YAML files that contain all of the information
3
+ VCR Cassettes are files that contain all of the information
4
4
  about the requests and corresponding responses in a
5
5
  human-readable/editable format. A cassette contains an array
6
6
  of HTTP interactions, each of which has the following:
@@ -18,13 +18,43 @@ Feature: Cassette format
18
18
  - body
19
19
  - http version
20
20
 
21
- Scenario Outline: Request/Response data is saved to disk as YAML
22
- Given a file named "cassette_format.rb" with:
21
+ By default, VCR uses YAML to serialize this data. You can configure
22
+ VCR to use a different serializer, either on a cassette-by-cassette
23
+ basis, or as a default for all cassettes if you use the `default_cassette_options`.
24
+
25
+ VCR supports the following serializers out of the box:
26
+
27
+ - `:yaml`--Uses ruby's standard library YAML. This may use psych or syck,
28
+ depending on your ruby installation.
29
+ - `:syck`--Uses syck (the ruby 1.8 YAML engine). This is useful when using
30
+ VCR on a project that must run in environments where psych is not available
31
+ (such as on ruby 1.8), to ensure that syck is always used.
32
+ - `:psych`--Uses psych (the new ruby 1.9 YAML engine). This is useful when
33
+ you want to ensure that psych is always used.
34
+ - `:json`--Uses [multi_json](https://github.com/intridea/multi_json)
35
+ to serialize the cassette data as JSON.
36
+
37
+ You can also register a custom serializer using:
38
+
39
+ VCR.configure do |config|
40
+ config.cassette_serializers[:my_custom_serializer] = my_serializer
41
+ end
42
+
43
+ Your serializer must implement the following methods:
44
+
45
+ - `file_extension`
46
+ - `serialize(hash)`
47
+ - `deserialize(string)`
48
+
49
+ Scenario Outline: Request/Response data is saved to disk as YAML by default
50
+ Given a file named "cassette_yaml.rb" with:
23
51
  """ruby
24
52
  include_http_adapter_for("<http_lib>")
25
53
 
26
- start_sinatra_app(:port => 7777) do
27
- get('/:path') { ARGV[0] + ' ' + params[:path] }
54
+ if ARGV.any?
55
+ start_sinatra_app(:port => 7777) do
56
+ get('/:path') { ARGV[0] + ' ' + params[:path] }
57
+ end
28
58
  end
29
59
 
30
60
  require 'vcr'
@@ -39,44 +69,46 @@ Feature: Cassette format
39
69
  make_http_request(:get, "http://localhost:7777/bar")
40
70
  end
41
71
  """
42
- When I run `ruby cassette_format.rb 'Hello'`
72
+ When I run `ruby cassette_yaml.rb 'Hello'`
43
73
  Then the file "cassettes/example.yml" should contain YAML like:
44
74
  """
45
- ---
46
- - !ruby/struct:VCR::HTTPInteraction
47
- request: !ruby/struct:VCR::Request
48
- method: :get
75
+ ---
76
+ http_interactions:
77
+ - request:
78
+ method: get
49
79
  uri: http://localhost:7777/foo
50
- body:
51
- headers:
52
- response: !ruby/struct:VCR::Response
53
- status: !ruby/struct:VCR::ResponseStatus
80
+ body: ''
81
+ headers: {}
82
+ response:
83
+ status:
54
84
  code: 200
55
85
  message: OK
56
- headers:
57
- content-type:
86
+ headers:
87
+ Content-Type:
58
88
  - text/html;charset=utf-8
59
- content-length:
60
- - "9"
89
+ Content-Length:
90
+ - '9'
61
91
  body: Hello foo
62
- http_version: "1.1"
63
- - !ruby/struct:VCR::HTTPInteraction
64
- request: !ruby/struct:VCR::Request
65
- method: :get
92
+ http_version: '1.1'
93
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
94
+ - request:
95
+ method: get
66
96
  uri: http://localhost:7777/bar
67
- body:
68
- headers:
69
- response: !ruby/struct:VCR::Response
70
- status: !ruby/struct:VCR::ResponseStatus
97
+ body: ''
98
+ headers: {}
99
+ response:
100
+ status:
71
101
  code: 200
72
102
  message: OK
73
- headers:
74
- content-type:
103
+ headers:
104
+ Content-Type:
75
105
  - text/html;charset=utf-8
76
- content-length:
77
- - "9"
106
+ Content-Length:
107
+ - '9'
78
108
  body: Hello bar
79
- http_version: "1.1"
109
+ http_version: '1.1'
110
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
111
+ recorded_with: VCR 2.0.0
80
112
  """
81
113
 
82
114
  Examples:
@@ -92,3 +124,153 @@ Feature: Cassette format
92
124
  | c.hook_into :excon | excon |
93
125
  | | faraday (w/ net_http) |
94
126
 
127
+ Scenario: Request/Response data can be saved as JSON
128
+ Given a file named "cassette_json.rb" with:
129
+ """ruby
130
+ include_http_adapter_for("net/http")
131
+
132
+ start_sinatra_app(:port => 7777) do
133
+ get('/:path') { ARGV[0] + ' ' + params[:path] }
134
+ end
135
+
136
+ require 'vcr'
137
+
138
+ VCR.configure do |c|
139
+ c.hook_into :webmock
140
+ c.cassette_library_dir = 'cassettes'
141
+ end
142
+
143
+ VCR.use_cassette('example', :serialize_with => :json) do
144
+ puts response_body_for(:get, "http://localhost:7777/foo")
145
+ puts response_body_for(:get, "http://localhost:7777/bar")
146
+ end
147
+ """
148
+ When I run `ruby cassette_json.rb 'Hello'`
149
+ Then the file "cassettes/example.json" should contain JSON like:
150
+ """json
151
+ {
152
+ "http_interactions": [
153
+ {
154
+ "response": {
155
+ "body": "Hello foo",
156
+ "http_version": null,
157
+ "status": { "code": 200, "message": "OK" },
158
+ "headers": {
159
+ "Date": [ "Thu, 27 Oct 2011 06:16:31 GMT" ],
160
+ "Content-Type": [ "text/html;charset=utf-8" ],
161
+ "Content-Length": [ "9" ],
162
+ "Server": [ "WEBrick/1.3.1 (Ruby/1.8.7/2011-06-30)" ],
163
+ "Connection": [ "Keep-Alive" ]
164
+ }
165
+ },
166
+ "request": {
167
+ "uri": "http://localhost:7777/foo",
168
+ "body": "",
169
+ "method": "get",
170
+ "headers": { }
171
+ },
172
+ "recorded_at": "Tue, 01 Nov 2011 04:58:44 GMT"
173
+ },
174
+ {
175
+ "response": {
176
+ "body": "Hello bar",
177
+ "http_version": null,
178
+ "status": { "code": 200, "message": "OK" },
179
+ "headers": {
180
+ "Date": [ "Thu, 27 Oct 2011 06:16:31 GMT" ],
181
+ "Content-Type": [ "text/html;charset=utf-8" ],
182
+ "Content-Length": [ "9" ],
183
+ "Server": [ "WEBrick/1.3.1 (Ruby/1.8.7/2011-06-30)" ],
184
+ "Connection": [ "Keep-Alive" ]
185
+ }
186
+ },
187
+ "request": {
188
+ "uri": "http://localhost:7777/bar",
189
+ "body": "",
190
+ "method": "get",
191
+ "headers": { }
192
+ },
193
+ "recorded_at": "Tue, 01 Nov 2011 04:58:44 GMT"
194
+ }
195
+ ],
196
+ "recorded_with": "VCR 2.0.0"
197
+ }
198
+ """
199
+ When I run `ruby cassette_json.rb`
200
+ Then it should pass with:
201
+ """
202
+ Hello foo
203
+ Hello bar
204
+ """
205
+
206
+ Scenario: Request/Response data can be saved using a custom serializer
207
+ Given a file named "cassette_ruby.rb" with:
208
+ """ruby
209
+ include_http_adapter_for("net/http")
210
+
211
+ start_sinatra_app(:port => 7777) do
212
+ get('/:path') { ARGV[0] + ' ' + params[:path] }
213
+ end
214
+
215
+ require 'vcr'
216
+
217
+ # purely for demonstration purposes; obviously, don't actually
218
+ # use ruby #inspect / #eval for your serialization...
219
+ ruby_serializer = Object.new
220
+ class << ruby_serializer
221
+ def file_extension; "ruby"; end
222
+ def serialize(hash); hash.inspect; end
223
+ def deserialize(string); eval(string); end
224
+ end
225
+
226
+ VCR.configure do |c|
227
+ c.hook_into :webmock
228
+ c.cassette_library_dir = 'cassettes'
229
+ c.cassette_serializers[:ruby] = ruby_serializer
230
+ end
231
+
232
+ VCR.use_cassette('example', :serialize_with => :ruby) do
233
+ puts response_body_for(:get, "http://localhost:7777/foo")
234
+ puts response_body_for(:get, "http://localhost:7777/bar")
235
+ end
236
+ """
237
+ When I run `ruby cassette_ruby.rb 'Hello'`
238
+ Then the file "cassettes/example.ruby" should contain ruby like:
239
+ """
240
+ {"http_interactions"=>
241
+ [{"request"=>
242
+ {"method"=>"get",
243
+ "uri"=>"http://localhost:7777/foo",
244
+ "body"=>"",
245
+ "headers"=>{"Accept"=>["*/*"], "User-Agent"=>["Ruby"]}},
246
+ "response"=>
247
+ {"status"=>{"code"=>200, "message"=>"OK "},
248
+ "headers"=>
249
+ {"Content-Type"=>["text/html;charset=utf-8"],
250
+ "Content-Length"=>["9"],
251
+ "Connection"=>["Keep-Alive"]},
252
+ "body"=>"Hello foo",
253
+ "http_version"=>nil},
254
+ "recorded_at"=>"Tue, 01 Nov 2011 04:58:44 GMT"},
255
+ {"request"=>
256
+ {"method"=>"get",
257
+ "uri"=>"http://localhost:7777/bar",
258
+ "body"=>"",
259
+ "headers"=>{"Accept"=>["*/*"], "User-Agent"=>["Ruby"]}},
260
+ "response"=>
261
+ {"status"=>{"code"=>200, "message"=>"OK "},
262
+ "headers"=>
263
+ {"Content-Type"=>["text/html;charset=utf-8"],
264
+ "Content-Length"=>["9"],
265
+ "Connection"=>["Keep-Alive"]},
266
+ "body"=>"Hello bar",
267
+ "http_version"=>nil},
268
+ "recorded_at"=>"Tue, 01 Nov 2011 04:58:44 GMT"}],
269
+ "recorded_with"=>"VCR 2.0.0"}
270
+ """
271
+ When I run `ruby cassette_ruby.rb`
272
+ Then it should pass with:
273
+ """
274
+ Hello foo
275
+ Hello bar
276
+ """
@@ -1,7 +1,7 @@
1
1
  Feature: Update content_length header
2
2
 
3
3
  When the `:update_content_length_header` option is set to a truthy value,
4
- VCR will ensure that the `content-length` header will have the correct
4
+ VCR will ensure that the `Content-Length` header will have the correct
5
5
  value. This is useful in several situations:
6
6
 
7
7
  - When you manually edit the cassette file and change the resonse body
@@ -12,33 +12,35 @@ Feature: Update content_length header
12
12
  - Syck, the default YAML engine for ruby 1.8 (and 1.9, unless you compile
13
13
  it to use Psych), has a bug where it sometimes will remove some
14
14
  whitespace strings when you serialize them. This may cause the
15
- `content-length` header to have the wrong value.
15
+ `Content-Length` header to have the wrong value.
16
16
 
17
17
  This is especially important when you use a client that checks the
18
- `content-length` header. Mechanize, for example, will raise an `EOFError`
18
+ `Content-Length` header. Mechanize, for example, will raise an `EOFError`
19
19
  when the header value does not match the actual body length.
20
20
 
21
21
  Background:
22
22
  Given a previously recorded cassette file "cassettes/example.yml" with:
23
23
  """
24
- ---
25
- - !ruby/struct:VCR::HTTPInteraction
26
- request: !ruby/struct:VCR::Request
27
- method: :get
28
- uri: http://example.com:80/
29
- body:
30
- headers:
31
- response: !ruby/struct:VCR::Response
32
- status: !ruby/struct:VCR::ResponseStatus
24
+ ---
25
+ http_interactions:
26
+ - request:
27
+ method: get
28
+ uri: http://example.com/
29
+ body: ''
30
+ headers: {}
31
+ response:
32
+ status:
33
33
  code: 200
34
34
  message: OK
35
- headers:
36
- content-type:
35
+ headers:
36
+ Content-Type:
37
37
  - text/html;charset=utf-8
38
- content-length:
39
- - "11"
38
+ Content-Length:
39
+ - '11'
40
40
  body: Hello <modified>
41
- http_version: "1.1"
41
+ http_version: '1.1'
42
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
43
+ recorded_with: VCR 2.0.0
42
44
  """
43
45
  And a file named "common_stuff.rb" with:
44
46
  """ruby
@@ -52,7 +54,7 @@ Feature: Update content_length header
52
54
  def make_request_and_print_results
53
55
  response = Net::HTTP.get_response('example.com', '/')
54
56
  puts "Body length: #{response.body.length}"
55
- puts "Header value: #{response['content-length']}"
57
+ puts "Header value: #{response['Content-Length']}"
56
58
  end
57
59
  """
58
60
 
@@ -124,15 +124,15 @@ Feature: Filter sensitive data
124
124
  c.hook_into :webmock
125
125
  c.cassette_library_dir = 'cassettes'
126
126
  c.filter_sensitive_data('<PASSWORD>') do |interaction|
127
- USER_PASSWORDS[interaction.request.headers['x-http-username'].first]
127
+ USER_PASSWORDS[interaction.request.headers['X-Http-Username'].first]
128
128
  end
129
129
  end
130
130
 
131
131
  VCR.use_cassette('example', :match_requests_on => [:method, :uri, :headers]) do
132
132
  puts "Response: " + response_body_for(
133
133
  :get, 'http://localhost:7777/', nil,
134
- 'X-HTTP-USERNAME' => 'john.doe',
135
- 'X-HTTP-PASSWORD' => USER_PASSWORDS['john.doe']
134
+ 'X-Http-Username' => 'john.doe',
135
+ 'X-Http-Password' => USER_PASSWORDS['john.doe']
136
136
  )
137
137
  end
138
138
  """
@@ -141,7 +141,7 @@ Feature: Filter sensitive data
141
141
  And the file "cassettes/example.yml" should contain "body: john.doe/<PASSWORD>"
142
142
  And the file "cassettes/example.yml" should contain a YAML fragment like:
143
143
  """
144
- x-http-password:
144
+ X-Http-Password:
145
145
  - <PASSWORD>
146
146
  """
147
147
 
@@ -39,23 +39,25 @@ Feature: Hooks
39
39
  Given a previously recorded cassette file "cassettes/example.yml" with:
40
40
  """
41
41
  ---
42
- - !ruby/struct:VCR::HTTPInteraction
43
- request: !ruby/struct:VCR::Request
44
- method: :get
45
- uri: http://example.com:80/foo
46
- body:
47
- headers:
48
- response: !ruby/struct:VCR::Response
49
- status: !ruby/struct:VCR::ResponseStatus
42
+ http_interactions:
43
+ - request:
44
+ method: get
45
+ uri: http://example.com/foo
46
+ body: ''
47
+ headers: {}
48
+ response:
49
+ status:
50
50
  code: 200
51
51
  message: OK
52
52
  headers:
53
- content-type:
53
+ Content-Type:
54
54
  - text/html;charset=utf-8
55
- content-length:
56
- - "20"
55
+ Content-Length:
56
+ - '20'
57
57
  body: example.com response
58
- http_version: "1.1"
58
+ http_version: '1.1'
59
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
60
+ recorded_with: VCR 2.0.0
59
61
  """
60
62
 
61
63
  Scenario: Replace sensitive data with before_record hook
@@ -135,23 +137,25 @@ Feature: Hooks
135
137
  Given a previously recorded cassette file "cassettes/localhost.yml" with:
136
138
  """
137
139
  ---
138
- - !ruby/struct:VCR::HTTPInteraction
139
- request: !ruby/struct:VCR::Request
140
- method: :get
140
+ http_interactions:
141
+ - request:
142
+ method: get
141
143
  uri: http://localhost:7777/
142
- body:
143
- headers:
144
- response: !ruby/struct:VCR::Response
145
- status: !ruby/struct:VCR::ResponseStatus
144
+ body: ''
145
+ headers: {}
146
+ response:
147
+ status:
146
148
  code: 200
147
149
  message: OK
148
150
  headers:
149
- content-type:
151
+ Content-Type:
150
152
  - text/html;charset=utf-8
151
- content-length:
152
- - "20"
153
+ Content-Length:
154
+ - '20'
153
155
  body: recorded response
154
- http_version: "1.1"
156
+ http_version: '1.1'
157
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
158
+ recorded_with: VCR 2.0.0
155
159
  """
156
160
  And a file named "before_playback_ignore.rb" with:
157
161
  """ruby