Back to Repositories

Testing Upstart Configuration Export Implementation in ddollar/foreman

This test suite validates the Upstart export functionality in Foreman, focusing on process management and configuration file generation. It ensures proper handling of service configurations and environment variables across different scenarios.

Test Coverage Overview

The test suite provides comprehensive coverage of Upstart export functionality in Foreman:

  • File system exports and configuration generation
  • Cleanup of existing export directories
  • Handling of concurrent process formations
  • Environment variable escaping and quoting
  • Template customization and overrides

Implementation Analysis

The testing approach utilizes RSpec’s behavior-driven development framework with FakeFS for file system simulation. It implements context-specific testing through let blocks for dynamic test setup and employs before hooks for template loading.

Key patterns include:
  • Isolated file system testing using FakeFS
  • Extensive use of example files for verification
  • Mock expectations for cleanup operations

Technical Details

Testing infrastructure includes:

  • RSpec test framework
  • FakeFS for file system manipulation
  • FileUtils for file operations
  • Custom template loading mechanism
  • Environment variable handling utilities

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolation of file system operations
  • Comprehensive edge case handling
  • Clear test organization using contexts
  • Effective use of setup and teardown
  • Thorough validation of file content and structure

ddollar/foreman

spec/foreman/export/upstart_spec.rb

            
require "spec_helper"
require "foreman/engine"
require "foreman/export/upstart"
require "tmpdir"

describe Foreman::Export::Upstart, :fakefs do
  let(:procfile)  { write_procfile("/tmp/app/Procfile") }
  let(:formation) { nil }
  let(:engine)    { Foreman::Engine.new(:formation => formation).load_procfile(procfile) }
  let(:options)   { Hash.new }
  let(:upstart)   { Foreman::Export::Upstart.new("/tmp/init", engine, options) }

  before(:each) { load_export_templates_into_fakefs("upstart") }
  before(:each) { allow(upstart).to receive(:say) }

  it "exports to the filesystem" do
    upstart.export

    expect(File.read("/tmp/init/app.conf")).to         eq(example_export_file("upstart/app.conf"))
    expect(File.read("/tmp/init/app-alpha.conf")).to   eq(example_export_file("upstart/app-alpha.conf"))
    expect(File.read("/tmp/init/app-alpha-1.conf")).to eq(example_export_file("upstart/app-alpha-1.conf"))
    expect(File.read("/tmp/init/app-bravo.conf")).to   eq(example_export_file("upstart/app-bravo.conf"))
    expect(File.read("/tmp/init/app-bravo-1.conf")).to eq(example_export_file("upstart/app-bravo-1.conf"))
  end

  it "cleans up if exporting into an existing dir" do
    expect(FileUtils).to receive(:rm).with("/tmp/init/app.conf")
    expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha.conf")
    expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha-1.conf")
    expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo.conf")
    expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo-1.conf")
    expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar.conf")
    expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar-1.conf")
    expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar.conf")
    expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar-1.conf")

    upstart.export
    upstart.export
  end

  it "does not delete exported files for similarly named applications" do
    FileUtils.mkdir_p "/tmp/init"

    ["app2", "app2-alpha", "app2-alpha-1"].each do |name|
      path = "/tmp/init/#{name}.conf"
      FileUtils.touch(path)
      expect(FileUtils).to_not receive(:rm).with(path)
    end

    upstart.export
  end

  it 'does not delete exported files for app which share name prefix' do
    FileUtils.mkdir_p "/tmp/init"

    ["app-worker", "app-worker-worker", "app-worker-worker-1"].each do |name|
      path = "/tmp/init/#{name}.conf"
      FileUtils.touch(path)
      expect(FileUtils).to_not receive(:rm).with(path)
    end

    upstart.export
    expect(File.exist?('/tmp/init/app.conf')).to be true
    expect(File.exist?('/tmp/init/app-worker.conf')).to be true
  end

  it "quotes and escapes environment variables" do
    engine.env['KEY'] = 'd"\|d'
    upstart.export
    expect("foobarfoo").to include "bar"
    expect(File.read("/tmp/init/app-alpha-1.conf")).to match(/KEY='d"\\\|d'/)
  end

  context "with a formation" do
    let(:formation) { "alpha=2" }

    it "exports to the filesystem with concurrency" do
      upstart.export

      expect(File.read("/tmp/init/app.conf")).to            eq(example_export_file("upstart/app.conf"))
      expect(File.read("/tmp/init/app-alpha.conf")).to      eq(example_export_file("upstart/app-alpha.conf"))
      expect(File.read("/tmp/init/app-alpha-1.conf")).to    eq(example_export_file("upstart/app-alpha-1.conf"))
      expect(File.read("/tmp/init/app-alpha-2.conf")).to    eq(example_export_file("upstart/app-alpha-2.conf"))
      expect(File.exist?("/tmp/init/app-bravo-1.conf")).to eq(false)
    end
  end

  context "with alternate templates" do
    let(:template) { "/tmp/alternate" }
    let(:options)  { { :app => "app", :template => template } }

    before do
      FileUtils.mkdir_p template
      File.open("#{template}/master.conf.erb", "w") { |f| f.puts "alternate_template" }
    end

    it "can export with alternate template files" do
      upstart.export
      expect(File.read("/tmp/init/app.conf")).to eq("alternate_template\n")
    end
  end

  context "with alternate templates from home dir" do

    before do
      FileUtils.mkdir_p File.expand_path("~/.foreman/templates/upstart")
      File.open(File.expand_path("~/.foreman/templates/upstart/master.conf.erb"), "w") do |file|
        file.puts "default_alternate_template"
      end
    end

    it "can export with alternate template files" do
      upstart.export
      expect(File.read("/tmp/init/app.conf")).to eq("default_alternate_template\n")
    end
  end

end