require 'spec_helper'

describe PuppetForge::Tar::Mini do
  let(:entry_class) do
    Class.new do
      attr_accessor :typeflag, :name, :full_name
      def initialize(name, full_name, typeflag)
        @name = name
        @full_name = full_name
        @typeflag = typeflag
      end
    end
  end
  let(:sourcefile) { '/the/module.tar.gz' }
  let(:destdir)    { File.expand_path '/the/dest/dir' }
  let(:sourcedir)  { '/the/src/dir' }
  let(:destfile)   { '/the/dest/file.tar.gz' }
  let(:minitar)    { described_class.new }
  let(:tarfile_contents) { [entry_class.new('file', 'full_file', '0'), \
                            entry_class.new('symlink', 'full_symlink', '2'), \
                            entry_class.new('invalid', 'full_invalid', 'F')] }

  it "unpacks a tar file" do
    unpacks_the_entry(:file_start, 'thefile')

    minitar.unpack(sourcefile, destdir)
  end

  it "does not allow an absolute path" do
    unpacks_the_entry(:file_start, '/thefile')

    expect {
      minitar.unpack(sourcefile, destdir)
    }.to raise_error(PuppetForge::InvalidPathInPackageError,
                     "Attempt to install file into \"/thefile\" under \"#{destdir}\"")
  end

  it "does not allow a file to be written outside the destination directory" do
    unpacks_the_entry(:file_start, '../../thefile')

    expect {
      minitar.unpack(sourcefile, destdir)
    }.to raise_error(PuppetForge::InvalidPathInPackageError,
                     "Attempt to install file into \"#{File.expand_path('/the/thefile')}\" under \"#{destdir}\"")
  end

  it "does not allow a directory to be written outside the destination directory" do
    unpacks_the_entry(:dir, '../../thedir')

    expect {
      minitar.unpack(sourcefile, destdir)
    }.to raise_error(PuppetForge::InvalidPathInPackageError,
                     "Attempt to install file into \"#{File.expand_path('/the/thedir')}\" under \"#{destdir}\"")
  end

  it "packs a tar file" do
    writer = double('GzipWriter')

    expect(Zlib::GzipWriter).to receive(:open).with(destfile).and_yield(writer)
    expect(Archive::Tar::Minitar).to receive(:pack).with(sourcedir, writer)

    minitar.pack(sourcedir, destfile)
  end

  it "returns filenames in a tar separated into correct categories" do
    reader = double('GzipReader')

    expect(Zlib::GzipReader).to receive(:open).with(sourcefile).and_yield(reader)
    expect(Archive::Tar::Minitar).to receive(:open).with(reader).and_return(tarfile_contents)
    expect(Archive::Tar::Minitar).to receive(:unpack).with(reader, destdir, ['full_file']).and_yield(:file_start, 'thefile', nil)

    file_lists = minitar.unpack(sourcefile, destdir)

    expect(file_lists[:valid]).to eq(['full_file'])
    expect(file_lists[:invalid]).to eq(['full_invalid'])
    expect(file_lists[:symlinks]).to eq(['full_symlink'])
  end

  def unpacks_the_entry(type, name)
    reader = double('GzipReader')

    expect(Zlib::GzipReader).to receive(:open).with(sourcefile).and_yield(reader)
    expect(minitar).to receive(:validate_files).with(reader).and_return({:valid => [name]})
    expect(Archive::Tar::Minitar).to receive(:unpack).with(reader, destdir, [name]).and_yield(type, name, nil)
  end
end
