require 'spec_helper'

class ClassMixedWithDSLInstallUtils
  include Beaker::DSL::Helpers
  include Beaker::DSL::Patterns
  include Beaker::DSL::InstallUtils

  def logger
    @logger ||= RSpec::Mocks::Double.new('logger').as_null_object
  end
end

describe ClassMixedWithDSLInstallUtils do
  let(:windows_temp)        { 'C:\\Windows\\Temp' }
  let(:msi_path)            { 'c:\\foo\\puppet.msi' }
  let(:winhost)             { make_host( 'winhost',
                              { :platform => 'windows',
                                :pe_ver => '3.0',
                                :working_dir => '/tmp',
                                :is_cygwin => true} ) }
  let(:winhost_non_cygwin)  { make_host( 'winhost_non_cygwin',
                              { :platform => 'windows',
                                :pe_ver => '3.0',
                                :working_dir => '/tmp',
                                :is_cygwin => 'false' } ) }
  let(:hosts)              { [ winhost, winhost_non_cygwin ] }

  def expect_install_called(times = hosts.length)
    result = expect( Beaker::Command ).to receive( :new )
      .with( /^"#{Regexp.quote(windows_temp)}\\install-puppet-msi.*\.bat"$/, [], {:cmdexe => true})
      .exactly( times ).times

    yield result if block_given?
  end

  def expect_status_called(times = hosts.length)
    expect( Beaker::Command ).to receive( :new )
      .with( "sc query puppet || sc query pe-puppet", [], {:cmdexe => true} )
      .exactly( times ).times
  end

  def expect_script_matches(hosts, contents)
    hosts.each do |host|
      expect( host )
        .to receive( :do_scp_to ) do |local_path, remote_path|
          expect(File.read(local_path)).to match(contents)
        end
        .and_return( true )
    end
  end

  describe "#install_msi_on" do
    before :each do
      FakeFS::FileSystem.add(File.expand_path '/tmp')

      allow( subject ).to receive( :on ).and_return( true )
      allow( subject ).to receive( :get_temp_path ).and_return( windows_temp )
    end

    it "will specify a PUPPET_AGENT_STARTUP_MODE of Manual (disabling the service) by default" do
      expect_install_called
      expect_status_called
      expected_cmd = /^start \/w msiexec\.exe \/i "c:\\foo\\puppet.msi" \/qn \/L\*V .*\.log PUPPET_AGENT_STARTUP_MODE=Manual$/
      expect_script_matches(hosts, expected_cmd)
      subject.install_msi_on(hosts, msi_path, {})
    end

    it "allows configuration of PUPPET_AGENT_STARTUP_MODE" do
      expect_install_called
      expect_status_called
      expected_cmd = /^start \/w msiexec\.exe \/i "c:\\foo\\puppet.msi" \/qn \/L\*V .*\.log PUPPET_AGENT_STARTUP_MODE=Automatic$/
      expect_script_matches(hosts, expected_cmd)
      subject.install_msi_on(hosts, msi_path, {'PUPPET_AGENT_STARTUP_MODE' => 'Automatic'})
    end

    it "will generate an appropriate command with a MSI file path using non-Windows slashes" do
      expect_install_called
      expect_status_called
      msi_path = 'c:/foo/puppet.msi'
      expected_cmd = /^start \/w msiexec\.exe \/i "c:\\foo\\puppet.msi" \/qn \/L\*V .*\.log PUPPET_AGENT_STARTUP_MODE=Manual$/
      expect_script_matches(hosts, expected_cmd)
      subject.install_msi_on(hosts, msi_path)
    end

    it "will generate an appropriate command with a MSI http(s) url" do
      expect_install_called
      expect_status_called
      msi_url = "https://downloads.puppetlabs.com/puppet.msi"
      expected_cmd = /^start \/w msiexec\.exe \/i "https\:\/\/downloads\.puppetlabs\.com\/puppet\.msi" \/qn \/L\*V .*\.log PUPPET_AGENT_STARTUP_MODE=Manual$/
      expect_script_matches(hosts, expected_cmd)
      subject.install_msi_on(hosts, msi_url)
    end

    it "will generate an appropriate command with a MSI file url" do
      expect_install_called
      expect_status_called
      msi_url = "file://c:\\foo\\puppet.msi"
      expected_cmd = /^start \/w msiexec\.exe \/i "file\:\/\/c:\\foo\\puppet\.msi" \/qn \/L\*V .*\.log PUPPET_AGENT_STARTUP_MODE=Manual$/
      expect_script_matches(hosts, expected_cmd)
      subject.install_msi_on(hosts, msi_url)
    end

    it "will not generate a command to emit a log file without the :debug option set" do
      expect_install_called
      expect_status_called
      hosts.each { |h| allow( h ).to receive( :do_scp_to ).and_return( true ) }
      expect( Beaker::Command ).not_to receive( :new ).with( /^type .*\.log$/, [], {:cmdexe => true} )
      subject.install_msi_on(hosts, msi_path)
    end

    it "will generate a command to emit a log file when the install script fails" do
      # note a single failure aborts executing against remaining hosts
      hosts_affected = 1

      expect_install_called(hosts_affected) { |e| e.and_raise }
      expect_status_called(0)
      hosts.each { |h| allow( h ).to receive( :do_scp_to ).and_return( true ) }

      expect( Beaker::Command ).to receive( :new ).with( /^type \".*\.log\"$/, [], {:cmdexe => true} ).exactly( hosts_affected ).times
      expect { subject.install_msi_on(hosts, msi_path) }.to raise_error(RuntimeError)
    end

    it "will generate a command to emit a log file with the :debug option set" do
      expect_install_called
      expect_status_called
      hosts.each { |h| allow( h ).to receive( :do_scp_to ).and_return( true ) }

      expect( Beaker::Command ).to receive( :new ).with( /^type \".*\.log\"$/, [], {:cmdexe => true} ).exactly( hosts.length ).times
      subject.install_msi_on(hosts, msi_path, {}, { :debug => true })
    end
  end
end