Back to Repositories

Validating Package Metadata and File Operations in FPM

This test suite validates the core functionality of the FPM::Package class, which handles package metadata and file operations in the FPM (Effing Package Management) tool. The tests ensure proper handling of package attributes, script templating, and file path management.

Test Coverage Overview

The test suite provides comprehensive coverage of package attribute management and file operations.

Key areas tested include:
  • Default values and mutators for package metadata (name, version, architecture, etc.)
  • File exclusion patterns and directory handling
  • Script templating functionality
  • Build and staging path management

Implementation Analysis

The tests utilize RSpec’s shared examples pattern to reduce code duplication and standardize testing of similar attributes.

Key implementation features:
  • Shared example groups for testing default values and mutators
  • Dynamic attribute testing using description-based method names
  • Environment-aware testing for maintainer and path configurations

Technical Details

Testing infrastructure includes:
  • RSpec as the testing framework
  • Temporary directory management for file operations
  • Environment variable handling for path testing
  • Custom matchers for file path validation
  • Before/after hooks for cleanup operations

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • DRY principle through shared example groups
  • Proper test isolation and cleanup
  • Comprehensive edge case coverage
  • Clear test organization by feature
  • Environment-independent testing

jordansissel/fpm

spec/fpm/package_spec.rb

            
require "spec_setup"
require "fpm" # local

describe FPM::Package do
  after do
    subject.cleanup
  end # after

  shared_examples_for :Default do |item, default|
    context "default value" do
      it "should be #{default}" do
        if default.nil?
          expect(subject.send(item)).to(be_nil)
        else
          expect(subject.send(item)).to(be == default)
        end
      end
    end
  end

  shared_examples_for :Mutator do |item|
    context "when set" do
      let(:value) { "whatever" }
      it "should return the set value" do
        expect(subject.send("#{item}=", value)).to(be == value)
      end

      context "the getter" do
        before do
          subject.send("#{item}=", value)
        end
        it "returns the value set previously" do
          expect(subject.send(item)).to(be == value)
        end
      end
    end
  end

  describe "#name" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, nil
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#version" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, nil
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#architecture" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, "native"
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#attributes" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, { :workdir => ::Dir.tmpdir }
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#category" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, "default"
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#config_files" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, []
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#conflicts" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, []
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#dependencies" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, []
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#description" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, "no description given"
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#epoch" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, nil
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#iteration" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, nil
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#license" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, "unknown"
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#maintainer" do
    require "socket"
    default_maintainer = "<#{ENV["USER"]}@#{Socket.gethostname}>"
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, default_maintainer
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#provides" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, []
  end

  describe "#replaces" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, []
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#scripts" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, {}
  end

  describe "#url" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, nil
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#vendor" do
    it_behaves_like :Default, description.gsub(/^#/, "").to_sym, "none"
    it_behaves_like :Mutator, description.gsub(/^#/, "").to_sym
  end

  describe "#exclude (internal method)" do
    it "should obey attributes[:excludes]" do
      File.write(subject.staging_path("hello"), "hello")
      File.write(subject.staging_path("world"), "world")
      subject.attributes[:excludes] = ["*world*"]
      subject.instance_eval { exclude }
      insist { subject.files } == ["hello"]
    end

    it "should obey attributes[:excludes] for directories" do
      Dir.mkdir(subject.staging_path("example"))
      Dir.mkdir(subject.staging_path("example/foo"))
      File.write(subject.staging_path("example/foo/delete_me"), "Hello!")
      File.write(subject.staging_path("keeper"), "Hello!")
      subject.attributes[:excludes] = ["example"]
      subject.instance_eval { exclude }
      insist { subject.files } == ["keeper"]
    end

    it "should obey attributes[:excludes] for child directories" do
      Dir.mkdir(subject.staging_path("example"))
      Dir.mkdir(subject.staging_path("example/foo"))
      File.write(subject.staging_path("example/foo/delete_me"), "Hello!")
      File.write(subject.staging_path("keeper"), "Hello!")
      subject.attributes[:excludes] = ["example/foo"]
      subject.instance_eval { exclude }
      insist { subject.files.sort } == ["example", "keeper"]
    end
  end

  describe "#script (internal method)" do
    scripts = [:after_install, :before_install, :after_remove, :before_remove]
    before do
      scripts.each do |script|
        subject.scripts[script] = "<%= name %>"
      end
      subject.name = "Example"
    end

    context "when :template_scripts? is true" do
      before do
        subject.attributes[:template_scripts?] = true
      end

      scripts.each do |script|
        it "should evaluate #{script} as a template" do
          expect(subject.script(script)).to(be == subject.name)
        end
      end
    end

    context "when :template_scripts? is false" do
      before do
        subject.attributes[:template_scripts?] = false
      end

      scripts.each do |script|
        it "should not process #{script} as a template" do
          expect(subject.script(script)).to(be == subject.scripts[script])
        end
      end
    end

    it "should not template by default" do
      expect(subject.attributes[:template_scripts?]).to(be_falsey)
    end
  end

  describe "#staging_path and #staging_path" do
    before() do
      @oldtmp = ENV["TMP"]
      ENV["TMP"] = '/var/tmp'
    end

    after() do
      ENV["TMP"] = @oldtmp
    end

    it "should be a subdirectory of workdir (#1124)" do
      expect(subject.build_path("hello1")).to(start_with("/var/tmp"))
      expect(subject.build_path("hello1")).to(end_with("hello1"))
      expect(subject.build_path("hello1")).to(include("build"))
    end

    it "should be a subdirectory of workdir (#1124)" do
      expect(subject.staging_path("hello2")).to(start_with("/var/tmp"))
      expect(subject.staging_path("hello2")).to(end_with("hello2"))
      expect(subject.staging_path("hello2")).to(include("staging"))
    end
  end
end # describe FPM::Package