spec/lib/asdf/tool_versions_spec.rb (213 lines of code) (raw):

# frozen_string_literal: true RSpec.describe Asdf::ToolVersions do let(:software_name) { 'golang' } let(:wanted_software_versions) { ['1.17.2', '1.21.9'] } let(:unnecessary_software_version) { '1.17.1' } let(:unnecessary_software_tool_version) { Asdf::ToolVersion.new(software_name, unnecessary_software_version) } let(:tmp_dir_we_pretend_exists) { '/tmp/dir/that/we/pretend/exists/asdf' } subject { described_class.new } describe '#default_tool_version_for' do context 'postgres' do it 'returns instance of Asdf::ToolVersion' do tool_version = subject.default_tool_version_for('postgres') expect(tool_version).to be_instance_of(Asdf::ToolVersion) expect(tool_version.version).to eq('16.8') end end end describe '#default_version_for' do context 'when a .tool-versions file is in the root directory' do it 'returns postgres version 16.8' do expect(subject.default_version_for('postgres')).to eq('16.8') end end context 'when a .tool-versions file is one level deep' do let(:first_level_file) { GDK.root.join('level1/.tool-versions') } before do allow(GDK.root).to receive(:glob).with('{.tool-versions,{*,*/*}/.tool-versions}').and_return([first_level_file]) allow(File).to receive(:readlines).with(first_level_file).and_return(['gitleaks 8.18.2']) end it 'retrieves the correct version' do expect(subject.default_version_for('gitleaks')).to eq('8.18.2') end end context 'when a .tool-versions file is more than one level deep' do let(:second_level_file) { GDK.root.join('level1/level2/.tool-versions') } before do allow(GDK.root).to receive(:glob).with('{.tool-versions,{*,*/*}/.tool-versions}').and_return([]) allow(File).to receive(:readlines).with(second_level_file).and_return(['nonexistent 1.2.3']) end it 'does not retrieve versions' do expect(subject.default_version_for('nonexistent')).to be_nil end end end describe '#unnecessary_software_to_uninstall?' do before do stub_asdf_data_installs_dir(tmp_dir_we_pretend_exists, exist: true) end context "when there isn't any software to uninstall" do it 'returns false' do stub_no_unnecessary_installed_software expect(subject.unnecessary_software_to_uninstall?).to be_falsey end end context 'when there is software to uninstall' do it 'returns true' do stub_some_unnecessary_installed_software expect(subject.unnecessary_software_to_uninstall?).to be_truthy end end end describe '#unnecessary_installed_versions_of_software' do before do stub_some_unnecessary_installed_software end it 'returns a Hash of software and versions that can be uninstalled' do expect(subject.unnecessary_installed_versions_of_software).to be_a(Hash) end it 'contains golang 1.17.1' do unnecessary_installed_versions_of_software = subject.unnecessary_installed_versions_of_software expect(unnecessary_installed_versions_of_software).to include(software_name => { unnecessary_software_version => unnecessary_software_tool_version }) end end describe '#uninstall_unnecessary_software!' do context 'when asdf installs directory does not exist (asdf not in use)' do it 'informs and returns true' do non_existent_asdf_dir = '/tmp/dir/that/doesnt/exist/asdf' stub_asdf_data_installs_dir(non_existent_asdf_dir, exist: false) expect(GDK::Output).to receive(:info).with("Skipping because '#{non_existent_asdf_dir}/installs' does not exist.") expect(subject.uninstall_unnecessary_software!).to be_truthy end end context 'when asdf installs directory does exist' do before do stub_asdf_data_installs_dir(tmp_dir_we_pretend_exists, exist: true) end context 'when asdf.opt_out is set to true' do it 'informs and returns true' do allow_any_instance_of(GDK::Config).to receive_message_chain('asdf.opt_out?').and_return(true) expect(GDK::Output).to receive(:info).with('Skipping because asdf.opt_out is set to true.') expect(subject.uninstall_unnecessary_software!).to be_truthy end end context 'when there is no software to uninstall' do it 'informs and returns true' do stub_no_unnecessary_installed_software expect(GDK::Output).to receive(:info).with('No unnecessary asdf software to uninstall.') expect(subject.uninstall_unnecessary_software!).to be_truthy end end context 'when there is software to uninstall' do before do stub_some_unnecessary_installed_software end context 'when prompted' do context 'and the user accepts' do context 'but an unhandled exception occurs' do it 'aborts with exception', :hide_output do stub_prompt('y') expect_warn_and_puts expect(unnecessary_software_tool_version).to receive(:uninstall!).and_raise(StandardError) expect { subject.uninstall_unnecessary_software! }.to raise_error(StandardError) end end context 'but the uninstall command returns a non-zero exit code' do it 'return false' do stub_prompt('y') expect_warn_and_puts expect(unnecessary_software_tool_version).to receive(:uninstall!).and_raise(Asdf::ToolVersion::UninstallFailedError) expect_uninstall_failure expect(subject.uninstall_unnecessary_software!).to be_falsey end end end context 'but the user does not accept' do it 'does not uninstall and returns true' do stub_prompt('n') expect_warn_and_puts expect(unnecessary_software_tool_version).not_to receive(:uninstall!) expect(subject.uninstall_unnecessary_software!).to be_truthy end end context 'when software succeeds in uninstalling' do context 'and the user accepts' do context 'by setting GDK_ASDF_UNINSTALL_UNNECESSARY_SOFTWARE_CONFIRM to true' do it 'uninstalls returns true' do stub_env('GDK_ASDF_UNINSTALL_UNNECESSARY_SOFTWARE_CONFIRM', 'true') expect_warn_and_puts expect(unnecessary_software_tool_version).to receive(:uninstall!).and_return(true) expect_uninstall_success expect(subject.uninstall_unnecessary_software!).to be_truthy end end context 'via a direct response' do it 'uninstalls returns true' do stub_prompt('y') expect_warn_and_puts expect(unnecessary_software_tool_version).to receive(:uninstall!).and_return(true) expect_uninstall_success expect(subject.uninstall_unnecessary_software!).to be_truthy end end end end end context 'when asked to not prompt' do context 'when software succeeds in uninstalling' do it 'returns true' do expect(GDK::Output).not_to receive(:warn).with('About to uninstall the following asdf software:') expect(GDK::Output).not_to receive(:puts).with("#{software_name} #{unnecessary_software_version}") expect(GDK::Output).not_to receive(:prompt).with('Are you sure? [y/N]') expect(unnecessary_software_tool_version).to receive(:uninstall!).and_return(true) expect_uninstall_success expect(subject.uninstall_unnecessary_software!(prompt: false)).to be_truthy end end end end end def expect_warn_and_puts expect(GDK::Output).to receive(:warn).with('About to uninstall the following asdf software:').ordered expect(GDK::Output).to receive(:puts).with(stderr: true).ordered expect(GDK::Output).to receive(:puts).with("#{software_name} #{unnecessary_software_version}").ordered expect(GDK::Output).to receive(:puts).with(stderr: true).ordered end def expect_uninstall_info expect(GDK::Output).to receive(:print).with("Uninstalling #{software_name} ").ordered expect(GDK::Output).to receive(:print).with(unnecessary_software_version).ordered end def expect_uninstall_success expect_uninstall_info expect(GDK::Output).to receive(:puts).with(" #{GDK::Output.icon(:success)}").ordered end def expect_uninstall_failure expect_uninstall_info expect(GDK::Output).to receive(:puts).with(" #{GDK::Output.icon(:error)}").ordered expect(GDK::Output).to receive(:puts).with(stderr: true).ordered expect(GDK::Output).to receive(:warn).with("Failed to uninstall the following:\n\n").ordered expect(GDK::Output).to receive(:puts).with("#{software_name} #{unnecessary_software_version}").ordered end end def stub_asdf_data_installs_dir(dir, exist:) stub_env('HOME', '/home/gdk') stub_env('ASDF_DATA_DIR', dir) asdf_data_installs_dir_double = instance_double(Pathname, exist?: exist, to_s: "#{dir}/installs") asdf_data_dir_double = instance_double(Pathname, join: asdf_data_installs_dir_double) allow(Pathname).to receive(:new).and_call_original allow(Pathname).to receive(:new).with(dir).and_return(asdf_data_dir_double) end def stub_no_unnecessary_installed_software stub_software(wanted_versions: wanted_software_versions, installed_versions: wanted_software_versions) end def stub_some_unnecessary_installed_software stub_software(wanted_versions: wanted_software_versions, installed_versions: [unnecessary_software_version]) end def stub_software(wanted_versions:, installed_versions:) allow(Asdf::ToolVersion).to receive(:new).and_call_original allow(subject).to receive(:raw_tool_versions_lines).and_return(wanted_versions.map { |version| "#{software_name} #{version}" }) allow(subject).to receive(:asdf_install_dirs_for).with(software_name).and_return(installed_versions.map { |version| Pathname.new("/tmp/.asdf_fake/installs/#{software_name}/#{version}") }) (wanted_versions + installed_versions).each do |version| allow(Asdf::ToolVersion).to receive(:new).with(software_name, version).and_return(Asdf::ToolVersion.new(software_name, version)) end end end