]
#
# Uploads a file to the Amazon S3 service.
# Outputs the URL for the newly uploaded file.
#
# Requirements:
# - AMAZON_ACCESS_KEY_ID
# - AMAZON_SECRET_ACCESS_KEY
# - openssl
# - curl
#
# Author: Mislav Marohnić
set -e
authorization() {
local signature="$(string_to_sign | hmac_sha1 | base64)"
echo "AWS ${AMAZON_ACCESS_KEY_ID?}:${signature}"
}
hmac_sha1() {
openssl dgst -binary -sha1 -hmac "${AMAZON_SECRET_ACCESS_KEY?}"
}
base64() {
openssl enc -base64
}
bin_md5() {
openssl dgst -binary -md5
}
string_to_sign() {
echo "$http_method"
echo "$content_md5"
echo "$content_type"
echo "$date"
echo "x-amz-acl:$acl"
printf "/$bucket/$remote_path"
}
date_string() {
LC_TIME=C date "+%a, %d %h %Y %T %z"
}
file="$1"
bucket="${2%%:*}"
remote_path="${2#*:}"
content_type="$3"
if [ -z "$remote_path" ] || [ "$remote_path" = "$bucket" ]; then
remote_path="${file##*/}"
fi
http_method=PUT
acl="public-read"
content_md5="$(bin_md5 < "$file" | base64)"
date="$(date_string)"
url="https://$bucket.s3.amazonaws.com/$remote_path"
curl -qsSf -T "$file" \
-H "Authorization: $(authorization)" \
-H "x-amz-acl: $acl" \
-H "Date: $date" \
-H "Content-MD5: $content_md5" \
-H "Content-Type: $content_type" \
"$url"
echo "$url"
vcr-5.0.0/script/test 0000775 0000000 0000000 00000001317 13473056530 0014527 0 ustar 00root root 0000000 0000000 #!/bin/bash
# Usage: script/test [FILES...]
set -e
filter() {
local prefix="$1"
shift 1
ls -d "$@" | sed "s:$PWD/::" | grep "$prefix"
}
color() {
if [ -t 1 ]; then
printf "\e[%sm%s\e[m" "$1" "$2"
else
echo -n "$2"
fi
}
run() {
color "1;34" "> $*"; echo
bundle exec "$@"
}
if [ $# -eq 0 ]; then
specs=( spec/ )
cukes=( features/ )
else
specs=( $(filter ^spec "$@" || true) )
cukes=( $(filter ^features "$@" 2>/dev/null || true) )
if [ "${#specs[@]}" -eq 0 ] && [ "${#cukes[@]}" -eq 0 ]; then
echo "Error: nothing to test." >&2
exit 1
fi
fi
if [ "${#specs[@]}" -gt 0 ]; then
run rspec "${specs[@]}"
fi
if [ "${#cukes[@]}" -gt 0 ]; then
run cucumber "${cukes[@]}"
fi
vcr-5.0.0/spec/ 0000775 0000000 0000000 00000000000 13473056530 0013246 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/acceptance/ 0000775 0000000 0000000 00000000000 13473056530 0015334 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/acceptance/concurrency_spec.rb 0000664 0000000 0000000 00000003001 13473056530 0021217 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe VCR do
def recorded_content_for(name)
VCR.cassette_persisters[:file_system]["#{name}.yml"].to_s
end
context 'when used in a multithreaded environment with an around_http_request', :with_monkey_patches => :excon do
def preload_yaml_serializer_to_avoid_circular_require_warning_race_condition
VCR.cassette_serializers[:yaml]
end
before { preload_yaml_serializer_to_avoid_circular_require_warning_race_condition }
it 'can use a cassette in an #around_http_request hook' do
VCR.configure do |vcr|
vcr.around_http_request do |req|
VCR.use_cassette(req.parsed_uri.path, &req)
end
end
threads = 50.times.map do
Thread.start do
Excon.get "http://localhost:#{VCR::SinatraApp.port}/search?q=thread"
end
end
Excon.get "http://localhost:#{VCR::SinatraApp.port}/foo"
threads.each(&:join)
expect(recorded_content_for("search") +
recorded_content_for("foo")).to include("query: thread", "FOO!")
end
end
context 'when used in a multithreaded environment with a cassette', :with_monkey_patches => :excon do
it 'properly stubs threaded requests' do
VCR.use_cassette('/foo') do
threads = 50.times.map do
Thread.start do
Excon.get "http://localhost:#{VCR::SinatraApp.port}/foo"
end
end
threads.each(&:join)
end
expect(
recorded_content_for("foo")).to include("FOO!")
end
end
end
vcr-5.0.0/spec/acceptance/threading_spec.rb 0000664 0000000 0000000 00000002020 13473056530 0020632 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe VCR do
context 'when used in a multithreaded environment', :with_monkey_patches => :excon do
def preload_yaml_serializer_to_avoid_circular_require_warning_race_condition
VCR.cassette_serializers[:yaml]
end
before { preload_yaml_serializer_to_avoid_circular_require_warning_race_condition }
def recorded_content_for(name)
VCR.cassette_persisters[:file_system]["#{name}.yml"].to_s
end
it 'can use a cassette in an #around_http_request hook' do
VCR.configure do |vcr|
vcr.around_http_request do |req|
VCR.use_cassette(req.parsed_uri.path, &req)
end
end
thread = Thread.start do
Excon.get "http://localhost:#{VCR::SinatraApp.port}/search?q=thread"
end
Excon.get "http://localhost:#{VCR::SinatraApp.port}/foo",
:response_block => Proc.new { thread.join }
expect(recorded_content_for("search") +
recorded_content_for("foo")).to include("query: thread", "FOO!")
end
end
end
vcr-5.0.0/spec/fixtures/ 0000775 0000000 0000000 00000000000 13473056530 0015117 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/fixtures/cassette_spec/ 0000775 0000000 0000000 00000000000 13473056530 0017744 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/fixtures/cassette_spec/1_x_cassette.yml 0000664 0000000 0000000 00000005450 13473056530 0023055 0 ustar 00root root 0000000 0000000 ---
- !ruby/struct:VCR::HTTPInteraction
request: !ruby/struct:VCR::Request
method: :get
uri: http://example.com:80/
body:
headers:
response: !ruby/struct:VCR::Response
status: !ruby/struct:VCR::ResponseStatus
code: 200
message: OK
headers:
last-modified:
- Tue, 15 Nov 2005 13:24:10 GMT
connection:
- Keep-Alive
date:
- Thu, 25 Feb 2010 07:52:38 GMT
content-type:
- text/html; charset=UTF-8
etag:
- "\"24ec5-1b6-4059a80bfd280\""
server:
- Apache/2.2.3 (CentOS)
content-length:
- "438"
age:
- "3009"
accept-ranges:
- bytes
body: |
Example Web Page
You have reached this web page by typing "example.com",
"example.net",
or "example.org" into your web browser.
These domain names are reserved for use in documentation and are not available
for registration. See RFC
2606, Section 3.
http_version: "1.1"
- !ruby/struct:VCR::HTTPInteraction
request: !ruby/struct:VCR::Request
method: :get
uri: http://example.com:80/foo
body:
headers:
response: !ruby/struct:VCR::Response
status: !ruby/struct:VCR::ResponseStatus
code: 404
message: Not Found
headers:
connection:
- close
content-type:
- text/html; charset=iso-8859-1
date:
- Thu, 25 Feb 2010 07:52:38 GMT
server:
- Apache/2.2.3 (CentOS)
content-length:
- "277"
body: |
404 Not Found
Not Found
The requested URL /foo was not found on this server.
Apache/2.2.3 (CentOS) Server at example.com Port 80
http_version: "1.1"
- !ruby/struct:VCR::HTTPInteraction
request: !ruby/struct:VCR::Request
method: :get
uri: http://example.com:80/
body:
headers:
response: !ruby/struct:VCR::Response
status: !ruby/struct:VCR::ResponseStatus
code: 200
message: OK
headers:
last-modified:
- Tue, 15 Nov 2005 13:24:10 GMT
connection:
- Keep-Alive
date:
- Thu, 25 Feb 2010 07:52:38 GMT
content-type:
- text/html; charset=UTF-8
etag:
- "\"24ec5-1b6-4059a80bfd280\""
server:
- Apache/2.2.3 (CentOS)
content-length:
- "438"
age:
- "3009"
accept-ranges:
- bytes
body: Another example.com response
http_version: "1.1"
vcr-5.0.0/spec/fixtures/cassette_spec/empty.yml 0000664 0000000 0000000 00000000000 13473056530 0021613 0 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/fixtures/cassette_spec/example.yml 0000664 0000000 0000000 00000005055 13473056530 0022127 0 ustar 00root root 0000000 0000000 ---
http_interactions:
- request:
method: get
uri: http://example.com/
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Last-Modified:
- Tue, 15 Nov 2005 13:24:10 GMT
Connection:
- Keep-Alive
Date:
- Thu, 25 Feb 2010 07:52:38 GMT
Content-Type:
- text/html; charset=UTF-8
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
Server:
- Apache/2.2.3 (CentOS)
Content-Length:
- '438'
Age:
- '3009'
Accept-Ranges:
- bytes
body: ! "\n\n Example Web Page\n \n \nYou
have reached this web page by typing "example.com",\n"example.net",\n
\ or "example.org" into your web browser.
\nThese domain names
are reserved for use in documentation and are not available \n for registration.
See RFC \n 2606,
Section 3.
\n\n\n"
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: get
uri: http://example.com/foo
body: ''
headers: {}
response:
status:
code: 404
message: Not Found
headers:
Connection:
- close
Content-Type:
- text/html; charset=iso-8859-1
Date:
- Thu, 25 Feb 2010 07:52:38 GMT
Server:
- Apache/2.2.3 (CentOS)
Content-Length:
- '277'
body: ! '
404 Not Found
Not Found
The requested URL /foo was not found on this server.
Apache/2.2.3 (CentOS) Server at example.com Port 80
'
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: get
uri: http://example.com/
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Last-Modified:
- Tue, 15 Nov 2005 13:24:10 GMT
Connection:
- Keep-Alive
Date:
- Thu, 25 Feb 2010 07:52:38 GMT
Content-Type:
- text/html; charset=UTF-8
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
Server:
- Apache/2.2.3 (CentOS)
Content-Length:
- '438'
Age:
- '3009'
Accept-Ranges:
- bytes
body: Another example.com response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
recorded_with: VCR 2.0.0
vcr-5.0.0/spec/fixtures/cassette_spec/with_localhost_requests.yml 0000664 0000000 0000000 00000004365 13473056530 0025455 0 ustar 00root root 0000000 0000000 ---
http_interactions:
- request:
method: get
uri: http://localhost/
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
Last-Modified:
- Tue, 15 Nov 2005 13:24:10 GMT
Connection:
- Keep-Alive
Content-Type:
- text/html; charset=UTF-8
Date:
- Thu, 25 Feb 2010 07:53:51 GMT
Server:
- Apache/2.2.3 (CentOS)
Content-Length:
- '438'
Age:
- '9260'
Accept-Ranges:
- bytes
body: Localhost response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: get
uri: http://127.0.0.1/
body: ''
headers: {}
response:
status:
code: 404
message: Not Found
headers:
Content-Type:
- text/html; charset=iso-8859-1
Connection:
- close
Server:
- Apache/2.2.3 (CentOS)
Date:
- Thu, 25 Feb 2010 07:53:52 GMT
Content-Length:
- '277'
body: 127.0.0.1 response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: get
uri: http://0.0.0.0/
body: ''
headers: {}
response:
status:
code: 404
message: Not Found
headers:
Content-Type:
- text/html; charset=iso-8859-1
Connection:
- close
Server:
- Apache/2.2.3 (CentOS)
Date:
- Thu, 25 Feb 2010 07:53:52 GMT
Content-Length:
- '277'
body: 127.0.0.1 response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: get
uri: http://example.com/
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
Last-Modified:
- Tue, 15 Nov 2005 13:24:10 GMT
Connection:
- Keep-Alive
Content-Type:
- text/html; charset=UTF-8
Date:
- Thu, 25 Feb 2010 07:53:51 GMT
Server:
- Apache/2.2.3 (CentOS)
Content-Length:
- '438'
Age:
- '9260'
Accept-Ranges:
- bytes
body: example.com response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
recorded_with: VCR 1.11.3
vcr-5.0.0/spec/fixtures/fake_example_responses.yml 0000664 0000000 0000000 00000004476 13473056530 0022377 0 ustar 00root root 0000000 0000000 ---
http_interactions:
- request:
method: get
uri: http://example.com/foo
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Last-Modified:
- Tue, 15 Nov 2005 13:24:10 GMT
Connection:
- Keep-Alive
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
Content-Type:
- text/html; charset=UTF-8
Date:
- Wed, 31 Mar 2010 02:43:23 GMT
Server:
- Apache/2.2.3 (CentOS)
Content-Length:
- '438'
Age:
- '3285'
Accept-Ranges:
- bytes
body: example.com get response 1 with path=foo
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: get
uri: http://example.com/foo
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Last-Modified:
- Tue, 15 Nov 2005 13:24:10 GMT
Connection:
- Keep-Alive
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
Content-Type:
- text/html; charset=UTF-8
Date:
- Wed, 31 Mar 2010 02:43:23 GMT
Server:
- Apache/2.2.3 (CentOS)
Content-Length:
- '438'
Age:
- '3285'
Accept-Ranges:
- bytes
body: example.com get response 2 with path=foo
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example.com/
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Last-Modified:
- Tue, 15 Nov 2005 13:24:10 GMT
Connection:
- close
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
Content-Type:
- text/html; charset=UTF-8
Date:
- Wed, 31 Mar 2010 02:43:26 GMT
Server:
- Apache/2.2.3 (CentOS)
Content-Length:
- '438'
Accept-Ranges:
- bytes
body: example.com post response with id=3
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: get
uri: http://example.com/two_set_cookie_headers
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Set-Cookie:
- bar=bazz
- foo=bar
body: this respons has two set-cookie headers
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
recorded_with: VCR 1.11.3
vcr-5.0.0/spec/fixtures/match_requests_on.yml 0000664 0000000 0000000 00000007371 13473056530 0021375 0 ustar 00root root 0000000 0000000 ---
http_interactions:
- request:
method: post
uri: http://example.com/method
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: post method response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: get
uri: http://example.com/method
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: get method response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example1.com/host
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: example1.com host response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example2.com/host
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: example2.com host response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example.com/path1
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: path1 response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example.com/path2
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: path2 response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example.com/uri1
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: uri1 response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example.com/uri2
body: ''
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: uri2 response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example.com/
body: param=val1
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: val1 body response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example.com/
body: param=val2
headers: {}
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: val2 body response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example.com/
body: ''
headers:
X-Http-Header1:
- val1
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: val1 header response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
- request:
method: post
uri: http://example.com/
body: ''
headers:
X-Http-Header1:
- val2
response:
status:
code: 200
message: OK
headers:
Etag:
- ! '"24ec5-1b6-4059a80bfd280"'
body: val2 header response
http_version: '1.1'
recorded_at: Tue, 01 Nov 2011 04:49:54 GMT
recorded_with: VCR 1.11.3
vcr-5.0.0/spec/lib/ 0000775 0000000 0000000 00000000000 13473056530 0014014 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/lib/vcr/ 0000775 0000000 0000000 00000000000 13473056530 0014606 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/lib/vcr/cassette/ 0000775 0000000 0000000 00000000000 13473056530 0016421 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/lib/vcr/cassette/erb_renderer_spec.rb 0000664 0000000 0000000 00000003624 13473056530 0022423 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe VCR::Cassette::ERBRenderer do
describe '#render' do
def render(*args)
described_class.new(*args).render
end
let(:no_vars_content) { '<%= 3 + 4 %>. Some ERB' }
let(:vars_content) { '<%= var1 %>. ERB with Vars! <%= var2 %>' }
context 'when ERB is disabled' do
it 'returns the given template' do
expect(render(no_vars_content, false)).to eq(no_vars_content)
expect(render(no_vars_content, nil)).to eq(no_vars_content)
end
end
context 'when ERB is enabled but no variables are passed' do
it 'renders the file content as ERB' do
expect(render(no_vars_content, true)).to eq("7. Some ERB")
end
it 'raises an appropriate error when the ERB template needs variables' do
expect {
render(vars_content, true, "vars")
}.to raise_error(VCR::Errors::MissingERBVariableError,
%{The ERB in the vars cassette file references undefined variable var1. } +
%{Pass it to the cassette using :erb => #{ {:var1=>"some value"}.inspect }.}
)
end
it 'gracefully handles the template being nil' do
expect(render(nil, true)).to be_nil
end
end
context 'when ERB is enabled and variables are passed' do
it 'renders the file content as ERB with the passed variables' do
expect(render(vars_content, :var1 => 'foo', :var2 => 'bar')).to eq('foo. ERB with Vars! bar')
end
it 'raises an appropriate error when one or more of the needed variables are not passed' do
expect {
render(vars_content, { :var1 => 'foo' }, "vars")
}.to raise_error(VCR::Errors::MissingERBVariableError,
%{The ERB in the vars cassette file references undefined variable var2. } +
%{Pass it to the cassette using :erb => #{ {:var1 => "foo", :var2 => "some value"}.inspect }.}
)
end
end
end
end
vcr-5.0.0/spec/lib/vcr/cassette/http_interaction_list_spec.rb 0000664 0000000 0000000 00000025535 13473056530 0024403 0 ustar 00root root 0000000 0000000 require 'vcr/util/logger'
require 'vcr/cassette/http_interaction_list'
require 'vcr/request_matcher_registry'
require 'vcr/structs'
require 'support/configuration_stubbing'
module VCR
class Cassette
describe HTTPInteractionList do
include_context "configuration stubbing"
::RSpec::Matchers.define :respond_with do |expected|
match { |a| expected.nil? ? a.nil? : a.body == expected }
end
before(:each) do
allow(VCR).to receive(:request_matchers).and_return(VCR::RequestMatcherRegistry.new)
allow(config).to receive(:logger).and_return(double.as_null_object)
end
def request_with(values)
VCR::Request.new.tap do |request|
values.each do |name, value|
request.send("#{name}=", value)
end
end
end
def response(body)
VCR::Response.new.tap do |r|
r.body = body
r.status = VCR::ResponseStatus.new(200)
end
end
def interaction(body, request_values)
VCR::HTTPInteraction.new \
request_with(request_values),
response(body)
end
let(:original_list_array) do [
interaction('put response', :method => :put),
interaction('post response 1', :method => :post),
interaction('post response 2', :method => :post)
] end
let(:allow_playback_repeats) { false } # the default
let(:list) { HTTPInteractionList.new(original_list_array, [:method], allow_playback_repeats) }
describe "#has_used_interaction_matching?" do
it 'returns false when no interactions have been used' do
expect(list).not_to have_used_interaction_matching(request_with(:method => :put))
end
it 'returns true when there is a matching used interaction (even if there is also an unused one that matches)' do
list.response_for(request_with(:method => :post))
expect(list).to have_used_interaction_matching(request_with(:method => :post))
end
it 'returns false when none of the used interactions match' do
list.response_for(request_with(:method => :put))
expect(list).not_to have_used_interaction_matching(request_with(:method => :post))
end
end
describe "#remaining_unused_interaction_count" do
it 'returns the number of unused interactions' do
expect(list.remaining_unused_interaction_count).to eq(3)
list.response_for(request_with(:method => :get))
expect(list.remaining_unused_interaction_count).to eq(3)
list.response_for(request_with(:method => :put))
expect(list.remaining_unused_interaction_count).to eq(2)
list.response_for(request_with(:method => :put))
expect(list.remaining_unused_interaction_count).to eq(2)
list.response_for(request_with(:method => :post))
expect(list.remaining_unused_interaction_count).to eq(1)
list.response_for(request_with(:method => :post))
expect(list.remaining_unused_interaction_count).to eq(0)
list.response_for(request_with(:method => :post))
expect(list.remaining_unused_interaction_count).to eq(0)
end
end
describe "#assert_no_unused_interactions?" do
it 'should raise a SkippedHTTPRequestError when there are unused interactions left' do
expect {
list.assert_no_unused_interactions!
}.to raise_error(Errors::UnusedHTTPInteractionError)
list.response_for(request_with(:method => :put))
expect {
list.assert_no_unused_interactions!
}.to raise_error(Errors::UnusedHTTPInteractionError)
end
it 'should raise nothing when there are no unused interactions left' do
[:put, :post, :post].each do |method|
list.response_for(request_with(:method => method))
end
expect {
list.assert_no_unused_interactions!
}.not_to raise_error
end
context 'when the null logger is in use' do
before { allow(config).to receive(:logger).and_return(Logger::Null) }
it 'includes formatted request details in the error message' do
expect {
list.assert_no_unused_interactions!
}.to raise_error(/\[put/)
end
it 'includes formatted response details in the error message' do
expect {
list.assert_no_unused_interactions!
}.to raise_error(/\[200 "put response"\]/)
end
end
end
describe "has_interaction_matching?" do
it 'returns false when the list is empty' do
expect(HTTPInteractionList.new([], [:method])).not_to have_interaction_matching(double)
end
it 'returns false when there is no matching interaction' do
expect(list).not_to have_interaction_matching(request_with(:method => :get))
end
it 'returns true when there is a matching interaction' do
expect(list).to have_interaction_matching(request_with(:method => :post))
end
it 'does not consume the interactions when they match' do
expect(list).to have_interaction_matching(request_with(:method => :post))
expect(list.remaining_unused_interaction_count).to eq(3)
expect(list).to have_interaction_matching(request_with(:method => :post))
expect(list.remaining_unused_interaction_count).to eq(3)
end
it 'invokes each matcher block to find the matching interaction' do
VCR.request_matchers.register(:foo) { |r1, r2| true }
VCR.request_matchers.register(:bar) { |r1, r2| true }
calls = 0
VCR.request_matchers.register(:baz) { |r1, r2| calls += 1; calls == 2 }
list = HTTPInteractionList.new([
interaction('response', :method => :put)
], [:foo, :bar, :baz])
expect(list).not_to have_interaction_matching(request_with(:method => :post))
expect(list).to have_interaction_matching(request_with(:method => :post))
end
it "delegates to the parent list when it can't find a matching interaction" do
parent_list = double(:has_interaction_matching? => true)
expect(HTTPInteractionList.new( [], [:method], false, parent_list)).to have_interaction_matching(double)
parent_list = double(:has_interaction_matching? => false)
expect(HTTPInteractionList.new( [], [:method], false, parent_list)).not_to have_interaction_matching(double)
end
context 'when allow_playback_repeats is set to true' do
let(:allow_playback_repeats) { true }
it 'considers used interactions' do
list.response_for(request_with(:method => :put))
results = 10.times.map do
list.has_interaction_matching?(request_with(:method => :put))
end
expect(results).to eq([true] * 10)
end
end
context 'when allow_playback_repeats is set to false' do
let(:allow_playback_repeats) { false }
it 'does not consider used interactions' do
list.response_for(request_with(:method => :put))
result = 10.times.map do
list.has_interaction_matching?(request_with(:method => :put))
end
expect(result).to eq([false] * 10)
end
end
end
describe "#response_for" do
it 'returns nil when the list is empty' do
expect(HTTPInteractionList.new([], [:method]).response_for(double)).to respond_with(nil)
end
it 'returns nil when there is no matching interaction' do
response = HTTPInteractionList.new([
interaction('foo', :method => :post),
interaction('foo', :method => :put)
], [:method]).response_for(
request_with(:method => :get)
)
expect(response).to respond_with(nil)
end
it 'returns the first matching interaction' do
list = HTTPInteractionList.new([
interaction('put response', :method => :put),
interaction('post response 1', :method => :post),
interaction('post response 2', :method => :post)
], [:method])
expect(list.response_for(request_with(:method => :post))).to respond_with("post response 1")
end
it 'invokes each matcher block to find the matching interaction' do
VCR.request_matchers.register(:foo) { |r1, r2| true }
VCR.request_matchers.register(:bar) { |r1, r2| true }
calls = 0
VCR.request_matchers.register(:baz) { |r1, r2| calls += 1; calls == 2 }
list = HTTPInteractionList.new([
interaction('response', :method => :put)
], [:foo, :bar, :baz])
expect(list.response_for(request_with(:method => :post))).to respond_with(nil)
expect(list.response_for(request_with(:method => :post))).to respond_with('response')
end
it "delegates to the parent list when it can't find a matching interaction" do
parent_list = double(:response_for => response('parent'))
result = HTTPInteractionList.new(
[], [:method], false, parent_list
).response_for(double)
expect(result).to respond_with('parent')
end
it 'consumes the first matching interaction so that it will not be used again' do
expect(list.response_for(request_with(:method => :post)).body).to eq("post response 1")
expect(list.response_for(request_with(:method => :post)).body).to eq("post response 2")
end
context 'when allow_playback_repeats is set to true' do
let(:allow_playback_repeats) { true }
it 'continues to return the response from the last matching interaction when there are no more' do
list.response_for(request_with(:method => :post))
results = 10.times.map do
response = list.response_for(request_with(:method => :post))
response ? response.body : nil
end
expect(results).to eq(["post response 2"] * 10)
end
end
context 'when allow_playback_repeats is set to false' do
let(:allow_playback_repeats) { false }
it 'returns nil when there are no more unused interactions' do
list.response_for(request_with(:method => :post))
list.response_for(request_with(:method => :post))
results = 10.times.map do
list.response_for(request_with(:method => :post))
end
expect(results).to eq([nil] * 10)
end
end
it 'does not modify the original interaction array the list was initialized with' do
original_dup = original_list_array.dup
list.response_for(request_with(:method => :post))
expect(original_list_array).to eq original_dup
end
end
end
end
end
vcr-5.0.0/spec/lib/vcr/cassette/migrator_spec.rb 0000664 0000000 0000000 00000012343 13473056530 0021607 0 ustar 00root root 0000000 0000000 require 'tmpdir'
require 'vcr/cassette/migrator'
require 'yaml'
describe VCR::Cassette::Migrator do
let(:original_contents) { <<-EOF
---
- !ruby/struct:VCR::HTTPInteraction
request: !ruby/struct:VCR::Request
method: :get
uri: http://example.com:80/foo
body:
headers:
response: !ruby/struct:VCR::Response
status: !ruby/struct:VCR::ResponseStatus
code: 200
message: OK
headers:
content-type:
- text/html;charset=utf-8
content-length:
- "9"
body: Hello foo
http_version: "1.1"
- !ruby/struct:VCR::HTTPInteraction
request: !ruby/struct:VCR::Request
method: :get
uri: http://localhost:7777/bar
body:
headers:
response: !ruby/struct:VCR::Response
status: !ruby/struct:VCR::ResponseStatus
code: 200
message: OK
headers:
content-type:
- text/html;charset=utf-8
content-length:
- "9"
body: Hello bar
http_version: "1.1"
EOF
}
let(:updated_contents) { <<-EOF
---
http_interactions:
- request:
method: get
uri: http://example.com/foo
body:
encoding: US-ASCII
string: ""
headers: {}
response:
status:
code: 200
message: OK
headers:
Content-Type:
- text/html;charset=utf-8
Content-Length:
- "9"
body:
encoding: UTF-8
string: Hello foo
http_version: "1.1"
recorded_at: Wed, 04 May 2011 12:30:00 GMT
- request:
method: get
uri: http://localhost:7777/bar
body:
encoding: US-ASCII
string: ""
headers: {}
response:
status:
code: 200
message: OK
headers:
Content-Type:
- text/html;charset=utf-8
Content-Length:
- "9"
body:
encoding: UTF-8
string: Hello bar
http_version: "1.1"
recorded_at: Wed, 04 May 2011 12:30:00 GMT
recorded_with: VCR #{VCR.version}
EOF
}
let(:dir) { './tmp/migrator' }
before(:each) do
# ensure the directory is empty
FileUtils.rm_rf dir
FileUtils.mkdir_p dir
end
before(:each) do
# the encoding won't be set on rubies that don't support it
updated_contents.gsub!(/^\s+encoding:.*$/, '')
end unless ''.respond_to?(:encoding)
# JRuby serializes YAML with some slightly different whitespace.
before(:each) do
[original_contents, updated_contents].each do |contents|
contents.gsub!(/^(\s+)-/, '\1 -')
end
updated_contents.gsub!(/^(- | )/, ' \1')
end if RUBY_PLATFORM == 'java'
# Use syck on all rubies for consistent results...
around(:each) do |example|
YAML::ENGINE.yamler = 'syck'
begin
example.call
ensure
YAML::ENGINE.yamler = 'psych'
end
end if defined?(YAML::ENGINE) && RUBY_VERSION.to_f < 2.0
let(:filemtime) { Time.utc(2011, 5, 4, 12, 30) }
let(:out_io) { StringIO.new }
let(:file_name) { File.join(dir, "example.yml") }
let(:output) { out_io.rewind; out_io.read }
subject { described_class.new(dir, out_io) }
before(:each) do
allow(File).to receive(:mtime).with(file_name).and_return(filemtime)
end
it 'migrates a cassette from the 1.x to 2.x format' do
File.open(file_name, 'w') { |f| f.write(original_contents) }
subject.migrate!
expect(YAML.load_file(file_name)).to eq(YAML.load(updated_contents))
expect(output).to match(/Migrated example.yml/)
end
it 'ignores files that do not contain arrays' do
File.open(file_name, 'w') { |f| f.write(true.to_yaml) }
subject.migrate!
expect(File.read(file_name)).to eq(true.to_yaml)
expect(output).to match(/Ignored example.yml since it does not appear to be a valid VCR 1.x cassette/)
end
it 'ignores files that contain YAML arrays of other things' do
File.open(file_name, 'w') { |f| f.write([{}, {}].to_yaml) }
subject.migrate!
expect(File.read(file_name)).to eq([{}, {}].to_yaml)
expect(output).to match(/Ignored example.yml since it does not appear to be a valid VCR 1.x cassette/)
end
it 'ignores URIs that have sensitive data substitutions' do
modified_contents = original_contents.gsub('example.com', '')
File.open(file_name, 'w') { |f| f.write(modified_contents) }
subject.migrate!
expect(YAML.load_file(file_name)).to eq(YAML.load(updated_contents.gsub('example.com', ':80')))
end
it 'ignores files that are empty' do
File.open(file_name, 'w') { |f| f.write('') }
subject.migrate!
expect(File.read(file_name)).to eq('')
expect(output).to match(/Ignored example.yml since it could not be parsed as YAML/)
end
shared_examples_for "ignoring invalid YAML" do
it 'ignores files that cannot be parsed as valid YAML (such as ERB cassettes)' do
modified_contents = original_contents.gsub(/\A---/, "---\n<% 3.times do %>")
modified_contents = modified_contents.gsub(/\z/, "<% end %>")
File.open(file_name, 'w') { |f| f.write(modified_contents) }
subject.migrate!
expect(File.read(file_name)).to eq(modified_contents)
expect(output).to match(/Ignored example.yml since it could not be parsed as YAML/)
end
end
context 'with syck' do
it_behaves_like "ignoring invalid YAML"
end
context 'with psych' do
before(:each) do
YAML::ENGINE.yamler = 'psych'
end
it_behaves_like "ignoring invalid YAML"
end if defined?(YAML::ENGINE)
end
vcr-5.0.0/spec/lib/vcr/cassette/persisters/ 0000775 0000000 0000000 00000000000 13473056530 0020624 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/lib/vcr/cassette/persisters/file_system_spec.rb 0000664 0000000 0000000 00000006161 13473056530 0024512 0 ustar 00root root 0000000 0000000 require 'spec_helper'
require 'vcr/cassette/persisters/file_system'
module VCR
class Cassette
class Persisters
describe FileSystem do
before { FileSystem.storage_location = VCR.configuration.cassette_library_dir }
describe "#[]" do
it 'reads from the given file, relative to the configured storage location' do
File.open(FileSystem.storage_location + '/foo.txt', 'w') { |f| f.write('1234') }
expect(FileSystem["foo.txt"]).to eq("1234")
end
it 'handles directories in the given file name' do
FileUtils.mkdir_p FileSystem.storage_location + '/a'
File.open(FileSystem.storage_location + '/a/b', 'w') { |f| f.write('1234') }
expect(FileSystem["a/b"]).to eq("1234")
end
it 'returns nil if the file does not exist' do
expect(FileSystem["non_existant_file"]).to be_nil
end
end
describe "#[]=" do
context 'with a simple file_name and binary content' do
let(:file_name) { 'foo.txt' }
let(:content) { SecureRandom.random_bytes(20) }
let(:location) { FileSystem.storage_location + '/' + file_name }
it 'writes the given file contents to the given file name' do
expect(File.exist?(location)).to be false
FileSystem[file_name] = content
expect(File.binread(location)).to eq(content)
end
end
it 'creates any needed intermediary directories' do
expect(File.exist?(FileSystem.storage_location + '/a')).to be false
FileSystem["a/b"] = "bar"
expect(File.read(FileSystem.storage_location + '/a/b')).to eq("bar")
end
end
describe "#absolute_path_to_file" do
it "returns the absolute path to the given relative file based on the storage location" do
expected = File.join(FileSystem.storage_location, "bar/bazz.json")
expect(FileSystem.absolute_path_to_file("bar/bazz.json")).to eq(expected)
end
it "returns nil if the storage_location is not set" do
FileSystem.storage_location = nil
expect(FileSystem.absolute_path_to_file("bar/bazz.json")).to be_nil
end
it "sanitizes the file name" do
expected = File.join(FileSystem.storage_location, "_t_i-t_1_2_f_n.json")
expect(FileSystem.absolute_path_to_file("\nt \t! i-t_1.2_f n.json")).to eq(expected)
expected = File.join(FileSystem.storage_location, "a_1/b")
expect(FileSystem.absolute_path_to_file("a 1/b")).to eq(expected)
expected = File.join(FileSystem.storage_location, "\u842c\u570b\u78bc")
expect(FileSystem.absolute_path_to_file("\u842c\u570b\u78bc")).to eq(expected)
end
it 'handles files with no extensions (even when there is a dot in the path)' do
expected = File.join(FileSystem.storage_location, "/foo_bar/baz_qux")
expect(FileSystem.absolute_path_to_file("/foo.bar/baz qux")).to eq(expected)
end
end
end
end
end
end
vcr-5.0.0/spec/lib/vcr/cassette/persisters_spec.rb 0000664 0000000 0000000 00000002170 13473056530 0022163 0 ustar 00root root 0000000 0000000 require 'vcr/cassette/persisters'
module VCR
class Cassette
describe Persisters do
describe "#[]=" do
context 'when there is already a persister registered for the given name' do
before(:each) do
subject[:foo] = :old_persister
allow(subject).to receive :warn
end
it 'overrides the existing persister' do
subject[:foo] = :new_persister
expect(subject[:foo]).to be(:new_persister)
end
it 'warns that there is a name collision' do
expect(subject).to receive(:warn).with(
/WARNING: There is already a VCR cassette persister registered for :foo\. Overriding it/
)
subject[:foo] = :new_persister
end
end
end
describe "#[]" do
it 'raises an error when given an unrecognized persister name' do
expect { subject[:foo] }.to raise_error(ArgumentError)
end
it 'returns the named persister' do
expect(subject[:file_system]).to be(VCR::Cassette::Persisters::FileSystem)
end
end
end
end
end
vcr-5.0.0/spec/lib/vcr/cassette/serializers_spec.rb 0000664 0000000 0000000 00000014531 13473056530 0022320 0 ustar 00root root 0000000 0000000 require 'support/ruby_interpreter'
require 'vcr/cassette/serializers'
require 'multi_json'
begin
require 'psych' # ensure psych is loaded for these tests if its available
rescue LoadError
end
module VCR
class Cassette
describe Serializers do
shared_examples_for "encoding error handling" do |name, error_class|
context "the #{name} serializer" do
it 'appends info about the :preserve_exact_body_bytes option to the error' do
expect {
result = serializer.serialize("a" => string)
serializer.deserialize(result)
}.to raise_error(error_class, /preserve_exact_body_bytes/)
end unless (RUBY_INTERPRETER == :rubinius && RUBY_VERSION =~ /^1.9/)
end
end
shared_examples_for "a serializer" do |name, file_extension, lazily_loaded|
let(:serializer) { subject[name] }
context "the #{name} serializer" do
it 'lazily loads the serializer' do
serializers = subject.instance_variable_get(:@serializers)
expect(serializers).not_to have_key(name)
expect(subject[name]).not_to be_nil
expect(serializers).to have_key(name)
end if lazily_loaded
it "returns '#{file_extension}' as the file extension" do
expect(serializer.file_extension).to eq(file_extension)
end
it "can serialize and deserialize a hash" do
hash = { "a" => 7, "nested" => { "hash" => [1, 2, 3] }}
serialized = serializer.serialize(hash)
expect(serialized).not_to eq(hash)
expect(serialized).to be_a(String)
deserialized = serializer.deserialize(serialized)
expect(deserialized).to eq(hash)
end
end
end
it_behaves_like "a serializer", :yaml, "yml", :lazily_loaded do
it_behaves_like "encoding error handling", :yaml, ArgumentError do
let(:string) { "\xFA".force_encoding("UTF-8") }
before { ::YAML::ENGINE.yamler = 'psych' if defined?(::YAML::ENGINE) }
end if ''.respond_to?(:encoding)
end
it_behaves_like "a serializer", :syck, "yml", :lazily_loaded do
it_behaves_like "encoding error handling", :syck, ArgumentError do
let(:string) { "\xFA".force_encoding("UTF-8") }
end if ''.respond_to?(:encoding)
end
it_behaves_like "a serializer", :psych, "yml", :lazily_loaded do
it_behaves_like "encoding error handling", :psych, ArgumentError do
let(:string) { "\xFA".force_encoding("UTF-8") }
end if ''.respond_to?(:encoding)
end if RUBY_VERSION =~ /1.9/
it_behaves_like "a serializer", :compressed, "zz", :lazily_loaded do
it_behaves_like "encoding error handling", :compressed, ArgumentError do
let(:string) { "\xFA".force_encoding("UTF-8") }
end if ''.respond_to?(:encoding)
end
it_behaves_like "a serializer", :json, "json", :lazily_loaded do
engines = {}
if RUBY_INTERPRETER == :jruby
# don't test yajl on jruby
else
engines[:yajl] = MultiJson::LoadError
end
if RUBY_VERSION =~ /1.9/
engines[:json_gem] = EncodingError
# Disable json_pure for now due to this bug:
# https://github.com/flori/json/issues/186
# engines[:json_pure] = EncodingError
end
engines.each do |engine, error|
context "when MultiJson is configured to use #{engine.inspect}", :unless => (RUBY_INTERPRETER == :jruby) do
before { MultiJson.engine = engine }
it_behaves_like "encoding error handling", :json, error do
let(:string) { "\xFA" }
end
end
end
end
context "a custom :ruby serializer" do
let(:custom_serializer) do
Object.new.tap do |obj|
def obj.file_extension
"rb"
end
def obj.serialize(hash)
hash.inspect
end
def obj.deserialize(string)
eval(string)
end
end
end
before(:each) do
subject[:ruby] = custom_serializer
end
it_behaves_like "a serializer", :ruby, "rb", false
end
describe "#[]=" do
context 'when there is already a serializer registered for the given name' do
before(:each) do
subject[:foo] = :old_serializer
allow(subject).to receive :warn
end
it 'overrides the existing serializer' do
subject[:foo] = :new_serializer
expect(subject[:foo]).to be(:new_serializer)
end
it 'warns that there is a name collision' do
expect(subject).to receive(:warn).with(
/WARNING: There is already a VCR cassette serializer registered for :foo\. Overriding it/
)
subject[:foo] = :new_serializer
end
end
end
describe "#[]" do
it 'raises an error when given an unrecognized serializer name' do
expect { subject[:foo] }.to raise_error(ArgumentError)
end
it 'returns the named serializer' do
expect(subject[:yaml]).to be(VCR::Cassette::Serializers::YAML)
end
end
# see https://gist.github.com/815769
problematic_syck_string = "1\n \n2"
describe "psych serializer" do
it 'serializes things using pysch even if syck is configured as the default YAML engine' do
::YAML::ENGINE.yamler = 'syck'
serialized = subject[:psych].serialize(problematic_syck_string)
expect(subject[:psych].deserialize(serialized)).to eq(problematic_syck_string)
end if defined?(::Psych) && RUBY_VERSION.to_f < 2.0
it 'raises an error if psych cannot be loaded' do
expect { subject[:psych] }.to raise_error(LoadError)
end unless defined?(::Psych)
end
describe "syck serializer" do
it 'forcibly serializes things using syck even if psych is the currently configured YAML engine' do
::YAML::ENGINE.yamler = 'psych'
serialized = subject[:syck].serialize(problematic_syck_string)
expect(subject[:syck].deserialize(serialized)).not_to eq(problematic_syck_string)
end if defined?(::Psych) && (RUBY_INTERPRETER != :jruby) && (RUBY_VERSION.to_f < 2.0)
end
end
end
end
vcr-5.0.0/spec/lib/vcr/cassette_spec.rb 0000664 0000000 0000000 00000066630 13473056530 0017773 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe VCR::Cassette do
def http_interaction
request = VCR::Request.new(:get)
response = VCR::Response.new
response.status = VCR::ResponseStatus.new
VCR::HTTPInteraction.new(request, response).tap { |i| yield i if block_given? }
end
def stub_old_interactions(interactions)
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
hashes = interactions.map(&:to_hash)
allow(VCR.cassette_serializers[:yaml]).to receive(:deserialize).and_return({ 'http_interactions' => hashes })
allow(VCR::HTTPInteraction).to receive(:from_hash) do |hash|
interactions[hashes.index(hash)]
end
end
describe '#file' do
it 'delegates the file resolution to the FileSystem persister' do
fs = VCR::Cassette::Persisters::FileSystem
expect(fs).to respond_to(:absolute_path_to_file).with(1).argument
expect(fs).to receive(:absolute_path_to_file).with("cassette name.yml") { "f.yml" }
expect(VCR::Cassette.new("cassette name").file).to eq("f.yml")
end
it 'raises a NotImplementedError when a different persister is used' do
VCR.cassette_persisters[:a] = double
cassette = VCR::Cassette.new("f", :persist_with => :a)
expect { cassette.file }.to raise_error(NotImplementedError)
end
end
describe '#tags' do
it 'returns a blank array if no tag has been set' do
expect(VCR::Cassette.new("name").tags).to eq([])
end
it 'converts a single :tag to an array' do
expect(VCR::Cassette.new("name", :tag => :foo).tags).to eq([:foo])
end
it 'accepts an array as the :tags option' do
expect(VCR::Cassette.new("name", :tags => [:foo]).tags).to eq([:foo])
end
end
describe '#record_http_interaction' do
let(:the_interaction) { double(:request => double(:method => :get).as_null_object).as_null_object }
it 'adds the interaction to #new_recorded_interactions' do
cassette = VCR::Cassette.new(:test_cassette)
expect(cassette.new_recorded_interactions).to eq([])
cassette.record_http_interaction(the_interaction)
expect(cassette.new_recorded_interactions).to eq([the_interaction])
end
end
describe "#serializable_hash" do
subject { VCR::Cassette.new("foo") }
let(:interaction_1) { http_interaction { |i| i.request.body = 'req body 1'; i.response.body = 'res body 1' } }
let(:interaction_2) { http_interaction { |i| i.request.body = 'req body 2'; i.response.body = 'res body 2' } }
let(:interactions) { [interaction_1, interaction_2] }
before(:each) do
interactions.each do |i|
subject.record_http_interaction(i)
end
end
let(:metadata) { subject.serializable_hash.reject { |k,v| k == "http_interactions" } }
it 'includes the hash form of all recorded interactions' do
hash_1 = interaction_1.to_hash
hash_2 = interaction_2.to_hash
expect(subject.serializable_hash).to include('http_interactions' => [hash_1, hash_2])
end
it 'includes additional metadata about the cassette' do
expect(metadata).to eq("recorded_with" => "VCR #{VCR.version}")
end
it 'does not allow the interactions to be mutated by configured hooks' do
VCR.configure do |c|
c.define_cassette_placeholder('') { 'body' }
end
expect {
subject.serializable_hash
}.not_to change { interaction_1.response.body }
end
describe 'clean_outdated_http_interactions' do
before(:each) do
subject.instance_variable_set(:@clean_outdated_http_interactions, true)
subject.instance_variable_set(:@previously_recorded_interactions, subject.instance_variable_get(:@new_recorded_interactions))
subject.instance_variable_set(:@new_recorded_interactions, [])
end
let(:interaction_hashes) { [interaction_1, interaction_2].map(&:to_hash) }
it "returns all interactions if re_record_interval is not set" do
expect(subject.serializable_hash).to include('http_interactions' => interaction_hashes)
end
it "returns all interactions if they are not outdated" do
subject.instance_variable_set(:@re_record_interval, 100)
expect(subject.serializable_hash).to include('http_interactions' => interaction_hashes)
end
it "rejects outdated interactions" do
subject.instance_variable_set(:@re_record_interval, 100)
allow(Time).to receive(:now).and_return(Time.now + 105)
expect(subject.serializable_hash['http_interactions']).to be_empty
end
end
end
describe "#recording?" do
[:all, :new_episodes].each do |mode|
it "returns true when the record mode is :#{mode}" do
cassette = VCR::Cassette.new("foo", :record => mode)
expect(cassette).to be_recording
end
end
it "returns false when the record mode is :none" do
cassette = VCR::Cassette.new("foo", :record => :none)
expect(cassette).not_to be_recording
end
context 'when the record mode is :once' do
before(:each) do
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
end
it 'returns false when there is an existing cassette file with content' do
cassette = VCR::Cassette.new("example", :record => :once)
expect(::File).to exist(cassette.file)
expect(::File.size?(cassette.file)).to be_truthy
expect(cassette).not_to be_recording
end
it 'returns true when there is an empty existing cassette file' do
cassette = VCR::Cassette.new("empty", :record => :once)
expect(::File).to exist(cassette.file)
expect(::File.size?(cassette.file)).to be_falsey
expect(cassette).to be_recording
end
it 'returns true when there is no existing cassette file' do
cassette = VCR::Cassette.new("non_existant_file", :record => :once)
expect(::File).not_to exist(cassette.file)
expect(cassette).to be_recording
end
end
end
describe '#match_requests_on' do
before(:each) { VCR.configuration.default_cassette_options.merge!(:match_requests_on => [:uri, :method]) }
it "returns the provided options" do
c = VCR::Cassette.new('example', :match_requests_on => [:uri])
expect(c.match_requests_on).to eq([:uri])
end
it "returns a the default #match_requests_on when it has not been specified for the cassette" do
c = VCR::Cassette.new('example')
expect(c.match_requests_on).to eq([:uri, :method])
end
end
describe "reading the file from disk" do
let(:empty_cassette_yaml) { YAML.dump("http_interactions" => []) }
it 'optionally renders the file as ERB using the ERBRenderer' do
allow(VCR::Cassette::Persisters::FileSystem).to receive(:[]).and_return(empty_cassette_yaml)
expect(VCR::Cassette::ERBRenderer).to receive(:new).with(
empty_cassette_yaml, anything, "foo"
).and_return(double('renderer', :render => empty_cassette_yaml))
VCR::Cassette.new('foo', :record => :new_episodes).http_interactions
end
[true, false, nil, { }].each do |erb|
it "passes #{erb.inspect} to the VCR::Cassette::ERBRenderer when given as the :erb option" do
# test that it overrides the default
VCR.configuration.default_cassette_options = { :erb => true }
expect(VCR::Cassette::ERBRenderer).to receive(:new).with(
anything, erb, anything
).and_return(double('renderer', :render => empty_cassette_yaml))
VCR::Cassette.new('foo', :record => :new_episodes, :erb => erb).http_interactions
end
it "passes #{erb.inspect} to the VCR::Cassette::ERBRenderer when it is the default :erb option and none is given" do
VCR.configuration.default_cassette_options = { :erb => erb }
expect(VCR::Cassette::ERBRenderer).to receive(:new).with(
anything, erb, anything
).and_return(double('renderer', :render => empty_cassette_yaml))
VCR::Cassette.new('foo', :record => :new_episodes).http_interactions
end
end
it 'raises a friendly error when the cassette file is in the old VCR 1.x format' do
VCR.configuration.cassette_library_dir = 'spec/fixtures/cassette_spec'
expect {
VCR::Cassette.new('1_x_cassette').http_interactions
}.to raise_error(VCR::Errors::InvalidCassetteFormatError)
end
end
describe '.new' do
it "raises an error if given an invalid record mode" do
expect { VCR::Cassette.new(:test, :record => :not_a_record_mode) }.to raise_error(ArgumentError)
end
it 'raises an error if given invalid options' do
expect {
VCR::Cassette.new(:test, :invalid => :option)
}.to raise_error(ArgumentError)
end
it 'does not raise an error in the case of an empty file' do
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
expect(VCR::Cassette.new('empty', :record => :none).send(:previously_recorded_interactions)).to eq([])
end
let(:custom_persister) { double("custom persister") }
it 'reads from the configured persister' do
VCR.configuration.cassette_library_dir = nil
VCR.cassette_persisters[:foo] = custom_persister
expect(custom_persister).to receive(:[]).with("abc.yml") { "" }
VCR::Cassette.new("abc", :persist_with => :foo).http_interactions
end
VCR::Cassette::VALID_RECORD_MODES.each do |record_mode|
stub_requests = (record_mode != :all)
context "when VCR.configuration.default_cassette_options[:record] is :#{record_mode}" do
before(:each) { VCR.configuration.default_cassette_options = { :record => record_mode } }
it "defaults the record mode to #{record_mode} when VCR.configuration.default_cassette_options[:record] is #{record_mode}" do
cassette = VCR::Cassette.new(:test)
expect(cassette.record_mode).to eq(record_mode)
end
end
context "when :#{record_mode} is passed as the record option" do
unless record_mode == :all
let(:interaction_1) { http_interaction { |i| i.request.uri = 'http://example.com/foo' } }
let(:interaction_2) { http_interaction { |i| i.request.uri = 'http://example.com/bar' } }
let(:interactions) { [interaction_1, interaction_2] }
it 'updates the content_length headers when given :update_content_length_header => true' do
stub_old_interactions(interactions)
expect(interaction_1.response).to receive(:update_content_length_header)
expect(interaction_2.response).to receive(:update_content_length_header)
VCR::Cassette.new('example', :record => record_mode, :update_content_length_header => true).http_interactions
end
[nil, false].each do |val|
it "does not update the content_lenth headers when given :update_content_length_header => #{val.inspect}" do
stub_old_interactions(interactions)
expect(interaction_1.response).not_to receive(:update_content_length_header)
expect(interaction_2.response).not_to receive(:update_content_length_header)
VCR::Cassette.new('example', :record => record_mode, :update_content_length_header => val).http_interactions
end
end
context "and re_record_interval is 7.days" do
let(:file_name) { ::File.join(VCR.configuration.cassette_library_dir, "cassette_name.yml") }
subject { VCR::Cassette.new(::File.basename(file_name).gsub('.yml', ''), :record => record_mode, :re_record_interval => 7.days) }
context 'when the cassette file does not exist' do
before(:each) { allow(::File).to receive(:exist?).with(file_name).and_return(false) }
it "has :#{record_mode} for the record mode" do
expect(subject.record_mode).to eq(record_mode)
end
end
context 'when the cassette file does exist' do
before(:each) do
interactions = timestamps.map do |ts|
http_interaction { |i| i.recorded_at = ts }.to_hash
end
yaml = YAML.dump("http_interactions" => interactions)
allow(::File).to receive(:exist?).with(file_name).and_return(true)
allow(::File).to receive(:size?).with(file_name).and_return(true)
allow(::File).to receive(:binread).with(file_name).and_return(yaml)
end
context 'and the earliest recorded interaction was recorded less than 7 days ago' do
let(:timestamps) do [
Time.now - 6.days + 60,
Time.now - 7.days + 60,
Time.now - 5.days + 60
] end
it "has :#{record_mode} for the record mode" do
expect(subject.record_mode).to eq(record_mode)
end
end
context 'and the earliest recorded interaction was recorded more than 7 days ago' do
let(:timestamps) do [
Time.now - 6.days - 60,
Time.now - 7.days - 60,
Time.now - 5.days - 60
] end
it "has :all for the record mode when there is an internet connection available" do
allow(VCR::InternetConnection).to receive(:available?).and_return(true)
expect(subject.record_mode).to eq(:all)
end
it "has :#{record_mode} for the record mode when there is no internet connection available" do
allow(VCR::InternetConnection).to receive(:available?).and_return(false)
expect(subject.record_mode).to eq(record_mode)
end
end
end
end
end
it 'does not load ignored interactions' do
allow(VCR.request_ignorer).to receive(:ignore?) do |request|
request.uri !~ /example\.com/
end
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
cassette = VCR::Cassette.new('with_localhost_requests', :record => record_mode)
expect(cassette.send(:previously_recorded_interactions).map { |i| URI.parse(i.request.uri).host }).to eq(%w[example.com])
end
it "loads the recorded interactions from the library yml file" do
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
cassette = VCR::Cassette.new('example', :record => record_mode)
expect(cassette.send(:previously_recorded_interactions).size).to eq(3)
i1, i2, i3 = *cassette.send(:previously_recorded_interactions)
expect(i1.request.method).to eq(:get)
expect(i1.request.uri).to eq('http://example.com/')
expect(i1.response.body).to match(/You have reached this web page by typing.+example\.com/)
expect(i2.request.method).to eq(:get)
expect(i2.request.uri).to eq('http://example.com/foo')
expect(i2.response.body).to match(/foo was not found on this server/)
expect(i3.request.method).to eq(:get)
expect(i3.request.uri).to eq('http://example.com/')
expect(i3.response.body).to match(/Another example\.com response/)
end
[true, false].each do |value|
it "instantiates the http_interactions with allow_playback_repeats = #{value} if given :allow_playback_repeats => #{value}" do
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
cassette = VCR::Cassette.new('example', :record => record_mode, :allow_playback_repeats => value)
expect(cassette.http_interactions.allow_playback_repeats).to eq(value)
end
end
it "instantiates the http_interactions with parent_list set to a null list if given :exclusive => true" do
allow(VCR).to receive(:http_interactions).and_return(double)
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
cassette = VCR::Cassette.new('example', :record => record_mode, :exclusive => true)
expect(cassette.http_interactions.parent_list).to be(VCR::Cassette::HTTPInteractionList::NullList)
end
it "instantiates the http_interactions with parent_list set to VCR.http_interactions if given :exclusive => false" do
allow(VCR).to receive(:http_interactions).and_return(double)
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
cassette = VCR::Cassette.new('example', :record => record_mode, :exclusive => false)
expect(cassette.http_interactions.parent_list).to be(VCR.http_interactions)
end
if stub_requests
it 'invokes the before_playback hooks' do
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
expect(VCR.configuration).to receive(:invoke_hook).with(
:before_playback,
an_instance_of(VCR::HTTPInteraction::HookAware),
an_instance_of(VCR::Cassette)
).exactly(3).times
cassette = VCR::Cassette.new('example', :record => record_mode)
expect(cassette.send(:previously_recorded_interactions).size).to eq(3)
end
it 'does not playback any interactions that are ignored in a before_playback hook' do
VCR.configure do |c|
c.before_playback { |i| i.ignore! if i.request.uri =~ /foo/ }
end
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
cassette = VCR::Cassette.new('example', :record => record_mode)
expect(cassette.send(:previously_recorded_interactions).size).to eq(2)
end
it 'instantiates the http_interactions with the loaded interactions and the request matchers' do
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
cassette = VCR::Cassette.new('example', :record => record_mode, :match_requests_on => [:body, :headers])
expect(cassette.http_interactions.interactions.size).to eq(3)
expect(cassette.http_interactions.request_matchers).to eq([:body, :headers])
end
else
it 'instantiates the http_interactions with the no interactions and the request matchers' do
VCR.configuration.cassette_library_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
cassette = VCR::Cassette.new('example', :record => record_mode, :match_requests_on => [:body, :headers])
expect(cassette.http_interactions.interactions.size).to eq(0)
expect(cassette.http_interactions.request_matchers).to eq([:body, :headers])
end
end
end
end
end
describe ".originally_recorded_at" do
it 'returns the earliest `recorded_at` timestamp' do
i1 = http_interaction { |i| i.recorded_at = Time.now - 1000 }
i2 = http_interaction { |i| i.recorded_at = Time.now - 10000 }
i3 = http_interaction { |i| i.recorded_at = Time.now - 100 }
stub_old_interactions([i1, i2, i3])
cassette = VCR::Cassette.new("example")
expect(cassette.originally_recorded_at).to eq(i2.recorded_at)
end
it 'records nil for a cassette that has no prior recorded interactions' do
stub_old_interactions([])
cassette = VCR::Cassette.new("example")
expect(cassette.originally_recorded_at).to be_nil
end
end
describe '#eject' do
let(:custom_persister) { double("custom persister", :[] => nil) }
context "when :allow_unused_http_interactions is set to false" do
it 'asserts that there are no unused interactions' do
cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => false)
interaction_list = cassette.http_interactions
expect(interaction_list).to respond_to(:assert_no_unused_interactions!).with(0).arguments
expect(interaction_list).to receive(:assert_no_unused_interactions!)
cassette.eject
end
it 'does not assert no unused interactions if there is an existing error' do
cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => false)
interaction_list = cassette.http_interactions
allow(interaction_list).to receive(:assert_no_unused_interactions!)
expect {
begin
raise "boom"
ensure
cassette.eject
end
}.to raise_error(/boom/)
expect(interaction_list).not_to have_received(:assert_no_unused_interactions!)
end
it 'does not assert no unused interactions if :skip_no_unused_interactions_assertion is passed' do
cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => false)
interaction_list = cassette.http_interactions
expect(interaction_list).not_to receive(:assert_no_unused_interactions!)
cassette.eject(:skip_no_unused_interactions_assertion => true)
end
end
it 'does not assert that there are no unused interactions if allow_unused_http_interactions is set to true' do
cassette = VCR.insert_cassette("foo", :allow_unused_http_interactions => true)
interaction_list = cassette.http_interactions
expect(interaction_list).to respond_to(:assert_no_unused_interactions!)
expect(interaction_list).not_to receive(:assert_no_unused_interactions!)
cassette.eject
end
it 'stores the cassette content using the configured persister' do
VCR.configuration.cassette_library_dir = nil
VCR.cassette_persisters[:foo] = custom_persister
cassette = VCR.insert_cassette("foo", :persist_with => :foo)
cassette.record_http_interaction http_interaction
expect(custom_persister).to receive(:[]=).with("foo.yml", /http_interactions/)
cassette.eject
end
it "writes the serializable_hash to disk as yaml" do
cassette = VCR::Cassette.new(:eject_test)
cassette.record_http_interaction http_interaction # so it has one
expect(cassette).to respond_to(:serializable_hash)
allow(cassette).to receive(:serializable_hash).and_return({ "http_interactions" => [1, 3, 5] })
expect { cassette.eject }.to change { ::File.exist?(cassette.file) }.from(false).to(true)
saved_stuff = YAML.load_file(cassette.file)
expect(saved_stuff).to eq("http_interactions" => [1, 3, 5])
end
it 'invokes the appropriately tagged before_record hooks' do
interactions = [
http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' },
http_interaction { |i| i.request.uri = 'http://bar.com/'; i.response.body = 'res 2' }
]
cassette = VCR::Cassette.new('example', :tag => :foo)
allow(cassette).to receive(:new_recorded_interactions).and_return(interactions)
allow(VCR.configuration).to receive(:invoke_hook).and_return([false])
interactions.each do |i|
expect(VCR.configuration).to receive(:invoke_hook).with(
:before_record,
an_instance_of(VCR::HTTPInteraction::HookAware),
cassette
).ordered
end
cassette.eject
end
it 'does not record interactions that have been ignored' do
VCR.configure do |c|
c.before_record { |i| i.ignore! if i.request.uri =~ /foo/ }
end
interaction_1 = http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' }
interaction_2 = http_interaction { |i| i.request.uri = 'http://bar.com/'; i.response.body = 'res 2' }
cassette = VCR::Cassette.new('test_cassette')
allow(cassette).to receive(:new_recorded_interactions).and_return([interaction_1, interaction_2])
cassette.eject
saved_recorded_interactions = ::YAML.load_file(cassette.file)
expect(saved_recorded_interactions["http_interactions"]).to eq([interaction_2.to_hash])
end
it 'does not write the cassette to disk if all interactions have been ignored' do
VCR.configure do |c|
c.before_record { |i| i.ignore! }
end
interaction_1 = http_interaction { |i| i.request.uri = 'http://foo.com/'; i.response.body = 'res 1' }
cassette = VCR::Cassette.new('test_cassette')
allow(cassette).to receive(:new_recorded_interactions).and_return([interaction_1])
cassette.eject
expect(::File).not_to exist(cassette.file)
end
it "writes the recorded interactions to a subdirectory if the cassette name includes a directory" do
recorded_interactions = [http_interaction { |i| i.response.body = "subdirectory response" }]
cassette = VCR::Cassette.new('subdirectory/test_cassette')
allow(cassette).to receive(:new_recorded_interactions).and_return(recorded_interactions)
expect { cassette.eject }.to change { ::File.exist?(cassette.file) }.from(false).to(true)
saved_recorded_interactions = YAML.load_file(cassette.file)
expect(saved_recorded_interactions["http_interactions"]).to eq(recorded_interactions.map(&:to_hash))
end
[:all, :none, :new_episodes].each do |record_mode|
context "for a :record => :#{record_mode} cassette with previously recorded interactions" do
subject { VCR::Cassette.new('example', :record => record_mode, :match_requests_on => [:uri]) }
before(:each) do
base_dir = "#{VCR::SPEC_ROOT}/fixtures/cassette_spec"
FileUtils.cp(base_dir + "/example.yml", VCR.configuration.cassette_library_dir + "/example.yml")
end
it "does not re-write to disk the previously recorded interactions if there are no new ones" do
yaml_file = subject.file
expect(::File).not_to receive(:open).with(subject.file, 'w')
expect { subject.eject }.to_not change { ::File.mtime(yaml_file) }
end
context 'when some new interactions have been recorded' do
def interaction(response_body, request_attributes)
http_interaction do |interaction|
interaction.response.body = response_body
request_attributes.each do |key, value|
interaction.request.send("#{key}=", value)
end
end
end
let(:interaction_foo_1) { interaction("foo 1", :uri => 'http://foo.com/') }
let(:interaction_foo_2) { interaction("foo 2", :uri => 'http://foo.com/') }
let(:interaction_bar) { interaction("bar", :uri => 'http://bar.com/') }
let(:saved_recorded_interactions) { YAML.load_file(subject.file)['http_interactions'].map { |h| VCR::HTTPInteraction.from_hash(h) } }
let(:now) { Time.utc(2011, 6, 11, 12, 30) }
before(:each) do
allow(Time).to receive(:now).and_return(now)
allow(subject).to receive(:previously_recorded_interactions).and_return([interaction_foo_1])
subject.record_http_interaction(interaction_foo_2)
subject.record_http_interaction(interaction_bar)
subject.eject
end
if record_mode == :all
it 'replaces previously recorded interactions with new ones when the requests match' do
expect(saved_recorded_interactions.first).to eq(interaction_foo_2)
expect(saved_recorded_interactions).not_to include(interaction_foo_1)
end
it 'appends new recorded interactions that do not match existing ones' do
expect(saved_recorded_interactions.last).to eq(interaction_bar)
end
else
it 'appends new recorded interactions after existing ones' do
expect(saved_recorded_interactions).to eq([interaction_foo_1, interaction_foo_2, interaction_bar])
end
end
end
end
end
end
end
vcr-5.0.0/spec/lib/vcr/configuration_spec.rb 0000664 0000000 0000000 00000030307 13473056530 0021017 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe VCR::Configuration do
describe '#cassette_library_dir=' do
let(:tmp_dir) { VCR::SPEC_ROOT + '/../tmp/cassette_library_dir/new_dir' }
after(:each) { FileUtils.rm_rf tmp_dir }
it 'creates the directory if it does not exist' do
expect { subject.cassette_library_dir = tmp_dir }.to change { File.exist?(tmp_dir) }.from(false).to(true)
end
it 'does not raise an error if given nil' do
expect { subject.cassette_library_dir = nil }.to_not raise_error
end
it 'resolves the given directory to an absolute path, so VCR continues to work even if the current directory changes' do
relative_dir = 'tmp/cassette_library_dir/new_dir'
subject.cassette_library_dir = relative_dir
absolute_dir = File.join(VCR::SPEC_ROOT.sub(/\/spec\z/, ''), relative_dir)
expect(subject.cassette_library_dir).to eq(absolute_dir)
end
end
describe '#default_cassette_options' do
it 'has a hash with some defaults' do
expect(subject.default_cassette_options).to eq({
:match_requests_on => VCR::RequestMatcherRegistry::DEFAULT_MATCHERS,
:allow_unused_http_interactions => true,
:record => :once,
:serialize_with => :yaml,
:persist_with => :file_system
})
end
it "returns #{VCR::RequestMatcherRegistry::DEFAULT_MATCHERS.inspect} for :match_requests_on when other defaults have been set" do
subject.default_cassette_options = { :record => :none }
expect(subject.default_cassette_options).to include(:match_requests_on => VCR::RequestMatcherRegistry::DEFAULT_MATCHERS)
end
it "returns :once for :record when other defaults have been set" do
subject.default_cassette_options = { :erb => :true }
expect(subject.default_cassette_options).to include(:record => :once)
end
it "allows defaults to be overriden" do
subject.default_cassette_options = { :record => :all }
expect(subject.default_cassette_options).to include(:record => :all)
end
it "allows other keys to be set" do
subject.default_cassette_options = { :re_record_interval => 10 }
expect(subject.default_cassette_options).to include(:re_record_interval => 10)
end
end
describe '#register_request_matcher' do
it 'registers the given request matcher' do
expect {
VCR.request_matchers[:custom]
}.to raise_error(VCR::UnregisteredMatcherError)
matcher_run = false
subject.register_request_matcher(:custom) { |r1, r2| matcher_run = true }
VCR.request_matchers[:custom].matches?(:r1, :r2)
expect(matcher_run).to be true
end
end
describe '#hook_into' do
it 'requires the named library hook' do
expect(subject).to receive(:require).with("vcr/library_hooks/webmock")
expect(subject).to receive(:require).with("vcr/library_hooks/excon")
subject.hook_into :webmock, :excon
end
it 'raises an error for unsupported stubbing libraries' do
expect {
subject.hook_into :unsupported_library
}.to raise_error(ArgumentError, /unsupported_library is not a supported VCR HTTP library hook/i)
end
it 'invokes the after_library_hooks_loaded hooks' do
called = false
subject.after_library_hooks_loaded { called = true }
subject.hook_into :webmock
expect(called).to be true
end
end
describe '#ignore_hosts' do
it 'delegates to the current request_ignorer instance' do
expect(VCR.request_ignorer).to receive(:ignore_hosts).with('example.com', 'example.net')
subject.ignore_hosts 'example.com', 'example.net'
end
end
describe '#unignore_hosts' do
it 'delegates to the current request_ignorer instance' do
expect(VCR.request_ignorer).to receive(:unignore_hosts).with('example.com', 'example.net')
subject.unignore_hosts 'example.com', 'example.net'
end
end
describe '#ignore_localhost=' do
it 'delegates to the current request_ignorer instance' do
expect(VCR.request_ignorer).to receive(:ignore_localhost=).with(true)
subject.ignore_localhost = true
end
end
describe '#ignore_request' do
let(:uri){ URI('http://foo.com') }
it 'registers the given block with the request ignorer' do
block_called = false
subject.ignore_request { |r| block_called = true }
VCR.request_ignorer.ignore?(double(:parsed_uri => uri))
expect(block_called).to be true
end
end
describe '#allow_http_connections_when_no_cassette=' do
[true, false].each do |val|
it "sets the allow_http_connections_when_no_cassette to #{val} when set to #{val}" do
subject.allow_http_connections_when_no_cassette = val
expect(subject.allow_http_connections_when_no_cassette?).to eq(val)
end
end
end
describe "request/configuration interactions", :with_monkey_patches => :webmock do
specify 'the request on the yielded interaction is not typed even though the request given to before_http_request is' do
before_record_req = before_request_req = nil
VCR.configure do |c|
c.before_http_request { |r| before_request_req = r }
c.before_record { |i| before_record_req = i.request }
end
VCR.use_cassette("example") do
::Net::HTTP.get_response(URI("http://localhost:#{VCR::SinatraApp.port}/foo"))
end
expect(before_record_req).not_to respond_to(:type)
expect(before_request_req).to respond_to(:type)
end unless (RUBY_VERSION =~ /^1\.8/ || RUBY_INTERPRETER == :jruby)
specify 'the filter_sensitive_data option works even when it modifies the URL in a way that makes it an invalid URI' do
VCR.configure do |c|
c.filter_sensitive_data('') { 'localhost' }
end
2.times do
VCR.use_cassette("example") do
::Net::HTTP.get_response(URI("http://localhost:#{VCR::SinatraApp.port}/foo"))
end
end
end
end
[:before_record, :before_playback].each do |hook_type|
describe "##{hook_type}" do
it 'sets up a tag filter' do
called = false
VCR.configuration.send(hook_type, :my_tag) { called = true }
VCR.configuration.invoke_hook(hook_type, double, double(:tags => []))
expect(called).to be false
VCR.configuration.invoke_hook(hook_type, double, double(:tags => [:my_tag]))
expect(called).to be true
end
end
end
%w[ filter_sensitive_data define_cassette_placeholder ].each do |method|
describe "##{method}" do
let(:interaction) { double('interaction').as_null_object }
before(:each) { allow(interaction).to receive(:filter!) }
it 'adds a before_record hook that replaces the string returned by the block with the given string' do
subject.send(method, 'foo', &lambda { 'bar' })
expect(interaction).to receive(:filter!).with('bar', 'foo')
subject.invoke_hook(:before_record, interaction, double.as_null_object)
end
it 'adds a before_playback hook that replaces the given string with the string returned by the block' do
subject.send(method, 'foo', &lambda { 'bar' })
expect(interaction).to receive(:filter!).with('foo', 'bar')
subject.invoke_hook(:before_playback, interaction, double.as_null_object)
end
it 'tags the before_record hook when given a tag' do
expect(subject).to receive(:before_record).with(:my_tag)
subject.send(method, 'foo', :my_tag) { 'bar' }
end
it 'tags the before_playback hook when given a tag' do
expect(subject).to receive(:before_playback).with(:my_tag)
subject.send(method, 'foo', :my_tag) { 'bar' }
end
it 'yields the interaction to the block for the before_record hook' do
yielded_interaction = nil
subject.send(method, 'foo', &lambda { |i| yielded_interaction = i; 'bar' })
subject.invoke_hook(:before_record, interaction, double.as_null_object)
expect(yielded_interaction).to equal(interaction)
end
it 'yields the interaction to the block for the before_playback hook' do
yielded_interaction = nil
subject.send(method, 'foo', &lambda { |i| yielded_interaction = i; 'bar' })
subject.invoke_hook(:before_playback, interaction, double.as_null_object)
expect(yielded_interaction).to equal(interaction)
end
end
end
describe "#after_http_request" do
let(:raw_request) { VCR::Request.new }
let(:response) { VCR::Response.new }
def request(type)
VCR::Request::Typed.new(raw_request, type)
end
it 'handles symbol request predicate filters properly' do
yielded = false
subject.after_http_request(:stubbed_by_vcr?) { |req| yielded = true }
subject.invoke_hook(:after_http_request, request(:stubbed_by_vcr), response)
expect(yielded).to be true
yielded = false
subject.invoke_hook(:after_http_request, request(:ignored), response)
expect(yielded).to be false
end
end
describe "#cassette_serializers" do
let(:custom_serializer) { double }
it 'allows a custom serializer to be registered' do
expect { subject.cassette_serializers[:custom] }.to raise_error(ArgumentError)
subject.cassette_serializers[:custom] = custom_serializer
expect(subject.cassette_serializers[:custom]).to be(custom_serializer)
end
end
describe "#cassette_persisters" do
let(:custom_persister) { double }
it 'allows a custom persister to be registered' do
expect { subject.cassette_persisters[:custom] }.to raise_error(ArgumentError)
subject.cassette_persisters[:custom] = custom_persister
expect(subject.cassette_persisters[:custom]).to be(custom_persister)
end
end
describe "#uri_parser=" do
let(:custom_parser) { double }
it 'allows a custom uri parser to be set' do
subject.uri_parser = custom_parser
expect(subject.uri_parser).to eq(custom_parser)
end
it "uses Ruby's standard library `URI` as a default" do
expect(subject.uri_parser).to eq(URI)
end
end
describe "#preserve_exact_body_bytes_for?" do
def message_for(body)
double(:body => body)
end
context "default hook" do
it "returns false when there is no current cassette" do
expect(subject.preserve_exact_body_bytes_for?(message_for "string")).to be false
end
it "returns false when the current cassette has been created without the :preserve_exact_body_bytes option" do
VCR.insert_cassette('foo')
expect(subject.preserve_exact_body_bytes_for?(message_for "string")).to be false
end
it 'returns true when the current cassette has been created with the :preserve_exact_body_bytes option' do
VCR.insert_cassette('foo', :preserve_exact_body_bytes => true)
expect(subject.preserve_exact_body_bytes_for?(message_for "string")).to be true
end
end
it "returns true when the configured block returns true" do
subject.preserve_exact_body_bytes { |msg| msg.body == "a" }
expect(subject.preserve_exact_body_bytes_for?(message_for "a")).to be true
expect(subject.preserve_exact_body_bytes_for?(message_for "b")).to be false
end
it "returns true when any of the registered blocks returns true" do
called_hooks = []
subject.preserve_exact_body_bytes { called_hooks << :hook_1; false }
subject.preserve_exact_body_bytes { called_hooks << :hook_2; true }
expect(subject.preserve_exact_body_bytes_for?(message_for "a")).to be true
expect(called_hooks).to eq([:hook_1, :hook_2])
end
it "invokes the configured hook with the http message and the current cassette" do
VCR.use_cassette('example') do |cassette|
expect(cassette).to be_a(VCR::Cassette)
message = double(:message)
yielded_objects = nil
subject.preserve_exact_body_bytes { |a, b| yielded_objects = [a, b] }
subject.preserve_exact_body_bytes_for?(message)
expect(yielded_objects).to eq([message, cassette])
end
end
end
describe "#configure_rspec_metadata!" do
it "only configures the underlying metadata once, no matter how many times it is called" do
expect(VCR::RSpec::Metadata).to receive(:configure!).once
VCR.configure do |c|
c.configure_rspec_metadata!
end
VCR.configure do |c|
c.configure_rspec_metadata!
end
end
end
end
vcr-5.0.0/spec/lib/vcr/deprecations_spec.rb 0000664 0000000 0000000 00000004277 13473056530 0020637 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe VCR, 'deprecations', :disable_warnings do
describe ".config" do
it 'delegates to VCR.configure' do
expect(VCR).to receive(:configure)
VCR.config { }
end
it 'yields the configuration object' do
config_object = nil
VCR.config { |c| config_object = c }
expect(config_object).to be(VCR.configuration)
end
it 'prints a deprecation warning' do
expect(VCR).to receive(:warn).with(/VCR.config.*deprecated/i)
VCR.config { }
end
end
describe "Config" do
it 'returns the same object referenced by VCR.configuration' do
expect(VCR::Config).to be(VCR.configuration)
end
it 'prints a deprecation warning' do
expect(VCR).to receive(:warn).with(/VCR::Config.*deprecated/i)
VCR::Config
end
it 'preserves the normal undefined constant behavior' do
expect {
VCR::SomeUndefinedConstant
}.to raise_error(NameError)
end
end
describe "Cassette::MissingERBVariableError" do
it 'returns VCR::Errors::MissingERBVariableError' do
expect(VCR::Cassette::MissingERBVariableError).to be(VCR::Errors::MissingERBVariableError)
end
it 'prints a deprecation warning' do
expect(VCR::Cassette).to receive(:warn).with(/VCR::Cassette::MissingERBVariableError.*deprecated/i)
VCR::Cassette::MissingERBVariableError
end
it 'preserves the normal undefined constant behavior' do
expect {
VCR::Cassette::SomeUndefinedConstant
}.to raise_error(NameError)
end
end
describe "VCR.configure { |c| c.stub_with ... }" do
it 'delegates to #hook_into' do
expect(VCR.configuration).to receive(:hook_into).with(:webmock, :excon)
VCR.configure { |c| c.stub_with :webmock, :excon }
end
it 'prints a deprecation warning' do
expect(VCR.configuration).to receive(:warn).with(/stub_with.*deprecated/i)
VCR.configure { |c| c.stub_with :webmock, :excon }
end
end
describe "VCR::Middleware::Faraday" do
it 'prints a deprecation warning when passed a block' do
expect(Kernel).to receive(:warn).with(/Passing a block .* is deprecated/)
VCR::Middleware::Faraday.new(double) { }
end
end
end
vcr-5.0.0/spec/lib/vcr/errors_spec.rb 0000664 0000000 0000000 00000020524 13473056530 0017464 0 ustar 00root root 0000000 0000000 require 'spec_helper'
module VCR
module Errors
describe UnhandledHTTPRequestError do
def message_for(request_values = {})
described_class.new(request_with request_values).message
end
alias message message_for
def request_with(options)
VCR::Request.new(*options.values_at(*VCR::Request.members))
end
it 'identifies the request by method and URI' do
expect(message_for(:method => :post, :uri => 'http://foo.com/')).to include(
'POST http://foo.com/'
)
end
context 'when there is no cassette' do
it 'identifies the request by its body when the default_cassette_options include the body in the match_requests_on option' do
VCR.configuration.default_cassette_options[:match_requests_on] = [:body]
expect(message_for(:body => 'param=val1')).to include(
"Body: param=val1"
)
end
it 'identifies the request by its headers when the default_cassette_options include the headers in the match_requests_on option' do
VCR.configuration.default_cassette_options[:match_requests_on] = [:headers]
expect(message_for(:headers => { "Content-Type" => "application/json", "X-Custom" => ["123", "ab\"c"]})).to include(
'Content-Type: "application/json"',
'X-Custom: "123"',
'X-Custom: "ab\"c"'
)
end
it 'mentions that there is no cassette' do
expect(message).to include('There is currently no cassette in use.')
end
it 'mentions that the request can be recorded by inserting a cassette' do
expect(message).to match(/record this request and play it back.*VCR.use_cassette/m)
end
it 'mentions the allow_http_connections_when_no_cassette option' do
expect(message).to include('allow_http_connections_when_no_cassette')
end
it 'mentions that the request can be ignored' do
expect(message).to include('set an `ignore_request` callback')
end
it 'does not double-insert the asterisks for the bullet points' do
expect(message).not_to match(/\s+\*\s+\*/)
end
it 'mentions the debug logging configuration option' do
expect(message).to include('debug_logger')
end
end
context 'when there are cassettes' do
it 'identifies the request by its body when the match_requests_on option includes the body' do
VCR.use_cassette('example', :match_requests_on => [:body]) do
expect(message_for(:body => 'param=val1')).to include(
"Body: param=val1"
)
end
end
it 'identifies the request by its headers when the match_requests_on option includes the headers' do
VCR.use_cassette('example', :match_requests_on => [:headers]) do
expect(message_for(:headers => { "Content-Type" => "application/json", "X-Custom" => ["123", "ab\"c"]})).to include(
'Content-Type: "application/json"',
'X-Custom: "123"',
'X-Custom: "ab\"c"'
)
end
end
it 'does not identify the request by its body when the cassette match_requests_on option does not include the body but the default_cassette_options do' do
VCR.configuration.default_cassette_options[:match_requests_on] = [:body]
VCR.use_cassette('example', :match_requests_on => [:uri]) do
expect(message_for(:body => 'param=val1')).to_not match(/body/i)
end
end
it 'mentions the details about the single cassette when there is one cassette' do
VCR.use_cassette('example') do
expect(message).to match(/VCR is currently using the following cassette:.+example.yml/m)
end
end
it 'mentions the details about all cassettes when there are a few cassettes' do
VCR.use_cassette('example') do
VCR.use_cassette('sample') do
expect(message).to match(/VCR are currently using the following cassettes:.+sample.yml.+example.yml/m)
end
end
end
it 'mentions that :new_episodes can be used to record the request' do
VCR.use_cassette('example') do
expect(message).to include('use the :new_episodes record mode')
end
end
it 'mentions that :once does not allow a cassette to be re-recorded' do
VCR.use_cassette('example', :record => :once) do
expect(message).to include('(:once) does not allow new requests to be recorded')
end
end
it 'mentions that :none does not allow any recording' do
VCR.use_cassette('example', :record => :none) do
expect(message).to include('(:none) does not allow requests to be recorded')
end
end
it 'does not mention the :once or :none record modes if using the :new_episodes record mode' do
VCR.use_cassette('example', :record => :new_episodes) do
expect(message).not_to include(':once', ':none')
end
end
it 'does not mention the :once or :none record modes if using the :new_episodes record mode at least in one cassette' do
VCR.use_cassette('example', :record => :new_episodes) do
VCR.use_cassette('sample') do
expect(message).not_to include('current record mode (:once)', 'current record mode (:none)')
end
end
end
it 'mentions :allow_playback_repeats if the cassette has a used matching interaction' do
VCR.use_cassette('example') do |cassette|
expect(cassette.http_interactions).to respond_to(:has_used_interaction_matching?)
allow(cassette.http_interactions).to receive(:has_used_interaction_matching?).and_return(true)
expect(message).to include('allow_playback_repeats')
end
end
it 'does not mention :allow_playback_repeats if the cassette does not have a used matching interaction' do
VCR.use_cassette('example') do |cassette|
expect(cassette.http_interactions).to respond_to(:has_used_interaction_matching?)
allow(cassette.http_interactions).to receive(:has_used_interaction_matching?).and_return(false)
expect(message).not_to include('allow_playback_repeats')
end
end
it 'does not mention using a different :match_requests_on option when there are no remaining unused interactions' do
VCR.use_cassette('example') do |cassette|
expect(cassette.http_interactions).to respond_to(:remaining_unused_interaction_count)
allow(cassette.http_interactions).to receive(:remaining_unused_interaction_count).and_return(0)
expect(message).not_to include('match_requests_on cassette option')
end
end
it 'mentions using a different :match_requests_on option when there are some remaining unused interactions' do
VCR.use_cassette('example') do |cassette|
expect(cassette.http_interactions).to respond_to(:remaining_unused_interaction_count)
allow(cassette.http_interactions).to receive(:remaining_unused_interaction_count).and_return(1)
expect(message).to include('match_requests_on cassette option')
end
end
it 'uses the singular (HTTP interaction) when there is only 1 left' do
VCR.use_cassette('example') do |cassette|
expect(cassette.http_interactions).to respond_to(:remaining_unused_interaction_count)
allow(cassette.http_interactions).to receive(:remaining_unused_interaction_count).and_return(1)
expect(message).to include('1 HTTP interaction ')
end
end
it 'uses the plural (HTTP interactions) when there is more than 1 left' do
VCR.use_cassette('example') do |cassette|
expect(cassette.http_interactions).to respond_to(:remaining_unused_interaction_count)
allow(cassette.http_interactions).to receive(:remaining_unused_interaction_count).and_return(2)
expect(message).to include('2 HTTP interactions ')
end
end
it 'mentions the debug logging configuration option' do
VCR.use_cassette('example', :record => :new_episodes) do
expect(message).to include('debug_logger')
end
end
end
end
end
end
vcr-5.0.0/spec/lib/vcr/library_hooks/ 0000775 0000000 0000000 00000000000 13473056530 0017455 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/lib/vcr/library_hooks/excon_spec.rb 0000664 0000000 0000000 00000006546 13473056530 0022143 0 ustar 00root root 0000000 0000000 require 'spec_helper'
require 'support/shared_example_groups/excon'
describe "Excon hook", :with_monkey_patches => :excon do
after(:each) do
::Excon.stubs.clear
::Excon.defaults[:mock] = false
end
def directly_stub_request(method, url, response_body)
::Excon.defaults[:mock] = true
::Excon.stub({ :method => method, :url => url }, { :body => response_body })
end
it_behaves_like 'a hook into an HTTP library', :excon, 'excon', :status_message_not_exposed
context "when the query is specified as a hash option" do
let(:excon) { ::Excon.new("http://localhost:#{VCR::SinatraApp.port}/search") }
it 'properly records and plays back the response' do
allow(VCR).to receive(:real_http_connections_allowed?).and_return(true)
recorded, played_back = [1, 2].map do
VCR.use_cassette('excon_query', :record => :once) do
excon.request(:method => :get, :query => { :q => 'Tolkien' }).body
end
end
expect(recorded).to eq(played_back)
expect(recorded).to eq('query: Tolkien')
end
end
context "when Excon's expects and idempotent middlewares cause errors to be raised" do
let(:excon) { ::Excon.new("http://localhost:#{VCR::SinatraApp.port}/404_not_200") }
def make_request
VCR.use_cassette('with_errors', :record => :once) do
excon.request(:method => :get, :expects => [200], :idempotent => true).body
end
end
it 'records and plays back properly' do
expect { make_request }.to raise_error(Excon::Errors::NotFound)
expect { make_request }.to raise_error(Excon::Errors::NotFound)
end
end
include_examples "Excon streaming"
context 'when Excon raises an error due to an unexpected response status' do
before(:each) do
allow(VCR).to receive(:real_http_connections_allowed?).and_return(true)
end
it 'still records properly' do
expect(VCR).to receive(:record_http_interaction) do |interaction|
expect(interaction.response.status.code).to eq(404)
expect(interaction.response.body).to eq('404 not 200')
end
expect {
Excon.get("http://localhost:#{VCR::SinatraApp.port}/404_not_200", :expects => 200)
}.to raise_error(Excon::Errors::Error)
end
def error_raised_by
yield
rescue => e
return e
else
raise "No error was raised"
end
it 'raises the same error class as excon itself raises' do
real_error, stubbed_error = 2.times.map do
error_raised_by do
VCR.use_cassette('excon_error', :record => :once) do
Excon.get("http://localhost:#{VCR::SinatraApp.port}/not_found", :expects => 200)
end
end
end
expect(stubbed_error.class).to be(real_error.class)
end
it_behaves_like "request hooks", :excon, :recordable do
undef make_request
def make_request(disabled = false)
expect {
Excon.get(request_url, :expects => 404)
}.to raise_error(Excon::Errors::Error)
end
end
end
describe "VCR.configuration.after_library_hooks_loaded hook" do
it 'disables the webmock excon adapter so it does not conflict with our typhoeus hook' do
expect(::WebMock::HttpLibAdapters::ExconAdapter).to respond_to(:disable!)
expect(::WebMock::HttpLibAdapters::ExconAdapter).to receive(:disable!)
$excon_after_loaded_hook.conditionally_invoke
end
end
end
vcr-5.0.0/spec/lib/vcr/library_hooks/faraday_spec.rb 0000664 0000000 0000000 00000003672 13473056530 0022433 0 ustar 00root root 0000000 0000000 require 'spec_helper'
require 'vcr/library_hooks/faraday'
describe "Faraday hook" do
it 'inserts the VCR middleware just before the adapter' do
conn = Faraday.new(:url => 'http://sushi.com') do |builder|
builder.request :url_encoded
builder.response :logger
builder.adapter :net_http
end
conn.builder.lock!
expect(conn.builder.handlers.last(2).map(&:klass)).to eq([
VCR::Middleware::Faraday,
Faraday::Adapter::NetHttp
])
end
it 'handles the case where no adapter is declared' do
conn = Faraday.new
conn.builder.lock!
expect(conn.builder.handlers.last(2).map(&:klass)).to eq([
VCR::Middleware::Faraday,
Faraday::Adapter::NetHttp
])
end
it 'does nothing if the VCR middleware has already been included' do
conn = Faraday.new(:url => 'http://sushi.com') do |builder|
builder.use VCR::Middleware::Faraday
builder.use Faraday::Response::Logger
builder.use Faraday::Adapter::NetHttp
end
conn.builder.lock!
expect(conn.builder.handlers.map(&:klass)).to eq([
VCR::Middleware::Faraday,
Faraday::Response::Logger,
Faraday::Adapter::NetHttp
])
end
it 'prints a warning if the faraday connection stack contains a middleware after the HTTP adapter' do
conn = Faraday.new(:url => 'http://sushi.com') do |builder|
builder.use Faraday::Adapter::NetHttp
builder.use Faraday::Response::Logger
end
expect(conn.builder).to receive(:warn).with(/Faraday::Response::Logger/)
conn.builder.lock!
end
it 'gracefully handles the case where there is no explicit HTTP adapter' do
conn = Faraday.new(:url => 'http://sushi.com') do |builder|
builder.request :url_encoded
builder.response :logger
end
conn.builder.lock!
expect(conn.builder.handlers.map(&:klass)).to eq([
Faraday::Request::UrlEncoded,
Faraday::Response::Logger,
VCR::Middleware::Faraday
])
end
end
vcr-5.0.0/spec/lib/vcr/library_hooks/typhoeus_0.4_spec.rb 0000664 0000000 0000000 00000002401 13473056530 0023252 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe "Typhoeus 0.4 hook", :with_monkey_patches => :typhoeus_0_4 do
after(:each) do
::Typhoeus::Hydra.clear_stubs
end
def disable_real_connections
::Typhoeus::Hydra.allow_net_connect = false
::Typhoeus::Hydra::NetConnectNotAllowedError
end
def enable_real_connections
::Typhoeus::Hydra.allow_net_connect = true
end
def directly_stub_request(method, url, response_body)
response = ::Typhoeus::Response.new(:code => 200, :body => response_body)
::Typhoeus::Hydra.stub(method, url).and_return(response)
end
it_behaves_like 'a hook into an HTTP library', :typhoeus, 'typhoeus 0.4'
describe "VCR.configuration.after_library_hooks_loaded hook" do
it 'disables the webmock typhoeus adapter so it does not conflict with our typhoeus hook' do
expect(::WebMock::HttpLibAdapters::TyphoeusAdapter).to receive(:disable!)
$typhoeus_after_loaded_hook.conditionally_invoke
end
it "warns about Typhoeus 0.4 deprecation" do
expect(::Kernel).to receive(:warn).with("WARNING: VCR's Typhoeus 0.4 integration is deprecated and will be removed in VCR 3.0.")
$typhoeus_0_4_after_loaded_hook.conditionally_invoke
end
end
end if RUBY_INTERPRETER == :mri && ::Typhoeus::VERSION.to_f < 0.5
vcr-5.0.0/spec/lib/vcr/library_hooks/typhoeus_spec.rb 0000664 0000000 0000000 00000013472 13473056530 0022703 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe "Typhoeus hook", :with_monkey_patches => :typhoeus, :if => (RUBY_INTERPRETER == :mri) do
after(:each) do
::Typhoeus::Expectation.clear
end
def disable_real_connections
::Typhoeus::Config.block_connection = true
::Typhoeus::Errors::NoStub
end
def enable_real_connections
::Typhoeus::Config.block_connection = false
end
def directly_stub_request(method, url, response_body)
response = ::Typhoeus::Response.new(:code => 200, :body => response_body)
::Typhoeus.stub(url, :method => method).and_return(response)
end
it_behaves_like 'a hook into an HTTP library', :typhoeus, 'typhoeus'
describe "VCR.configuration.after_library_hooks_loaded hook" do
it 'disables the webmock typhoeus adapter so it does not conflict with our typhoeus hook' do
expect(::WebMock::HttpLibAdapters::TyphoeusAdapter).to receive(:disable!)
$typhoeus_after_loaded_hook.conditionally_invoke
end
end
context 'when there are nested hydra queues' do
def make_requests
VCR.use_cassette("nested") do
response_1 = response_2 = nil
hydra = Typhoeus::Hydra.new
request = Typhoeus::Request.new("http://localhost:#{VCR::SinatraApp.port}/")
request.on_success do |r1|
response_1 = r1
nested = Typhoeus::Request.new("http://localhost:#{VCR::SinatraApp.port}/foo")
nested.on_success { |r2| response_2 = r2 }
hydra.queue(nested)
end
hydra.queue(request)
hydra.run
return body_for(response_1), body_for(response_2)
end
end
def body_for(response)
return :no_response if response.nil?
response.body
end
it 'records and plays back properly' do
recorded = make_requests
played_back = make_requests
expect(played_back).to eq(recorded)
end
end
context "when used with a typhoeus-based faraday connection" do
let(:base_url) { "http://localhost:#{VCR::SinatraApp.port}" }
let(:conn) do
Faraday.new(:url => base_url) do |faraday|
faraday.adapter :typhoeus
end
end
def get_response
# Ensure faraday hook doesn't handle the request.
VCR.library_hooks.exclusively_enabled(:typhoeus) do
VCR.use_cassette("faraday") do
conn.get("/")
end
end
end
it 'records and replays headers correctly' do
recorded = get_response
played_back = get_response
expect(played_back.headers).to eq(recorded.headers)
end
end
context 'when a request is made with a hash for the POST body' do
def make_request
VCR.use_cassette("hash_body") do
Typhoeus::Request.post(
"http://localhost:#{VCR::SinatraApp.port}/return-request-body",
:body => { :foo => "17" }
)
end
end
it 'records and replays correctly' do
recorded = make_request
played_back = make_request
expect(recorded.body).to eq("foo=17")
expect(played_back.body).to eq(recorded.body)
end
end
context 'when using on_body callback' do
def make_request
VCR.use_cassette('no_body') do
request = Typhoeus::Request.new("http://localhost:#{VCR::SinatraApp.port}/localhost_test")
request.on_headers { on_headers_counter.increment }
request.on_body { on_body_counter.increment }
request.run
end
end
let(:on_headers_counter) { double(:increment => nil) }
let(:on_body_counter) { double(:increment => nil) }
it 'records and replays correctly' do
expect(on_headers_counter).to receive(:increment).exactly(2).times
expect(on_body_counter).to receive(:increment).exactly(2).times
recorded = make_request
played_back = make_request
expect(recorded.body).to eq('Localhost response')
expect(played_back.body).to eq(recorded.body)
end
end
context 'when using on_body callback returning :abort' do
def make_request
VCR.use_cassette('no_body') do
request = Typhoeus::Request.new("http://localhost:#{VCR::SinatraApp.port}/localhost_test")
request.on_body { next :abort }
request.run
end
end
it 'records and replays correctly' do
recorded = make_request
played_back = make_request
expect(recorded.body).to eq('Localhost response')
expect(played_back.body).to eq(recorded.body)
end
end
context '#effective_url' do
ResponseValues = Struct.new(:status, :body, :effective_url)
def url_for(path)
"http://localhost:#{VCR::SinatraApp.port}#{path}"
end
def make_single_request(path, options = {})
VCR.use_cassette('single') do |cassette|
response = Typhoeus::Request.new(url_for(path), options).run
yield cassette if block_given?
ResponseValues.new(
response.code,
response.body,
response.effective_url
)
end
end
it 'records and plays back properly' do
recorded = make_single_request('/')
played_back = make_single_request('/')
expect(recorded.effective_url).to eq(url_for('/'))
expect(played_back).to eq(recorded)
end
it 'falls back to the request url when it was not recorded (e.g. on VCR <= 2.5.0)' do
make_single_request('/') do |cassette|
cassette.new_recorded_interactions.each { |i| i.response.adapter_metadata.clear }
end
played_back = make_single_request('/')
expect(played_back.effective_url).to eq(url_for('/'))
end
context "when following redirects" do
it 'records and plays back properly' do
recorded = make_single_request('/redirect-to-root', :followlocation => true)
played_back = make_single_request('/redirect-to-root', :followlocation => true)
expect(recorded.effective_url).to eq(url_for('/'))
expect(played_back).to eq(recorded)
end
end
end
end
vcr-5.0.0/spec/lib/vcr/library_hooks/webmock_spec.rb 0000664 0000000 0000000 00000007255 13473056530 0022454 0 ustar 00root root 0000000 0000000 require 'spec_helper'
require 'support/shared_example_groups/excon'
describe "WebMock hook", :with_monkey_patches => :webmock do
after(:each) do
::WebMock.reset!
end
def disable_real_connections(options = {})
::WebMock.disable_net_connect!(options)
::WebMock::NetConnectNotAllowedError
end
def enable_real_connections
::WebMock.allow_net_connect!
end
def directly_stub_request(method, url, response_body)
::WebMock.stub_request(method, url).to_return(:body => response_body)
end
describe "our WebMock.after_request hook" do
let(:webmock_request) { ::WebMock::RequestSignature.new(:get, "http://foo.com/", :body => "", :headers => {}) }
let(:webmock_response) { ::WebMock::Response.new(:body => 'OK', :status => [200, '']) }
def run_after_request_callback
::WebMock::CallbackRegistry.invoke_callbacks(
{ :real_request => true },
webmock_request,
webmock_response)
end
it 'removes the @__typed_vcr_request instance variable so as not to pollute the webmock object' do
request = VCR::Request::Typed.new(VCR::Request, :ignored?)
webmock_request.instance_variable_set(:@__typed_vcr_request, request)
run_after_request_callback
expect(webmock_request.instance_variables.map(&:to_sym)).not_to include(:@__typed_vcr_request)
end
context "when there'ss a bug and the request does not have the @__typed_vcr_request in the after_request callbacks" do
let(:warner) { VCR::LibraryHooks::WebMock }
before { allow(warner).to receive(:warn) }
it 'records the HTTP interaction properly' do
expect(VCR).to receive(:record_http_interaction) do |i|
expect(i.request.uri).to eq("http://foo.com/")
expect(i.response.body).to eq("OK")
end
run_after_request_callback
end
it 'invokes the after_http_request hook with an :unknown request' do
request = nil
VCR.configuration.after_http_request do |req, res|
request = req
end
run_after_request_callback
expect(request.uri).to eq("http://foo.com/")
expect(request.type).to eq(:unknown)
end
it 'prints a warning' do
expect(warner).to receive(:warn).at_least(:once).with(/bug.*after_request/)
run_after_request_callback
end
end
end
http_libs = %w[net/http patron httpclient em-http-request curb typhoeus excon]
http_libs.each do |lib|
other = []
other << :status_message_not_exposed if lib == 'excon'
it_behaves_like 'a hook into an HTTP library', :webmock, lib, *other do
if lib == 'net/http'
def normalize_request_headers(headers)
headers.merge(DEFAULT_REQUEST_HEADERS)
end
end
end
http_lib_unsupported = (RUBY_INTERPRETER != :mri && lib =~ /(typhoeus|curb|patron|em-http)/)
adapter_module = HTTP_LIBRARY_ADAPTERS.fetch(lib)
describe "using #{adapter_module.http_library_name}", :unless => http_lib_unsupported do
include adapter_module
let!(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
context 'when real connections are disabled and VCR is turned off' do
it 'can allow connections to localhost' do
VCR.turn_off!
disable_real_connections(:allow_localhost => true)
expect {
make_http_request(:get, request_url)
}.to_not raise_error
end
it 'can allow connections to matching urls' do
VCR.turn_off!
disable_real_connections(:allow => /foo/)
expect {
make_http_request(:get, request_url)
}.to_not raise_error
end
end
end
end
it_behaves_like "Excon streaming"
end
vcr-5.0.0/spec/lib/vcr/library_hooks_spec.rb 0000664 0000000 0000000 00000003073 13473056530 0021017 0 ustar 00root root 0000000 0000000 require 'vcr/library_hooks'
module VCR
describe LibraryHooks do
describe '#disabled?' do
it 'returns false by default for any argument given' do
expect(subject.disabled?(:foo)).to be false
expect(subject.disabled?(:bar)).to be false
end
context 'when a library hook is exclusively enabled' do
it 'returns false for the exclusively enabled hook' do
faraday_disabled = nil
subject.exclusively_enabled :faraday do
faraday_disabled = subject.disabled?(:faraday)
end
expect(faraday_disabled).to eq(false)
end
it 'returns true for every other argument given' do
foo_disabled = bar_disabled = nil
subject.exclusively_enabled :faraday do
foo_disabled = subject.disabled?(:foo)
bar_disabled = subject.disabled?(:bar)
end
expect(foo_disabled).to be true
expect(bar_disabled).to be true
end
end
end
describe '#exclusively_enabled' do
it 'restores all hook to being enabled when the block completes' do
subject.exclusively_enabled(:faraday) { }
expect(subject.disabled?(:foo)).to be false
expect(subject.disabled?(:faraday)).to be false
end
it 'restores all hooks to being enabled when the block completes, even if there is an error' do
subject.exclusively_enabled(:faraday) { raise "boom" } rescue
expect(subject.disabled?(:foo)).to be false
expect(subject.disabled?(:faraday)).to be false
end
end
end
end
vcr-5.0.0/spec/lib/vcr/middleware/ 0000775 0000000 0000000 00000000000 13473056530 0016723 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/lib/vcr/middleware/faraday_spec.rb 0000664 0000000 0000000 00000013064 13473056530 0021675 0 ustar 00root root 0000000 0000000 require 'spec_helper'
require 'vcr/library_hooks/faraday'
describe VCR::Middleware::Faraday do
http_libs = %w[ typhoeus net_http patron ]
http_libs.each do |lib|
flags = [ :does_not_support_rotating_responses ]
if lib == 'typhoeus'
flags << :status_message_not_exposed
end
it_behaves_like 'a hook into an HTTP library', :faraday, "faraday (w/ #{lib})", *flags
end
context 'when performing a multipart upload' do
let(:connection) do
::Faraday.new("http://localhost:#{VCR::SinatraApp.port}/") do |b|
b.request :multipart
end
end
def self.test_recording
it 'records the request body correctly' do
payload = { :file => Faraday::UploadIO.new(__FILE__, 'text/plain') }
expect(VCR).to receive(:record_http_interaction) do |i|
expect(i.request.headers['Content-Type'].first).to include("multipart")
expect(i.request.body).to include(File.read(__FILE__))
end
VCR.use_cassette("upload") do
connection.post '/files', payload
end
end
end
context 'when the net_http adapter is used' do
before { connection.builder.adapter :net_http }
test_recording
end
context 'when no adapter is used' do
test_recording
end
end
context 'when extending the response body with an extension module' do
let(:connection) { ::Faraday.new("http://localhost:#{VCR::SinatraApp.port}/") }
def process_response(response)
response.body.extend Module.new { attr_accessor :_response }
response.body._response = response
end
it 'does not record the body extensions to the cassette' do
3.times do |i|
VCR.use_cassette("hack", :record => :new_episodes) do
response = connection.get("/foo")
process_response(response)
# Do something different after the first time to
# ensure new interactions are added to an existing
# cassette.
if i > 1
response = connection.get("/")
process_response(response)
end
end
end
contents = VCR::Cassette.new("hack").send(:raw_cassette_bytes)
expect(contents).not_to include("ruby/object:Faraday::Response")
end
end
context 'when making parallel requests' do
include VCRStubHelpers
let(:connection) { ::Faraday.new { |b| b.adapter :typhoeus } }
let(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/" }
it 'works correctly with multiple parallel requests' do
recorded, played_back = [1, 2].map do
responses = []
VCR.use_cassette("multiple_parallel") do
connection.in_parallel do
responses << connection.get(request_url)
responses << connection.get(request_url)
end
end
responses.map(&:body)
end
# there should be no blanks
expect(recorded.select { |r| r.to_s == '' }).to eq([])
expect(played_back).to eq(recorded)
end
shared_examples_for "exclusive library hook" do
def make_request
connection.in_parallel { connection.get(request_url) }
end
it 'makes the faraday middleware exclusively enabled for the duration of the request' do
expect(VCR.library_hooks).not_to be_disabled(:webmock)
hook_called = false
VCR.configuration.after_http_request do
hook_called = true
expect(VCR.library_hooks).to be_disabled(:webmock)
end
make_request
expect(VCR.library_hooks).not_to be_disabled(:webmock)
expect(hook_called).to be true
end
end
context 'for an ignored request' do
before(:each) { VCR.configuration.ignore_request { true } }
it_behaves_like "exclusive library hook"
end
context 'for a stubbed request' do
it_behaves_like "exclusive library hook" do
before(:each) do
stub_requests([http_interaction(request_url)], [:method, :uri])
end
end
end
context "when another adapter is exclusive" do
it 'still makes requests properly' do
response = VCR.library_hooks.exclusively_enabled(:typhoeus) do
Faraday.get("http://localhost:#{VCR::SinatraApp.port}/")
end
expect(response.body).to eq("GET to root")
end
end
context 'for a recorded request' do
let!(:inserted_cassette) { VCR.insert_cassette('new_cassette') }
before(:each) { expect(VCR).to receive(:record_http_interaction) }
it_behaves_like "exclusive library hook"
end
context 'for a disallowed request' do
it_behaves_like "exclusive library hook" do
undef make_request
def make_request
expect {
connection.in_parallel { connection.get(request_url) }
}.to raise_error(VCR::Errors::UnhandledHTTPRequestError)
end
end
end
it_behaves_like "request hooks", :faraday, :recordable do
let!(:inserted_cassette) { VCR.insert_cassette('new_cassette') }
undef make_request
def make_request(disabled = false)
response = nil
connection.in_parallel do
response = connection.get(request_url)
end
response
end
it 'can be used to eject a cassette after the request is recorded' do
VCR.configuration.after_http_request { |request| VCR.eject_cassette }
expect(VCR).to receive(:record_http_interaction) do |interaction|
expect(VCR.current_cassette).to be(inserted_cassette)
end
make_request
expect(VCR.current_cassette).to be_nil
end
end
end if defined?(::Typhoeus)
end
vcr-5.0.0/spec/lib/vcr/middleware/rack_spec.rb 0000664 0000000 0000000 00000006640 13473056530 0021210 0 ustar 00root root 0000000 0000000 require 'spec_helper'
require 'vcr/middleware/rack'
module VCR
module Middleware
describe CassetteArguments do
describe '#name' do
it 'initially returns nil' do
expect(subject.name).to be_nil
end
it 'stores the given value, returning it when no arg is given' do
subject.name :value1
expect(subject.name).to eq(:value1)
subject.name :value2
expect(subject.name).to eq(:value2)
end
end
describe '#options' do
it 'initially returns an empty hash' do
expect(subject.options).to eq({})
end
it 'merges the given hash options, returning them when no arg is given' do
subject.options :record => :new_episodes
expect(subject.options).to eq({ :record => :new_episodes })
subject.options :erb => true
expect(subject.options).to eq({ :record => :new_episodes, :erb => true })
end
end
end
describe Rack do
describe '.new' do
it 'raises an error if no cassette arguments block is provided' do
expect {
described_class.new(lambda { |env| })
}.to raise_error(ArgumentError)
end
end
describe '#call' do
let(:env_hash) { { :env => :hash } }
it 'calls the provided rack app and returns its response' do
rack_app = double
expect(rack_app).to receive(:call).with(env_hash).and_return(:response)
instance = described_class.new(rack_app) { |c| c.name 'cassette_name' }
expect(instance.call(env_hash)).to eq(:response)
end
it 'uses a cassette when the rack app is called' do
expect(VCR.current_cassette).to be_nil
rack_app = lambda { |env| expect(VCR.current_cassette).not_to be_nil }
instance = described_class.new(rack_app) { |c| c.name 'cassette_name' }
instance.call({})
expect(VCR.current_cassette).to be_nil
end
it 'sets the cassette name based on the provided block' do
rack_app = lambda do |env|
expect(VCR.current_cassette.name).to eq('rack_cassette')
end
instance = described_class.new(rack_app) { |c| c.name 'rack_cassette' }
instance.call({})
end
it 'sets the cassette options based on the provided block' do
rack_app = lambda do |env|
expect(VCR.current_cassette.erb).to eq({ :foo => :bar })
end
instance = described_class.new(rack_app, &lambda do |c|
c.name 'c'
c.options :erb => { :foo => :bar }
end)
instance.call({})
end
it 'yields the rack env to the provided block when the block accepts 2 arguments' do
instance = described_class.new(lambda { |env| }, &lambda do |c, env|
expect(env).to eq(env_hash)
c.name 'c'
end)
instance.call(env_hash)
end
end
let(:threaded_app) do
lambda do |env|
sleep 0.15
expect(VCR.send(:cassettes).size).to eq(1)
[200, {}, ['OK']]
end
end
it 'is thread safe' do
stack = described_class.new(threaded_app) do |cassette|
cassette.name 'c'
end
thread = Thread.new { stack.call({}) }
stack.call({})
thread.join
expect(VCR.current_cassette).to be_nil
end
end
end
end
vcr-5.0.0/spec/lib/vcr/request_ignorer_spec.rb 0000664 0000000 0000000 00000006266 13473056530 0021374 0 ustar 00root root 0000000 0000000 require 'vcr/structs'
require 'vcr/request_ignorer'
module VCR
describe RequestIgnorer do
def request(uri)
VCR::Request.new.tap { |r| r.uri = uri }
end
shared_examples_for "#ignore?" do |url, expected_value|
it "returns #{expected_value} if given a request with a url like #{url}" do
expect(subject.ignore?(request(url))).to eq(expected_value)
end
end
context 'when example.com and example.net are ignored' do
before(:each) { subject.ignore_hosts 'example.com', 'example.net' }
it_behaves_like "#ignore?", "http://www.example.com/foo", false
it_behaves_like "#ignore?", "http://example.com/foo", true
it_behaves_like "#ignore?", "http://example.net:890/foo", true
it_behaves_like "#ignore?", "http://some-other-domain.com/", false
end
context 'when example.com is unignored' do
before(:each) do
subject.instance_variable_set(:@ignored_hosts, Set['example.com'])
subject.unignore_hosts 'example.com'
end
it_behaves_like "#ignore?", "http://example.com/foo", false
end
context 'when two of three example hosts are unignored' do
before(:each) do
subject.instance_variable_set(:@ignored_hosts, Set['example.com', 'example.net', 'example.org'])
subject.unignore_hosts 'example.com', 'example.net'
end
it_behaves_like "#ignore?", "http://example.com/foo", false
it_behaves_like "#ignore?", "http://example.net:890/foo", false
it_behaves_like "#ignore?", "https://example.org:890/foo", true
end
context 'when not ignored host is unignored' do
it 'no errors should be raised' do
expect { subject.unignore_hosts 'example.com' }.not_to raise_error
end
end
context 'when ignore_localhost is set to true' do
before(:each) { subject.ignore_localhost = true }
it_behaves_like "#ignore?", "http://some-host.com/foo", false
RequestIgnorer::LOCALHOST_ALIASES.each do |host|
it_behaves_like "#ignore?", "http://#{host}/foo", true
end
end
context 'when ignore_localhost is not set' do
it_behaves_like "#ignore?", "http://some-host.com/foo", false
RequestIgnorer::LOCALHOST_ALIASES.each do |host|
it_behaves_like "#ignore?", "http://#{host}/foo", false
end
end
context 'when ignore_localhost is set to false after being set to true' do
before(:each) do
subject.ignore_localhost = true
subject.ignore_localhost = false
end
it_behaves_like "#ignore?", "http://some-host.com/foo", false
RequestIgnorer::LOCALHOST_ALIASES.each do |host|
it_behaves_like "#ignore?", "http://#{host}/foo", false
end
end
context 'when a custom ignore_request hook has been set' do
before(:each) do
subject.ignore_request do |request|
URI(request.uri).port == 5
end
end
it 'ignores requests for which the block returns true' do
expect(subject.ignore?(request('http://foo.com:5/bar'))).to be true
end
it 'does not ignore requests for which the block returns false' do
expect(subject.ignore?(request('http://foo.com:6/bar'))).to be false
end
end
end
end
vcr-5.0.0/spec/lib/vcr/request_matcher_registry_spec.rb 0000664 0000000 0000000 00000027041 13473056530 0023274 0 ustar 00root root 0000000 0000000 require 'vcr/request_matcher_registry'
require 'vcr/structs'
require 'support/limited_uri'
require 'cgi'
require 'support/configuration_stubbing'
module VCR
describe RequestMatcherRegistry do
include_context "configuration stubbing"
before do
allow(config).to receive(:uri_parser) { LimitedURI }
allow(config).to receive(:query_parser) { CGI.method(:parse) }
end
def request_with(values)
VCR::Request.new.tap do |request|
values.each do |name, value|
request.send("#{name}=", value)
end
end
end
describe "#register" do
it 'registers a request matcher block that can be used later' do
matcher_called = false
subject.register(:my_matcher) { |*a| matcher_called = true }
subject[:my_matcher].matches?(double, double)
expect(matcher_called).to be true
end
context 'when there is already a matcher for the given name' do
before(:each) do
subject.register(:foo) { |*a| false }
allow(subject).to receive :warn
end
it 'overrides the existing matcher' do
subject.register(:foo) { |*a| true }
expect(subject[:foo].matches?(double, double)).to be true
end
it 'warns that there is a name collision' do
expect(subject).to receive(:warn).with(
/WARNING: There is already a VCR request matcher registered for :foo\. Overriding it/
)
subject.register(:foo) { |*a| true }
end
end
end
describe "#[]" do
it 'returns a previously registered matcher' do
matcher = lambda { }
subject.register(:my_matcher, &matcher)
expect(subject[:my_matcher]).to eq(RequestMatcherRegistry::Matcher.new(matcher))
end
it 'raises an ArgumentError when no matcher has been registered for the given name' do
expect {
subject[:some_unregistered_matcher]
}.to raise_error(VCR::Errors::UnregisteredMatcherError)
end
it 'returns an object that calls the named block when #matches? is called on it' do
subject.register(:foo) { |r1, r2| r1 == 5 || r2 == 10 }
expect(subject[:foo].matches?(5, 0)).to be true
expect(subject[:foo].matches?(0, 10)).to be true
expect(subject[:foo].matches?(7, 7)).to be false
end
it 'returns an object that calls the given callable when #matches? is called on it' do
block_called = false
subject[lambda { |r1, r2| block_called = true }].matches?(5, 0)
expect(block_called).to be true
end
end
[:uri_without_param, :uri_without_params].each do |meth|
describe "##{meth}" do
it 'returns a matcher that can be registered for later use' do
matcher = subject.send(meth, :foo)
subject.register(:uri_without_foo, &matcher)
matches = subject[:uri_without_foo].matches?(
request_with(:uri => 'http://example.com/search?foo=123'),
request_with(:uri => 'http://example.com/search?foo=123')
)
expect(matches).to be true
end
it 'matches two requests with URIs that are identical' do
matches = subject[subject.send(meth, :foo)].matches?(
request_with(:uri => 'http://example.com/search?foo=123'),
request_with(:uri => 'http://example.com/search?foo=123')
)
expect(matches).to be true
end
it 'does not match two requests with different path parts' do
matches = subject[subject.send(meth, :foo)].matches?(
request_with(:uri => 'http://example.com/search?foo=123'),
request_with(:uri => 'http://example.com/find?foo=123')
)
expect(matches).to be false
end
it 'ignores the given query parameters when it is at the start' do
matches = subject[subject.send(meth, :foo)].matches?(
request_with(:uri => 'http://example.com/search?foo=123&bar=r'),
request_with(:uri => 'http://example.com/search?foo=124&bar=r')
)
expect(matches).to be true
end
it 'ignores the given query parameters when it is at the end' do
matches = subject[subject.send(meth, :bar)].matches?(
request_with(:uri => 'http://example.com/search?foo=124&bar=r'),
request_with(:uri => 'http://example.com/search?foo=124&bar=q')
)
expect(matches).to be true
end
it 'still takes into account other query params' do
matches = subject[subject.send(meth, :bar)].matches?(
request_with(:uri => 'http://example.com/search?foo=123&bar=r'),
request_with(:uri => 'http://example.com/search?foo=124&bar=q')
)
expect(matches).to be false
end
it 'handles multiple query params of the same name' do
matches = subject[subject.send(meth, :tag)].matches?(
request_with(:uri => 'http://example.com/search?foo=124&tag[]=a&tag[]=b'),
request_with(:uri => 'http://example.com/search?foo=124&tag[]=d&tag[]=e')
)
expect(matches).to be true
end
it 'can ignore multiple named parameters' do
matches = subject[subject.send(meth, :foo, :bar)].matches?(
request_with(:uri => 'http://example.com/search?foo=123&bar=r&baz=9'),
request_with(:uri => 'http://example.com/search?foo=124&baz=9&bar=q')
)
expect(matches).to be true
end
it 'matches two requests with URIs that have no params' do
matches = subject[subject.send(meth, :foo, :bar)].matches?(
request_with(:uri => 'http://example.com/search'),
request_with(:uri => 'http://example.com/search')
)
expect(matches).to be true
end
it 'does not match two requests with URIs that have no params but different paths' do
matches = subject[subject.send(meth, :foo, :bar)].matches?(
request_with(:uri => 'http://example.com/foo'),
request_with(:uri => 'http://example.com/bar')
)
expect(matches).to be false
end
it 'matches a second request when all parameters are filtered' do
matches = subject[subject.send(meth, :q, :oq)].matches?(
request_with(:uri => 'http://example.com/search'),
request_with(:uri => 'http://example.com/search?q=vcr&oq=vcr')
)
expect(matches).to be true
end
end
end
describe "built-ins" do
describe ":method" do
it 'matches when it is the same' do
matches = subject[:method].matches?(
request_with(:method => :get),
request_with(:method => :get)
)
expect(matches).to be true
end
it 'does not match when it is not the same' do
matches = subject[:method].matches?(
request_with(:method => :get),
request_with(:method => :post)
)
expect(matches).to be false
end
end
describe ":uri" do
it 'matches when it is exactly the same' do
matches = subject[:uri].matches?(
request_with(:uri => 'http://foo.com/bar?baz=7'),
request_with(:uri => 'http://foo.com/bar?baz=7')
)
expect(matches).to be true
end
it 'does not match when it is different' do
matches = subject[:uri].matches?(
request_with(:uri => 'http://foo1.com/bar?baz=7'),
request_with(:uri => 'http://foo2.com/bar?baz=7')
)
expect(matches).to be false
end
end
describe ":host" do
it 'matches when it is the same' do
matches = subject[:host].matches?(
request_with(:uri => 'http://foo.com/bar'),
request_with(:uri => 'http://foo.com/car')
)
expect(matches).to be true
end
it 'does not match when it is not the same' do
matches = subject[:host].matches?(
request_with(:uri => 'http://foo.com/bar'),
request_with(:uri => 'http://goo.com/bar')
)
expect(matches).to be false
end
end
describe ":path" do
it 'matches when it is the same' do
matches = subject[:path].matches?(
request_with(:uri => 'http://foo.com/bar?a=8'),
request_with(:uri => 'http://goo.com/bar?a=9')
)
expect(matches).to be true
end
it 'does not match when it is not the same' do
matches = subject[:path].matches?(
request_with(:uri => 'http://foo.com/bar?a=8'),
request_with(:uri => 'http://foo.com/car?a=8')
)
expect(matches).to be false
end
end
describe ":body" do
it 'matches when it is the same' do
matches = subject[:body].matches?(
request_with(:body => 'foo'),
request_with(:body => 'foo')
)
expect(matches).to be true
end
it 'does not match when it is not the same' do
matches = subject[:body].matches?(
request_with(:body => 'foo'),
request_with(:body => 'bar')
)
expect(matches).to be false
end
end
describe ":body_as_json" do
it 'matches when the body json is reordered' do
matches = subject[:body_as_json].matches?(
request_with(:body => '{ "a": "1", "b": "2" }'),
request_with(:body => '{ "b": "2", "a": "1" }')
)
expect(matches).to be true
end
it 'does not match when json is not the same' do
matches = subject[:body_as_json].matches?(
request_with(:body => '{ "a": "1", "b": "2" }'),
request_with(:body => '{ "a": "1", "b": "1" }')
)
expect(matches).to be false
end
end
describe ":headers" do
it 'matches when it is the same' do
matches = subject[:headers].matches?(
request_with(:headers => { 'a' => 1, 'b' => 2 }),
request_with(:headers => { 'b' => 2, 'a' => 1 })
)
expect(matches).to be true
end
it 'does not match when it is not the same' do
matches = subject[:headers].matches?(
request_with(:headers => { 'a' => 3, 'b' => 2 }),
request_with(:headers => { 'b' => 2, 'a' => 1 })
)
expect(matches).to be false
end
end
describe ":query" do
it 'matches when it is identical' do
matches = subject[:query].matches?(
request_with(:uri => 'http://foo.com/bar?a=8'),
request_with(:uri => 'http://goo.com/car?a=8')
)
expect(matches).to be true
end
it 'matches when empty' do
matches = subject[:query].matches?(
request_with(:uri => 'http://foo.com/bar'),
request_with(:uri => 'http://goo.com/car')
)
expect(matches).to be true
end
it 'matches when parameters are reordered' do
matches = subject[:query].matches?(
request_with(:uri => 'http://foo.com/bar?a=8&b=9'),
request_with(:uri => 'http://goo.com/car?b=9&a=8')
)
expect(matches).to be true
end
it 'does not match when it is not the same' do
matches = subject[:query].matches?(
request_with(:uri => 'http://foo.com/bar?a=8'),
request_with(:uri => 'http://goo.com/car?b=8')
)
expect(matches).to be false
end
end
end
end
end
vcr-5.0.0/spec/lib/vcr/structs_spec.rb 0000664 0000000 0000000 00000061510 13473056530 0017657 0 ustar 00root root 0000000 0000000 # encoding: UTF-8
require 'support/ruby_interpreter'
require 'yaml'
require 'vcr/structs'
require 'vcr/errors'
require 'zlib'
require 'stringio'
require 'support/limited_uri'
require 'support/configuration_stubbing'
shared_examples_for "a header normalizer" do
let(:instance) do
with_headers('Some_Header' => 'value1', 'aNother' => ['a', 'b'], 'third' => [], 'fourth' => nil)
end
it 'ensures header keys are serialized to yaml as raw strings' do
key = 'my-key'
key.instance_variable_set(:@foo, 7)
instance = with_headers(key => ['value1'])
expect(YAML.dump(instance.headers)).to eq(YAML.dump('my-key' => ['value1']))
end
it 'ensures header values are serialized to yaml as raw strings' do
value = 'my-value'
value.instance_variable_set(:@foo, 7)
instance = with_headers('my-key' => [value])
expect(YAML.dump(instance.headers)).to eq(YAML.dump('my-key' => ['my-value']))
end
it 'handles nested arrays' do
accept_encoding = [["gzip", "1.0"], ["deflate", "1.0"], ["sdch", "1.0"]]
instance = with_headers('accept-encoding' => accept_encoding)
expect(instance.headers['accept-encoding']).to eq(accept_encoding)
end
it 'handles nested arrays with floats' do
accept_encoding = [["gzip", 1.0], ["deflate", 1.0], ["sdch", 1.0]]
instance = with_headers('accept-encoding' => accept_encoding)
expect(instance.headers['accept-encoding']).to eq(accept_encoding)
end
end
shared_examples_for "a body normalizer" do
it "ensures the body is serialized to yaml as a raw string" do
body = "My String"
body.instance_variable_set(:@foo, 7)
expect(YAML.dump(instance(body).body)).to eq(YAML.dump("My String"))
end
it 'converts nil to a blank string' do
expect(instance(nil).body).to eq("")
end
it 'raises an error if given another type of object as the body' do
expect {
instance(:a => "hash")
}.to raise_error(ArgumentError)
end
end
module VCR
describe HTTPInteraction do
include_context "configuration stubbing"
before { allow(config).to receive(:uri_parser) { LimitedURI } }
if ''.respond_to?(:encoding)
def body_hash(key, value)
{ key => value, 'encoding' => 'UTF-8' }
end
else
def body_hash(key, value)
{ key => value }
end
end
describe "#recorded_at" do
let(:now) { Time.now }
it 'is initialized to the current time' do
allow(Time).to receive(:now).and_return(now)
expect(VCR::HTTPInteraction.new.recorded_at).to eq(now)
end
end
let(:status) { ResponseStatus.new(200, "OK") }
let(:response) { Response.new(status, { "foo" => ["bar"] }, "res body", "1.1") }
let(:request) { Request.new(:get, "http://foo.com/", "req body", { "bar" => ["foo"] }) }
let(:recorded_at) { Time.utc(2011, 5, 4, 12, 30) }
let(:interaction) { HTTPInteraction.new(request, response, recorded_at) }
describe ".from_hash" do
let(:hash) do
{
'request' => {
'method' => 'get',
'uri' => 'http://foo.com/',
'body' => body_hash('string', 'req body'),
'headers' => { "bar" => ["foo"] }
},
'response' => {
'status' => {
'code' => 200,
'message' => 'OK'
},
'headers' => { "foo" => ["bar"] },
'body' => body_hash('string', 'res body'),
'http_version' => '1.1'
},
'recorded_at' => "Wed, 04 May 2011 12:30:00 GMT"
}
end
it 'constructs an HTTP interaction from the given hash' do
expect(HTTPInteraction.from_hash(hash)).to eq(interaction)
end
it 'initializes the recorded_at timestamp from the hash' do
expect(HTTPInteraction.from_hash(hash).recorded_at).to eq(recorded_at)
end
it 'initializes the response adapter_metadata from the hash if it is included' do
hash['response']['adapter_metadata'] = { 'foo' => 12 }
interaction = HTTPInteraction.from_hash(hash)
expect(interaction.response.adapter_metadata).to eq("foo" => 12)
end
it 'works when the response adapter_metadata is missing' do
expect(hash['response'].keys).not_to include('adapter_metadata')
interaction = HTTPInteraction.from_hash(hash)
expect(interaction.response.adapter_metadata).to eq({})
end
it 'uses a blank request when the hash lacks one' do
hash.delete('request')
i = HTTPInteraction.from_hash(hash)
expect(i.request).to eq(Request.new)
end
it 'uses a blank response when the hash lacks one' do
hash.delete('response')
i = HTTPInteraction.from_hash(hash)
expect(i.response).to eq(Response.new(ResponseStatus.new))
end
it 'decodes the base64 body string' do
hash['request']['body'] = body_hash('base64_string', Base64.encode64('req body'))
hash['response']['body'] = body_hash('base64_string', Base64.encode64('res body'))
i = HTTPInteraction.from_hash(hash)
expect(i.request.body).to eq('req body')
expect(i.response.body).to eq('res body')
end
if ''.respond_to?(:encoding)
it 'force encodes the decoded base64 string as the original encoding' do
string = "café"
string.force_encoding("US-ASCII")
expect(string).not_to be_valid_encoding
hash['request']['body'] = { 'base64_string' => Base64.encode64(string.dup), 'encoding' => 'US-ASCII' }
hash['response']['body'] = { 'base64_string' => Base64.encode64(string.dup), 'encoding' => 'US-ASCII' }
i = HTTPInteraction.from_hash(hash)
expect(i.request.body.encoding.name).to eq("US-ASCII")
expect(i.response.body.encoding.name).to eq("US-ASCII")
expect(i.request.body.bytes.to_a).to eq(string.bytes.to_a)
expect(i.response.body.bytes.to_a).to eq(string.bytes.to_a)
expect(i.request.body).not_to be_valid_encoding
expect(i.response.body).not_to be_valid_encoding
end
it 'does not attempt to force encode the decoded base64 string when there is no encoding given (i.e. if the cassette was recorded on ruby 1.8)' do
hash['request']['body'] = { 'base64_string' => Base64.encode64('foo') }
i = HTTPInteraction.from_hash(hash)
expect(i.request.body).to eq('foo')
expect(i.request.body.encoding.name).to eq("ASCII-8BIT")
end
it 'tries to encode strings to the original encoding' do
hash['request']['body'] = { 'string' => "abc", 'encoding' => 'ISO-8859-1' }
hash['response']['body'] = { 'string' => "abc", 'encoding' => 'ISO-8859-1' }
i = HTTPInteraction.from_hash(hash)
expect(i.request.body).to eq("abc")
expect(i.response.body).to eq("abc")
expect(i.request.body.encoding.name).to eq("ISO-8859-1")
expect(i.response.body.encoding.name).to eq("ISO-8859-1")
end
it 'does not attempt to encode the string when there is no encoding given (i.e. if the cassette was recorded on ruby 1.8)' do
string = 'foo'
string.force_encoding("ISO-8859-1")
hash['request']['body'] = { 'string' => string }
i = HTTPInteraction.from_hash(hash)
expect(i.request.body).to eq('foo')
expect(i.request.body.encoding.name).to eq("ISO-8859-1")
end
it 'force encodes to ASCII-8BIT (since it just means "no encoding" or binary)' do
string = "\u00f6"
string.encode("UTF-8")
expect(string).to be_valid_encoding
hash['request']['body'] = { 'string' => string, 'encoding' => 'ASCII-8BIT' }
expect(Request).not_to receive(:warn)
i = HTTPInteraction.from_hash(hash)
expect(i.request.body).to eq(string)
expect(i.request.body.bytes.to_a).to eq(string.bytes.to_a)
expect(i.request.body.encoding.name).to eq("ASCII-8BIT")
end
context 'when the string cannot be encoded as the original encoding' do
def verify_encoding_error
expect { "\xFAbc".encode("ISO-8859-1") }.to raise_error(EncodingError)
end
before do
allow(Request).to receive(:warn)
allow(Response).to receive(:warn)
hash['request']['body'] = { 'string' => "\xFAbc", 'encoding' => 'ISO-8859-1' }
hash['response']['body'] = { 'string' => "\xFAbc", 'encoding' => 'ISO-8859-1' }
verify_encoding_error
end
it 'does not force the encoding' do
i = HTTPInteraction.from_hash(hash)
expect(i.request.body).to eq("\xFAbc")
expect(i.response.body).to eq("\xFAbc")
expect(i.request.body.encoding.name).not_to eq("ISO-8859-1")
expect(i.response.body.encoding.name).not_to eq("ISO-8859-1")
end
it 'prints a warning and informs users of the :preserve_exact_body_bytes option' do
expect(Request).to receive(:warn).with(/ISO-8859-1.*preserve_exact_body_bytes/)
expect(Response).to receive(:warn).with(/ISO-8859-1.*preserve_exact_body_bytes/)
HTTPInteraction.from_hash(hash)
end
end
end
end
describe "#to_hash" do
include_context "configuration stubbing"
before(:each) do
allow(config).to receive(:preserve_exact_body_bytes_for?).and_return(false)
allow(config).to receive(:uri_parser).and_return(URI)
end
let(:hash) { interaction.to_hash }
it 'returns a nested hash containing all of the pertinent details' do
expect(hash.keys).to match_array %w[ request response recorded_at ]
expect(hash['recorded_at']).to eq(interaction.recorded_at.httpdate)
expect(hash['request']).to eq({
'method' => 'get',
'uri' => 'http://foo.com/',
'body' => body_hash('string', 'req body'),
'headers' => { "bar" => ["foo"] }
})
expect(hash['response']).to eq({
'status' => {
'code' => 200,
'message' => 'OK'
},
'headers' => { "foo" => ["bar"] },
'body' => body_hash('string', 'res body'),
'http_version' => '1.1'
})
end
it 'includes the response adapter metadata when it is not empty' do
interaction.response.adapter_metadata['foo'] = 17
expect(hash['response']['adapter_metadata']).to eq('foo' => 17)
end
it 'does not include the response adapter metadata when it is empty' do
expect(interaction.response.adapter_metadata).to eq({})
expect(hash['response'].keys).not_to include('adapter_metadata')
end
context "when the body is extended with a module and some state" do
it 'serializes to YAML w/o the extra state' do
interaction.request.body.extend Module.new { attr_accessor :foo }
interaction.response.body.extend Module.new { attr_accessor :foo }
interaction.request.body.foo = 98765
interaction.response.body.foo = 98765
expect(YAML.dump(interaction.to_hash)).not_to include("98765")
end
end
it 'encodes the body as base64 when the configuration is so set' do
allow(config).to receive(:preserve_exact_body_bytes_for?).and_return(true)
expect(hash['request']['body']).to eq(body_hash('base64_string', Base64.encode64('req body')))
expect(hash['response']['body']).to eq(body_hash('base64_string', Base64.encode64('res body')))
end
it "sets the string's original encoding", :if => ''.respond_to?(:encoding) do
interaction.request.body.force_encoding('ISO-8859-10')
interaction.response.body.force_encoding('ASCII-8BIT')
expect(hash['request']['body']['encoding']).to eq('ISO-8859-10')
expect(hash['response']['body']['encoding']).to eq('ASCII-8BIT')
end
def assert_yielded_keys(hash, *keys)
yielded_keys = []
hash.each { |k, v| yielded_keys << k }
expect(yielded_keys).to eq(keys)
end
it 'yields the entries in the expected order so the hash can be serialized in that order' do
assert_yielded_keys hash, 'request', 'response', 'recorded_at'
assert_yielded_keys hash['request'], 'method', 'uri', 'body', 'headers'
assert_yielded_keys hash['response'], 'status', 'headers', 'body', 'http_version'
assert_yielded_keys hash['response']['status'], 'code', 'message'
end
it 'yields `adapter_metadata` if it has any data' do
interaction.response.adapter_metadata['foo'] = 17
assert_yielded_keys hash['response'], 'status', 'headers', 'body', 'http_version', 'adapter_metadata'
end
end
describe "#parsed_uri" do
before :each do
allow(uri_parser).to receive(:parse).and_return(uri)
allow(config).to receive(:uri_parser).and_return(uri_parser)
end
let(:uri_parser){ double('parser') }
let(:uri){ double('uri').as_null_object }
it "parses the uri using the current uri_parser" do
expect(uri_parser).to receive(:parse).with(request.uri)
request.parsed_uri
end
it "returns the parsed uri" do
expect(request.parsed_uri).to eq uri
end
end
end
describe HTTPInteraction::HookAware do
include_context "configuration stubbing"
before do
allow(config).to receive(:uri_parser) { LimitedURI }
end
let(:response_status) { VCR::ResponseStatus.new(200, "OK foo") }
let(:body) { "The body foo this is (foo-Foo)" }
let(:headers) do {
'x-http-foo' => ['bar23', '23foo'],
'x-http-bar' => ['foo23', '18']
} end
let(:response) do
VCR::Response.new(
response_status,
headers.dup,
body.dup,
'1.1'
)
end
let(:request) do
VCR::Request.new(
:get,
'http://example-foo.com:80/foo/',
body.dup,
headers.dup
)
end
let(:interaction) { VCR::HTTPInteraction.new(request, response) }
subject { HTTPInteraction::HookAware.new(interaction) }
describe '#ignored?' do
it 'returns false by default' do
should_not be_ignored
end
it 'returns true when #ignore! has been called' do
subject.ignore!
should be_ignored
end
end
describe '#filter!' do
let(:filtered) { subject.filter!('foo', 'AAA') }
it 'does nothing when given a blank argument' do
expect {
subject.filter!(nil, 'AAA')
subject.filter!('foo', nil)
subject.filter!("", 'AAA')
subject.filter!('foo', "")
}.not_to change { interaction }
end
[:request, :response].each do |part|
it "replaces the sensitive text in the #{part} header keys and values" do
expect(filtered.send(part).headers).to eq({
'x-http-AAA' => ['bar23', '23AAA'],
'x-http-bar' => ['AAA23', '18']
})
end
it "replaces the sensitive text in the #{part} body" do
expect(filtered.send(part).body).to eq("The body AAA this is (AAA-Foo)")
end
end
it 'replaces the sensitive text in the response status' do
expect(filtered.response.status.message).to eq('OK AAA')
end
it 'replaces sensitive text in the request URI' do
expect(filtered.request.uri).to eq('http://example-AAA.com/AAA/')
end
it 'handles numbers (such as the port) properly' do
request.uri = "http://foo.com:9000/bar"
subject.filter!(9000, "")
expect(request.uri).to eq("http://foo.com:/bar")
end
end
end
describe Request::Typed do
[:uri, :method, :headers, :body].each do |method|
it "delegates ##{method} to the request" do
request = double(method => "delegated value")
expect(Request::Typed.new(request, :type).send(method)).to eq("delegated value")
end
end
describe "#type" do
it 'returns the initialized type' do
expect(Request::Typed.new(double, :ignored).type).to be(:ignored)
end
end
valid_types = [:ignored, :stubbed_by_vcr, :externally_stubbed, :recordable, :unhandled]
valid_types.each do |type|
describe "##{type}?" do
it "returns true if the type is set to :#{type}" do
expect(Request::Typed.new(double, type).send("#{type}?")).to be true
end
it "returns false if the type is set to :other" do
expect(Request::Typed.new(double, :other).send("#{type}?")).to be false
end
end
end
describe "#real?" do
real_types = [:ignored, :recordable]
real_types.each do |type|
it "returns true if the type is set to :#{type}" do
expect(Request::Typed.new(double, type)).to be_real
end
end
(valid_types - real_types).each do |type|
it "returns false if the type is set to :#{type}" do
expect(Request::Typed.new(double, type)).not_to be_real
end
end
end
describe "#stubbed?" do
stubbed_types = [:externally_stubbed, :stubbed_by_vcr]
stubbed_types.each do |type|
it "returns true if the type is set to :#{type}" do
expect(Request::Typed.new(double, type)).to be_stubbed
end
end
(valid_types - stubbed_types).each do |type|
it "returns false if the type is set to :#{type}" do
expect(Request::Typed.new(double, type)).not_to be_stubbed
end
end
end
end
describe Request do
include_context "configuration stubbing"
before do
allow(config).to receive(:uri_parser) { LimitedURI }
end
describe '#method' do
subject { VCR::Request.new(:get) }
context 'when given no arguments' do
it 'returns the HTTP method' do
expect(subject.method).to eq(:get)
end
end
context 'when given an argument' do
it 'returns the method object for the named method' do
m = subject.method(:class)
expect(m).to be_a(Method)
expect(m.call).to eq(described_class)
end
end
it 'gets normalized to a lowercase symbol' do
expect(VCR::Request.new("GET").method).to eq(:get)
expect(VCR::Request.new(:GET).method).to eq(:get)
expect(VCR::Request.new(:get).method).to eq(:get)
expect(VCR::Request.new("get").method).to eq(:get)
end
end
describe "#uri" do
def uri_for(uri)
VCR::Request.new(:get, uri).uri
end
it 'removes the default http port' do
expect(uri_for("http://foo.com:80/bar")).to eq("http://foo.com/bar")
end
it 'removes the default https port' do
expect(uri_for("https://foo.com:443/bar")).to eq("https://foo.com/bar")
end
it 'does not remove a non-standard http port' do
expect(uri_for("http://foo.com:81/bar")).to eq("http://foo.com:81/bar")
end
it 'does not remove a non-standard https port' do
expect(uri_for("https://foo.com:442/bar")).to eq("https://foo.com:442/bar")
end
end
describe Request::FiberAware do
subject { Request::FiberAware.new(Request.new) }
it 'adds a #proceed method that yields in a fiber' do
fiber = Fiber.new do |request|
request.proceed
:done
end
expect(fiber.resume(subject)).to be_nil
expect(fiber.resume).to eq(:done)
end
it 'can be cast to a proc' do
expect(Fiber).to receive(:yield)
lambda(&subject).call
end
end if RUBY_VERSION > '1.9'
it_behaves_like 'a header normalizer' do
def with_headers(headers)
described_class.new(:get, 'http://example.com/', nil, headers)
end
end
it_behaves_like 'a body normalizer' do
def instance(body)
described_class.new(:get, 'http://example.com/', body, {})
end
end
end
describe Response do
it_behaves_like 'a header normalizer' do
def with_headers(headers)
described_class.new(:status, headers, nil, '1.1')
end
end
it_behaves_like 'a body normalizer' do
def instance(body)
described_class.new(:status, {}, body, '1.1')
end
end
describe "#adapter_metadata" do
it 'returns the hash given as the last #initialize argument' do
response = Response.new(
ResponseStatus.new(200, "OK"),
{}, "the body", "1.1",
{ "meta" => "value" }
)
expect(response.adapter_metadata).to eq("meta" => "value")
end
it 'returns a blank hash when nil is passed to #initialize' do
response = Response.new(
ResponseStatus.new(200, "OK"),
{}, "the body", "1.1", nil
)
expect(response.adapter_metadata).to eq({})
end
end
describe '#update_content_length_header' do
%w[ content-length Content-Length ].each do |header|
context "for the #{header} header" do
define_method :instance do |body, content_length|
headers = { 'content-type' => 'text' }
headers.merge!(header => content_length) if content_length
described_class.new(VCR::ResponseStatus.new, headers, body)
end
it 'does nothing when the response lacks a content_length header' do
inst = instance('the body', nil)
expect {
inst.update_content_length_header
}.not_to change { inst.headers[header] }
end
it 'sets the content_length header to the response body length when the header is present' do
inst = instance('the body', '3')
expect {
inst.update_content_length_header
}.to change { inst.headers[header] }.from(['3']).to(['8'])
end
it 'sets the content_length header to 0 if the response body is nil' do
inst = instance(nil, '3')
expect {
inst.update_content_length_header
}.to change { inst.headers[header] }.from(['3']).to(['0'])
end
it 'sets the header according to RFC 2616 based on the number of bytes (not the number of characters)' do
inst = instance('aؼ', '2') # the second char is a double byte char
expect {
inst.update_content_length_header
}.to change { inst.headers[header] }.from(['2']).to(['3'])
end
end
end
end
describe '#decompress' do
%w[ content-encoding Content-Encoding ].each do |header|
context "for the #{header} header" do
define_method :instance do |body, content_encoding|
headers = { 'content-type' => 'text',
'content-length' => body.bytesize.to_s }
headers[header] = content_encoding if content_encoding
described_class.new(VCR::ResponseStatus.new, headers, body)
end
let(:content) { 'The quick brown fox jumps over the lazy dog' }
it "does nothing when no compression" do
resp = instance('Hello', nil)
expect(resp).not_to be_compressed
expect {
expect(resp.decompress).to equal(resp)
}.to_not change { resp.headers['content-length'] }
end
it "does nothing when encoding is 'identity'" do
resp = instance('Hello', 'identity')
expect(resp).not_to be_compressed
expect {
expect(resp.decompress).to equal(resp)
}.to_not change { resp.headers['content-length'] }
end
it "raises error for unrecognized encoding" do
resp = instance('Hello', 'flabbergaster')
expect(resp).not_to be_compressed
expect { resp.decompress }.
to raise_error(Errors::UnknownContentEncodingError, 'unknown content encoding: flabbergaster')
end
it "unzips gzipped response" do
io = StringIO.new
writer = Zlib::GzipWriter.new(io)
writer << content
writer.close
gzipped = io.string
resp = instance(gzipped, 'gzip')
expect(resp).to be_compressed
expect {
expect(resp.decompress).to equal(resp)
expect(resp).not_to be_compressed
expect(resp.body).to eq(content)
}.to change { resp.headers['content-length'] }.
from([gzipped.bytesize.to_s]).
to([content.bytesize.to_s])
end
it "inflates deflated response" do
deflated = Zlib::Deflate.deflate(content)
resp = instance(deflated, 'deflate')
expect(resp).to be_compressed
expect {
expect(resp.decompress).to equal(resp)
expect(resp).not_to be_compressed
expect(resp.body).to eq(content)
}.to change { resp.headers['content-length'] }.
from([deflated.bytesize.to_s]).
to([content.bytesize.to_s])
end
end
end
end
end
end
vcr-5.0.0/spec/lib/vcr/test_frameworks/ 0000775 0000000 0000000 00000000000 13473056530 0020025 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/lib/vcr/test_frameworks/cucumber_spec.rb 0000664 0000000 0000000 00000010063 13473056530 0023171 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe VCR::CucumberTags do
subject { described_class.new(self) }
let(:before_blocks_for_tags) { {} }
let(:after_blocks_for_tags) { {} }
def scenario(name)
double(:name => name, :feature => double(:name => "My feature name\nThe preamble text is not included"), :failed? => false)
end
let(:current_scenario) { scenario "My scenario name\nThe preamble text is not included" }
# define our own Before/After so we can test this in isolation from cucumber's implementation.
def Before(tag, &block)
before_blocks_for_tags[tag.sub('@', '')] = block
end
def After(tag, &block)
after_blocks_for_tags[tag.sub('@', '')] = block
end
def test_tag(cassette_attribute, tag, expected_value, scenario=current_scenario)
expect(VCR.current_cassette).to be_nil
before_blocks_for_tags[tag].call(scenario)
expect(VCR.current_cassette.send(cassette_attribute)).to eq(expected_value)
after_blocks_for_tags[tag].call(scenario)
expect(VCR.current_cassette).to be_nil
end
%w(tags tag).each do |tag_method|
describe "##{tag_method}" do
it "creates a cucumber Around hook for each given tag so that the scenario runs with the cassette inserted" do
subject.send(tag_method, 'tag1', 'tag2')
test_tag(:name, 'tag1', 'cucumber_tags/tag1')
test_tag(:name, 'tag2', 'cucumber_tags/tag2')
end
it "works with tags that start with an @" do
subject.send(tag_method, '@tag1', '@tag2')
test_tag(:name, 'tag1', 'cucumber_tags/tag1')
test_tag(:name, 'tag2', 'cucumber_tags/tag2')
end
it "passes along the given options to the cassette" do
subject.send(tag_method, 'tag1', :record => :none)
subject.send(tag_method, 'tag2', :record => :new_episodes)
test_tag(:record_mode, 'tag1', :none)
test_tag(:record_mode, 'tag2', :new_episodes)
end
context 'with :use_scenario_name as an option' do
it "uses the scenario's name as the cassette name" do
subject.send(tag_method, 'tag1', :use_scenario_name => true)
test_tag(:name, 'tag1', 'My feature name/My scenario name')
end
it "makes a unique name for each element of scenario outline" do
subject.send(tag_method, 'tag1', :use_scenario_name => true)
scenario_with_outline = double(:name => "My row name", :failed? => false,
:scenario_outline => double(:feature => double(:name => "My feature name\nThe preamble text is not included"),
:name => "My scenario outline name"))
test_tag(:name, 'tag1', 'My feature name/My scenario outline name/My row name', scenario_with_outline)
end
it 'does not pass :use_scenario_name along the given options to the cassette' do
subject.send(tag_method, 'tag1', :use_scenario_name => true)
expect(VCR::Cassette).to receive(:new).with(anything, hash_not_including(:use_scenario_name))
before_blocks_for_tags['tag1'].call(current_scenario)
end
it 'does not modify the options passed to the cassette' do
original_options = { :use_scenario_name => true, :record => :none }
subject.send(tag_method, 'tag1', original_options)
before_blocks_for_tags['tag1'].call(current_scenario)
expect(original_options.size).to eq(2)
expect(original_options[:use_scenario_name]).to eq(true)
expect(original_options[:record]).to eq(:none)
end
it "works properly when multiple scenarios use the tag" do
subject.send(tag_method, 'tag1', :use_scenario_name => true)
test_tag(:name, 'tag1', 'My feature name/Foo', scenario("Foo"))
test_tag(:name, 'tag1', 'My feature name/Bar', scenario("Bar"))
end
end
end
end
describe '.tags' do
it 'returns the list of cucumber tags' do
subject.tags 'tag1', 'tag2'
subject.tags 'tag3', 'tag4'
expect(described_class.tags[-4, 4]).to eq(%w(@tag1 @tag2 @tag3 @tag4))
end
end
end
vcr-5.0.0/spec/lib/vcr/test_frameworks/rspec_spec.rb 0000664 0000000 0000000 00000002752 13473056530 0022506 0 ustar 00root root 0000000 0000000 require 'spec_helper'
VCR.configuration.configure_rspec_metadata!
describe VCR::RSpec::Metadata, :skip_vcr_reset do
before(:all) { VCR.reset! }
after(:each) { VCR.reset! }
context 'an example group', :vcr do
context 'with a nested example group' do
it 'uses a cassette for any examples' do
expect(VCR.current_cassette.name.split('/')).to eq([
'VCR::RSpec::Metadata',
'an example group',
'with a nested example group',
'uses a cassette for any examples'
])
end
end
context 'when the spec has no description' do
it do
expect(VCR.current_cassette.name.split('/')).to eq([
'VCR::RSpec::Metadata',
'an example group',
'when the spec has no description',
'1:1:2:1'
])
end
end
end
context 'with the cassette name overridden at the example group level', :vcr => { :cassette_name => 'foo' } do
it 'overrides the cassette name for an example' do
expect(VCR.current_cassette.name).to eq('foo')
end
it 'overrides the cassette name for another example' do
expect(VCR.current_cassette.name).to eq('foo')
end
end
it 'allows the cassette name to be overriden', :vcr => { :cassette_name => 'foo' } do
expect(VCR.current_cassette.name).to eq('foo')
end
it 'allows the cassette options to be set', :vcr => { :match_requests_on => [:method] } do
expect(VCR.current_cassette.match_requests_on).to eq([:method])
end
end
vcr-5.0.0/spec/lib/vcr/util/ 0000775 0000000 0000000 00000000000 13473056530 0015563 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/lib/vcr/util/hooks_spec.rb 0000664 0000000 0000000 00000010777 13473056530 0020261 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe VCR::Hooks::FilteredHook do
describe "#conditionally_invoke" do
it 'invokes the hook' do
called = false
subject.hook = lambda { called = true }
subject.conditionally_invoke
expect(called).to be true
end
it 'forwards the given arguments to the hook' do
args = nil
subject.hook = lambda { |a, b| args = [a, b] }
subject.conditionally_invoke(3, 5)
expect(args).to eq([3, 5])
end
it 'forwards only as many arguments as the hook block accepts' do
args = nil
subject.hook = lambda { |a| args = [a] }
subject.conditionally_invoke(3, 5)
expect(args).to eq([3])
end
it 'does not invoke the hook if all of the filters return false' do
called = false
subject.hook = lambda { called = true }
subject.filters = lambda { false }
subject.conditionally_invoke
expect(called).to be false
end
it 'does not invoke the hook if any of the filters returns false' do
called = false
subject.hook = lambda { called = true }
subject.filters = [lambda { false }, lambda { true }]
subject.conditionally_invoke
expect(called).to be false
end
it 'forwards arguments to the filters' do
filter_args = nil
subject.filters = lambda { |a, b| filter_args = [a, b]; false }
subject.conditionally_invoke(3, 5)
expect(filter_args).to eq([3, 5])
end
it 'handles splat args properly' do
filter_args = nil
subject.filters = lambda { |*args| filter_args = args; false }
subject.conditionally_invoke(3, 5)
expect(filter_args).to eq([3, 5])
end
it 'forwards only as many arguments as the filter blocks accept' do
args1 = args2 = nil
subject.filters = [
lambda { |a| args1 = [a]; true },
lambda { |a, b| args2 = [a, b]; false }
]
subject.conditionally_invoke(3, 5)
expect(args1).to eq([3])
expect(args2).to eq([3, 5])
end
it '#to_procs the filter objects' do
filter_called = false
subject.hook = lambda { }
subject.filters = [double(:to_proc => lambda { filter_called = true })]
subject.conditionally_invoke
expect(filter_called).to be true
end
end
end
describe VCR::Hooks do
let(:hooks_class) { Class.new { include VCR::Hooks } }
subject { hooks_class.new }
let(:invocations) { [] }
before(:each) do
hooks_class.instance_eval do
define_hook :before_foo
define_hook :before_bar, :prepend
end
end
it 'allows the class to override the hook method and super to the main definition' do
override_called = nil
hooks_class.class_eval do
define_method :before_foo do |&block|
override_called = true
super(&block)
end
end
subject.before_foo { }
expect(override_called).to be true
end
describe '#clear_hooks' do
it 'clears all hooks' do
subject.before_foo { invocations << :callback }
subject.clear_hooks
subject.invoke_hook(:before_foo)
expect(invocations).to be_empty
end
end
describe '#invoke_hook' do
it 'invokes each of the callbacks' do
subject.before_foo { invocations << :callback_1 }
subject.before_foo { invocations << :callback_2 }
expect(invocations).to be_empty
subject.invoke_hook(:before_foo)
expect(invocations).to eq([:callback_1, :callback_2])
end
it 'maps the return value of each callback' do
subject.before_foo { 17 }
subject.before_foo { 12 }
expect(subject.invoke_hook(:before_foo)).to eq([17, 12])
end
it 'does not invoke any filtered callbacks' do
subject.before_foo(:real?) { invocations << :blue_callback }
subject.invoke_hook(:before_foo, double(:real? => false))
expect(invocations).to be_empty
end
it 'invokes them in reverse order if the hook was defined with :prepend' do
subject.before_bar { 17 }
subject.before_bar { 12 }
expect(subject.invoke_hook(:before_bar)).to eq([12, 17])
end
end
describe "#has_hooks_for?" do
it 'returns false when given an unrecognized hook name' do
expect(subject).not_to have_hooks_for(:abcd)
end
it 'returns false when given the name of a defined hook that has no registered callbacks' do
expect(subject).not_to have_hooks_for(:before_foo)
end
it 'returns true when given the name of a defined hook that has registered callbacks' do
subject.before_foo { }
expect(subject).to have_hooks_for(:before_foo)
end
end
end
vcr-5.0.0/spec/lib/vcr/util/internet_connection_spec.rb 0000664 0000000 0000000 00000002156 13473056530 0023175 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe VCR::InternetConnection do
describe '.available?' do
before(:each) do
described_class.send(:remove_instance_variable, :@available) if described_class.instance_variable_defined?(:@available)
end
def stub_pingecho_with(value)
allow(VCR::Ping).to receive(:pingecho).with("example.com", anything, anything).and_return(value)
end
context 'when pinging example.com succeeds' do
it 'returns true' do
stub_pingecho_with(true)
expect(described_class).to be_available
end
it 'memoizes the value so no extra pings are made' do
expect(VCR::Ping).to receive(:pingecho).once.and_return(true)
3.times { described_class.available? }
end
end
context 'when pinging example.com fails' do
it 'returns false' do
stub_pingecho_with(false)
expect(described_class).not_to be_available
end
it 'memoizes the value so no extra pings are made' do
expect(VCR::Ping).to receive(:pingecho).once.and_return(false)
3.times { described_class.available? }
end
end
end
end
vcr-5.0.0/spec/lib/vcr/util/version_checker_spec.rb 0000664 0000000 0000000 00000002131 13473056530 0022270 0 ustar 00root root 0000000 0000000 require 'spec_helper'
module VCR
describe VersionChecker do
it 'raises an error if the major version is too low' do
checker = VersionChecker.new('foo', '0.7.3', '1.0.0')
expect { checker.check_version! }.to raise_error(Errors::LibraryVersionTooLowError)
end
it 'raises an error if the minor version is too low' do
checker = VersionChecker.new('foo', '1.0.99', '1.1.3')
expect { checker.check_version! }.to raise_error(Errors::LibraryVersionTooLowError)
end
it 'raises an error if the patch version is too low' do
checker = VersionChecker.new('foo', '1.0.8', '1.0.10')
expect { checker.check_version! }.to raise_error(Errors::LibraryVersionTooLowError)
end
it 'does not raise an error when the version is equal' do
checker = VersionChecker.new('foo', '1.0.0', '1.0.0')
expect { checker.check_version! }.not_to raise_error
end
it 'does not raise an error when the version is higher' do
checker = VersionChecker.new('foo', '2.0.0', '1.0.0')
expect { checker.check_version! }.not_to raise_error
end
end
end
vcr-5.0.0/spec/lib/vcr/version_spec.rb 0000664 0000000 0000000 00000001006 13473056530 0017627 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe "VCR.version" do
subject { VCR.version }
it { should =~ /\A\d+\.\d+\.\d+(\.\w+)?\z/ }
describe '#parts' do
subject { super().parts }
it { should be_kind_of(Array) }
end
describe '#major' do
subject { super().major }
it { should be_kind_of(Integer) }
end
describe '#minor' do
subject { super().minor }
it { should be_kind_of(Integer) }
end
describe '#patch' do
subject { super().patch }
it { should be_kind_of(Integer) }
end
end
vcr-5.0.0/spec/lib/vcr_spec.rb 0000664 0000000 0000000 00000027273 13473056530 0016160 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe VCR do
def insert_cassette(name = :cassette_test)
VCR.insert_cassette(name)
end
describe '.insert_cassette' do
it 'creates a new cassette' do
expect(insert_cassette).to be_instance_of(VCR::Cassette)
end
it 'takes over as the #current_cassette' do
orig_cassette = VCR.current_cassette
new_cassette = insert_cassette
expect(new_cassette).not_to eq(orig_cassette)
expect(VCR.current_cassette).to eq(new_cassette)
end
it 'raises an error if the stack of inserted cassettes already contains a cassette with the same name' do
insert_cassette(:foo)
expect {
insert_cassette(:foo)
}.to raise_error(/There is already a cassette with the same name/)
end
end
describe '.eject_cassette' do
it 'ejects the current cassette' do
cassette = insert_cassette
expect(cassette).to receive(:eject)
VCR.eject_cassette
end
it 'forwards the given options to `Cassette#eject`' do
cassette = insert_cassette
expect(cassette).to receive(:eject).with(:some => :options)
VCR.eject_cassette(:some => :options)
end
it 'returns the ejected cassette' do
cassette = insert_cassette
expect(VCR.eject_cassette).to eq(cassette)
end
it 'returns the #current_cassette to the previous one' do
cassette1, cassette2 = insert_cassette(:foo1), insert_cassette(:foo2)
expect { VCR.eject_cassette }.to change(VCR, :current_cassette).from(cassette2).to(cassette1)
end
it 'keeps the cassette as the current one until after #eject has finished' do
cassette = insert_cassette
current = nil
allow(cassette).to receive(:eject) { current = VCR.current_cassette }
VCR.eject_cassette
expect(current).to be(cassette)
expect(VCR.current_cassette).not_to be(cassette)
end
it 'properly pops the cassette off the stack even if an error occurs' do
cassette = insert_cassette
allow(cassette).to receive(:eject) { raise "boom" }
expect { VCR.eject_cassette }.to raise_error("boom")
expect(VCR.current_cassette).to be_nil
end
end
describe '.use_cassette' do
it 'inserts a new cassette' do
new_cassette = VCR::Cassette.new(:use_cassette_test)
expect(VCR).to receive(:insert_cassette).and_return(new_cassette)
VCR.use_cassette(:cassette_test) { }
end
it 'yields' do
yielded = false
VCR.use_cassette(:cassette_test, &lambda { yielded = true })
expect(yielded).to be true
end
it 'yields the cassette instance if the block expects an argument' do
VCR.use_cassette('name', :record => :new_episodes, &lambda do |cassette|
expect(cassette).to equal(VCR.current_cassette)
end)
end
it 'yields the cassette instance if the block expects a variable number of args' do
VCR.use_cassette('name', :record => :new_episodes) do |*args|
expect(args.size).to eq(1)
expect(args.first).to equal(VCR.current_cassette)
end
end
it 'ejects the cassette' do
expect(VCR).to receive(:eject_cassette)
VCR.use_cassette(:cassette_test) { }
end
it 'ejects the cassette even if there is an error' do
expect(VCR).to receive(:eject_cassette)
test_error = Class.new(StandardError)
expect { VCR.use_cassette(:cassette_test) { raise test_error } }.to raise_error(test_error)
end
it 'does not eject a cassette if there was an error inserting it' do
expect(VCR).to receive(:insert_cassette).and_raise(StandardError.new('Boom!'))
expect(VCR).not_to receive(:eject_cassette)
expect { VCR.use_cassette(:test) { } }.to raise_error(StandardError, 'Boom!')
end
it 'raises a helpful error if no block is given' do
expect {
VCR.use_cassette(:test)
}.to raise_error(/requires a block/)
end
end
describe '.http_interactions' do
it 'returns the current_cassette.http_interactions when there is a current cassette' do
cassette = VCR.insert_cassette("a cassette")
expect(VCR.http_interactions).to be(cassette.http_interactions)
end
it 'returns a null list when there is no current cassette' do
expect(VCR.current_cassette).to be_nil
expect(VCR.http_interactions).to be(VCR::Cassette::HTTPInteractionList::NullList)
end
end
describe '.real_http_connections_allowed?' do
context 'when a cassette is inserted' do
it 'returns true if the cassette is recording' do
VCR.insert_cassette('foo', :record => :all)
expect(VCR.current_cassette).to be_recording
expect(VCR.real_http_connections_allowed?).to be true
end
it 'returns false if the cassette is not recording' do
VCR.insert_cassette('foo', :record => :none)
expect(VCR.current_cassette).not_to be_recording
expect(VCR.real_http_connections_allowed?).to be false
end
end
context 'when no cassette is inserted' do
before(:each) do
expect(VCR.current_cassette).to be_nil
end
it 'returns true if the allow_http_connections_when_no_cassette option is set to true' do
expect(VCR).to be_turned_on
VCR.configure { |c| c.allow_http_connections_when_no_cassette = true }
expect(VCR.real_http_connections_allowed?).to be true
end
it 'returns true if VCR is turned off' do
VCR.turn_off!
VCR.configure { |c| c.allow_http_connections_when_no_cassette = false }
expect(VCR.real_http_connections_allowed?).to be true
end
it 'returns false if the allow_http_connections_when_no_cassette option is set to false and VCR is turned on' do
expect(VCR).to be_turned_on
VCR.configure { |c| c.allow_http_connections_when_no_cassette = false }
expect(VCR.real_http_connections_allowed?).to be false
end
end
end
describe '.request_matchers' do
it 'always returns the same memoized request matcher registry instance' do
expect(VCR.request_matchers).to be_a(VCR::RequestMatcherRegistry)
expect(VCR.request_matchers).to be(VCR.request_matchers)
end
end
describe '.request_ignorer' do
it 'always returns the same memoized request ignorer instance' do
expect(VCR.request_ignorer).to be_a(VCR::RequestIgnorer)
expect(VCR.request_ignorer).to be(VCR.request_ignorer)
end
end
describe '.library_hooks' do
it 'always returns the same memoized LibraryHooks instance' do
expect(VCR.library_hooks).to be_a(VCR::LibraryHooks)
expect(VCR.library_hooks).to be(VCR.library_hooks)
end
end
describe '.cassette_serializers' do
it 'always returns the same memoized cassette serializers instance' do
expect(VCR.cassette_serializers).to be_a(VCR::Cassette::Serializers)
expect(VCR.cassette_serializers).to be(VCR.cassette_serializers)
end
end
describe ".cassette_persisters" do
it "always returns the same memoized Cassette::Persisters instance" do
expect(VCR.cassette_persisters).to be_a(VCR::Cassette::Persisters)
expect(VCR.cassette_persisters).to be(VCR.cassette_persisters)
end
end
describe '.configuration' do
it 'returns the configuration object' do
expect(VCR.configuration).to be_a(VCR::Configuration)
end
it 'memoizes the instance' do
expect(VCR.configuration).to be(VCR.configuration)
end
end
describe '.configure' do
it 'yields the configuration object' do
yielded_object = nil
VCR.configure do |obj|
yielded_object = obj
end
expect(yielded_object).to eq(VCR.configuration)
end
end
describe '.cucumber_tags' do
it 'yields a cucumber tags object' do
yielded_object = nil
VCR.cucumber_tags do |obj|
yielded_object = obj
end
expect(yielded_object).to be_instance_of(VCR::CucumberTags)
end
end
describe '.record_http_interaction' do
before(:each) { allow(VCR).to receive(:current_cassette).and_return(current_cassette) }
let(:interaction) { double(:request => double) }
context 'when there is not a current cassette' do
let(:current_cassette) { nil }
it 'does not record a request' do
# we can't set a message expectation on nil, but there is no place to record it to...
# this mostly tests that there is no error.
VCR.record_http_interaction(interaction)
end
end
context 'when there is a current cassette' do
let(:current_cassette) { double('current cassette') }
it 'records the request when it should not be ignored' do
allow(VCR.request_ignorer).to receive(:ignore?).with(interaction.request).and_return(false)
expect(current_cassette).to receive(:record_http_interaction).with(interaction)
VCR.record_http_interaction(interaction)
end
it 'does not record the request when it should be ignored' do
allow(VCR.request_ignorer).to receive(:ignore?).with(interaction.request).and_return(true)
expect(current_cassette).not_to receive(:record_http_interaction)
VCR.record_http_interaction(interaction)
end
end
end
describe '.turn_off!' do
it 'indicates it is turned off' do
VCR.turn_off!
expect(VCR).not_to be_turned_on
end
it 'raises an error if a cassette is in use' do
VCR.insert_cassette('foo')
expect {
VCR.turn_off!
}.to raise_error(VCR::CassetteInUseError, /foo/)
end
it 'causes an error to be raised if you insert a cassette while VCR is turned off' do
VCR.turn_off!
expect {
VCR.insert_cassette('foo')
}.to raise_error(VCR::TurnedOffError)
end
it 'raises an ArgumentError when given an invalid option' do
expect {
VCR.turn_off!(:invalid_option => true)
}.to raise_error(ArgumentError)
end
it 'sets ignore_cassettes to false' do
VCR.turn_off!
expect(VCR.send(:ignore_cassettes?)).to equal(false)
end
context 'when `:ignore_cassettes => true` is passed' do
before(:each) { VCR.turn_off!(:ignore_cassettes => true) }
it 'ignores cassette insertions' do
VCR.insert_cassette('foo')
expect(VCR.current_cassette).to be_nil
end
it 'still runs a block passed to use_cassette' do
yielded = false
VCR.use_cassette('foo') do
yielded = true
expect(VCR.current_cassette).to be_nil
end
expect(yielded).to be true
end
end
end
describe '.turn_on!' do
before(:each) { VCR.turn_off! }
it 'indicates it is turned on' do
VCR.turn_on!
expect(VCR).to be_turned_on
end
end
describe '.turned_off' do
it 'yields with VCR turned off' do
expect(VCR).to be_turned_on
yielded = false
VCR.turned_off do
yielded = true
expect(VCR).not_to be_turned_on
end
expect(yielded).to eq(true)
expect(VCR).to be_turned_on
end
it 'passes options through to .turn_off!' do
expect(VCR).to receive(:turn_off!).with(:ignore_cassettes => true)
VCR.turned_off(:ignore_cassettes => true) { }
end
end
describe '.turned_on?' do
it 'is on by default' do
expect(VCR).to be_turned_on
end
end
describe '.use_cassettes' do
it 'uses multiple cassettes' do
cassette_by_github = VCR::Cassette.new(:use_cassette_test_call_github)
cassette_by_apple = VCR::Cassette.new(:use_cassette_test_call_apple)
expect(VCR).to receive(:insert_cassette).with(cassette_by_github, {}).and_return(cassette_by_github)
expect(VCR).to receive(:insert_cassette).with(cassette_by_apple, { erb: true }).and_return(cassette_by_apple)
cassettes = [
{ name: cassette_by_github },
{ name: cassette_by_apple, options: { erb: true } }
]
VCR.use_cassettes(cassettes) { }
end
end
end
vcr-5.0.0/spec/monkey_patches.rb 0000664 0000000 0000000 00000007070 13473056530 0016610 0 ustar 00root root 0000000 0000000 module MonkeyPatches
extend self
def enable!(scope)
case scope
when :webmock
::WebMock.reset!
::WebMock::HttpLibAdapters::NetHttpAdapter.enable!
::WebMock::HttpLibAdapters::TyphoeusAdapter.enable! if defined?(::Typhoeus)
::WebMock::HttpLibAdapters::ExconAdapter.enable! if defined?(::Excon)
$original_webmock_callbacks.each do |cb|
::WebMock::CallbackRegistry.add_callback(cb[:options], cb[:block])
end
when :typhoeus
$original_typhoeus_global_hooks.each do |hook|
::Typhoeus.on_complete << hook
end
::Typhoeus.before.clear
$original_typhoeus_before_hooks.each do |hook|
::Typhoeus.before << hook
end
when :typhoeus_0_4
::Typhoeus::Hydra.global_hooks = $original_typhoeus_global_hooks
::Typhoeus::Hydra.stub_finders.clear
$original_typhoeus_stub_finders.each do |finder|
::Typhoeus::Hydra.stub_finders << finder
end
when :excon
VCR::LibraryHooks::Excon.configure_middleware
else raise ArgumentError.new("Unexpected scope: #{scope}")
end
end
def disable_all!
if defined?(::WebMock::HttpLibAdapters)
::WebMock::HttpLibAdapters::NetHttpAdapter.disable!
::WebMock::HttpLibAdapters::TyphoeusAdapter.disable! if defined?(::Typhoeus)
::WebMock::HttpLibAdapters::ExconAdapter.disable! if defined?(::Excon)
::WebMock::CallbackRegistry.reset
::WebMock::StubRegistry.instance.request_stubs = []
end
if defined?(::Typhoeus.before)
::Typhoeus.on_complete.clear
::Typhoeus.before.clear
elsif defined?(::Typhoeus::Hydra)
::Typhoeus::Hydra.clear_global_hooks
::Typhoeus::Hydra.stub_finders.clear
end
if defined?(::Excon)
::Excon.defaults[:middlewares].delete(VCR::Middleware::Excon::Request)
::Excon.defaults[:middlewares].delete(VCR::Middleware::Excon::Response)
end
end
end
# Require all the HTTP libraries--these must be required before WebMock
# for WebMock to work with them.
require 'httpclient'
if RUBY_INTERPRETER == :mri
require 'typhoeus'
begin
require 'patron'
require 'em-http-request'
require 'curb'
rescue LoadError
# these are not always available, depending on the Gemfile used
warn $!.message
end
end
if defined?(::Typhoeus.before)
require 'vcr/library_hooks/typhoeus'
$typhoeus_after_loaded_hook = VCR.configuration.hooks[:after_library_hooks_loaded].last
$original_typhoeus_global_hooks = Typhoeus.on_complete.dup
$original_typhoeus_before_hooks = Typhoeus.before.dup
elsif defined?(::Typhoeus::Hydra.global_hooks)
require 'vcr/library_hooks/typhoeus'
$typhoeus_0_4_after_loaded_hook = VCR.configuration.hooks[:after_library_hooks_loaded].first
$typhoeus_after_loaded_hook = VCR.configuration.hooks[:after_library_hooks_loaded].last
$original_typhoeus_global_hooks = Typhoeus::Hydra.global_hooks.dup
$original_typhoeus_stub_finders = Typhoeus::Hydra.stub_finders.dup
end
require 'vcr/library_hooks/webmock'
$original_webmock_callbacks = ::WebMock::CallbackRegistry.callbacks
require 'vcr/library_hooks/excon'
$excon_after_loaded_hook = VCR.configuration.hooks[:after_library_hooks_loaded].last
# disable all by default; we'll enable specific ones when we need them
MonkeyPatches.disable_all!
RSpec.configure do |config|
[:webmock, :typhoeus, :typhoeus_0_4, :excon].each do |scope|
config.before(:all, :with_monkey_patches => scope) { MonkeyPatches.enable!(scope) }
config.after(:all, :with_monkey_patches => scope) { MonkeyPatches.disable_all! }
end
end
vcr-5.0.0/spec/spec_helper.rb 0000664 0000000 0000000 00000003234 13473056530 0016066 0 ustar 00root root 0000000 0000000 require "codeclimate-test-reporter"
CodeClimate::TestReporter.start
require "pry"
require "rspec"
require "vcr"
require "date"
require "forwardable"
require "uri"
require "vcr/util/internet_connection"
require_relative "support/integer_extension"
require_relative "support/limited_uri"
require_relative "support/ruby_interpreter"
require_relative "support/shared_example_groups/hook_into_http_library"
require_relative "support/shared_example_groups/request_hooks"
require_relative "support/vcr_stub_helpers"
require_relative "support/vcr_localhost_server"
require_relative "support/sinatra_app"
require_relative "monkey_patches"
require_relative "support/http_library_adapters"
module VCR
SPEC_ROOT = File.dirname(File.expand_path('.', __FILE__))
def reset!(hook = nil)
instance_variables.each do |ivar|
instance_variable_set(ivar, nil)
end
initialize_ivars
configuration.hook_into hook if hook
end
end
RSpec.configure do |config|
tmp_dir = File.expand_path('../../tmp/cassette_library_dir', __FILE__)
config.before(:each) do |example|
unless example.metadata[:skip_vcr_reset]
VCR.reset!
VCR.configuration.cassette_library_dir = tmp_dir
VCR.configuration.uri_parser = LimitedURI
end
end
config.after(:each) do
FileUtils.rm_rf tmp_dir
end
config.before(:all, :disable_warnings => true) do
@orig_std_err = $stderr
$stderr = StringIO.new
end
config.after(:all, :disable_warnings => true) do
$stderr = @orig_std_err
end
config.filter_run :focus => true
config.run_all_when_everything_filtered = true
config.alias_it_should_behave_like_to :it_performs, 'it performs'
end
VCR::SinatraApp.boot
vcr-5.0.0/spec/support/ 0000775 0000000 0000000 00000000000 13473056530 0014762 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/support/configuration_stubbing.rb 0000664 0000000 0000000 00000000250 13473056530 0022050 0 ustar 00root root 0000000 0000000 shared_context "configuration stubbing" do
let(:config) { double("VCR::Configuration") }
before do
allow(VCR).to receive(:configuration) { config }
end
end
vcr-5.0.0/spec/support/cucumber_helpers.rb 0000664 0000000 0000000 00000001711 13473056530 0020636 0 ustar 00root root 0000000 0000000 require "rubygems"
require "vcr"
require "support/integer_extension"
module Gem
def self.win_platform?() false end
end unless defined?(Gem)
# pretend we're always on the internet (so that we don't have an
# internet connection dependency for our cukes)
VCR::InternetConnection.class_eval do
def available?; true; end
end
if ENV['DATE_STRING']
require 'timecop'
Timecop.travel(Date.parse(ENV['DATE_STRING']))
end
def include_http_adapter_for(lib)
require((lib =~ /faraday/) ? 'faraday' : lib)
require 'typhoeus' if lib.include?('typhoeus') # for faraday-typhoeus
require 'support/http_library_adapters'
include HTTP_LIBRARY_ADAPTERS[lib]
end
def response_body_for(*args)
get_body_string(make_http_request(*args))
end
def start_sinatra_app(&block)
require 'sinatra/base'
require 'support/vcr_localhost_server'
klass = Class.new(Sinatra::Base)
klass.disable :protection
klass.class_eval(&block)
VCR::LocalhostServer.new(klass.new)
end
vcr-5.0.0/spec/support/http_library_adapters.rb 0000664 0000000 0000000 00000017425 13473056530 0021706 0 ustar 00root root 0000000 0000000 module HeaderDowncaser
def downcase_headers(headers)
{}.tap do |downcased|
headers.each do |k, v|
downcased[k.downcase] = v
end
end
end
end
HTTP_LIBRARY_ADAPTERS = {}
HTTP_LIBRARY_ADAPTERS['net/http'] = Module.new do
include HeaderDowncaser
def self.http_library_name; 'Net::HTTP'; end
def get_body_string(response); response.body; end
alias get_body_object get_body_string
def get_header(header_key, response)
response.get_fields(header_key)
end
def make_http_request(method, url, body = nil, headers = {})
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
if uri.scheme == "https"
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
http.send_request(method.to_s.upcase, uri.request_uri, body, headers)
end
DEFAULT_REQUEST_HEADERS = { "Accept"=>["*/*"] }
DEFAULT_REQUEST_HEADERS['User-Agent'] = ["Ruby"]
DEFAULT_REQUEST_HEADERS['Accept-Encoding'] = ["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"] if RUBY_VERSION.to_f > 1.9
def normalize_request_headers(headers)
defined?(super) ? super :
downcase_headers(headers.merge(DEFAULT_REQUEST_HEADERS))
end
end
HTTP_LIBRARY_ADAPTERS['patron'] = Module.new do
def self.http_library_name; 'Patron'; end
def get_body_string(response); response.body; end
alias get_body_object get_body_string
def get_header(header_key, response)
response.headers[header_key]
end
def make_http_request(method, url, body = nil, headers = {})
Patron::Session.new.request(method, url, headers, :data => body || '')
end
def normalize_request_headers(headers)
headers.merge('Expect' => [''])
end
end
HTTP_LIBRARY_ADAPTERS['httpclient'] = Module.new do
def self.http_library_name; 'HTTP Client'; end
def get_body_string(response)
body = response.body
string = body.is_a?(String) ? body : body.content
string.respond_to?(:read) ? string.read : string
end
def get_body_object(response)
response.body
end
def get_header(header_key, response)
response.header[header_key]
end
def make_http_request(method, url, body = nil, headers = {})
HTTPClient.new.request(method, url, nil, body, headers)
end
def normalize_request_headers(headers)
headers.merge({
'Accept' => ["*/*"],
'User-Agent' => ["HTTPClient/1.0 (#{HTTPClient::VERSION}, ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}))"],
'Date' => [Time.now.httpdate]
})
end
end
HTTP_LIBRARY_ADAPTERS['em-http-request'] = Module.new do
def self.http_library_name; 'EM HTTP Request'; end
def get_body_string(response)
response.response
end
alias get_body_object get_body_string
def get_header(header_key, response)
values = response.response_header[header_key.upcase.gsub('-', '_')]
values.is_a?(Array) ? values : values.split(', ')
end
def make_http_request(method, url, body = nil, headers = {})
http = nil
EventMachine.run do
http = EventMachine::HttpRequest.new(url).send(method, :body => body, :head => headers)
http.callback { EventMachine.stop }
end
http
end
def normalize_request_headers(headers)
headers
end
end
HTTP_LIBRARY_ADAPTERS['curb'] = Module.new do
def self.http_library_name; "Curb"; end
def get_body_string(response)
response.body_str
end
alias get_body_object get_body_string
def get_header(header_key, response)
headers = response.header_str.split("\r\n")[1..-1]
value = nil
headers.each do |h|
next unless h =~ /^#{Regexp.escape(header_key)}: (.*)$/
new_value = $1.split(', ')
value = value ? Array(value) + Array(new_value) : new_value
end
value
end
def make_http_request(method, url, body = nil, headers = {})
Curl::Easy.new(url) do |c|
c.headers = headers
if [:post, :put].include?(method)
c.send("http_#{method}", body)
else
c.send("http_#{method}")
end
end
end
def normalize_request_headers(headers)
headers
end
end
HTTP_LIBRARY_ADAPTERS['typhoeus'] = Module.new do
def self.http_library_name; "Typhoeus"; end
def get_body_string(response)
response.body
end
alias get_body_object get_body_string
def get_header(header_key, response)
# Due to https://github.com/typhoeus/typhoeus/commit/256c95473d5d40d7ec2f5db603687323ddd73689
# headers are now downcased.
# ...except when they're not. I'm not 100% why (I haven't had time to dig into it yet)
# but in some situations the headers aren't downcased. I think it has to do with playback; VCR
# isn't sending the headers in downcased to typhoeus. It gets complicated with the interaction
# w/ WebMock, and the fact that webmock normalizes headers in a different fashion.
#
# For now this hack works.
response.headers.fetch(header_key.downcase) { response.headers[header_key] }
end
def make_http_request(method, url, body = nil, headers = {})
request = Typhoeus::Request.new(url, :method => method, :body => body, :headers => headers)
request.run
request.response
end
def normalize_request_headers(headers)
headers.merge("User-Agent"=>["Typhoeus - https://github.com/typhoeus/typhoeus"], 'Expect' => [''])
end
end
HTTP_LIBRARY_ADAPTERS['typhoeus 0.4'] = Module.new do
def self.http_library_name; "Typhoeus"; end
def get_body_string(response)
response.body
end
alias get_body_object get_body_string
def get_header(header_key, response)
response.headers_hash[header_key]
end
def make_http_request(method, url, body = nil, headers = {})
Typhoeus::Request.send(method, url, :body => body, :headers => headers)
end
def normalize_request_headers(headers)
headers
end
end
HTTP_LIBRARY_ADAPTERS['excon'] = Module.new do
def self.http_library_name; "Excon"; end
def get_body_string(response)
response.body
end
alias get_body_object get_body_string
def get_header(header_key, response)
response.headers[header_key]
end
def make_http_request(method, url, body = nil, headers = {})
# There are multiple ways to use Excon but this is how fog (the main user of excon) uses it:
# https://github.com/fog/fog/blob/v1.1.1/lib/fog/aws/rds.rb#L139-147
Excon.new(url).request(:method => method.to_s.upcase, :body => body, :headers => headers)
end
def normalize_request_headers(headers)
headers.merge('User-Agent' => [Excon::USER_AGENT])
end
end
%w[ net_http typhoeus patron ].each do |_faraday_adapter|
if _faraday_adapter == 'typhoeus' &&
defined?(::Typhoeus::VERSION) &&
::Typhoeus::VERSION.to_f >= 0.5
require 'typhoeus/adapters/faraday'
end
HTTP_LIBRARY_ADAPTERS["faraday (w/ #{_faraday_adapter})"] = Module.new do
class << self; self; end.class_eval do
define_method(:http_library_name) do
"Faraday (#{_faraday_adapter})"
end
end
define_method(:faraday_adapter) { _faraday_adapter.to_sym }
def get_body_string(response)
response.body
end
alias get_body_object get_body_string
def get_header(header_key, response)
value = response.headers[header_key]
value.split(', ') if value
end
def make_http_request(method, url, body = nil, headers = {})
url_root, url_rest = split_url(url)
faraday_connection(url_root).send(method) do |req|
req.url url_rest
headers.each { |k, v| req[k] = v }
req.body = body if body
end
end
def split_url(url)
uri = URI.parse(url)
url_root = "#{uri.scheme}://#{uri.host}:#{uri.port}"
rest = url.sub(url_root, '')
[url_root, rest]
end
def faraday_connection(url_root)
Faraday::Connection.new(:url => url_root) do |builder|
builder.adapter faraday_adapter
end
end
def normalize_request_headers(headers)
headers.merge("User-Agent" => ["Faraday v#{Faraday::VERSION}"])
end
end
end
vcr-5.0.0/spec/support/integer_extension.rb 0000664 0000000 0000000 00000000225 13473056530 0021037 0 ustar 00root root 0000000 0000000 unless 7.respond_to?(:days)
class Integer
def days
self *
24 * # hours
60 * # minutes
60 # seconds
end
end
end
vcr-5.0.0/spec/support/limited_uri.rb 0000664 0000000 0000000 00000000626 13473056530 0017621 0 ustar 00root root 0000000 0000000 class LimitedURI
extend Forwardable
def_delegators :@uri, :scheme, :host, :port, :port=, :path, :query, :query=, :to_s
def initialize(uri)
@uri = uri
end
def ==(other)
to_s == other.to_s
end
def self.parse(uri)
return uri if uri.is_a? LimitedURI
return new(uri) if uri.is_a? URI
return new(URI.parse(uri)) if uri.is_a? String
raise URI::InvalidURIError
end
end
vcr-5.0.0/spec/support/ruby_interpreter.rb 0000664 0000000 0000000 00000000207 13473056530 0020712 0 ustar 00root root 0000000 0000000 RUBY_INTERPRETER = if RUBY_PLATFORM == 'java'
:jruby
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
:rubinius
else
:mri
end
vcr-5.0.0/spec/support/shared_example_groups/ 0000775 0000000 0000000 00000000000 13473056530 0021342 5 ustar 00root root 0000000 0000000 vcr-5.0.0/spec/support/shared_example_groups/excon.rb 0000664 0000000 0000000 00000004044 13473056530 0023005 0 ustar 00root root 0000000 0000000 shared_examples "Excon streaming" do
context "when Excon's streaming API is used" do
def make_request_to(path)
chunks = []
Excon.get "http://localhost:#{VCR::SinatraApp.port}#{path}", :response_block => lambda { |chunk, remaining_bytes, total_bytes|
chunks << chunk
}
chunks.join
end
it 'properly records and plays back the response' do
allow(VCR).to receive(:real_http_connections_allowed?).and_return(true)
recorded, played_back = [1, 2].map do
make_request_to('/foo')
end
expect(recorded).to eq(played_back)
expect(recorded).to eq("FOO!")
end
it 'properly records and plays back the response for unexpected status' do
allow(VCR).to receive(:real_http_connections_allowed?).and_return(true)
recorded, played_back = [1, 2].map do
chunks = []
VCR.use_cassette('excon_streaming_error', :record => :once) do
begin
Excon.get "http://localhost:#{VCR::SinatraApp.port}/404_not_200", :expects => 200, :response_block => lambda { |chunk, remaining_bytes, total_bytes|
chunks << chunk
}
rescue Excon::Errors::Error => e
chunks << e.response.body
end
end
chunks.join
end
expect(recorded).to eq(played_back)
expect(recorded).to eq('404 not 200')
end
context "when a cassette is played back and appended to" do
it 'does not allow Excon to mutate the response body in the cassette' do
VCR.use_cassette('excon_streaming', :record => :new_episodes) do
expect(make_request_to('/')).to eq('GET to root')
end
VCR.use_cassette('excon_streaming', :record => :new_episodes) do
expect(make_request_to('/')).to eq('GET to root')
expect(make_request_to('/foo')).to eq('FOO!') # so it will save to disk again
end
VCR.use_cassette('excon_streaming', :record => :new_episodes) do
expect(make_request_to('/')).to eq('GET to root')
end
end
end
end
end
vcr-5.0.0/spec/support/shared_example_groups/hook_into_http_library.rb 0000664 0000000 0000000 00000054627 13473056530 0026461 0 ustar 00root root 0000000 0000000 require 'cgi'
NET_CONNECT_NOT_ALLOWED_ERROR = /An HTTP request has been made that VCR does not know how to handle/
shared_examples_for "a hook into an HTTP library" do |library_hook_name, library, *other|
include HeaderDowncaser
include VCRStubHelpers
unless adapter_module = HTTP_LIBRARY_ADAPTERS[library]
raise ArgumentError.new("No http library adapter module could be found for #{library}")
end
http_lib_unsupported = (RUBY_INTERPRETER != :mri && library =~ /(typhoeus|curb|patron|em-http)/)
describe "using #{adapter_module.http_library_name}", :unless => http_lib_unsupported do
include adapter_module
# Necessary for ruby 1.9.2. On 1.9.2 we get an error when we use super,
# so this gives us another alias we can use for the original method.
alias make_request make_http_request
1.upto(2) do |header_count|
describe "making an HTTP request that responds with #{header_count} Set-Cookie header(s)" do
define_method :get_set_cookie_header do
VCR.use_cassette('header_test', :record => :once) do
get_header 'Set-Cookie', make_http_request(:get, "http://localhost:#{VCR::SinatraApp.port}/set-cookie-headers/#{header_count}")
end
end
it 'returns the same header value when recording and replaying' do
expect((recorded_val = get_set_cookie_header)).not_to be_nil
replayed_val = get_set_cookie_header
expect(replayed_val).to eq(recorded_val)
end
end
end
def self.test_record_and_playback(description, query)
describe "a request to a URL #{description}" do
define_method :get_body do
VCR.use_cassette('record_and_playback', :record => :once) do
get_body_string make_http_request(:get, "http://localhost:#{VCR::SinatraApp.port}/record-and-playback?#{query}")
end
end
it "properly records and playsback a request with a URL #{description}" do
recorded_body = get_body
played_back_body = get_body
expect(played_back_body).to eq(recorded_body)
end
end
end
test_record_and_playback "with spaces encoded as +", "q=a+b"
test_record_and_playback "with spaces encoded as %20", "q=a%20b"
test_record_and_playback "with a complex escaped query param", "q=#{CGI.escape("A&(! 234k !@ kasdj232\#$ kjw35")}"
it 'plays back an empty body response exactly as it was recorded (e.g. nil vs empty string)' do
skip "Faraday 0.8 may return nil bodies" if library_hook_name == :faraday && !defined?(::Faraday::RackBuilder)
get_body = lambda do
VCR.use_cassette('empty_body', :record => :once) do
get_body_object make_http_request(:get, "http://localhost:#{VCR::SinatraApp.port}/204")
end
end
recorded = get_body.call
played_back = get_body.call
expect(played_back).to eq(recorded)
end
describe 'making an HTTP request' do
let(:status) { VCR::ResponseStatus.new(200, 'OK') }
let(:interaction) { VCR::HTTPInteraction.new(request, response) }
let(:response_body) { "The response body" }
before(:each) do
stub_requests([interaction], [:method, :uri])
end
context "when the the stubbed request and response has no headers" do
let(:request) { VCR::Request.new(:get, 'http://example.com:80/') }
let(:response) { VCR::Response.new(status, nil, response_body, '1.1') }
it 'returns the response for a matching request' do
expect(get_body_string(make_http_request(:get, 'http://example.com/'))).to eq(response_body)
end
end
def self.test_playback(description, url)
context "when a URL #{description} has been stubbed" do
let(:request) { VCR::Request.new(:get, url) }
let(:response) { VCR::Response.new(status, nil, response_body, '1.1') }
it 'returns the expected response for the same request' do
expect(get_body_string(make_http_request(:get, url))).to eq(response_body)
end
end
end
test_playback "using https and no explicit port", "https://example.com/foo"
test_playback "using https and port 443", "https://example.com:443/foo"
test_playback "using https and some other port", "https://example.com:5190/foo"
test_playback "that has query params", "http://example.com/search?q=param"
test_playback "with an encoded ampersand", "http://example.com:80/search?q=#{CGI.escape("Q&A")}"
end
it 'does not query the http interaction list excessively' do
call_count = 0
[:has_interaction_matching?, :response_for].each do |method_name|
orig_meth = VCR.http_interactions.method(method_name)
allow(VCR.http_interactions).to receive(method_name) do |*args|
call_count += 1
orig_meth.call(*args)
end
end
VCR.insert_cassette('foo')
make_http_request(:get, "http://localhost:#{VCR::SinatraApp.port}/foo")
expect(call_count).to eq(1)
end
describe "using the library's stubbing/disconnection APIs" do
let!(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
if method_defined?(:disable_real_connections)
it 'can make a real request when VCR is turned off' do
enable_real_connections
VCR.turn_off!
expect(get_body_string(make_http_request(:get, request_url))).to eq("FOO!")
end
it 'does not mess with VCR when real connections are disabled' do
VCR.insert_cassette('example')
disable_real_connections
expect(VCR).to receive(:record_http_interaction) do |interaction|
expect(interaction.request.uri).to eq(request_url)
end
make_http_request(:get, request_url)
end
it 'can disable real connections when VCR is turned off' do
VCR.turn_off!
expected_error = disable_real_connections
expect {
make_http_request(:get, request_url)
}.to raise_error(expected_error)
end
end
if method_defined?(:directly_stub_request)
it 'can directly stub the request when VCR is turned off' do
VCR.turn_off!
directly_stub_request(:get, request_url, "stubbed response")
expect(get_body_string(make_http_request(:get, request_url))).to eq("stubbed response")
end
it 'can directly stub the request when VCR is turned on and no cassette is in use' do
directly_stub_request(:get, request_url, "stubbed response")
expect(get_body_string(make_http_request(:get, request_url))).to eq("stubbed response")
end
it 'can directly stub the request when VCR is turned on and a cassette is in use' do
VCR.use_cassette("temp") do
directly_stub_request(:get, request_url, "stubbed response")
expect(get_body_string(make_http_request(:get, request_url))).to eq("stubbed response")
end
end
it 'does not record requests that are directly stubbed' do
expect(VCR).to respond_to(:record_http_interaction)
expect(VCR).not_to receive(:record_http_interaction)
VCR.use_cassette("temp") do
directly_stub_request(:get, request_url, "stubbed response")
expect(get_body_string(make_http_request(:get, request_url))).to eq("stubbed response")
end
end
end
end
describe "request hooks" do
context 'when there is an around_http_request hook' do
let(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
it 'yields the request to the block' do
yielded_request = nil
VCR.configuration.around_http_request do |request|
yielded_request = request
request.proceed
end
VCR.use_cassette('new_cassette') do
make_http_request(:get, request_url)
end
expect(yielded_request.method).to eq(:get)
expect(yielded_request.uri).to eq(request_url)
end
it 'returns the response from request.proceed' do
response = nil
VCR.configuration.around_http_request do |request|
response = request.proceed
end
VCR.use_cassette('new_cassette') do
make_http_request(:get, request_url)
end
expect(response.body).to eq("FOO!")
end
it 'can be used to use a cassette for a request' do
VCR.configuration.around_http_request do |request|
VCR.use_cassette('new_cassette', &request)
end
expect(VCR).to receive(:record_http_interaction) do
expect(VCR.current_cassette.name).to eq('new_cassette')
end
expect(VCR.current_cassette).to be_nil
make_http_request(:get, request_url)
expect(VCR.current_cassette).to be_nil
end
it 'nests them inside each other, making the first declared hook the outermost' do
order = []
VCR.configure do |c|
c.ignore_request { |r| true }
c.around_http_request do |request|
order << :before_1
request.proceed
order << :after_1
end
c.around_http_request do |request|
order << :before_2
request.proceed
order << :after_2
end
end
make_http_request(:get, request_url)
expect(order).to eq([:before_1, :before_2, :after_2, :after_1])
end
it 'raises an appropriate error if the hook does not call request.proceed' do
VCR.configuration.ignore_request { |r| true }
hook_declaration = "#{__FILE__}:#{__LINE__ + 1}"
VCR.configuration.around_http_request { |r| }
expect {
make_http_request(:get, request_url)
}.to raise_error { |error|
expect(error.message).to include('must call #proceed on the yielded request')
expect(error.message).to include(hook_declaration)
}
end
it 'does not get a dead fiber error when multiple requests are made' do
VCR.configuration.around_http_request do |request|
VCR.use_cassette('new_cassette', &request)
end
3.times { make_http_request(:get, request_url) }
end
it 'allows the hook to be filtered' do
order = []
VCR.configure do |c|
c.ignore_request { |r| true }
c.around_http_request(lambda { |r| r.uri =~ /foo/}) do |request|
order << :before_foo
request.proceed
order << :after_foo
end
c.around_http_request(lambda { |r| r.uri !~ /foo/}) do |request|
order << :before_not_foo
request.proceed
order << :after_not_foo
end
end
make_http_request(:get, request_url)
expect(order).to eq([:before_foo, :after_foo])
end
it 'ensures that both around/before are invoked or neither' do
order = []
allow_1, allow_2 = false, true
VCR.configure do |c|
c.ignore_request { |r| true }
c.around_http_request(lambda { |r| allow_1 = !allow_1 }) do |request|
order << :before_1
request.proceed
order << :after_1
end
c.around_http_request(lambda { |r| allow_2 = !allow_2 }) do |request|
order << :before_2
request.proceed
order << :after_2
end
end
make_http_request(:get, request_url)
expect(order).to eq([:before_1, :after_1])
end
end if RUBY_VERSION >= '1.9'
it 'correctly assigns the correct type to both before and after request hooks, even if they are different' do
before_type = after_type = nil
VCR.configuration.before_http_request do |request|
before_type = request.type
VCR.insert_cassette('example')
end
VCR.configuration.after_http_request do |request|
after_type = request.type
VCR.eject_cassette
end
make_http_request(:get, "http://localhost:#{VCR::SinatraApp.port}/foo")
expect(before_type).to be(:unhandled)
expect(after_type).to be(:recordable)
end
context "when the request is ignored" do
before(:each) do
VCR.configuration.ignore_request { |r| true }
end
it_behaves_like "request hooks", library_hook_name, :ignored
end
context "when the request is directly stubbed" do
before(:each) do
directly_stub_request(:get, request_url, "FOO!")
end
it_behaves_like "request hooks", library_hook_name, :externally_stubbed
end if method_defined?(:directly_stub_request)
context 'when the request is recorded' do
let!(:inserted_cassette) { VCR.insert_cassette('new_cassette') }
it_behaves_like "request hooks", library_hook_name, :recordable do
let(:string_in_cassette) { 'example.com get response 1 with path=foo' }
it 'plays back the cassette when a request is made' do
VCR.eject_cassette
VCR.configure do |c|
c.cassette_library_dir = File.join(VCR::SPEC_ROOT, 'fixtures')
c.before_http_request do |request|
VCR.insert_cassette('fake_example_responses', :record => :none)
end
end
expect(get_body_string(make_http_request(:get, 'http://example.com/foo'))).to eq(string_in_cassette)
end
specify 'the after_http_request hook can be used to eject a cassette after the request is recorded' do
VCR.configuration.after_http_request { |request| VCR.eject_cassette }
expect(VCR).to receive(:record_http_interaction) do |interaction|
expect(VCR.current_cassette).to be(inserted_cassette)
end
make_request
expect(VCR.current_cassette).to be_nil
end
end
end
context 'when a stubbed response is played back for the request' do
before(:each) do
stub_requests([http_interaction(request_url)], [:method, :uri])
end
it_behaves_like "request hooks", library_hook_name, :stubbed_by_vcr
end
context 'when the request is not allowed' do
it_behaves_like "request hooks", library_hook_name, :unhandled do
undef assert_expected_response
def assert_expected_response(response)
expect(response).to be_nil
end
undef make_request
def make_request(disabled = false)
if disabled
make_http_request(:get, request_url)
else
expect { make_http_request(:get, request_url) }.to raise_error(NET_CONNECT_NOT_ALLOWED_ERROR)
end
end
end
end
end
describe '.stub_requests using specific match_attributes' do
before(:each) { allow(VCR).to receive(:real_http_connections_allowed?).and_return(false) }
let(:interactions) { interactions_from('match_requests_on.yml') }
let(:normalized_interactions) do
interactions.each do |i|
i.request.headers = normalize_request_headers(i.request.headers)
end
interactions
end
def self.matching_on(attribute, valid, invalid, &block)
describe ":#{attribute}" do
let(:perform_stubbing) { stub_requests(normalized_interactions, [attribute]) }
before(:each) { perform_stubbing }
module_eval(&block)
valid.each do |val, response|
it "returns the expected response for a #{val.inspect} request" do
expect(get_body_string(make_http_request(val))).to eq(response)
end
end
it "raises an error for a request with a different #{attribute}" do
expect { make_http_request(invalid) }.to raise_error(NET_CONNECT_NOT_ALLOWED_ERROR)
end
end
end
matching_on :method, { :get => "get method response", :post => "post method response" }, :put do
def make_http_request(http_method)
make_request(http_method, 'http://some-wrong-domain.com/', nil, {})
end
end
matching_on :host, { 'example1.com' => 'example1.com host response', 'example2.com' => 'example2.com host response' }, 'example3.com' do
def make_http_request(host)
make_request(:get, "http://#{host}/some/wrong/path", nil, {})
end
end
matching_on :path, { '/path1' => 'path1 response', '/path2' => 'path2 response' }, '/path3' do
def make_http_request(path)
make_request(:get, "http://some.wrong.domain.com#{path}?p=q", nil, {})
end
end
matching_on :uri, { 'http://example.com/uri1' => 'uri1 response', 'http://example.com/uri2' => 'uri2 response' }, 'http://example.com/uri3' do
def make_http_request(uri)
make_request(:get, uri, nil, {})
end
end
matching_on :body, { 'param=val1' => 'val1 body response', 'param=val2' => 'val2 body response' }, 'param=val3' do
def make_http_request(body)
make_request(:put, "http://wrong-domain.com/wrong/path", body, {})
end
end
matching_on :headers, {{ 'X-Http-Header1' => 'val1' } => 'val1 header response', { 'X-Http-Header1' => 'val2' } => 'val2 header response' }, { 'X-Http-Header1' => 'val3' } do
def make_http_request(headers)
make_request(:get, "http://wrong-domain.com/wrong/path", nil, headers)
end
end
end
def self.test_real_http_request(http_allowed, *other)
let(:url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
if http_allowed
it 'allows real http requests' do
expect(get_body_string(make_http_request(:get, url))).to eq('FOO!')
end
describe 'recording new http requests' do
let(:recorded_interaction) do
interaction = nil
expect(VCR).to receive(:record_http_interaction) { |i| interaction = i }
make_http_request(:post, url, "the body", { 'X-Http-Foo' => 'bar' })
interaction
end
it 'does not record the request if the hook is disabled' do
VCR.library_hooks.exclusively_enabled :something_else do
expect(VCR).not_to receive(:record_http_interaction)
make_http_request(:get, url)
end
end
it 'records the request uri' do
expect(recorded_interaction.request.uri).to eq(url)
end
it 'records the request method' do
expect(recorded_interaction.request.method).to eq(:post)
end
it 'records the request body' do
expect(recorded_interaction.request.body).to eq("the body")
end
it 'records the request headers' do
headers = downcase_headers(recorded_interaction.request.headers)
expect(headers).to include('x-http-foo' => ['bar'])
end
it 'records the response status code' do
expect(recorded_interaction.response.status.code).to eq(200)
end
it 'records the response status message' do
expect(recorded_interaction.response.status.message.strip).to eq('OK')
end unless other.include?(:status_message_not_exposed)
it 'records the response body' do
expect(recorded_interaction.response.body).to eq('FOO!')
end
it 'records the response headers' do
headers = downcase_headers(recorded_interaction.response.headers)
expect(headers).to include('content-type' => ["text/html;charset=utf-8"])
end
end
else
it 'does not allow real HTTP requests or record them' do
expect(VCR).to receive(:record_http_interaction).never
expect { make_http_request(:get, url) }.to raise_error(NET_CONNECT_NOT_ALLOWED_ERROR)
end
end
end
[true, false].each do |http_allowed|
context "when VCR.real_http_connections_allowed? is returning #{http_allowed}" do
before(:each) { allow(VCR).to receive(:real_http_connections_allowed?).and_return(http_allowed) }
test_real_http_request(http_allowed, *other)
unless http_allowed
localhost_response = "Localhost response"
context 'when ignore_hosts is configured to "127.0.0.1", "localhost"' do
before(:each) do
VCR.configure { |c| c.ignore_hosts "127.0.0.1", "localhost" }
end
%w[ 127.0.0.1 localhost ].each do |localhost_alias|
it "allows requests to #{localhost_alias}" do
expect(get_body_string(make_http_request(:get, "http://#{localhost_alias}:#{VCR::SinatraApp.port}/localhost_test"))).to eq(localhost_response)
end
end
it 'does not allow requests to 0.0.0.0' do
expect { make_http_request(:get, "http://0.0.0.0:#{VCR::SinatraApp.port}/localhost_test") }.to raise_error(NET_CONNECT_NOT_ALLOWED_ERROR)
end
end
end
context 'when some requests are stubbed' do
let(:interactions) { interactions_from('fake_example_responses.yml') }
before(:each) do
stub_requests(interactions, VCR::RequestMatcherRegistry::DEFAULT_MATCHERS)
end
it 'gets the stubbed responses when requests are made to http://example.com/foo, and does not record them' do
expect(VCR).to receive(:record_http_interaction).never
expect(get_body_string(make_http_request(:get, 'http://example.com/foo'))).to match(/example\.com get response \d with path=foo/)
end
it 'rotates through multiple responses for the same request' do
expect(get_body_string(make_http_request(:get, 'http://example.com/foo'))).to eq('example.com get response 1 with path=foo')
expect(get_body_string(make_http_request(:get, 'http://example.com/foo'))).to eq('example.com get response 2 with path=foo')
end unless other.include?(:does_not_support_rotating_responses)
it "correctly handles stubbing multiple values for the same header" do
header = get_header('Set-Cookie', make_http_request(:get, 'http://example.com/two_set_cookie_headers'))
header = header.split(', ') if header.respond_to?(:split)
expect(header).to match_array ['bar=bazz', 'foo=bar']
end
end
end
end
end
end
vcr-5.0.0/spec/support/shared_example_groups/request_hooks.rb 0000664 0000000 0000000 00000003356 13473056530 0024571 0 ustar 00root root 0000000 0000000 shared_examples_for "request hooks" do |library_hook_name, request_type|
let(:request_url) { "http://localhost:#{VCR::SinatraApp.port}/foo" }
def make_request(disabled = false)
make_http_request(:get, request_url)
end
def assert_expected_response(response)
expect(response.status.code).to eq(200)
expect(response.body).to eq('FOO!')
end
[:before_http_request, :after_http_request].each do |hook|
specify "the #{hook} hook is only called once per request" do
call_count = 0
VCR.configuration.send(hook) { |r| call_count += 1 }
make_request
expect(call_count).to eq(1)
end
specify "the #{hook} hook yields the request" do
request = nil
VCR.configuration.send(hook) { |r| request = r }
make_request
expect(request.method).to be(:get)
expect(request.uri).to eq(request_url)
end
specify "the #{hook} hook is not called if the library hook is disabled" do
expect(VCR.library_hooks).to respond_to(:disabled?)
allow(VCR.library_hooks).to receive(:disabled?).and_return(true)
hook_called = false
VCR.configuration.send(hook) { |r| hook_called = true }
make_request(:disabled)
expect(hook_called).to be false
end
specify "the #type of the yielded request given to the #{hook} hook is #{request_type}" do
request = nil
VCR.configuration.send(hook) { |r| request = r }
make_request
expect(request.type).to be(request_type)
end
end
specify "the after_http_request hook yields the response if there is one and the second block arg is given" do
response = nil
VCR.configuration.after_http_request { |req, res| response = res }
make_request
assert_expected_response(response)
end
end
vcr-5.0.0/spec/support/sinatra_app.rb 0000664 0000000 0000000 00000003101 13473056530 0017603 0 ustar 00root root 0000000 0000000 require 'sinatra'
module VCR
class SinatraApp < ::Sinatra::Base
disable :protection
get '/' do
"GET to root"
end
get '/search' do
"query: #{params[:q]}"
end
get '/localhost_test' do
"Localhost response"
end
get '/foo' do
"FOO!"
end
get '/redirect-to-root' do
redirect to('/')
end
post '/foo' do
"FOO!"
end
post '/return-request-body' do
request.body
end
get '/set-cookie-headers/1' do
headers 'Set-Cookie' => 'foo'
'header set'
end
get '/set-cookie-headers/2' do
headers 'Set-Cookie' => %w[ bar foo ]
'header set'
end
get '/204' do
status 204
end
get '/404_not_200' do
status 404
'404 not 200'
end
# we use a global counter so that every response is different;
# this ensures that the test demonstrates that the response
# is being played back (and not running a 2nd real request)
$record_and_playback_response_count ||= 0
get '/record-and-playback' do
"Response #{$record_and_playback_response_count += 1}"
end
post '/record-and-playback' do
"Response #{$record_and_playback_response_count += 1}"
end
@_boot_failed = false
class << self
def port
server.port
end
def server
raise "Sinatra app failed to boot." if @_boot_failed
@server ||= begin
VCR::LocalhostServer.new(new)
rescue
@_boot_failed = true
raise
end
end
alias boot server
end
end
end
vcr-5.0.0/spec/support/vcr_localhost_server.rb 0000664 0000000 0000000 00000004022 13473056530 0021535 0 ustar 00root root 0000000 0000000 require 'rack'
require 'rack/handler/webrick'
require 'net/http'
# The code for this is inspired by Capybara's server:
# http://github.com/jnicklas/capybara/blob/0.3.9/lib/capybara/server.rb
module VCR
class LocalhostServer
READY_MESSAGE = "VCR server ready"
class Identify
def initialize(app)
@app = app
end
def call(env)
if env["PATH_INFO"] == "/__identify__"
[200, {}, [VCR::LocalhostServer::READY_MESSAGE]]
else
@app.call(env)
end
end
end
attr_reader :port
def initialize(rack_app, port = nil)
@port = port || find_available_port
@rack_app = rack_app
concurrently { boot }
wait_until(30, "Boot failed.") { booted? }
end
private
def find_available_port
server = TCPServer.new('127.0.0.1', 0)
server.addr[1]
ensure
server.close if server
end
def boot
# Use WEBrick since it's part of the ruby standard library and is available on all ruby interpreters.
options = { :Port => port, :ShutdownSocketWithoutClose => true }
options.merge!(:AccessLog => [], :Logger => WEBrick::BasicLog.new(StringIO.new)) unless ENV['VERBOSE_SERVER']
Rack::Handler::WEBrick.run(Identify.new(@rack_app), options)
end
def booted?
res = ::Net::HTTP.get_response("localhost", '/__identify__', port)
if res.is_a?(::Net::HTTPSuccess) or res.is_a?(::Net::HTTPRedirection)
return res.body == READY_MESSAGE
end
rescue Errno::ECONNREFUSED, Errno::EBADF
return false
end
def concurrently
# JRuby doesn't support forking.
# Rubinius does, but there's a weird issue with the booted? check not working,
# so we're just using a thread for now.
Thread.new { yield }
end
def wait_until(timeout, error_message, &block)
start_time = Time.now
while true
return if yield
raise TimeoutError.new(error_message) if (Time.now - start_time) > timeout
sleep(0.01)
end
end
end
end
vcr-5.0.0/spec/support/vcr_stub_helpers.rb 0000664 0000000 0000000 00000001227 13473056530 0020662 0 ustar 00root root 0000000 0000000 module VCRStubHelpers
def interactions_from(file)
hashes = YAML.load_file(File.join(VCR::SPEC_ROOT, 'fixtures', file))['http_interactions']
hashes.map { |h| VCR::HTTPInteraction.from_hash(h) }
end
def stub_requests(*args)
allow(VCR).to receive(:http_interactions).and_return(VCR::Cassette::HTTPInteractionList.new(*args))
end
def http_interaction(url, response_body = "FOO!", status_code = 200)
request = VCR::Request.new(:get, request_url)
response_status = VCR::ResponseStatus.new(status_code)
response = VCR::Response.new(response_status, nil, response_body, '1.1')
VCR::HTTPInteraction.new(request, response)
end
end
vcr-5.0.0/vcr.gemspec 0000664 0000000 0000000 00000003554 13473056530 0014462 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
# coding: utf-8
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "vcr/version"
Gem::Specification.new do |spec|
spec.name = "vcr"
spec.version = VCR.version
spec.authors = ["Myron Marston"]
spec.email = ["myron.marston@gmail.com"]
spec.summary = %q{Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.}
spec.description = spec.summary
spec.homepage = "https://relishapp.com/vcr/vcr/docs"
spec.license = "MIT"
spec.files = Dir[File.join("lib", "**", "*")]
spec.executables = Dir[File.join("bin", "**", "*")].map! { |f| f.gsub(/bin\//, "") }
spec.require_paths = ["lib"]
spec.required_ruby_version = ">= 1.9.3"
spec.add_development_dependency "bundler", "~> 2.0"
spec.add_development_dependency "rspec", "~> 3.0"
spec.add_development_dependency "test-unit", "~> 3.1.4"
spec.add_development_dependency "rake", "~> 10.1"
spec.add_development_dependency "pry", "~> 0.9"
spec.add_development_dependency "pry-doc", "~> 0.6"
spec.add_development_dependency "codeclimate-test-reporter", "~> 0.4"
spec.add_development_dependency "yard"
spec.add_development_dependency "rack"
spec.add_development_dependency "webmock"
spec.add_development_dependency "cucumber", "~> 2.0.2"
spec.add_development_dependency "aruba", "~> 0.5.3"
spec.add_development_dependency "faraday", "~> 0.11.0"
spec.add_development_dependency "httpclient"
spec.add_development_dependency "excon", "0.62.0"
spec.add_development_dependency "timecop"
spec.add_development_dependency "multi_json"
spec.add_development_dependency "json"
spec.add_development_dependency "relish"
spec.add_development_dependency "mime-types"
spec.add_development_dependency "sinatra"
end