sous-chefs / homebrew

Development repository for the homebrew cookbook
https://supermarket.chef.io/cookbooks/homebrew
Apache License 2.0
152 stars 136 forks source link

Analytics shell_out breaks chefspec #116

Closed rmoriz closed 7 years ago

rmoriz commented 7 years ago

When the homebrewrecipe is included and the chefspec tests are running on a non-homebrew box (or Linux), the following error is thrown:

1) base::osx executes the homebrew cookbook
   Failure/Error: cached(:chef_run) { ChefSpec::ServerRunner.new(platform: 'mac_os_x', version: '10.12').converge(described_recipe) }

   Errno::ENOENT:
     No such file or directory - /usr/local/bin/brew
   # /var/folders/48/v3jpvqg155d2qfktth99_8t40000gp/T/chefspec20161223-30733-n42dowfile_cache_path/cookbooks/homebrew/recipes/default.rb:44:in `block (2 levels) in from_file'
   # ./spec/recipes/osx_spec.rb:4:in `block (2 levels) in <top (required)>'
   # ./spec/recipes/osx_spec.rb:19:in `block (2 levels) in <top (required)

osx.rb recipe code:

include_recipe 'build-essential'
include_recipe 'homebrew'

chefspec code:


describe 'base::osx' do
  cached(:chef_run) { ChefSpec::ServerRunner.new(platform: 'mac_os_x', version: '10.12').converge(described_recipe) }

  before do
    stub_command('which git').and_return(true)
  end

  it 'executes the homebrew cookbook' do
    expect(chef_run).to include_recipe('homebrew')
  end
end

AFAIK this is caused by the latest "disable analytics" feature and its shell_out call, see https://github.com/chef-cookbooks/homebrew/commit/23257de53673842f180c7f38551a977ca2ceb261

While I am aware of shell_out stubbing in chefspec (https://github.com/sethvargo/chefspec/issues/264) I hate it because I always need to know what a cookbook internally does when I just want to check the include_recipe call.

tas50 commented 7 years ago

Specs are fixed now with some refactoring in #119

trinitronx commented 5 years ago

I believe this is still an issue with homebrew 5.0.8 when running ChefSpec to test cookbooks depending on this one.

For example, running ChefSpec tests on a cookbook mocking platform via fauxhai with depends 'homebrew' in metadata.rb would return an error such as:

      Failure/Error:
            klass.new(platform: 'mac_os_x', version: '10.11') do |node|
              create_singleton_struct "EtcPasswd", [ :name, :passwd, :uid, :gid, :gecos, :dir, :shell, :change, :uclass, :expire ]
              node.normal['etc']['passwd']['brubble'] = Struct::EtcPasswd.new('brubble', '********', 501, 20, 'Barney Rubble', '/Users/brubble', '/bin/bash', 0, '', 0)
              node.normal['lyraphase_workstation']['user'] = 'brubble'
              node.normal['lyraphase_workstation']['home'] = '/Users/brubble'

              stub_command("which git").and_return('/usr/local/bin/git')

      ChefSpec::Error::ShellOutNotStubbed:
        Executing a real shell_out in execute[set analytics] is not allowed:

            shell_out("/usr/local/bin/brew analytics state", {:user=>"brubble"})

        You can stub this with:

            stubs_for_resource("execute[set analytics]") do |resource|
              allow(resource).to receive_shell_out("/usr/local/bin/brew analytics state", {:user=>"brubble"})
            end

Seems that one solution is for dependent cookbooks to include the stub for brew analytics command:

  let(:chef_run) {
    klass = ChefSpec.constants.include?(:SoloRunner) ? ChefSpec::SoloRunner : ChefSpec::Runner
    klass.new(platform: 'mac_os_x', version: '10.11') do |node|
      create_singleton_struct "EtcPasswd", [ :name, :passwd, :uid, :gid, :gecos, :dir, :shell, :change, :uclass, :expire ]
      node.normal['etc']['passwd']['brubble'] = Struct::EtcPasswd.new('brubble', '********', 501, 20, 'Barney Rubble', '/Users/brubble', '/bin/bash', 0, '', 0)
      node.normal['foo']['user'] = 'brubble'
      node.normal['foo']['home'] = '/Users/brubble'

      stub_command("which git").and_return('/usr/local/bin/git')
      stub_command("/usr/local/bin/brew analytics state").and_return('Analytics is disabled')
      stubs_for_resource("execute[set analytics]") do |resource|
        allow(resource).to receive_shell_out("/usr/local/bin/brew analytics state", {:user=>"brubble"})
        allow(resource).to receive_shell_out("/usr/local/bin/brew analytics state")
      end
    end.converge(described_recipe)
  }

Original poster's comment is that ideally dependent cookbooks shouldn't need to know inner workings of their dependencies to run their internal Unit tests. However, in practice I'm not sure how realistic this is without truly decoupled behavior regarding running the dependency cookbooks shell_out somewhere in the testing framework. Seems like a shortcoming of how ChefSpec treats resources including shell_out?