2018-11-02 19:07:56 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require "bundler"
|
|
|
|
require "bundler/friendly_errors"
|
|
|
|
require "cgi"
|
|
|
|
|
|
|
|
RSpec.describe Bundler, "friendly errors" do
|
|
|
|
context "with invalid YAML in .gemrc" do
|
2020-05-08 01:19:04 -04:00
|
|
|
before do
|
|
|
|
File.open(home(".gemrc"), "w") do |f|
|
|
|
|
f.write "invalid: yaml: hah"
|
2020-04-29 07:17:43 -04:00
|
|
|
end
|
2018-11-02 19:07:56 -04:00
|
|
|
end
|
|
|
|
|
2020-05-08 01:19:04 -04:00
|
|
|
after do
|
|
|
|
FileUtils.rm(home(".gemrc"))
|
|
|
|
end
|
2020-04-29 07:17:43 -04:00
|
|
|
|
2020-05-08 01:19:04 -04:00
|
|
|
it "reports a relevant friendly error message" do
|
|
|
|
gemfile <<-G
|
|
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
|
|
gem "rack"
|
|
|
|
G
|
2020-04-29 07:17:43 -04:00
|
|
|
|
2020-05-08 01:19:04 -04:00
|
|
|
bundle :install, :env => { "DEBUG" => "true" }
|
2020-04-29 07:17:43 -04:00
|
|
|
|
2020-05-08 01:19:04 -04:00
|
|
|
expect(err).to include("Failed to load #{home(".gemrc")}")
|
2018-11-02 19:07:56 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "calls log_error in case of exception" do
|
|
|
|
exception = Exception.new
|
|
|
|
expect(Bundler::FriendlyErrors).to receive(:exit_status).with(exception).and_return(1)
|
|
|
|
expect do
|
|
|
|
Bundler.with_friendly_errors do
|
|
|
|
raise exception
|
|
|
|
end
|
|
|
|
end.to raise_error(SystemExit)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "calls exit_status on exception" do
|
|
|
|
exception = Exception.new
|
|
|
|
expect(Bundler::FriendlyErrors).to receive(:log_error).with(exception)
|
|
|
|
expect do
|
|
|
|
Bundler.with_friendly_errors do
|
|
|
|
raise exception
|
|
|
|
end
|
|
|
|
end.to raise_error(SystemExit)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#log_error" do
|
|
|
|
shared_examples "Bundler.ui receive error" do |error, message|
|
|
|
|
it "" do
|
|
|
|
expect(Bundler.ui).to receive(:error).with(message || error.message)
|
|
|
|
Bundler::FriendlyErrors.log_error(error)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples "Bundler.ui receive trace" do |error|
|
|
|
|
it "" do
|
|
|
|
expect(Bundler.ui).to receive(:trace).with(error)
|
|
|
|
Bundler::FriendlyErrors.log_error(error)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "YamlSyntaxError" do
|
|
|
|
it_behaves_like "Bundler.ui receive error", Bundler::YamlSyntaxError.new(StandardError.new, "sample_message")
|
|
|
|
|
|
|
|
it "Bundler.ui receive trace" do
|
|
|
|
std_error = StandardError.new
|
|
|
|
exception = Bundler::YamlSyntaxError.new(std_error, "sample_message")
|
|
|
|
expect(Bundler.ui).to receive(:trace).with(std_error)
|
|
|
|
Bundler::FriendlyErrors.log_error(exception)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "Dsl::DSLError, GemspecError" do
|
|
|
|
it_behaves_like "Bundler.ui receive error", Bundler::Dsl::DSLError.new("description", "dsl_path", "backtrace")
|
|
|
|
it_behaves_like "Bundler.ui receive error", Bundler::GemspecError.new
|
|
|
|
end
|
|
|
|
|
|
|
|
context "GemRequireError" do
|
|
|
|
let(:orig_error) { StandardError.new }
|
|
|
|
let(:error) { Bundler::GemRequireError.new(orig_error, "sample_message") }
|
|
|
|
|
|
|
|
before do
|
|
|
|
allow(orig_error).to receive(:backtrace).and_return([])
|
|
|
|
end
|
|
|
|
|
|
|
|
it "Bundler.ui receive error" do
|
|
|
|
expect(Bundler.ui).to receive(:error).with(error.message)
|
|
|
|
Bundler::FriendlyErrors.log_error(error)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "writes to Bundler.ui.trace" do
|
2019-11-11 03:57:45 -05:00
|
|
|
expect(Bundler.ui).to receive(:trace).with(orig_error)
|
2018-11-02 19:07:56 -04:00
|
|
|
Bundler::FriendlyErrors.log_error(error)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "BundlerError" do
|
|
|
|
it "Bundler.ui receive error" do
|
|
|
|
error = Bundler::BundlerError.new
|
|
|
|
expect(Bundler.ui).to receive(:error).with(error.message, :wrap => true)
|
|
|
|
Bundler::FriendlyErrors.log_error(error)
|
|
|
|
end
|
|
|
|
it_behaves_like "Bundler.ui receive trace", Bundler::BundlerError.new
|
|
|
|
end
|
|
|
|
|
|
|
|
context "Thor::Error" do
|
|
|
|
it_behaves_like "Bundler.ui receive error", Bundler::Thor::Error.new
|
|
|
|
end
|
|
|
|
|
|
|
|
context "LoadError" do
|
|
|
|
let(:error) { LoadError.new("cannot load such file -- openssl") }
|
|
|
|
|
2020-05-08 01:19:04 -04:00
|
|
|
before do
|
|
|
|
allow(error).to receive(:backtrace).and_return(["backtrace"])
|
2018-11-02 19:07:56 -04:00
|
|
|
end
|
|
|
|
|
2020-05-08 01:19:04 -04:00
|
|
|
it "Bundler.ui receive error" do
|
|
|
|
expect(Bundler.ui).to receive(:error).with("\nCould not load OpenSSL. LoadError: cannot load such file -- openssl\nbacktrace")
|
2018-11-02 19:07:56 -04:00
|
|
|
Bundler::FriendlyErrors.log_error(error)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "Interrupt" do
|
|
|
|
it "Bundler.ui receive error" do
|
|
|
|
expect(Bundler.ui).to receive(:error).with("\nQuitting...")
|
|
|
|
Bundler::FriendlyErrors.log_error(Interrupt.new)
|
|
|
|
end
|
|
|
|
it_behaves_like "Bundler.ui receive trace", Interrupt.new
|
|
|
|
end
|
|
|
|
|
|
|
|
context "Gem::InvalidSpecificationException" do
|
|
|
|
it "Bundler.ui receive error" do
|
|
|
|
error = Gem::InvalidSpecificationException.new
|
|
|
|
expect(Bundler.ui).to receive(:error).with(error.message, :wrap => true)
|
|
|
|
Bundler::FriendlyErrors.log_error(error)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "SystemExit" do
|
|
|
|
# Does nothing
|
|
|
|
end
|
|
|
|
|
|
|
|
context "Java::JavaLang::OutOfMemoryError" do
|
|
|
|
module Java
|
|
|
|
module JavaLang
|
|
|
|
class OutOfMemoryError < StandardError; end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "Bundler.ui receive error" do
|
|
|
|
error = Java::JavaLang::OutOfMemoryError.new
|
|
|
|
expect(Bundler.ui).to receive(:error).with(/JVM has run out of memory/)
|
|
|
|
Bundler::FriendlyErrors.log_error(error)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "unexpected error" do
|
|
|
|
it "calls request_issue_report_for with error" do
|
|
|
|
error = StandardError.new
|
|
|
|
expect(Bundler::FriendlyErrors).to receive(:request_issue_report_for).with(error)
|
|
|
|
Bundler::FriendlyErrors.log_error(error)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#exit_status" do
|
|
|
|
it "calls status_code for BundlerError" do
|
|
|
|
error = Bundler::BundlerError.new
|
|
|
|
expect(error).to receive(:status_code).and_return("sample_status_code")
|
|
|
|
expect(Bundler::FriendlyErrors.exit_status(error)).to eq("sample_status_code")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns 15 for Thor::Error" do
|
|
|
|
error = Bundler::Thor::Error.new
|
|
|
|
expect(Bundler::FriendlyErrors.exit_status(error)).to eq(15)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "calls status for SystemExit" do
|
|
|
|
error = SystemExit.new
|
|
|
|
expect(error).to receive(:status).and_return("sample_status")
|
|
|
|
expect(Bundler::FriendlyErrors.exit_status(error)).to eq("sample_status")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns 1 in other cases" do
|
|
|
|
error = StandardError.new
|
|
|
|
expect(Bundler::FriendlyErrors.exit_status(error)).to eq(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#request_issue_report_for" do
|
|
|
|
it "calls relevant methods for Bundler.ui" do
|
2020-10-15 00:20:25 -04:00
|
|
|
expect(Bundler.ui).not_to receive(:info)
|
|
|
|
expect(Bundler.ui).to receive(:error).exactly(3).times
|
|
|
|
expect(Bundler.ui).not_to receive(:warn)
|
2018-11-02 19:07:56 -04:00
|
|
|
Bundler::FriendlyErrors.request_issue_report_for(StandardError.new)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "includes error class, message and backlog" do
|
|
|
|
error = StandardError.new
|
|
|
|
allow(Bundler::FriendlyErrors).to receive(:issues_url).and_return("")
|
|
|
|
|
|
|
|
expect(error).to receive(:class).at_least(:once)
|
|
|
|
expect(error).to receive(:message).at_least(:once)
|
|
|
|
expect(error).to receive(:backtrace).at_least(:once)
|
|
|
|
Bundler::FriendlyErrors.request_issue_report_for(error)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#issues_url" do
|
|
|
|
it "generates a search URL for the exception message" do
|
|
|
|
exception = Exception.new("Exception message")
|
|
|
|
|
2020-05-15 08:31:12 -04:00
|
|
|
expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/rubygems/rubygems/search?q=Exception+message&type=Issues")
|
2018-11-02 19:07:56 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "generates a search URL for only the first line of a multi-line exception message" do
|
|
|
|
exception = Exception.new(<<END)
|
|
|
|
First line of the exception message
|
|
|
|
Second line of the exception message
|
|
|
|
END
|
|
|
|
|
2020-05-15 08:31:12 -04:00
|
|
|
expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/rubygems/rubygems/search?q=First+line+of+the+exception+message&type=Issues")
|
2018-11-02 19:07:56 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "generates the url without colons" do
|
|
|
|
exception = Exception.new(<<END)
|
|
|
|
Exception ::: with ::: colons :::
|
|
|
|
END
|
|
|
|
issues_url = Bundler::FriendlyErrors.issues_url(exception)
|
|
|
|
expect(issues_url).not_to include("%3A")
|
2020-05-15 08:31:12 -04:00
|
|
|
expect(issues_url).to eq("https://github.com/rubygems/rubygems/search?q=#{CGI.escape("Exception with colons ")}&type=Issues")
|
2018-11-02 19:07:56 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "removes information after - for Errono::EACCES" do
|
|
|
|
exception = Exception.new(<<END)
|
|
|
|
Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/foo/bar/
|
|
|
|
END
|
|
|
|
allow(exception).to receive(:is_a?).with(Errno).and_return(true)
|
|
|
|
issues_url = Bundler::FriendlyErrors.issues_url(exception)
|
|
|
|
expect(issues_url).not_to include("/Users/foo/bar")
|
2020-05-15 08:31:12 -04:00
|
|
|
expect(issues_url).to eq("https://github.com/rubygems/rubygems/search?q=#{CGI.escape("Errno EACCES Permission denied @ dir_s_mkdir ")}&type=Issues")
|
2018-11-02 19:07:56 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|