# encoding: UTF-8
require 'pathname'

describe WinRM::FS::FileManager, integration: true do
  let(:dest_dir) { File.join(subject.temp_dir, "winrm_#{rand(2**16)}") }
  let(:temp_upload_dir) { '$env:TEMP/winrm-upload' }
  let(:this_dir) { File.expand_path(File.dirname(__FILE__)) }
  let(:this_file) { __FILE__ }
  let(:service) { winrm_connection }

  subject { WinRM::FS::FileManager.new(service) }

  before(:each) do
    expect(subject.delete(dest_dir)).to be true
    expect(subject.delete(temp_upload_dir)).to be true
  end

  context 'exists?' do
    it 'should exist' do
      expect(subject.exists?('c:/windows')).to be true
      expect(subject.exists?('c:/foobar')).to be false
    end
  end

  context 'create and delete dir' do
    it 'should create the directory recursively' do
      subdir = File.join(dest_dir, 'subdir1', 'subdir2')
      expect(subject.create_dir(subdir)).to be true
      expect(subject.exists?(subdir)).to be true
      expect(subject.create_dir(subdir)).to be true
      expect(subject.delete(subdir)).to be true
      expect(subject.exists?(subdir)).to be false
    end
  end

  context 'temp_dir' do
    it 'should return the remote users temp dir' do
      expect(subject.temp_dir).to match(%r{C:/Users/\w+/AppData/Local/Temp})
    end
  end

  context 'upload file' do
    let(:dest_file) { File.join(dest_dir, File.basename(this_file)) }

    before(:each) do
      expect(subject.delete(dest_dir)).to be true
    end

    it 'should upload the specified file' do
      subject.upload(this_file, dest_file)
      expect(subject).to have_created(dest_file).with_content(this_file)
    end

    it 'should upload to root of the c: drive' do
      subject.upload(this_file, 'c:/winrmtest.rb')
      expect(subject).to have_created('c:/winrmtest.rb').with_content(this_file)
      subject.delete('c:/winrmtest.rb')
    end

    it 'should upload using relative file path' do
      subject.upload('./spec/file_manager_spec.rb', dest_file)
      expect(subject).to have_created(dest_file).with_content(this_file)
    end

    it 'should upload to the specified directory' do
      subject.upload(this_file, dest_dir)
      expect(subject).to have_created(dest_file).with_content(this_file)
    end

    it 'should upload to the specified directory with env var' do
      subject.upload(this_file, '$env:Temp')
      expected_dest_file = File.join(subject.temp_dir, File.basename(this_file))
      expect(subject).to have_created(expected_dest_file).with_content(this_file)
    end

    it 'should upload to Program Files sub dir' do
      subject.upload(this_file, '$env:ProgramFiles/foo')
      expect(subject).to have_created('c:/Program Files/foo/file_manager_spec.rb') \
        .with_content(this_file)
    end

    it 'should upload to the specified nested directory' do
      dest_sub_dir = File.join(dest_dir, 'subdir')
      dest_sub_dir_file = File.join(dest_sub_dir, File.basename(this_file))
      subject.upload(this_file, dest_sub_dir)
      expect(subject).to have_created(dest_sub_dir_file).with_content(this_file)
    end

    it 'yields progress data' do
      total = subject.upload(this_file, dest_file) do \
        |bytes_copied, total_bytes, local_path, remote_path|
        expect(total_bytes).to be > 0
        expect(bytes_copied).to eq(total_bytes)
        expect(local_path).to eq(this_file)
        expect(remote_path).to eq(dest_file)
      end
      expect(total).to be > 0
    end

    it 'should not upload when content matches' do
      subject.upload(this_file, dest_dir)
      bytes_uploaded = subject.upload(this_file, dest_dir)
      expect(bytes_uploaded).to eq 0
    end

    it 'should upload when content differs' do
      matchers_file = File.join(this_dir, 'matchers.rb')
      subject.upload(matchers_file, dest_file)
      bytes_uploaded = subject.upload(this_file, dest_file)
      expect(bytes_uploaded).to be > 0
    end

    it 'raises WinRMUploadError when a bad source path is specified' do
      expect { subject.upload('c:/some/non-existant/path/foo', dest_file) }.to raise_error
    end
  end

  context 'upload empty file' do
    let(:empty_src_file) { Tempfile.new('empty').path }
    let(:dest_file) { File.join(dest_dir, 'emptyfile.txt') }

    it 'creates a new empty file' do
      expect(subject.upload(empty_src_file, dest_file)).to be 0
      expect(subject).to have_created(dest_file).with_content('')
    end

    it 'overwrites an existing file' do
      expect(subject.upload(this_file, dest_file)).to be > 0
      expect(subject.upload(empty_src_file, dest_file)).to be 0
      expect(subject).to have_created(dest_file).with_content('')
    end
  end

  context 'upload directory' do
    let(:root_dir) { File.expand_path('../', File.dirname(__FILE__)) }
    let(:winrm_fs_dir) { File.join(root_dir, 'lib/winrm-fs') }
    let(:core_dir) { File.join(root_dir, 'lib/winrm-fs/core') }

    it 'copies the entire directory recursively' do
      bytes_uploaded = subject.upload(winrm_fs_dir, dest_dir)
      expect(bytes_uploaded).to be > 0

      Dir.glob(winrm_fs_dir + '/**/*.rb').each do |host_file|
        host_file_rel = Pathname.new(host_file).relative_path_from(Pathname.new(winrm_fs_dir)).to_s
        remote_file = File.join(dest_dir, host_file_rel)
        expect(subject).to have_created(remote_file).with_content(host_file)
      end
    end

    it 'does not copy the directory when content is the same' do
      subject.upload(winrm_fs_dir, dest_dir)
      bytes_uploaded = subject.upload(winrm_fs_dir, dest_dir)
      expect(bytes_uploaded).to eq 0
    end

    it 'copies the directory when content differs' do
      subject.upload(winrm_fs_dir, dest_dir)
      bytes_uploaded = subject.upload(core_dir, dest_dir)
      expect(bytes_uploaded).to be > 0
    end
  end
end
