require "spec_helper"

module Omnibus
  describe Packager::Solaris do
    let(:project) do
      Project.new.tap do |project|
        project.name("project")
        project.homepage("https://example.com")
        project.install_dir("/opt/project")
        project.build_version("1.2.3")
        project.build_iteration("1")
        project.maintainer("Chef Software")
      end
    end

    subject { described_class.new(project) }

    let(:project_root) { File.join(tmp_path, "project/root") }
    let(:package_dir)  { File.join(tmp_path, "package/dir") }
    let(:staging_dir)  { File.join(tmp_path, "staging/dir") }
    let(:architecture) { "i86pc" }

    before do
      # This is here to allow this unit test to run on windows.
      allow(File).to receive(:expand_path).and_wrap_original do |m, *args|
        m.call(*args).sub(/^[A-Za-z]:/, "")
      end
      Config.project_root(project_root)
      Config.package_dir(package_dir)

      allow(subject).to receive(:staging_dir).and_return(staging_dir)
      create_directory(staging_dir)

      stub_ohai(platform: "solaris2", version: "5.11") do |data|
        data["kernel"]["machine"] = architecture
      end
    end

    describe "#id" do
      it "is :solaris" do
        expect(subject.id).to eq(:solaris)
      end
    end

    describe "#package_name" do
      it "includes the name, version, iteration and architecture" do
        expect(subject.package_name).to eq("project-1.2.3-1.i386.solaris")
      end
    end

    describe "#pkgmk_version" do
      it "includes the version and iteration" do
        expect(subject.pkgmk_version).to eq("1.2.3-1")
      end
    end

    describe "#install_dirname" do
      it "returns the parent directory" do
        expect(subject.install_dirname).to eq("/opt")
      end
    end

    describe "#install_basename" do
      it "name of the install directory" do
        expect(subject.install_basename).to eq("project")
      end
    end

    describe "#write_scripts" do
      context "when scripts are given" do
        let(:scripts) { %w{ postinstall postremove } }
        before do
          scripts.each do |script_name|
            create_file("#{project_root}/package-scripts/project/#{script_name}") do
              "Contents of #{script_name}"
            end
          end
        end

        it "writes the scripts into scripts staging dir" do
          subject.write_scripts

          scripts.each do |script_name|
            script_file = "#{staging_dir}/#{script_name}"
            contents = File.read(script_file)
            expect(contents).to include("Contents of #{script_name}")
          end
        end
      end

      context "when scripts with default omnibus naming are given" do
        let(:default_scripts) { %w{ postinst postrm } }
        before do
          default_scripts.each do |script_name|
            create_file("#{project_root}/package-scripts/project/#{script_name}") do
              "Contents of #{script_name}"
            end
          end
        end

        it "writes the scripts into scripts staging dir" do
          subject.write_scripts

          default_scripts.each do |script_name|
            mapped_name = Packager::Solaris::SCRIPT_MAP[script_name.to_sym]
            script_file = "#{staging_dir}/#{mapped_name}"
            contents = File.read(script_file)
            expect(contents).to include("Contents of #{script_name}")
          end
        end
      end
    end

    describe "#write_prototype_file" do
      let(:prototype_file) { File.join(staging_dir, "Prototype") }

      before do
        allow(subject).to receive(:shellout!)
        File.open("#{staging_dir}/files", "w+") do |f|
          f.write <<~EOF
            /foo/bar/baz
            /a file with spaces
          EOF
        end
      end

      it "creates the prototype file" do
        subject.write_prototype_file
        contents = File.read(prototype_file)

        expect(contents).to include(
          <<-EOH.gsub(/^ {12}/, "")
            i pkginfo
            i postinstall
            i postremove
          EOH
        )
      end

      it "uses the correct commands" do
        expect(subject).to receive(:shellout!)
          .with("cd /opt && find project -print > #{File.join(staging_dir, "files")}")
        expect(subject).to receive(:shellout!)
          .with("cd /opt && pkgproto < #{File.join(staging_dir, "files.clean")} > #{File.join(staging_dir, "Prototype.files")}")
        expect(subject).to receive(:shellout!)
          .with("awk '{ $5 = \"root\"; $6 = \"root\"; print }' < #{File.join(staging_dir, "Prototype.files")} >> #{File.join(staging_dir, "Prototype")}")
        subject.write_prototype_file
      end

      it "strips out the file with spaces from files.clean" do
        subject.write_prototype_file
        contents = File.read(File.join(staging_dir, "files.clean"))
        expect(contents).not_to include("a file with spaces")
        expect(contents).to include("/foo/bar/baz")
      end
    end

    describe "#create_solaris_file" do
      it "uses the correct commands" do
        expect(subject).to receive(:shellout!)
          .with("pkgmk -o -r /opt -d #{staging_dir} -f #{File.join(staging_dir, "Prototype")}")
        expect(subject).to receive(:shellout!)
          .with("pkgchk -vd #{staging_dir} project")
        expect(subject).to receive(:shellout!)
          .with("pkgtrans #{staging_dir} #{File.join(package_dir, "project-1.2.3-1.i386.solaris")} project")

        subject.create_solaris_file
      end
    end

    describe "#write_pkginfo_file" do
      let(:pkginfo_file) { File.join(staging_dir, "pkginfo") }
      let(:hostname) { Socket.gethostname }
      let(:now) { Time.now }

      it "generates the file" do
        subject.write_pkginfo_file
        expect(pkginfo_file).to be_a_file
      end

      it "has the correct content" do
        allow(Time).to receive(:now).and_return(now)
        subject.write_pkginfo_file
        contents = File.read(pkginfo_file)

        expect(contents).to include("CLASSES=none")
        expect(contents).to include("TZ=PST")
        expect(contents).to include("PATH=/sbin:/usr/sbin:/usr/bin:/usr/sadm/install/bin")
        expect(contents).to include("BASEDIR=/opt")
        expect(contents).to include("PKG=project")
        expect(contents).to include("NAME=project")
        expect(contents).to include("ARCH=i386")
        expect(contents).to include("VERSION=1.2.3-1")
        expect(contents).to include("CATEGORY=application")
        expect(contents).to include("DESC=")
        expect(contents).to include("VENDOR=Chef Software")
        expect(contents).to include("EMAIL=Chef Software")
        expect(contents).to include("PSTAMP=#{hostname}#{now.utc.iso8601}")
      end
    end

    describe "#create_solaris_file" do
      before do
        allow(subject).to receive(:shellout!)
        allow(Dir).to receive(:chdir) { |_, &b| b.call }
      end

      it "uses the correct commands" do
        expect(subject).to receive(:shellout!)
          .with("pkgmk -o -r /opt -d #{staging_dir} -f #{File.join(staging_dir, "Prototype")}")
        expect(subject).to receive(:shellout!)
          .with("pkgchk -vd #{staging_dir} project")
        expect(subject).to receive(:shellout!)
          .with("pkgtrans #{staging_dir} #{File.join(package_dir, "project-1.2.3-1.i386.solaris")} project")

        subject.create_solaris_file
      end
    end

    describe "#safe_architecture" do
      context "the architecture is Intel-based" do
        let(:architecture) { "i86pc" }

        it "returns `i386`" do
          expect(subject.safe_architecture).to eq("i386")
        end
      end

      context "the architecture is SPARC-based" do
        let(:architecture) { "sun4v" }

        it "returns `sparc`" do
          expect(subject.safe_architecture).to eq("sparc")
        end
      end

      context "anything else" do
        let(:architecture) { "FOO" }

        it "returns the value from Ohai" do
          expect(subject.safe_architecture).to eq("FOO")
        end
      end
    end
  end
end
