ruby/spec/bundler/commands/check_spec.rb
David Rodríguez e67f595e8f [rubygems/rubygems] Build bundler gem just once for specs
When we need to reset system gems during specs, there's no need to
rebuild bundler, we can copy over the original gem home.

7b4f80747b
2025-07-25 11:10:37 +09:00

654 lines
16 KiB
Ruby

# frozen_string_literal: true
RSpec.describe "bundle check" do
it "returns success when the Gemfile is satisfied" do
install_gemfile <<-G
source "https://gem.repo1"
gem "rails"
G
bundle :check
expect(out).to include("The Gemfile's dependencies are satisfied")
end
it "works with the --gemfile flag when not in the directory" do
install_gemfile <<-G
source "https://gem.repo1"
gem "rails"
G
bundle "check --gemfile bundled_app/Gemfile", dir: tmp
expect(out).to include("The Gemfile's dependencies are satisfied")
end
it "creates a Gemfile.lock by default if one does not exist" do
install_gemfile <<-G
source "https://gem.repo1"
gem "rails"
G
FileUtils.rm(bundled_app_lock)
bundle "check"
expect(bundled_app_lock).to exist
end
it "does not create a Gemfile.lock if --dry-run was passed" do
install_gemfile <<-G
source "https://gem.repo1"
gem "rails"
G
FileUtils.rm(bundled_app_lock)
bundle "check --dry-run"
expect(bundled_app_lock).not_to exist
end
it "prints an error that shows missing gems" do
system_gems ["rails-2.3.2"], path: default_bundle_path
gemfile <<-G
source "https://gem.repo1"
gem "rails"
G
bundle :check, raise_on_error: false
expect(err).to include("The following gems are missing")
expect(err).to include(" * rake (#{rake_version})")
expect(err).to include(" * actionpack (2.3.2)")
expect(err).to include(" * activerecord (2.3.2)")
expect(err).to include(" * actionmailer (2.3.2)")
expect(err).to include(" * activeresource (2.3.2)")
expect(err).to include(" * activesupport (2.3.2)")
expect(err).to include("Install missing gems with `bundle install`")
end
it "prints an error that shows missing gems if a Gemfile.lock does not exist and a toplevel dependency is missing" do
gemfile <<-G
source "https://gem.repo1"
gem "rails"
G
bundle :check, raise_on_error: false
expect(exitstatus).to be > 0
expect(err).to include("The following gems are missing")
expect(err).to include(" * rails (2.3.2)")
expect(err).to include(" * rake (#{rake_version})")
expect(err).to include(" * actionpack (2.3.2)")
expect(err).to include(" * activerecord (2.3.2)")
expect(err).to include(" * actionmailer (2.3.2)")
expect(err).to include(" * activeresource (2.3.2)")
expect(err).to include(" * activesupport (2.3.2)")
expect(err).to include("Install missing gems with `bundle install`")
end
it "prints a generic error if gem git source is not checked out" do
build_git "foo", path: lib_path("foo")
bundle "config path vendor/bundle"
install_gemfile <<-G
source "https://gem.repo1"
gem "foo", git: "#{lib_path("foo")}"
G
FileUtils.rm_r bundled_app("vendor/bundle")
bundle :check, raise_on_error: false
expect(exitstatus).to eq 1
expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.")
end
it "prints a generic message if you changed your lockfile" do
build_repo2 do
build_gem "rails_pinned_to_old_activesupport" do |s|
s.add_dependency "activesupport", "= 1.2.3"
end
end
install_gemfile <<-G
source "https://gem.repo2"
gem 'rails'
G
gemfile <<-G
source "https://gem.repo2"
gem "rails"
gem "rails_pinned_to_old_activesupport"
G
bundle :check, raise_on_error: false
expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.")
end
it "remembers --without option from install" do
gemfile <<-G
source "https://gem.repo1"
group :foo do
gem "myrack"
end
G
bundle "install --without foo"
bundle "check"
expect(out).to include("The Gemfile's dependencies are satisfied")
end
it "uses the without setting" do
bundle "config set without foo"
install_gemfile <<-G
source "https://gem.repo1"
group :foo do
gem "myrack"
end
G
bundle "check"
expect(out).to include("The Gemfile's dependencies are satisfied")
end
it "ensures that gems are actually installed and not just cached" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack", :group => :foo
G
bundle "config set --local without foo"
bundle :install
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
G
bundle "check", raise_on_error: false
expect(err).to include("* myrack (1.0.0)")
expect(exitstatus).to eq(1)
end
it "ensures that gems are actually installed and not just cached in applications' cache" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
G
bundle "config set --local path vendor/bundle"
bundle :cache
gem_command "uninstall myrack", env: { "GEM_HOME" => vendored_gems.to_s }
bundle "check", raise_on_error: false
expect(err).to include("* myrack (1.0.0)")
expect(exitstatus).to eq(1)
end
it "ignores missing gems restricted to other platforms" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
platforms :#{not_local_tag} do
gem "activesupport"
end
G
system_gems "myrack-1.0.0", path: default_bundle_path
lockfile <<-G
GEM
remote: https://gem.repo1/
specs:
activesupport (2.3.5)
myrack (1.0.0)
PLATFORMS
#{generic_local_platform}
#{not_local}
DEPENDENCIES
myrack
activesupport
G
bundle :check
expect(out).to include("The Gemfile's dependencies are satisfied")
end
it "works with env conditionals" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
env :NOT_GOING_TO_BE_SET do
gem "activesupport"
end
G
system_gems "myrack-1.0.0", path: default_bundle_path
lockfile <<-G
GEM
remote: https://gem.repo1/
specs:
activesupport (2.3.5)
myrack (1.0.0)
PLATFORMS
#{generic_local_platform}
#{not_local}
DEPENDENCIES
myrack
activesupport
G
bundle :check
expect(out).to include("The Gemfile's dependencies are satisfied")
end
it "outputs an error when the default Gemfile is not found" do
bundle :check, raise_on_error: false
expect(exitstatus).to eq(10)
expect(err).to include("Could not locate Gemfile")
end
it "does not output fatal error message" do
bundle :check, raise_on_error: false
expect(exitstatus).to eq(10)
expect(err).not_to include("Unfortunately, a fatal error has occurred. ")
end
it "fails when there's no lockfile and frozen is set" do
install_gemfile <<-G
source "https://gem.repo1"
gem "foo"
G
bundle "config set --local deployment true"
bundle "install"
FileUtils.rm(bundled_app_lock)
bundle :check, raise_on_error: false
expect(last_command).to be_failure
end
context "--path" do
context "after installing gems in the proper directory" do
before do
gemfile <<-G
source "https://gem.repo1"
gem "rails"
G
bundle "install --path vendor/bundle"
FileUtils.rm_r(bundled_app(".bundle"))
end
it "returns success" do
bundle "check --path vendor/bundle"
expect(out).to include("The Gemfile's dependencies are satisfied")
end
it "should write to .bundle/config" do
bundle "check --path vendor/bundle"
bundle "check"
end
end
context "after installing gems on a different directory" do
before do
install_gemfile <<-G
source "https://gem.repo1"
gem "rails"
G
bundle "check --path vendor/bundle", raise_on_error: false
end
it "returns false" do
expect(exitstatus).to eq(1)
expect(err).to match(/The following gems are missing/)
end
end
end
describe "when locked" do
before :each do
system_gems "myrack-1.0.0"
install_gemfile <<-G
source "https://gem.repo1"
gem "myrack", "1.0"
G
end
it "returns success when the Gemfile is satisfied" do
bundle :install
bundle :check
expect(out).to include("The Gemfile's dependencies are satisfied")
end
it "shows what is missing with the current Gemfile if it is not satisfied" do
FileUtils.rm_r default_bundle_path
default_system_gems
bundle :check, raise_on_error: false
expect(err).to match(/The following gems are missing/)
expect(err).to include("* myrack (1.0")
end
end
describe "when locked with multiple dependents with different requirements" do
before :each do
build_repo4 do
build_gem "depends_on_myrack" do |s|
s.add_dependency "myrack", ">= 1.0"
end
build_gem "also_depends_on_myrack" do |s|
s.add_dependency "myrack", "~> 1.0"
end
build_gem "myrack"
end
gemfile <<-G
source "https://gem.repo4"
gem "depends_on_myrack"
gem "also_depends_on_myrack"
G
bundle "lock"
end
it "shows what is missing with the current Gemfile without duplications" do
bundle :check, raise_on_error: false
expect(err).to match(/The following gems are missing/)
expect(err).to include("* myrack (1.0").once
end
end
describe "when locked under multiple platforms" do
before :each do
build_repo4 do
build_gem "myrack"
end
gemfile <<-G
source "https://gem.repo4"
gem "myrack"
G
lockfile <<-L
GEM
remote: https://gem.repo4/
specs:
myrack (1.0)
PLATFORMS
ruby
#{local_platform}
DEPENDENCIES
myrack
BUNDLED WITH
#{Bundler::VERSION}
L
end
it "shows what is missing with the current Gemfile without duplications" do
bundle :check, raise_on_error: false
expect(err).to match(/The following gems are missing/)
expect(err).to include("* myrack (1.0").once
end
end
describe "when using only scoped rubygems sources" do
before do
gemfile <<~G
source "https://gem.repo2"
source "https://gem.repo1" do
gem "myrack"
end
G
end
it "returns success when the Gemfile is satisfied" do
system_gems "myrack-1.0.0", path: default_bundle_path
bundle :check, artifice: "compact_index"
expect(out).to include("The Gemfile's dependencies are satisfied")
end
end
describe "when using only scoped rubygems sources with indirect dependencies" do
before do
build_repo4 do
build_gem "depends_on_myrack" do |s|
s.add_dependency "myrack"
end
build_gem "myrack"
end
gemfile <<~G
source "https://gem.repo1"
source "https://gem.repo4" do
gem "depends_on_myrack"
end
G
end
it "returns success when the Gemfile is satisfied and generates a correct lockfile" do
system_gems "depends_on_myrack-1.0", "myrack-1.0", gem_repo: gem_repo4, path: default_bundle_path
bundle :check, artifice: "compact_index"
checksums = checksums_section_when_enabled do |c|
c.checksum gem_repo4, "depends_on_myrack", "1.0"
c.checksum gem_repo4, "myrack", "1.0"
end
expect(out).to include("The Gemfile's dependencies are satisfied")
expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo1/
specs:
GEM
remote: https://gem.repo4/
specs:
depends_on_myrack (1.0)
myrack
myrack (1.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
depends_on_myrack!
#{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
end
context "with gemspec directive and scoped sources" do
before do
build_repo4 do
build_gem "awesome_print"
end
build_repo2 do
build_gem "dex-dispatch-engine"
end
build_lib("bundle-check-issue", path: tmp("bundle-check-issue")) do |s|
s.write "Gemfile", <<-G
source "https://localgemserver.test"
gemspec
source "https://localgemserver.test/extra" do
gem "dex-dispatch-engine"
end
G
s.add_dependency "awesome_print"
end
bundle "install", artifice: "compact_index_extra", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, dir: tmp("bundle-check-issue")
end
it "does not corrupt lockfile when changing version" do
version_file = tmp("bundle-check-issue/bundle-check-issue.gemspec")
File.write(version_file, File.read(version_file).gsub(/s\.version = .+/, "s.version = '9999'"))
bundle "check --verbose", dir: tmp("bundle-check-issue")
lockfile = File.read(tmp("bundle-check-issue/Gemfile.lock"))
checksums = checksums_section_when_enabled(lockfile) do |c|
c.checksum gem_repo4, "awesome_print", "1.0"
c.no_checksum "bundle-check-issue", "9999"
c.checksum gem_repo2, "dex-dispatch-engine", "1.0"
end
expect(lockfile).to eq <<~L
PATH
remote: .
specs:
bundle-check-issue (9999)
awesome_print
GEM
remote: https://localgemserver.test/
specs:
awesome_print (1.0)
GEM
remote: https://localgemserver.test/extra/
specs:
dex-dispatch-engine (1.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
bundle-check-issue!
dex-dispatch-engine!
#{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
end
context "with scoped and unscoped sources" do
it "does not corrupt lockfile" do
build_repo2 do
build_gem "foo"
build_gem "wadus"
build_gem("baz") {|s| s.add_dependency "wadus" }
end
build_repo4 do
build_gem "bar"
end
bundle "config set path.system true"
# Add all gems to ensure all gems are installed so that a bundle check
# would be successful
install_gemfile(<<-G, artifice: "compact_index_extra")
source "https://gem.repo2"
source "https://gem.repo4" do
gem "bar"
end
gem "foo"
gem "baz"
G
original_lockfile = lockfile
# Remove "baz" gem from the Gemfile, and bundle install again to generate
# a functional lockfile with no "baz" dependency or "wadus" transitive
# dependency
install_gemfile(<<-G, artifice: "compact_index_extra")
source "https://gem.repo2"
source "https://gem.repo4" do
gem "bar"
end
gem "foo"
G
# Add back "baz" gem back to the Gemfile, but _crucially_ we do not perform a
# bundle install
gemfile(gemfile + 'gem "baz"')
bundle :check, verbose: true
# Bundle check should succeed and restore the lockfile to its original
# state
expect(lockfile).to eq(original_lockfile)
end
end
describe "BUNDLED WITH" do
def lock_with(bundler_version = nil)
lock = <<~L
GEM
remote: https://gem.repo1/
specs:
myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
myrack
L
if bundler_version
lock += "\nBUNDLED WITH\n #{bundler_version}\n"
end
lock
end
before do
bundle "config set --local path vendor/bundle"
install_gemfile <<-G
source "https://gem.repo1"
gem "myrack"
G
end
context "is not present" do
it "does not change the lock" do
lockfile lock_with(nil)
bundle :check
expect(lockfile).to eq lock_with(nil)
end
end
context "is newer" do
it "does not change the lock and does not warn" do
lockfile lock_with(Bundler::VERSION.succ)
bundle :check
expect(err).to be_empty
expect(lockfile).to eq lock_with(Bundler::VERSION.succ)
end
end
context "is older" do
it "does not change the lock" do
system_gems "bundler-1.18.0"
lockfile lock_with("1.18.0")
bundle :check
expect(lockfile).to eq lock_with("1.18.0")
end
end
end
end