require 'spec_helper'

module Omnibus
  describe GitCache do
    let(:project) do
      Project.new('/path/to/demo.rb').evaluate do
        name 'demo'
        install_dir '/opt/demo'

        build_version '1.0.0'

        maintainer 'Chef Software, Inc'
        homepage 'http://getchef.com'

        dependency 'preparation'
        dependency 'snoopy'
        dependency 'zlib'
      end
    end

    let(:install_dir) { project.install_dir }

    let(:zlib) do
      Software.new(project, 'zlib.rb').evaluate do
        name 'zlib'
        default_version '1.7.2'
      end
    end

    let(:snoopy) do
      Software.new(project, 'snoopy.rb').evaluate do
        name 'snoopy'
        default_version '1.0.0'
      end
    end

    let(:preparation) do
      Software.new(project, 'preparation.rb').evaluate do
        name 'preparation'
        default_version '1.0.0'
      end
    end

    let(:cache_path) { File.join('/var/cache/omnibus/cache/git_cache', install_dir) }

    let(:ipc) do
      project.library.component_added(preparation)
      project.library.component_added(snoopy)
      project.library.component_added(zlib)
      described_class.new(zlib)
    end

    describe '#cache_path' do
      it 'returns the install path appended to the install_cache path' do
        expect(ipc.cache_path).to eq(cache_path)
      end
    end

    describe '#tag' do
      it 'returns the correct tag' do
        expect(ipc.tag).to eql('zlib-24a8ec71da04059dcf7ed3c6e8e0fd9d155476abe4b5156d1f13c42e85478c2b')
      end

      describe 'with no deps' do
        let(:ipc) do
          described_class.new(zlib)
        end

        it 'returns the correct tag' do
          expect(ipc.tag).to eql('zlib-ee71fc1a512f03b9dd46c1fd9b5ab71fcc51b638857bf328496a31abb2654c2b')
        end
      end
    end

    describe '#create_cache_path' do
      it 'runs git init if the cache path does not exist' do
        allow(File).to receive(:directory?)
          .with(ipc.cache_path)
          .and_return(false)
        allow(File).to receive(:directory?)
          .with(File.dirname(ipc.cache_path))
          .and_return(false)
        expect(FileUtils).to receive(:mkdir_p)
          .with(File.dirname(ipc.cache_path))
        expect(ipc).to receive(:shellout!)
          .with("git -c core.autocrlf=false --git-dir=#{cache_path} --work-tree=#{install_dir} init -q")
        ipc.create_cache_path
      end

      it 'does not run git init if the cache path exists' do
        allow(File).to receive(:directory?)
          .with(ipc.cache_path)
          .and_return(true)
        allow(File).to receive(:directory?)
          .with(File.dirname(ipc.cache_path))
          .and_return(true)
        expect(ipc).to_not receive(:shellout!)
        ipc.create_cache_path
      end
    end

    describe '#incremental' do
      before(:each) do
        allow(ipc).to receive(:shellout!)
        allow(ipc).to receive(:create_cache_path)
      end

      it 'creates the cache path' do
        expect(ipc).to receive(:create_cache_path)
        ipc.incremental
      end

      it 'adds all the changes to git removing git directories' do
        expect(ipc).to receive(:remove_git_dirs)
        expect(ipc).to receive(:shellout!)
          .with("git -c core.autocrlf=false --git-dir=#{cache_path} --work-tree=#{install_dir} add -A -f")
        ipc.incremental
      end

      it 'commits the backup for the software' do
        expect(ipc).to receive(:shellout!)
          .with(%Q(git -c core.autocrlf=false --git-dir=#{cache_path} --work-tree=#{install_dir} commit -q -m "Backup of #{ipc.tag}"))
        ipc.incremental
      end

      it 'tags the software backup' do
        expect(ipc).to receive(:shellout!)
          .with(%Q(git -c core.autocrlf=false --git-dir=#{cache_path} --work-tree=#{install_dir} tag -f "#{ipc.tag}"))
        ipc.incremental
      end
    end

    describe '#remove_git_dirs' do
      let(:git_files) { ['git/HEAD', 'git/description', 'git/hooks', 'git/info', 'git/objects', 'git/refs' ] }
      it 'removes bare git directories' do
        allow(Dir).to receive(:glob).and_return(['git/config'])
        git_files.each do |git_file|
          expect(File).to receive(:exist?).with(git_file).and_return(true)
        end
        allow(File).to receive(:dirname).and_return('git')
        expect(FileUtils).to receive(:rm_rf).with('git')

        ipc.remove_git_dirs
      end

      it 'does ignores non git directories' do
        allow(Dir).to receive(:glob).and_return(['not_git/config'])
        expect(File).to receive(:exist?).with('not_git/HEAD').and_return(false)
        allow(File).to receive(:dirname).and_return('not_git')
        expect(FileUtils).not_to receive(:rm_rf).with('not_git')

        ipc.remove_git_dirs
      end
    end

    describe '#restore' do
      let(:git_tag_output) { "#{ipc.tag}\n" }

      let(:tag_cmd) do
        cmd_double = double(Mixlib::ShellOut)
        allow(cmd_double).to receive(:stdout).and_return(git_tag_output)
        allow(cmd_double).to receive(:error!).and_return(cmd_double)
        cmd_double
      end

      before(:each) do
        allow(ipc).to receive(:shellout!)
          .with(%Q(git -c core.autocrlf=false --git-dir=#{cache_path} --work-tree=#{install_dir} tag -l "#{ipc.tag}"))
          .and_return(tag_cmd)
        allow(ipc).to receive(:shellout!)
          .with(%Q(git -c core.autocrlf=false --git-dir=#{cache_path} --work-tree=#{install_dir} checkout -f "#{ipc.tag}"))
        allow(ipc).to receive(:create_cache_path)
      end

      it 'creates the cache path' do
        expect(ipc).to receive(:create_cache_path)
        ipc.restore
      end

      it 'checks for a tag with the software and version, and if it finds it, checks it out' do
        expect(ipc).to receive(:shellout!)
          .with(%Q(git -c core.autocrlf=false --git-dir=#{cache_path} --work-tree=#{install_dir} tag -l "#{ipc.tag}"))
          .and_return(tag_cmd)
        expect(ipc).to receive(:shellout!)
          .with(%Q(git -c core.autocrlf=false --git-dir=#{cache_path} --work-tree=#{install_dir} checkout -f "#{ipc.tag}"))
        ipc.restore
      end

      describe 'if the tag does not exist' do
        let(:git_tag_output) { "\n" }

        it 'does nothing' do
          expect(ipc).to receive(:shellout!)
            .with(%Q(git -c core.autocrlf=false --git-dir=#{cache_path} --work-tree=#{install_dir} tag -l "#{ipc.tag}"))
            .and_return(tag_cmd)
          expect(ipc).to_not receive(:shellout!)
            .with(%Q(git -c core.autocrlf=false --git-dir=#{cache_path} --work-tree=#{install_dir} checkout -f "#{ipc.tag}"))
          ipc.restore
        end
      end
    end
  end
end
