spec/unit/mutest/zombifier_spec.rb
RSpec.describe Mutest::Zombifier do
  let(:root_require) { Pathname.new('project') }
  let(:pathname)     { class_double(Pathname)  }

  let(:require_highjack) do
    lambda do |block|
      original = ruby_vm.method(:require)
      allow(ruby_vm).to receive(:require) do |argument|
        return_value = ruby_vm.expected_events.first.return_value
        expect(block.call(argument)).to be(return_value)
      end
      original
    end
  end

  let(:options) do
    {
      load_path:        %w[a b],
      includes:         %w[project bar],
      namespace:        :Zombie,
      require_highjack: require_highjack,
      root_require:     root_require,
      pathname:         pathname,
      kernel:           ruby_vm
    }
  end

  let(:ruby_vm) do
    MutestSpec::RubyVM.new(
      [
        MutestSpec::RubyVM::EventExpectation::Require.new(
          expected_payload: {
            logical_name: 'project'
          },
          return_value:     true
        ),
        MutestSpec::RubyVM::EventExpectation::Eval.new(
          expected_payload: {
            binding:         TOPLEVEL_BINDING,
            source:          "module Zombie\n  module Project\n  end\nend",
            source_location: 'a/project.rb'
          },
          trigger_requires: %w[foo bar],
          return_value:     nil
        ),
        MutestSpec::RubyVM::EventExpectation::Require.new(
          expected_payload: {
            logical_name: 'foo'
          },
          trigger_requires: %w[bar],
          return_value:     true
        ),
        MutestSpec::RubyVM::EventExpectation::Require.new(
          expected_payload: {
            logical_name: 'bar'
          },
          return_value:     true
        ),
        MutestSpec::RubyVM::EventExpectation::Eval.new(
          expected_payload: {
            binding:         TOPLEVEL_BINDING,
            source:          "module Zombie\n  module Bar\n  end\nend",
            source_location: 'b/bar.rb'
          },
          trigger_requires: %w[],
          return_value:     nil
        ),
        MutestSpec::RubyVM::EventExpectation::Require.new(
          expected_payload: {
            logical_name: 'bar'
          },
          return_value:     false
        )
      ]
    )
  end

  let(:file_entries) do
    {
      'a/project.rb' => { file: true, contents: 'module Project; end' },
      'b/bar.rb'     => { file: true, contents: 'module Bar; end'     }
    }
  end

  let(:file_system) do
    MutestSpec::FileSystem.new(
      Hash[
        file_entries.map { |key, attributes| [key, MutestSpec::FileState.new(attributes)] }
      ]
    )
  end

  describe '.call' do
    def apply
      described_class.call(options)
    end

    before do
      allow(pathname).to receive(:new, &file_system.method(:path))
    end

    it 'returns self' do
      expect(apply).to be(described_class)
    end

    it 'walks the VM through expected steps' do
      expect { apply }.to change(ruby_vm, :done?).from(false).to(true)
    end

    context 'when zombifier require fails' do
      let(:file_entries) do
        {}
      end

      it 'raises zombifier specific load error' do
        expect { apply }.to raise_error(described_class::LoadError, 'Cannot find file "project.rb" in load path')
      end
    end
  end
end
