require 'spec_helper'

describe Chef::Sugar::Shell do
  describe '#which' do
    it 'returns the first executable matching the command' do
      allow(File).to receive(:executable?).and_return(false)
      allow(File).to receive(:executable?).with('/usr/bin/mongo').and_return(true)
      expect(described_class.which('mongo')).to eq('/usr/bin/mongo')
    end

    it 'returns nil when no command is found' do
      allow(File).to receive(:executable?).and_return(false)
      expect(described_class.which('node')).to be_nil
    end

    context 'with an absolute path' do
      it 'returns the executable if it exists' do
        allow(File).to receive(:executable?).with('/usr/local/bin/bash').and_return(true)
        expect(described_class.which('/usr/local/bin/bash')).to eq('/usr/local/bin/bash')
      end

      it 'returns nil when the file is not executable' do
        allow(File).to receive(:executable?).with('/usr/local/bin/bash').and_return(false)
        expect(described_class.which('/usr/local/bin/bash')).to be_nil
      end
    end
  end

  describe '#dev_null' do
    it 'returns NUL on Windows' do
      node = { 'platform_family' => 'windows' }
      expect(described_class.dev_null(node)).to eq('NUL')
    end

    it 'returns /dev/null on Linux' do
      node = { 'platform_family' => 'debian' }
      expect(described_class.dev_null(node)).to eq('/dev/null')
    end
  end

  describe '#installed?' do
    it 'returns true if the given binary exists' do
      allow(described_class).to receive(:which).and_return(nil)
      allow(described_class).to receive(:which).with('mongo').and_return(true)
      expect(described_class.installed?('mongo')).to be true
    end

    it 'returns false if the given binary does not exist' do
      allow(File).to receive(:executable?).and_return(false)
      expect(described_class.installed?('node')).to be false
    end
  end

  describe '#installed_at_version?' do
    it 'returns true if the command is installed at the correct version' do
      allow(described_class).to receive(:which).and_return(true)
      allow(described_class).to receive(:version_for).and_return('1.2.3')
      expect(described_class.installed_at_version?('mongo', '1.2.3')).to be true
    end

    it 'returns true if the command is installed at the correct version and has additional output' do
      allow(described_class).to receive(:which).and_return(true)
      allow(described_class).to receive(:version_for).and_return('Mongo DB version 1.2.3. Some other text.')
      expect(described_class.installed_at_version?('mongo', '1.2.3')).to be true
    end

    it 'returns true if the command is installed at the correct version with a regex' do
      allow(described_class).to receive(:which).and_return(true)
      allow(described_class).to receive(:version_for).and_return('1.2.3')
      expect(described_class.installed_at_version?('mongo', /1\.2/)).to be true
    end

    it 'returns false if the command is installed at the wrong version' do
      allow(described_class).to receive(:which).and_return(true)
      allow(described_class).to receive(:version_for).and_return('1.2.3')
      expect(described_class.installed_at_version?('mongo', '4.5.6')).to be false
    end

    it 'returns false if #version_for is nil' do
      allow(described_class).to receive(:which).and_return(true)
      allow(described_class).to receive(:version_for).and_return(nil)
      expect(described_class.installed_at_version?('mongo', '4.5.6')).to be false
    end

    it 'returns false if the command is not installed' do
      allow(described_class).to receive(:which).and_return(nil)
      expect(described_class.installed_at_version?('mongo', '1.0.0')).to be false
    end
  end

  describe '#version_for' do
    let(:shell_out) { double('shell_out', run_command: nil, error!: nil, stdout: '1.2.3', stderr: 'Oh no!') }
    before { allow(Mixlib::ShellOut).to receive(:new).and_return(shell_out) }

    it 'runs the thing in shellout' do
      expect(Mixlib::ShellOut).to receive(:new).with('mongo --version')
      described_class.version_for('mongo')
    end

    it 'returns the combined stdout and stderr' do
      expect(described_class.version_for('mongo')).to eq("1.2.3\nOh no!")
    end
  end
end