rodjek / rspec-puppet

RSpec tests for your Puppet manifests
http://rspec-puppet.com
MIT License
364 stars 203 forks source link

structured facts do not work in a `let :facts` block #295

Closed hunner closed 8 years ago

hunner commented 9 years ago

For a given class:

class puppet4::placebo() {
  notice( $facts['os']['family'] )
}

and a given spec:

require 'spec_helper'
describe 'puppet4::placebo' do
  context 'with defaults for all parameters' do
    let :facts do
      { :os => { 'family' => 'RedHat' } }
    end

    it { should contain_class('puppet4::placebo') }
    it { should compile.with_all_deps }
  end
end

rspec gives the resulting output:

Failures:

 1) puppet4::placebo with defaults for all parameters should contain Class[puppet4::placebo]
    Failure/Error: should contain_class('puppet4::placebo')
    Puppet::PreformattedError:
      Evaluation Error: Operator '[]' is not applicable to an Undef Value. at /etc/puppetlabs/code/modules/puppet4/spec/fixtures/modules/puppet4/manifests/placebo.pp:2:11 on node client.example.com
    # ./spec/classes/placebo_spec.rb:15:in `block (3 levels) in <top (required)>'

 2) puppet4::placebo with defaults for all parameters should compile into a catalogue without dependency cycles
    Failure/Error: should compile.with_all_deps
      error during compilation: Evaluation Error: Operator '[]' is not applicable to an Undef Value. at /etc/puppetlabs/code/modules/puppet4/spec/fixtures/modules/puppet4/manifests/placebo.pp:2:11 on node client.example.com
    # ./spec/classes/placebo_spec.rb:19:in `block (3 levels) in <top (required)>'

Finished in 0.13206 seconds (files took 0.69979 seconds to load)
2 examples, 2 failures

Source: https://gist.github.com/jorhett/7e7b33ecba62758200f5

bkeyser commented 9 years ago

Submitted PR #317 to address this issue

jantman commented 9 years ago

This PR is merged, but I'm still seeing a possibly-related issue.

Using a spec including:

  let(:facts) {{
    :disks           => {
      'sda' => {
        'model' => "APPLE SSD SM0256",
        'size' => "233.76 GiB",
        'size_bytes' => 251000193024,
        'vendor' => "ATA"
      },
      'sdb' => {
        'model' => "SD Card Reader",
        'size' => "0 bytes",
        'size_bytes' => 0,
        'vendor' => "APPLE"
      }
    },
  }}

In my class, I'm trying to filter on this:

  $ssd_disks = filter($::disks) |$x| {
    $x[1]['model'] =~ /(?i:ssd)/
  }

This works when run with a puppet apply, but now every rspec including that class errors with:

     Puppet::PreformattedError:
       Evaluation Error: Error while evaluating a Function Call, filter(): wrong argument type (NilClass; must be something enumerable. at /home/jantman/GIT/puppet-archlinux-macbookretina/spec/fixtures/modules/archlinux_macbookretina/manifests/ssd.pp:26:21 on node phoenix.jasonantman.com

Even though doing a notify {'foo': message => $::disks } shows the proper content. As far as I can tell, this may be related to the fact that, through puppet apply, type_of($::disks) is Struct but in rspec-puppet it is Puppet::Pops::Types::PStructType.

DavidS commented 9 years ago

The error message clearly says that nil is passed into filter. Meanwhile PStructType is Enumerable as https://github.com/puppetlabs/puppet/blob/master/lib/puppet/pops/types/types.rb#L814 shows.

Can you provide a minimal test case that hits this issue?

JesperTerkelsen commented 8 years ago

:+1: I have this problem as well mocking when puppet read stuff like

$::facts['ec2_metadata']['block-device-mapping']
tnn commented 8 years ago

@rodjek If possible, could you direct us on how to solve this? It's a major for us blocker right now.

bkeyser commented 8 years ago

@tnn @JesperTerkelsen Have you been able to create a minimal test case?

bkeyser commented 8 years ago

Using the rough example provided by @jantman as follows

test/manifests/init.pp

class test {
  $ssd_disks = filter($::disks) |$x| {
    $x[1]['model'] =~ /(?i:ssd)/
  }

  notify { 'ssd_disks':
    message => $ssd_disks
  }
}

and the relevant spec (which includes the fact definition provided previously)

    context "test class without any parameters" do
      it { is_expected.to compile.with_all_deps }

      it { is_expected.to contain_notify('ssd_disks').with({
          'message' => /SSD /
        })
      }

      it { should have_notify_resource_count(1) }
    end

The test runs successfully

$ bundle exec rake spec
...
test
  test
    test class without any parameters
      should compile into a catalogue without dependency cycles
      should contain Notify[ssd_disks] with message =~ /SSD /
      should contain exactly 1 Notify resource
...

The module elements were provided by garethr/puppet-module-skeleton project.

DavidS commented 8 years ago

Pending a failing example, I'm closing this. @tnn there'll be a release with bkeyser's work merged RSN, I hope this resolves your issues!