1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/spec/bundler/support/artifice/vcr.rb
David Rodríguez 0fb2a7fa1c Fix bin/rake spec:realworld:record
Previously it was failing and not recording. Also, the
`BUNDLER_SPEC_FORCE_RECORD` environment variable was forcing every
request to be recorded, including duplicated requests to the same
endpoint. That meant the recorded `versions` file would be incorrect
because it would contain the result of the last request, which would be
`304 NOT MODIFIED`.

So, to rerecord, delete cassettes and let them be recorded when needed.
2020-06-05 07:32:42 +09:00

151 lines
4.2 KiB
Ruby

# frozen_string_literal: true
require "net/http"
require_relative "../path"
CASSETTE_PATH = "#{Spec::Path.spec_dir}/support/artifice/vcr_cassettes"
CASSETTE_NAME = ENV.fetch("BUNDLER_SPEC_VCR_CASSETTE_NAME") { "realworld" }
class BundlerVCRHTTP < Net::HTTP
class RequestHandler
attr_reader :http, :request, :body, :response_block
def initialize(http, request, body = nil, &response_block)
@http = http
@request = request
@body = body
@response_block = response_block
end
def handle_request
handler = self
request.instance_eval do
@__vcr_request_handler = handler
end
if recorded_response?
recorded_response
else
record_response
end
end
def recorded_response?
return true if ENV["BUNDLER_SPEC_PRE_RECORDED"]
request_pair_paths.all? {|f| File.exist?(f) }
end
def recorded_response
File.open(request_pair_paths.last, "rb:ASCII-8BIT") do |response_file|
response_io = ::Net::BufferedIO.new(response_file)
::Net::HTTPResponse.read_new(response_io).tap do |response|
response.decode_content = request.decode_content if request.respond_to?(:decode_content)
response.uri = request.uri if request.respond_to?(:uri)
response.reading_body(response_io, request.response_body_permitted?) do
response_block.call(response) if response_block
end
end
end
end
def record_response
request_path, response_path = *request_pair_paths
@recording = true
response = http.request_without_vcr(request, body, &response_block)
@recording = false
unless @recording
FileUtils.mkdir_p(File.dirname(request_path))
binwrite(request_path, request_to_string(request))
binwrite(response_path, response_to_string(response))
end
response
end
def key
[request["host"] || http.address, request.path, request.method].compact
end
def file_name_for_key(key)
key.join("/").gsub(/[\:*?"<>|]/, "-")
end
def request_pair_paths
%w[request response].map do |kind|
File.join(CASSETTE_PATH, CASSETTE_NAME, file_name_for_key(key + [kind]))
end
end
def read_stored_request(path)
contents = File.binread(path)
headers = {}
method = nil
path = nil
contents.lines.grep(/^> /).each do |line|
if line =~ /^> (GET|HEAD|POST|PATCH|PUT|DELETE) (.*)/
method = $1
path = $2.strip
elsif line =~ /^> (.*?): (.*)/
headers[$1] = $2
end
end
body = contents =~ /^([^>].*)/m && $1
Net::HTTP.const_get(method.capitalize).new(path, headers).tap {|r| r.body = body if body }
end
def request_to_string(request)
request_string = []
request_string << "> #{request.method.upcase} #{request.path}"
request.to_hash.each do |key, value|
request_string << "> #{key}: #{Array(value).first}"
end
request << "" << request.body if request.body
request_string.join("\n")
end
def response_to_string(response)
headers = response.to_hash
body = response.body
response_string = []
response_string << "HTTP/1.1 #{response.code} #{response.message}"
headers["content-length"] = [body.bytesize.to_s] if body
headers.each do |header, value|
response_string << "#{header}: #{value.join(", ")}"
end
response_string << "" << body
response_string = response_string.join("\n")
if response_string.respond_to?(:force_encoding)
response_string.force_encoding("ASCII-8BIT")
else
response_string
end
end
def binwrite(path, contents)
File.open(path, "wb:ASCII-8BIT") {|f| f.write(contents) }
end
end
def request_with_vcr(request, *args, &block)
handler = request.instance_eval do
remove_instance_variable(:@__vcr_request_handler) if defined?(@__vcr_request_handler)
end || RequestHandler.new(self, request, *args, &block)
handler.handle_request
end
alias_method :request_without_vcr, :request
alias_method :request, :request_with_vcr
end
# Replace Net::HTTP with our VCR subclass
::Net.class_eval do
remove_const(:HTTP)
const_set(:HTTP, BundlerVCRHTTP)
end