require 'spec_helper'

class ClassMixedWithDSLInstallUtils
  include Beaker::DSL::Outcomes
  include BeakerPuppet::Install::Puppet5

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

describe ClassMixedWithDSLInstallUtils do
  let(:hosts)   { make_hosts( { :pe_ver => '3.0',
                                      :platform => 'linux',
                                      :roles => [ 'agent' ],
                                      :type  => 'foss' }, 4 ) }

  before :each do
    allow( subject ).to receive( :hosts ) { hosts }
  end


  describe '#fetch_build_details' do
    let( :platform_data ) { @platform_data || '' }
    let( :deets ) { { :platform_data => platform_data } }

    it 'sets & returns the relative folder' do
      sha_yaml_folder = '/jams/of/the/rain'
      sha_yaml_url    = "#{sha_yaml_folder}/puns.tgz"
      expect( subject ).to receive( :fetch_http_file  )
      expect( YAML    ).to receive( :load_file        ) { deets }

      url, _ = subject.fetch_build_details( sha_yaml_url )
      expect( url ).to be === sha_yaml_folder
    end

    it 'fetches & returns the build details' do
      @platform_data = 'test of the man in the can carol?'
      expect( subject ).to receive( :fetch_http_file  )
      expect( YAML    ).to receive( :load_file        ) { deets }

      _, hash = subject.fetch_build_details( 'sha_yaml_url' )
      expect( hash ).to be === @platform_data
    end

    it 'stores the file in a good location' do
      allow( YAML ).to receive( :load_file ) { deets }

      correct_location = '/my/fake/test/dir'
      expect( Dir ).to receive( :mktmpdir ) { correct_location }
      expect( subject ).to receive( :fetch_http_file ).with(
        anything, anything, correct_location
      )
      subject.fetch_build_details( 'sha_yaml_url' )
    end

  end

  describe '#host_urls' do

    let( :artifact_path       ) { @artifact_path      || ''             }
    let( :repo_config         ) { @repo_config        || nil            }
    let( :packaging_platform  ) { @packaging_platform || 'el-7-x86_64'  }
    let( :host                ) {
      host = hosts[0]
      allow( host ).to receive( :[] ).with( :packaging_platform ) {
        packaging_platform
      }
      host
    }
    let( :build_details   ) {
      details = {
        packaging_platform => {
          :artifact     => artifact_path,
          :repo_config  => repo_config
        }
      }
      details
    }

    it 'fails if there\'s no artifact value for the given platform' do
      allow( artifact_path ).to receive( :nil? ) { true }
      expect {
        subject.host_urls( host, build_details, '' )
      }.to raise_error(
        Beaker::DSL::Outcomes::FailTest,
        /^no artifact.*path found$/
      )
    end

    it 'fails if the artifact_url doesn\'t exist' do
      allow( subject ).to receive( :link_exists? ) { false }
      expect {
        subject.host_urls( host, build_details, '' )
      }.to raise_error(
        Beaker::DSL::Outcomes::FailTest,
        /^artifact url.*incorrectly$/
      )
    end

    it 'fails if the host doesn\'t have a packaging_platform' do
      allow( packaging_platform ).to receive( :nil? ) { true }
      allow( host ).to receive( :[] ).with( :platform ) { 'fake-platform' }
      expect {
        subject.host_urls( host, build_details, '' )
      }.to raise_error(
        Beaker::DSL::Outcomes::FailTest,
        /packaging_platform not provided for host/
      )
    end

    it 'returns a join of the base_url & the platform-specific artifact path' do
      base_url = 'base_url/base_url'
      @artifact_path = 'pants.install.pkg'

      allow( subject ).to receive( :link_exists? ) { true }
      artifact_url, _ = subject.host_urls( host, build_details, base_url )
      expect( artifact_url ).to be === "#{base_url}/#{@artifact_path}"
    end

    it 'returns a join of the base_url & the platform-specific artifact path' do
      base_url = 'base_url/base_url'
      @repo_config = 'pants.install.list'

      allow( subject ).to receive( :link_exists? ) { true }
      _, repoconfig_url = subject.host_urls( host, build_details, base_url )
      expect( repoconfig_url ).to be === "#{base_url}/#{@repo_config}"
    end

    it 'returns nil for the repoconfig_url if one isn\'t provided by the build_details' do
      allow( subject ).to receive( :link_exists? ) { true }
      _, repoconfig_url = subject.host_urls( host, build_details, '' )
      expect( repoconfig_url ).to be_nil
    end

  end

  describe '#install_artifact_on' do

    let( :artifact_url ) { 'url://in/the/jungle/lies/the/prize.pnc' }
    let( :platform ) { @platform || 'linux' }
    let( :mock_platform ) {
      mock_platform = Object.new
      allow( mock_platform ).to receive( :to_array ) { [platform, '', '', ''] }
      mock_platform
    }
    let( :host ) {
      host = hosts[0]
      allow( host ).to receive( :[] ).with( :platform ) { mock_platform }
      host
    }

    it 'calls host.install_package in the common case' do
      expect( subject ).to receive( :fetch_http_file ).never
      expect( subject ).to receive( :on ).never
      expect( host ).to receive( :install_local_package ).never
      expect( host ).to receive( :install_package ).once

      subject.install_artifact_on( host, artifact_url, 'project_name' )
    end

    context 'local install cases' do

      def run_shared_test_steps
        expect( host ).to receive( :install_local_package ).once
        expect( host ).to receive( :install_package ).never
        subject.install_artifact_on( host, artifact_url, 'project_name' )
      end

      it 'SOLARIS: fetches the file & runs local install' do
        @platform = 'solaris'

        expect( subject ).to receive( :fetch_http_file ).once
        expect( subject ).to receive( :scp_to ).once
        run_shared_test_steps()
      end

      it 'OSX: curls the file & runs local install' do
        @platform = 'osx'

        expect( subject ).to receive( :on ).with( host, /^curl.*#{artifact_url}$/ )
        run_shared_test_steps()
      end

    end

  end

  describe '#install_repo_configs_on' do
    let( :host ) { hosts[0] }

    it 'passes the parameters through to #install_repo_configs_from_url' do
      repoconfig_url = 'string/test/repo_config/stuff.stuff'
      expect( subject ).to receive( :install_repo_configs_from_url ).with(
        host,
        repoconfig_url
      )
      expect( subject.logger ).to receive( :warn ).never
      subject.install_repo_configs_on( host, repoconfig_url )
    end

    it 'returns without calling #install_repo_configs_from_url if repoconfig_url is nil' do
      expect( subject ).to receive( :install_repo_configs_from_url ).never
      expect( subject.logger ).to receive( :warn ).with(
        /^No repo_config.*Skipping repo_config install$/
      )
      subject.install_repo_configs_on( host, nil )
    end
  end

  describe '#install_from_build_data_url' do

    before :each do
      allow( subject ).to receive( :link_exists? ) { true }
    end

    it 'only calls #fetch_build_details once' do
      allow( subject ).to receive( :host_urls )
      allow( subject ).to receive( :install_artifact_on )
      allow( subject ).to receive( :configure_type_defaults_on )

      expect( subject ).to receive( :fetch_build_details ).once
      subject.install_from_build_data_url( 'project_name', 'project_sha' )
    end

    it 'calls #configure_type_defaults_on all hosts' do
      allow( subject ).to receive( :fetch_build_details )
      allow( subject ).to receive( :host_urls )
      allow( subject ).to receive( :install_artifact_on )

      hosts.each do |host|
        expect( subject ).to receive( :configure_type_defaults_on ).with( host ).once
      end
      subject.install_from_build_data_url( 'project_name', 'project_sha' )
    end

    it 'calls #configure_type_defaults_on one host if set' do
      allow( subject ).to receive( :fetch_build_details )
      allow( subject ).to receive( :host_urls )
      allow( subject ).to receive( :install_artifact_on )

      expect( subject ).to receive( :configure_type_defaults_on ).with( hosts[0] ).once
      subject.install_from_build_data_url( 'project_name', 'project_sha', hosts[0] )
    end

    it 'calls #configure_type_defaults_on custom array of hosts if set' do
      allow( subject ).to receive( :fetch_build_details )
      allow( subject ).to receive( :host_urls )
      allow( subject ).to receive( :install_artifact_on )

      custom_host_list = hosts.sample(1 + rand(hosts.count))

      custom_host_list.each do |host|
        expect( subject ).to receive( :configure_type_defaults_on ).with( host ).once
      end
      subject.install_from_build_data_url( 'project_name', 'project_sha', custom_host_list )
    end

    it 'passes the artifact_url from #hosts_artifact_url to #install_artifact_on' do
      allow( subject ).to receive( :fetch_build_details )
      allow( subject ).to receive( :configure_type_defaults_on )

      artifact_url = 'url://in/my/shoe/lies/the/trophy.jnk'
      allow( subject ).to receive( :host_urls ) { artifact_url }


      expect( subject ).to receive( :install_artifact_on ).with(
        anything, artifact_url, anything
      ).exactly( hosts.length ).times
      subject.install_from_build_data_url('project_name', 'project_sha' )
    end

    it 'fails properly if the given sha_yaml_url doesn\'t exist' do
      allow( subject ).to receive( :link_exists? ) { false }
      sha_yaml_url = 'pants/to/the/man/jeans.txt'

      expect {
        subject.install_from_build_data_url( 'project_name', sha_yaml_url )
      }.to raise_error(
        Beaker::DSL::Outcomes::FailTest,
        /#{sha_yaml_url}' does not exist/
      )
    end

    it 'runs host.install_package instead of #install_artifact_on if theres a repo_config' do
      repoconfig_url = 'pants/man/shoot/to/the/stars'
      project_name = 'fake_project_66'
      allow( subject ).to receive( :fetch_build_details )
      allow( subject ).to receive( :configure_type_defaults_on )
      allow( subject ).to receive( :host_urls ) { ['', repoconfig_url] }

      expect( subject ).to receive( :install_artifact_on ).never
      hosts.each do |host|
        expect( subject ).to receive( :install_repo_configs_on ).with(
          host,
          repoconfig_url
        )
        expect( host ).to receive( :install_package ).with( project_name )
      end
      subject.install_from_build_data_url( project_name, 'sha_yaml_url' )
    end
  end

end