require "spec_helper" module Omnibus describe Packager::BFF 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("2") 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") } 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) create_directory(subject.scripts_staging_dir) end describe "#id" do it "is :bff" do expect(subject.id).to eq(:bff) end end describe "#package_name" do before do allow(subject).to receive(:safe_architecture).and_return("x86_64") end it "includes the name and version" do expect(subject.package_name).to eq("project-1.2.3-2.x86_64.bff") end end describe "#scripts_install_dir" do it "is nested inside the project install_dir" do expect(subject.scripts_install_dir).to start_with(project.install_dir) end end describe "#scripts_staging_dir" do it "is nested inside the staging_dir" do expect(subject.scripts_staging_dir).to start_with(staging_dir) end end describe "#write_scripts" do context "when scripts are given" do let(:scripts) { %w{ preinst postinst prerm postrm } } 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 = "#{subject.scripts_staging_dir}/#{script_name}" contents = File.read(script_file) expect(contents).to include("Contents of #{script_name}") end end end end describe "#write_gen_template" do before do allow(subject).to receive(:safe_architecture).and_return("x86_64") end let(:gen_file) { "#{staging_dir}/gen.template" } it "generates the file" do subject.write_gen_template expect(gen_file).to be_a_file end it "has the correct content" do subject.write_gen_template contents = File.read(gen_file) expect(contents).to include("Package Name: project") expect(contents).to include("Package VRMF: 1.2.3.2") expect(contents).to include("Update: N") expect(contents).to include("Fileset") expect(contents).to include(" Fileset Name: project") expect(contents).to include(" Fileset VRMF: 1.2.3.2") expect(contents).to include(" Fileset Description: The full stack of project") expect(contents).to include(" USRLIBLPPFiles") expect(contents).to include(" EOUSRLIBLPPFiles") expect(contents).to include(" Bosboot required: N") expect(contents).to include(" License agreement acceptance required: N") expect(contents).to include(" Include license files in this package: N") expect(contents).to include(" Requisites:") expect(contents).to include(" ROOT Part: Y") expect(contents).to include(" USRFiles") expect(contents).to include(" EOUSRFiles") expect(contents).to include("EOFileset") end context "when files and directories are present" do before do create_file("#{staging_dir}/.file1") create_file("#{staging_dir}/file2") create_directory("#{staging_dir}/.dir1") create_directory("#{staging_dir}/dir2") end it "writes them into the template" do subject.write_gen_template contents = File.read(gen_file) expect(contents).to include("/.dir1") expect(contents).to include("/.file1") expect(contents).to include("/dir2") expect(contents).to include("/file2") end end context "when paths with colons/commas are present", if: !windows? do let(:contents) do subject.write_gen_template File.read(gen_file) end before do create_file("#{staging_dir}/man3/App::Cpan.3") create_file("#{staging_dir}/comma,file") create_directory("#{staging_dir}/colon::dir/file") create_directory("#{staging_dir}/comma,dir/file") end it "renames colon filenames in the template" do expect(contents).to include("/man3/App____Cpan.3") end it "renames colon directory names in the template" do expect(contents).to include("/colon____dir/file") end it "renames comma filenames in the template" do expect(contents).to include("/comma__file") end it "renames comma directory names in the template" do expect(contents).to include("/comma__dir/file") end context "creates a config script" do it "when there wasn't one provided" do FileUtils.rm_f("#{subject.scripts_staging_dir}/config") subject.write_gen_template expect(File).to exist("#{subject.scripts_staging_dir}/config") end it "when one is provided in the project's def" do create_file("#{project_root}/package-scripts/project/config") subject.write_gen_template contents = File.read("#{subject.scripts_staging_dir}/config") expect(contents).to include("mv '/man3/App____Cpan.3' '/man3/App::Cpan.3'") end it "with mv commands for all the renamed files" do subject.write_gen_template contents = File.read("#{subject.scripts_staging_dir}/config") expect(contents).to include("mv '/man3/App____Cpan.3' '/man3/App::Cpan.3'") expect(contents).to include("mv '/comma__file' '/comma,file'") expect(contents).to include("mv '/colon____dir/file' '/colon::dir/file'") expect(contents).to include("mv '/comma__dir/file' '/comma,dir/file'") end end end context "when paths with invalid characters are present", if: !windows? do let(:contents) do subject.write_gen_template File.read(gen_file) end before do create_file("#{staging_dir}/file with_a_space") create_file("#{staging_dir}/file_with_{left_brace") create_file("#{staging_dir}/file_with_}right_brace") create_file("#{staging_dir}/file_that_meets_expectations") end it "includes a file that does not include invalid characters" do expect(contents).to include("/file_that_meets_expectations") end it "does not include a file with spaces in the path" do expect(contents).to_not include("/file with_a_space") end it "does not include a file with left braces in the path" do expect(contents).to_not include("/file_with_{left_brace") end it "does not include a file with right braces in the path" do expect(contents).to_not include("/file_with_}right_brace") end end context "when script files are present" do before do create_file("#{subject.scripts_staging_dir}/preinst") create_file("#{subject.scripts_staging_dir}/postinst") create_file("#{subject.scripts_staging_dir}/prerm") create_file("#{subject.scripts_staging_dir}/postrm") create_file("#{subject.scripts_staging_dir}/config") end it "writes them into the template" do subject.write_gen_template contents = File.read(gen_file) expect(contents).to include(" Pre-installation Script: #{subject.scripts_staging_dir}/preinst") expect(contents).to include(" Post-installation Script: #{subject.scripts_staging_dir}/postinst") expect(contents).to include(" Configuration Script: #{subject.scripts_staging_dir}/config") expect(contents).to include(" Pre_rm Script: #{subject.scripts_staging_dir}/prerm") expect(contents).to include(" Unconfiguration Script: #{subject.scripts_staging_dir}/postrm") end end context "when the log_level is :debug, it" do before do Omnibus.logger.level = :debug end it "prints the rendered template" do output = capture_logging { subject.write_gen_template } expect(output).to include("Package Name: project") end end end describe "#create_bff_file" do # Need to mock out the id calls let(:id_shellout) do shellout_mock = double("shellout_mock") allow(shellout_mock).to receive(:stdout).and_return("300") shellout_mock end before do allow(subject).to receive(:shellout!) allow(Dir).to receive(:chdir) { |_, &b| b.call } allow(subject).to receive(:shellout!) .with("id -u").and_return(id_shellout) allow(subject).to receive(:shellout!) .with("id -g").and_return(id_shellout) create_file(File.join(staging_dir, ".info", "#{project.name}.inventory")) do <<-INVENTORY.gsub(/^\s{12}/, "") /opt/project/version-manifest.txt: owner = root group = system mode = 644 type = FILE class = apply,inventory,angry-omnibus-toolchain size = 1906 checksum = "02776 2 " INVENTORY end create_file("#{staging_dir}/file") { "http://goo.gl/TbkO01" } end it "gets the build uid" do expect(subject).to receive(:shellout!) .with("id -u") subject.create_bff_file end it "gets the build gid" do expect(subject).to receive(:shellout!) .with("id -g") subject.create_bff_file end it "chowns the directory to root" do # A note - the /opt/ here is essentially project.install_dir one level up. # There is nothing magical about 'opt' as a directory. expect(subject).to receive(:shellout!) .with(%r{chown -Rh 0:0 #{staging_dir}/opt$}) subject.create_bff_file end it "logs a message" do output = capture_logging { subject.create_bff_file } expect(output).to include("Creating .bff file") end it "uses the correct command" do expect(subject).to receive(:shellout!) .with(%r{/usr/sbin/mkinstallp -d}) subject.create_bff_file end it "chowns the directory back to the build user" do # A note - the /opt/ here is essentially project.install_dir one level up. # There is nothing magical about 'opt' as a directory. # 300 is just what we set the mock for the build uid/gid to return. expect(subject).to receive(:shellout!) .with(/chown -Rh 300:300 #{staging_dir}/) subject.create_bff_file end context "when the log_level is :debug, it" do before do Omnibus.logger.level = :debug end it "prints the inventory file" do output = capture_logging { subject.create_bff_file } expect(output).to match(%r{^/opt/project}) end end end describe "#safe_base_package_name" do context 'when the project name is "safe"' do it "returns the value without logging a message" do expect(subject.safe_base_package_name).to eq("project") expect(subject).to_not receive(:log) end end context "when the project name has invalid characters" do before { project.name("Pro$ject123.for-realz_2") } it "returns the value while logging a message" do output = capture_logging do expect(subject.safe_base_package_name).to eq("pro-ject123.for-realz-2") end expect(output).to include("The `name' component of BFF package names can only include") end end end describe "#create_bff_file_name" do it "constructs the proper package name" do expect(subject.create_bff_file_name).to eq("project-1.2.3-2.x86_64.bff") end end describe "#bff_version" do it "returns the build version up with the build iteration" do expect(subject.bff_version).to eq("1.2.3.2") end end describe "#safe_architecture" do before do stub_ohai(platform: "ubuntu", version: "16.04") do |data| data["kernel"]["machine"] = "i386" end end it "returns the value from Ohai" do expect(subject.safe_architecture).to eq("i386") end end end end