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