summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Giddins <segiddins@segiddins.me>2016-12-24 16:54:00 +0100
committerSamuel Giddins <segiddins@segiddins.me>2017-06-13 11:12:22 -0500
commit3c643eafd76c1514aa9ce565e7754cf5049f483d (patch)
treea61af7de44c3afe92fda743c4367498b585a0145
parent3cd996c3a62a7b95e18de2982d0fb5e6daf159c4 (diff)
downloadbundler-3c643eafd76c1514aa9ce565e7754cf5049f483d.tar.gz
Re-implement VCR to support marshalled responses with incorrect content-length headers
-rw-r--r--spec/support/artifice/vcr.rb213
1 files changed, 82 insertions, 131 deletions
diff --git a/spec/support/artifice/vcr.rb b/spec/support/artifice/vcr.rb
index 9b35ff9629..a4ae34ba92 100644
--- a/spec/support/artifice/vcr.rb
+++ b/spec/support/artifice/vcr.rb
@@ -1,13 +1,4 @@
# frozen_string_literal: true
-require File.expand_path("../../path.rb", __FILE__)
-include Spec::Path
-
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{vcr}-*/lib")].map(&:to_s))
-
-require "vcr"
-require "vcr/request_handler"
-require "vcr/extensions/net_http_response"
-
require "net/http"
if RUBY_VERSION < "1.9"
begin
@@ -17,163 +8,123 @@ if RUBY_VERSION < "1.9"
end
end # but only for 1.8
-VCR.configure do |config|
- config.cassette_library_dir = File.expand_path("../vcr_cassettes", __FILE__)
- config.preserve_exact_body_bytes do |_response|
- true
- end
- # config.debug_logger = File.open(File.expand_path("../vcr.log", __FILE__), "w")
-end
-
-VCR.insert_cassette \
- ENV.fetch("BUNDLER_SPEC_VCR_CASSETTE_NAME") { "realworld" },
- :record => :new_episodes,
- :match_requests_on => [:method, :uri, :query]
-
-at_exit { VCR.eject_cassette }
+CASSETTE_PATH = File.expand_path("../vcr_cassettes", __FILE__)
+CASSETTE_NAME = ENV.fetch("BUNDLER_SPEC_VCR_CASSETTE_NAME") { "realworld" }
class BundlerVCRHTTP < Net::HTTP
- # @private
- class RequestHandler < ::VCR::RequestHandler
- attr_reader :net_http, :request, :request_body, :response_block
- def initialize(net_http, request, request_body = nil, &response_block)
- @net_http = net_http
+ class RequestHandler
+ attr_reader :http, :request, :body, :response_block
+ def initialize(http, request, body = nil, &response_block)
+ @http = http
@request = request
- @request_body = request_body
+ @body = body
@response_block = response_block
- @stubbed_response = nil
- @vcr_response = nil
- @recursing = false
- end
-
- def handle
- super
- ensure
- invoke_after_request_hook(@vcr_response) unless @recursing
end
- private
-
- def on_recordable_request
- perform_request(net_http.started?, :record_interaction)
- end
-
- def on_stubbed_by_vcr_request
- status = stubbed_response.status
- headers = stubbed_response.headers
- body = stubbed_response.body
-
- response_string = []
- response_string << "HTTP/1.1 #{status.code} #{status.message}"
-
- headers.each do |header, value|
- response_string << "#{header}: #{value}"
+ def handle_request
+ handler = self
+ request.instance_eval do
+ @__vcr_request_handler = handler
end
- response_string << "" << body
-
- response_io = ::Net::BufferedIO.new(StringIO.new(response_string.join("\n")))
- res = ::Net::HTTPResponse.read_new(response_io)
-
- res.reading_body(response_io, true) do
- yield res if block_given?
+ if recorded_response?
+ recorded_response
+ else
+ record_response
end
-
- res
end
- def on_ignored_request
- raise "no ignored requests allowed"
+ def recorded_response?
+ return true if ENV["BUNDLER_SPEC_RECORDING"]
+ request_pair_paths.all? {|f| File.exist?(f) }
end
- def perform_request(started, record_interaction = false)
- # Net::HTTP calls #request recursively in certain circumstances.
- # We only want to record the request when the request is started, as
- # that is the final time through #request.
- unless started
- @recursing = true
- request.instance_variable_set(:@__vcr_request_handler, recursive_request_handler)
- return net_http.request_without_vcr(request, request_body, &response_block)
- 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
+ response.uri = request.uri
- net_http.request_without_vcr(request, request_body) do |response|
- @vcr_response = vcr_response_from(response)
-
- if record_interaction
- VCR.record_http_interaction VCR::HTTPInteraction.new(vcr_request, @vcr_response)
+ response.reading_body(response_io, request.response_body_permitted?) do
+ response_block.call(response) if response_block
+ end
end
-
- response.extend ::VCR::Net::HTTPResponse # "unwind" the response
- response_block.call(response) if response_block
end
end
- def uri
- @uri ||= begin
- protocol = net_http.use_ssl? ? "https" : "http"
+ def record_response
+ request_path, response_path = *request_pair_paths
- path = request.path
- path = URI.parse(request.path).request_uri if request.path =~ /^http/
+ @recording = true
- "#{protocol}://#{net_http.address}#{path}"
+ response = http.request_without_vcr(request, body, &response_block)
+ @recording = false
+ unless @recording
+ FileUtils.mkdir_p(File.dirname(request_path))
+ File.binwrite(request_path, request_to_string(request))
+ File.binwrite(response_path, response_to_string(response))
end
+ response
end
- def response_hash(response)
- (response.headers || {}).merge(
- :body => response.body,
- :status => [response.status.code.to_s, response.status.message]
- )
+ def key
+ [request.uri, request["host"] || http.address, request.path, request.method].compact
end
- def request_method
- request.method.downcase.to_sym
+ def file_name_for_key(key)
+ key.join("/")
end
- def vcr_request
- @vcr_request ||= VCR::Request.new \
- request_method,
- uri,
- (request_body || request.body),
- request.to_hash
+ 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 vcr_response_from(response)
- VCR::Response.new \
- VCR::ResponseStatus.new(response.code.to_i, response.message),
- response.to_hash,
- response.body,
- response.http_version
+ def read_stored_request(path)
+ contents = File.read(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 recursive_request_handler
- @recursive_request_handler ||= RecursiveRequestHandler.new(
- @after_hook_typed_request.type, @stubbed_response, @vcr_request,
- @net_http, @request, @request_body, &@response_block
- )
+ 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
- end
- # @private
- class RecursiveRequestHandler < RequestHandler
- attr_reader :stubbed_response
+ def response_to_string(response)
+ headers = response.to_hash
+ body = response.body
- def initialize(request_type, stubbed_response, vcr_request, *args, &response_block)
- @request_type = request_type
- @stubbed_response = stubbed_response
- @vcr_request = vcr_request
- super(*args)
- end
+ response_string = []
+ response_string << "HTTP/1.1 #{response.code} #{response.message}"
- def handle
- set_typed_request_for_after_hook(@request_type)
- send "on_#{@request_type}_request"
- ensure
- invoke_after_request_hook(@vcr_response)
- end
+ headers["content-length"] = [body.bytesize.to_s] if body
+
+ headers.each do |header, value|
+ response_string << "#{header}: #{value.join(", ")}"
+ end
+
+ response_string << "" << body
- def request_type(*args)
- @request_type
+ response_string.join("\n").force_encoding("ASCII-8BIT")
end
end
@@ -182,7 +133,7 @@ class BundlerVCRHTTP < Net::HTTP
remove_instance_variable(:@__vcr_request_handler) if defined?(@__vcr_request_handler)
end || RequestHandler.new(self, request, *args, &block)
- handler.handle
+ handler.handle_request
end
alias_method :request_without_vcr, :request