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/install/gems/dependency_api_spec.rb
Hiroshi SHIBATA d22511fd75 Merge rubygems/rubygems HEAD.
Picked at 12aeef6ba9a3be0022be9934c1a3e4c46a03ed3a
2022-01-19 15:01:44 +09:00

759 lines
22 KiB
Ruby

# frozen_string_literal: true
RSpec.describe "gemcutter's dependency API" do
let(:source_hostname) { "localgemserver.test" }
let(:source_uri) { "http://#{source_hostname}" }
it "should use the API" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle :install, :artifice => "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "should URI encode gem names" do
gemfile <<-G
source "#{source_uri}"
gem " sinatra"
G
bundle :install, :artifice => "endpoint", :raise_on_error => false
expect(err).to include("' sinatra' is not a valid gem name because it contains whitespace.")
end
it "should handle nested dependencies" do
gemfile <<-G
source "#{source_uri}"
gem "rails"
G
bundle :install, :artifice => "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}/...")
expect(the_bundle).to include_gems(
"rails 2.3.2",
"actionpack 2.3.2",
"activerecord 2.3.2",
"actionmailer 2.3.2",
"activeresource 2.3.2",
"activesupport 2.3.2"
)
end
it "should handle multiple gem dependencies on the same gem" do
gemfile <<-G
source "#{source_uri}"
gem "net-sftp"
G
bundle :install, :artifice => "endpoint"
expect(the_bundle).to include_gems "net-sftp 1.1.1"
end
it "should use the endpoint when using deployment mode" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle :install, :artifice => "endpoint"
bundle "config set --local deployment true"
bundle "config set --local path vendor/bundle"
bundle :install, :artifice => "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "handles git dependencies that are in rubygems" do
build_git "foo" do |s|
s.executables = "foobar"
s.add_dependency "rails", "2.3.2"
end
gemfile <<-G
source "#{source_uri}"
git "#{file_uri_for(lib_path("foo-1.0"))}" do
gem 'foo'
end
G
bundle :install, :artifice => "endpoint"
expect(the_bundle).to include_gems("rails 2.3.2")
end
it "handles git dependencies that are in rubygems using deployment mode" do
build_git "foo" do |s|
s.executables = "foobar"
s.add_dependency "rails", "2.3.2"
end
gemfile <<-G
source "#{source_uri}"
gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}"
G
bundle :install, :artifice => "endpoint"
bundle "config set --local deployment true"
bundle :install, :artifice => "endpoint"
expect(the_bundle).to include_gems("rails 2.3.2")
end
it "doesn't fail if you only have a git gem with no deps when using deployment mode" do
build_git "foo"
gemfile <<-G
source "#{source_uri}"
gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}"
G
bundle "install", :artifice => "endpoint"
bundle "config set --local deployment true"
bundle :install, :artifice => "endpoint"
expect(the_bundle).to include_gems("foo 1.0")
end
it "falls back when the API errors out" do
simulate_platform mswin
build_repo2 do
# The rcov gem is platform mswin32, but has no arch
build_gem "rcov" do |s|
s.platform = Gem::Platform.new([nil, "mswin32", nil])
s.write "lib/rcov.rb", "RCOV = '1.0.0'"
end
end
gemfile <<-G
source "#{source_uri}"
gem "rcov"
G
bundle :install, :artifice => "windows", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems "rcov 1.0.0"
end
it "falls back when hitting the Gemcutter Dependency Limit" do
gemfile <<-G
source "#{source_uri}"
gem "activesupport"
gem "actionpack"
gem "actionmailer"
gem "activeresource"
gem "thin"
gem "rack"
gem "rails"
G
bundle :install, :artifice => "endpoint_fallback"
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems(
"activesupport 2.3.2",
"actionpack 2.3.2",
"actionmailer 2.3.2",
"activeresource 2.3.2",
"activesupport 2.3.2",
"thin 1.0.0",
"rack 1.0.0",
"rails 2.3.2"
)
end
it "falls back when Gemcutter API doesn't return proper Marshal format" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle :install, :verbose => true, :artifice => "endpoint_marshal_fail"
expect(out).to include("could not fetch from the dependency API, trying the full index")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "falls back when the API URL returns 403 Forbidden" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle :install, :verbose => true, :artifice => "endpoint_api_forbidden"
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "handles host redirects" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle :install, :artifice => "endpoint_host_redirect"
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "handles host redirects without Net::HTTP::Persistent" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
FileUtils.mkdir_p lib_path
File.open(lib_path("disable_net_http_persistent.rb"), "w") do |h|
h.write <<-H
module Kernel
alias require_without_disabled_net_http require
def require(*args)
raise LoadError, 'simulated' if args.first == 'openssl' && !caller.grep(/vendored_persistent/).empty?
require_without_disabled_net_http(*args)
end
end
H
end
bundle :install, :artifice => "endpoint_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
expect(out).to_not match(/Too many redirects/)
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "timeouts when Bundler::Fetcher redirects too much" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle :install, :artifice => "endpoint_redirect", :raise_on_error => false
expect(err).to match(/Too many redirects/)
end
context "when --full-index is specified" do
it "should use the modern index for install" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle "install --full-index", :artifice => "endpoint"
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "should use the modern index for update" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle "update --full-index", :artifice => "endpoint", :all => true
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
end
it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
source "#{source_uri}"
source "#{source_uri}/extra"
gem "back_deps"
G
bundle :install, :artifice => "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
end
it "fetches again when more dependencies are found in subsequent sources using blocks" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
end
G
bundle :install, :artifice => "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
end
it "fetches gem versions even when those gems are already installed" do
gemfile <<-G
source "#{source_uri}"
gem "rack", "1.0.0"
G
bundle :install, :artifice => "endpoint_extra_api"
build_repo4 do
build_gem "rack", "1.2" do |s|
s.executables = "rackup"
end
end
gemfile <<-G
source "#{source_uri}" do; end
source "#{source_uri}/extra"
gem "rack", "1.2"
G
bundle :install, :artifice => "endpoint_extra_api"
expect(the_bundle).to include_gems "rack 1.2"
end
it "considers all possible versions of dependencies from all api gem sources", :bundler => "< 3" do
# In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
# exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
# of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
# repo and installs it.
build_repo4 do
build_gem "activesupport", "1.2.0"
build_gem "somegem", "1.0.0" do |s|
s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
end
end
gemfile <<-G
source "#{source_uri}"
source "#{source_uri}/extra"
gem 'somegem', '1.0.0'
G
bundle :install, :artifice => "endpoint_extra_api"
expect(the_bundle).to include_gems "somegem 1.0.0"
expect(the_bundle).to include_gems "activesupport 1.2.3"
end
it "prints API output properly with back deps" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
end
G
bundle :install, :artifice => "endpoint_extra"
expect(out).to include("Fetching gem metadata from http://localgemserver.test/.")
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
end
it "does not fetch every spec if the index of gems is large when doing back deps", :bundler => "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
build_gem "missing"
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
api_request_limit = low_api_request_limit_for(gem_repo2)
install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
source "#{source_uri}"
source "#{source_uri}/extra"
gem "back_deps"
G
expect(the_bundle).to include_gems "back_deps 1.0"
end
it "does not fetch every spec if the index of gems is large when doing back deps using blocks" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
build_gem "missing"
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
api_request_limit = low_api_request_limit_for(gem_repo2)
install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
end
G
expect(the_bundle).to include_gems "back_deps 1.0"
end
it "fetches again when more dependencies are found in subsequent sources using deployment mode", :bundler => "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
source "#{source_uri}"
source "#{source_uri}/extra"
gem "back_deps"
G
bundle :install, :artifice => "endpoint_extra"
bundle "config set --local deployment true"
bundle :install, :artifice => "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
it "fetches again when more dependencies are found in subsequent sources using deployment mode with blocks" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
end
G
bundle :install, :artifice => "endpoint_extra"
bundle "config set --local deployment true"
bundle "install", :artifice => "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
it "does not refetch if the only unmet dependency is bundler" do
build_repo2 do
build_gem "bundler_dep" do |s|
s.add_dependency "bundler"
end
end
gemfile <<-G
source "#{source_uri}"
gem "bundler_dep"
G
bundle :install, :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
sudo "mkdir -p #{system_gem_path("bin")}"
sudo "chown -R root #{system_gem_path("bin")}"
gemfile <<-G
source "#{source_uri}"
gem "rails"
G
bundle :install, :artifice => "endpoint"
expect(the_bundle).to include_gems "rails 2.3.2"
end
it "installs the binstubs", :bundler => "< 3" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle "install --binstubs", :artifice => "endpoint"
gembin "rackup"
expect(out).to eq("1.0.0")
end
it "installs the bins when using --path and uses autoclean", :bundler => "< 3" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle "install --path vendor/bundle", :artifice => "endpoint"
expect(vendored_gems("bin/rackup")).to exist
end
it "installs the bins when using --path and uses bundle clean", :bundler => "< 3" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
bundle "install --path vendor/bundle --no-clean", :artifice => "endpoint"
expect(vendored_gems("bin/rackup")).to exist
end
it "prints post_install_messages" do
gemfile <<-G
source "#{source_uri}"
gem 'rack-obama'
G
bundle :install, :artifice => "endpoint"
expect(out).to include("Post-install message from rack:")
end
it "should display the post install message for a dependency" do
gemfile <<-G
source "#{source_uri}"
gem 'rack_middleware'
G
bundle :install, :artifice => "endpoint"
expect(out).to include("Post-install message from rack:")
expect(out).to include("Rack's post install message")
end
context "when using basic authentication" do
let(:user) { "user" }
let(:password) { "pass" }
let(:basic_auth_source_uri) do
uri = Bundler::URI.parse(source_uri)
uri.user = user
uri.password = password
uri
end
it "passes basic authentication details and strips out creds" do
gemfile <<-G
source "#{basic_auth_source_uri}"
gem "rack"
G
bundle :install, :artifice => "endpoint_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "passes basic authentication details and strips out creds also in verbose mode" do
gemfile <<-G
source "#{basic_auth_source_uri}"
gem "rack"
G
bundle :install, :verbose => true, :artifice => "endpoint_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "strips http basic authentication creds for modern index" do
gemfile <<-G
source "#{basic_auth_source_uri}"
gem "rack"
G
bundle :install, :artifice => "endpoint_marshal_fail_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "strips http basic auth creds when it can't reach the server" do
gemfile <<-G
source "#{basic_auth_source_uri}"
gem "rack"
G
bundle :install, :artifice => "endpoint_500", :raise_on_error => false
expect(out).not_to include("#{user}:#{password}")
end
it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 3" do
gemfile <<-G
source "#{basic_auth_source_uri}"
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
bundle :install, :artifice => "endpoint_basic_authentication"
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
expect(err).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "does not pass the user / password to different hosts on redirect" do
gemfile <<-G
source "#{basic_auth_source_uri}"
gem "rack"
G
bundle :install, :artifice => "endpoint_creds_diff_host"
expect(the_bundle).to include_gems "rack 1.0.0"
end
describe "with host including dashes" do
before do
gemfile <<-G
source "http://local-gemserver.test"
gem "rack"
G
end
it "reads authentication details from a valid ENV variable" do
bundle :install, :artifice => "endpoint_strict_basic_authentication", :env => { "BUNDLE_LOCAL___GEMSERVER__TEST" => "#{user}:#{password}" }
expect(out).to include("Fetching gem metadata from http://local-gemserver.test")
expect(the_bundle).to include_gems "rack 1.0.0"
end
end
describe "with authentication details in bundle config" do
before do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
end
it "reads authentication details by host name from bundle config" do
bundle "config set #{source_hostname} #{user}:#{password}"
bundle :install, :artifice => "endpoint_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "reads authentication details by full url from bundle config" do
# The trailing slash is necessary here; Fetcher canonicalizes the URI.
bundle "config set #{source_uri}/ #{user}:#{password}"
bundle :install, :artifice => "endpoint_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "should use the API" do
bundle "config set #{source_hostname} #{user}:#{password}"
bundle :install, :artifice => "endpoint_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "prefers auth supplied in the source uri" do
gemfile <<-G
source "#{basic_auth_source_uri}"
gem "rack"
G
bundle "config set #{source_hostname} otheruser:wrong"
bundle :install, :artifice => "endpoint_strict_basic_authentication"
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "shows instructions if auth is not provided for the source" do
bundle :install, :artifice => "endpoint_strict_basic_authentication", :raise_on_error => false
expect(err).to include("bundle config set --global #{source_hostname} username:password")
end
it "fails if authentication has already been provided, but failed" do
bundle "config set #{source_hostname} #{user}:wrong"
bundle :install, :artifice => "endpoint_strict_basic_authentication", :raise_on_error => false
expect(err).to include("Bad username or password")
end
end
describe "with no password" do
let(:password) { nil }
it "passes basic authentication details" do
gemfile <<-G
source "#{basic_auth_source_uri}"
gem "rack"
G
bundle :install, :artifice => "endpoint_basic_authentication"
expect(the_bundle).to include_gems "rack 1.0.0"
end
end
end
context "when ruby is compiled without openssl" do
before do
# Install a monkeypatch that reproduces the effects of openssl being
# missing when the fetcher runs, as happens in real life. The reason
# we can't just overwrite openssl.rb is that Artifice uses it.
bundled_app("broken_ssl").mkpath
bundled_app("broken_ssl/openssl.rb").open("w") do |f|
f.write <<-RUBY
raise LoadError, "cannot load such file -- openssl"
RUBY
end
end
it "explains what to do to get it" do
gemfile <<-G
source "#{source_uri.gsub(/http/, "https")}"
gem "rack"
G
bundle :install, :env => { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, :raise_on_error => false
expect(err).to include("OpenSSL")
end
end
context "when SSL certificate verification fails" do
it "explains what happened" do
# Install a monkeypatch that reproduces the effects of openssl raising
# a certificate validation error when RubyGems tries to connect.
gemfile <<-G
class Net::HTTP
def start
raise OpenSSL::SSL::SSLError, "certificate verify failed"
end
end
source "#{source_uri.gsub(/http/, "https")}"
gem "rack"
G
bundle :install, :raise_on_error => false
expect(err).to match(/could not verify the SSL certificate/i)
end
end
context ".gemrc with sources is present" do
it "uses other sources declared in the Gemfile" do
File.open(home(".gemrc"), "w") do |file|
file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
end
begin
gemfile <<-G
source "#{source_uri}"
gem 'rack'
G
bundle "install", :artifice => "endpoint_marshal_fail"
ensure
home(".gemrc").rmtree
end
end
end
end