ruby/test/rubygems/test_gem_remote_fetcher.rb

606 lines
16 KiB
Ruby

# frozen_string_literal: true
require_relative "helper"
require "rubygems/remote_fetcher"
require "rubygems/package"
class TestGemRemoteFetcher < Gem::TestCase
include Gem::DefaultUserInteraction
def setup
super
@cache_dir = File.join @gemhome, "cache"
# TODO: why does the remote fetcher need it written to disk?
@a1, @a1_gem = util_gem "a", "1" do |s|
s.executables << "a_bin"
end
@a1.loaded_from = File.join(@gemhome, "specifications", @a1.full_name)
Gem::RemoteFetcher.fetcher = nil
@stub_ui = Gem::MockGemUi.new
@fetcher = Gem::RemoteFetcher.fetcher
end
def test_self_fetcher
fetcher = Gem::RemoteFetcher.fetcher
refute_nil fetcher
assert_kind_of Gem::RemoteFetcher, fetcher
end
def test_self_fetcher_with_proxy
proxy_uri = "http://proxy.example.com"
Gem.configuration[:http_proxy] = proxy_uri
Gem::RemoteFetcher.fetcher = nil
fetcher = Gem::RemoteFetcher.fetcher
refute_nil fetcher
assert_kind_of Gem::RemoteFetcher, fetcher
assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy).to_s
ensure
Gem.configuration[:http_proxy] = nil
end
def test_fetch_path_bad_uri
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
e = assert_raise ArgumentError do
@fetcher.fetch_path("gems.example.com/yaml", nil, true)
end
assert_equal "uri scheme is invalid: nil", e.message
end
def test_cache_update_path
uri = Gem::URI "http://example/file"
path = File.join @tempdir, "file"
fetcher = util_fuck_with_fetcher "hello"
data = fetcher.cache_update_path uri, path
assert_equal "hello", data
assert_equal "hello", File.read(path)
end
def test_cache_update_path_with_utf8_internal_encoding
with_internal_encoding("UTF-8") do
uri = Gem::URI "http://example/file"
path = File.join @tempdir, "file"
data = String.new("\xC8").force_encoding(Encoding::BINARY)
fetcher = util_fuck_with_fetcher data
written_data = fetcher.cache_update_path uri, path
assert_equal data, written_data
assert_equal data, File.binread(path)
end
end
def test_cache_update_path_no_update
uri = Gem::URI "http://example/file"
path = File.join @tempdir, "file"
fetcher = util_fuck_with_fetcher "hello"
data = fetcher.cache_update_path uri, path, false
assert_equal "hello", data
assert_path_not_exist path
end
def util_fuck_with_fetcher(data, blow = false)
fetcher = Gem::RemoteFetcher.fetcher
fetcher.instance_variable_set :@test_data, data
if blow
def fetcher.fetch_path(arg, *rest)
# OMG I'm such an ass
class << self; remove_method :fetch_path; end
def self.fetch_path(arg, *rest)
@test_arg = arg
@test_data
end
raise Gem::RemoteFetcher::FetchError.new("haha!", "")
end
else
def fetcher.fetch_path(arg, *rest)
@test_arg = arg
@test_data
end
end
fetcher
end
def test_download
a1_data = nil
File.open @a1_gem, "rb" do |fp|
a1_data = fp.read
end
fetcher = util_fuck_with_fetcher a1_data
a1_cache_gem = @a1.cache_file
assert_equal a1_cache_gem, fetcher.download(@a1, "http://gems.example.com")
assert_equal("http://gems.example.com/gems/a-1.gem",
fetcher.instance_variable_get(:@test_arg).to_s)
assert File.exist?(a1_cache_gem)
end
def test_download_with_auth
a1_data = nil
File.open @a1_gem, "rb" do |fp|
a1_data = fp.read
end
fetcher = util_fuck_with_fetcher a1_data
a1_cache_gem = @a1.cache_file
assert_equal a1_cache_gem, fetcher.download(@a1, "http://user:password@gems.example.com")
assert_equal("http://user:password@gems.example.com/gems/a-1.gem",
fetcher.instance_variable_get(:@test_arg).to_s)
assert File.exist?(a1_cache_gem)
end
def test_download_with_token
a1_data = nil
File.open @a1_gem, "rb" do |fp|
a1_data = fp.read
end
fetcher = util_fuck_with_fetcher a1_data
a1_cache_gem = @a1.cache_file
assert_equal a1_cache_gem, fetcher.download(@a1, "http://token@gems.example.com")
assert_equal("http://token@gems.example.com/gems/a-1.gem",
fetcher.instance_variable_get(:@test_arg).to_s)
assert File.exist?(a1_cache_gem)
end
def test_download_with_x_oauth_basic
a1_data = nil
File.open @a1_gem, "rb" do |fp|
a1_data = fp.read
end
fetcher = util_fuck_with_fetcher a1_data
a1_cache_gem = @a1.cache_file
assert_equal a1_cache_gem, fetcher.download(@a1, "http://token:x-oauth-basic@gems.example.com")
assert_equal("http://token:x-oauth-basic@gems.example.com/gems/a-1.gem",
fetcher.instance_variable_get(:@test_arg).to_s)
assert File.exist?(a1_cache_gem)
end
def test_download_with_encoded_auth
a1_data = nil
File.open @a1_gem, "rb" do |fp|
a1_data = fp.read
end
fetcher = util_fuck_with_fetcher a1_data
a1_cache_gem = @a1.cache_file
assert_equal a1_cache_gem, fetcher.download(@a1, "http://user:%25pas%25sword@gems.example.com")
assert_equal("http://user:%25pas%25sword@gems.example.com/gems/a-1.gem",
fetcher.instance_variable_get(:@test_arg).to_s)
assert File.exist?(a1_cache_gem)
end
def test_download_cached
FileUtils.mv @a1_gem, @cache_dir
inst = Gem::RemoteFetcher.fetcher
assert_equal @a1.cache_file, inst.download(@a1, "http://gems.example.com")
end
def test_download_local
omit "doesn't work if tempdir has +" if @tempdir.include?("+")
FileUtils.mv @a1_gem, @tempdir
local_path = File.join @tempdir, @a1.file_name
inst = nil
Dir.chdir @tempdir do
inst = Gem::RemoteFetcher.fetcher
end
assert_equal @a1.cache_file, inst.download(@a1, local_path)
end
def test_download_local_space
omit "doesn't work if tempdir has +" if @tempdir.include?("+")
space_path = File.join @tempdir, "space path"
FileUtils.mkdir space_path
FileUtils.mv @a1_gem, space_path
local_path = File.join space_path, @a1.file_name
inst = nil
Dir.chdir @tempdir do
inst = Gem::RemoteFetcher.fetcher
end
assert_equal @a1.cache_file, inst.download(@a1, local_path)
end
def test_download_install_dir
a1_data = File.open @a1_gem, "rb", &:read
fetcher = util_fuck_with_fetcher a1_data
install_dir = File.join @tempdir, "more_gems"
a1_cache_gem = File.join install_dir, "cache", @a1.file_name
FileUtils.mkdir_p(File.dirname(a1_cache_gem))
actual = fetcher.download(@a1, "http://gems.example.com", install_dir)
assert_equal a1_cache_gem, actual
assert_equal("http://gems.example.com/gems/a-1.gem",
fetcher.instance_variable_get(:@test_arg).to_s)
assert File.exist?(a1_cache_gem)
end
unless Gem.win_platform? || Process.uid.zero? # File.chmod doesn't work
def test_download_local_read_only
omit "doesn't work if tempdir has +" if @tempdir.include?("+")
FileUtils.mv @a1_gem, @tempdir
local_path = File.join @tempdir, @a1.file_name
inst = nil
FileUtils.chmod 0o555, @a1.cache_dir
begin
FileUtils.mkdir_p File.join(Gem.user_dir, "cache")
rescue StandardError
nil
end
FileUtils.chmod 0o555, File.join(Gem.user_dir, "cache")
Dir.chdir @tempdir do
inst = Gem::RemoteFetcher.fetcher
end
assert_equal(File.join(@tempdir, @a1.file_name),
inst.download(@a1, local_path))
ensure
if local_path
FileUtils.chmod 0o755, File.join(Gem.user_dir, "cache")
FileUtils.chmod 0o755, @a1.cache_dir
end
end
def test_download_read_only
FileUtils.chmod 0o555, @a1.cache_dir
FileUtils.chmod 0o555, @gemhome
fetcher = util_fuck_with_fetcher File.read(@a1_gem)
fetcher.download(@a1, "http://gems.example.com")
a1_cache_gem = File.join Gem.user_dir, "cache", @a1.file_name
assert File.exist? a1_cache_gem
ensure
FileUtils.chmod 0o755, @gemhome
FileUtils.chmod 0o755, @a1.cache_dir
end
end
def test_download_platform_legacy
original_platform = "old-platform"
e1, e1_gem = util_gem "e", "1" do |s|
s.platform = Gem::Platform::CURRENT
s.instance_variable_set :@original_platform, original_platform
end
e1.loaded_from = File.join(@gemhome, "specifications", e1.full_name)
e1_data = nil
File.open e1_gem, "rb" do |fp|
e1_data = fp.read
end
fetcher = util_fuck_with_fetcher e1_data, :blow_chunks
e1_cache_gem = e1.cache_file
assert_equal e1_cache_gem, fetcher.download(e1, "http://gems.example.com")
assert_equal("http://gems.example.com/gems/#{e1.original_name}.gem",
fetcher.instance_variable_get(:@test_arg).to_s)
assert File.exist?(e1_cache_gem)
end
def test_download_same_file
omit "doesn't work if tempdir has +" if @tempdir.include?("+")
FileUtils.mv @a1_gem, @tempdir
local_path = File.join @tempdir, @a1.file_name
inst = nil
Dir.chdir @tempdir do
inst = Gem::RemoteFetcher.fetcher
end
cache_path = @a1.cache_file
FileUtils.mv local_path, cache_path
gem = Gem::Package.new cache_path
assert_equal cache_path, inst.download(gem.spec, cache_path)
end
def test_download_unsupported
inst = Gem::RemoteFetcher.fetcher
e = assert_raise ArgumentError do
inst.download @a1, "ftp://gems.rubyforge.org"
end
assert_equal "unsupported URI scheme ftp", e.message
end
def test_download_to_cache
@a2, @a2_gem = util_gem "a", "2"
util_setup_spec_fetcher @a1, @a2
@fetcher.instance_variable_set :@a1, @a1
@fetcher.instance_variable_set :@a2, @a2
def @fetcher.fetch_path(uri, mtime = nil, head = false)
case uri.request_uri
when /#{@a1.spec_name}/ then
Gem.deflate Marshal.dump @a1
when /#{@a2.spec_name}/ then
Gem.deflate Marshal.dump @a2
else
uri.to_s
end
end
gem = Gem::RemoteFetcher.fetcher.download_to_cache dep "a"
assert_equal @a2.file_name, File.basename(gem)
end
def test_fetch_path_gzip
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
def fetcher.fetch_http(uri, mtime, head = nil)
Gem::Util.gzip "foo"
end
assert_equal "foo", fetcher.fetch_path(@uri + "foo.gz")
end
def test_fetch_path_gzip_unmodified
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
def fetcher.fetch_http(uri, mtime, head = nil)
nil
end
assert_nil fetcher.fetch_path(@uri + "foo.gz", Time.at(0))
end
def test_fetch_path_io_error
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
def fetcher.fetch_http(*)
raise EOFError
end
url = "http://example.com/uri"
e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path url
end
assert_equal "EOFError: EOFError (#{url})", e.message
assert_equal url, e.uri
end
def test_fetch_path_socket_error
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
def fetcher.fetch_http(uri, mtime, head = nil)
raise SocketError
end
url = "http://example.com/uri"
e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path url
end
assert_equal "SocketError: SocketError (#{url})", e.message
assert_equal url, e.uri
end
def test_fetch_path_system_call_error
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
def fetcher.fetch_http(uri, mtime = nil, head = nil)
raise Errno::ECONNREFUSED, "connect(2)"
end
url = "http://example.com/uri"
e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path url
end
assert_match(/ECONNREFUSED:.*connect\(2\) \(#{Regexp.escape url}\)\z/,
e.message)
assert_equal url, e.uri
end
def test_fetch_path_timeout_error
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
def fetcher.fetch_http(uri, mtime = nil, head = nil)
raise Gem::Timeout::Error, "timed out"
end
url = "http://example.com/uri"
e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path url
end
assert_match(/Gem::Timeout::Error: timed out \(#{Regexp.escape url}\)\z/,
e.message)
assert_equal url, e.uri
end
def test_fetch_path_getaddrinfo_error
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
def fetcher.fetch_http(uri, mtime = nil, head = nil)
raise SocketError, "getaddrinfo: nodename nor servname provided"
end
url = "http://example.com/uri"
e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path url
end
assert_match(/SocketError: getaddrinfo: nodename nor servname provided \(#{Regexp.escape url}\)\z/,
e.message)
assert_equal url, e.uri
end
def test_fetch_path_openssl_ssl_sslerror
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
def fetcher.fetch_http(uri, mtime = nil, head = nil)
raise OpenSSL::SSL::SSLError
end
url = "http://example.com/uri"
e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path url
end
assert_equal "OpenSSL::SSL::SSLError: OpenSSL::SSL::SSLError (#{url})", e.message
assert_equal url, e.uri
end
def test_fetch_path_unmodified
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
def fetcher.fetch_http(uri, mtime, head = nil)
nil
end
assert_nil fetcher.fetch_path(Gem::URI.parse(@gem_repo), Time.at(0))
end
def test_fetch_http
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
url = "http://gems.example.com/redirect"
def fetcher.request(uri, request_class, last_modified = nil)
url = "http://gems.example.com/redirect"
if defined? @requested
res = Gem::Net::HTTPOK.new nil, 200, nil
def res.body
"real_path"
end
else
@requested = true
res = Gem::Net::HTTPMovedPermanently.new nil, 301, nil
res.add_field "Location", url
end
res
end
data = fetcher.fetch_http Gem::URI.parse(url)
assert_equal "real_path", data
end
def test_fetch_http_redirects
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
url = "http://gems.example.com/redirect"
def fetcher.request(uri, request_class, last_modified = nil)
url = "http://gems.example.com/redirect"
res = Gem::Net::HTTPMovedPermanently.new nil, 301, nil
res.add_field "Location", url
res
end
e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_http Gem::URI.parse(url)
end
assert_equal "too many redirects (#{url})", e.message
end
def test_fetch_http_redirects_without_location
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
url = "http://gems.example.com/redirect"
def fetcher.request(uri, request_class, last_modified = nil)
res = Gem::Net::HTTPMovedPermanently.new nil, 301, nil
res
end
e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_http Gem::URI.parse(url)
end
assert_equal "redirecting but no redirect location was given (#{url})", e.message
end
def test_request_block
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
assert_throws :block_called do
fetcher.request Gem::URI("http://example"), Gem::Net::HTTP::Get do |req|
assert_kind_of Gem::Net::HTTPGenericRequest, req
throw :block_called
end
end
end
def test_yaml_error_on_size
use_ui @stub_ui do
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
assert_error { fetcher.size }
end
end
def assert_error(exception_class=Exception)
got_exception = false
begin
yield
rescue exception_class
got_exception = true
end
assert got_exception, "Expected exception conforming to #{exception_class}"
end
end