class RSpec::Puppet::ManifestMatchers::CreateGeneric

Public Class Methods

new(*args, &block) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 9
def initialize(*args, &block)
  @exp_resource_type = args.shift.to_s.gsub(/^(create|contain)_/, '')
  @args = args
  @block = block
  @referenced_type = referenced_type(@exp_resource_type)
  @title = args[0]

  @errors = []
  @expected_params = []
  @expected_undef_params = []
  @notifies = []
  @subscribes = []
  @requires = []
  @befores = []
end

Public Instance Methods

actual() click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 197
def actual
  @errors.map {|e| e.actual if e.respond_to?(:actual)}.compact.join("\n\n")
end
description() click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 134
def description
  values = []
  value_str_prefix = "with"

  if @expected_params_count
    values << "exactly #{@expected_params_count} parameters"
  end

  if @expected_params.any?
    values.concat(generate_param_list(@expected_params, :should))
  end

  if @expected_undef_params.any?
    values.concat(generate_param_list(@expected_undef_params, :not))
  end

  if @notifies.any?
    value_str_prefix = "that notifies"
    values = @notifies
  end

  if @subscribes.any?
    value_str_prefix = "that subscribes to"
    values = @subscribes
  end

  if @requires.any?
    value_str_prefix = "that requires"
    values = @requires
  end

  if @befores.any?
    value_str_prefix = "that comes before"
    values = @befores
  end

  unless values.empty?
    if values.length == 1
      value_str = " #{value_str_prefix} #{values.first}"
    else
      value_str = " #{value_str_prefix} #{values[0..-2].join(", ")} and #{values[-1]}"
    end
  end

  "contain #{@referenced_type}[#{@title}]#{value_str}"
end
diffable?() click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 181
def diffable?
  true
end
expected() click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 193
def expected
  @errors.map {|e| e.expected if e.respond_to?(:expected)}.compact.join("\n\n")
end
failure_message() click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 126
def failure_message
  "expected that the catalogue would contain #{@referenced_type}[#{@title}]#{errors}"
end
failure_message_when_negated() click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 130
def failure_message_when_negated
  "expected that the catalogue would not contain #{@referenced_type}[#{@title}]#{errors}"
end
matches?(catalogue) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 82
def matches?(catalogue)
  ret = true
  @catalogue = catalogue.is_a?(Puppet::Resource::Catalog) ? catalogue : catalogue.call
  resource = @catalogue.resource(@referenced_type, @title)

  if resource.nil?
    false
  else
    RSpec::Puppet::Coverage.cover!(resource)
    rsrc_hsh = resource.to_hash
    if resource.respond_to?(:sensitive_parameters)
      resource.sensitive_parameters.each do |s_param|
        rsrc_hsh[s_param] = ::Puppet::Pops::Types::PSensitiveType::Sensitive.new(rsrc_hsh[s_param])
      end
    end

    if resource.builtin_type?
      namevar = resource.resource_type.key_attributes.first.to_s
    else
      namevar = 'name'
    end

    unless @expected_params.any? { |param| param.first.to_s == namevar }
      rsrc_hsh.delete(namevar.to_sym) if rsrc_hsh.has_key?(namevar.to_sym)
    end

    if @expected_params_count
      unless rsrc_hsh.size == @expected_params_count
        ret = false
        (@errors ||= []) << "exactly #{@expected_params_count} parameters but the catalogue contains #{rsrc_hsh.size}"
      end
    end

    check_params(rsrc_hsh, @expected_params, :should) if @expected_params.any?
    check_params(rsrc_hsh, @expected_undef_params, :not) if @expected_undef_params.any?
    check_befores(@catalogue, resource) if @befores.any?
    check_requires(@catalogue, resource) if @requires.any?
    check_notifies(@catalogue, resource) if @notifies.any?
    check_subscribes(@catalogue, resource) if @subscribes.any?

    @errors.empty?
  end
end
method_missing(method, *args, &block) click to toggle source
Calls superclass method
# File lib/rspec-puppet/matchers/create_generic.rb, line 63
def method_missing(method, *args, &block)
  if method.to_s =~ /^with_/
    param = method.to_s.gsub(/^with_/, '')
    @expected_params << [param, args[0]]
    self
  elsif method.to_s =~ /^only_with_/
    param = method.to_s.gsub(/^only_with_/, '')
    @expected_params_count = (@expected_params_count || 0) + 1
    @expected_params << [param, args[0]]
    self
  elsif method.to_s =~ /^without_/
    param = method.to_s.gsub(/^without_/, '')
    @expected_undef_params << [param, args[0]]
    self
  else
    super
  end
end
only_with(*args, &block) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 31
def only_with(*args, &block)
  params = args.shift
  @expected_params_count = (@expected_params_count || 0) + params.select { |_, v| !v.nil? }.size
  self.with(params, &block)
end
supports_block_expectations() click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 185
def supports_block_expectations
  true
end
supports_value_expectations() click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 189
def supports_value_expectations
  true
end
that_comes_before(resource) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 58
def that_comes_before(resource)
  @befores.concat(Array(resource))
  self
end
that_notifies(resource) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 43
def that_notifies(resource)
  @notifies.concat(Array(resource))
  self
end
that_requires(resource) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 53
def that_requires(resource)
  @requires.concat(Array(resource))
  self
end
that_subscribes_to(resource) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 48
def that_subscribes_to(resource)
  @subscribes.concat(Array(resource))
  self
end
with(*args, &block) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 25
def with(*args, &block)
  params = args.shift
  @expected_params = @expected_params | params.to_a
  self
end
without(*args, &block) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 37
def without(*args, &block)
  params = args.shift
  @expected_undef_params = @expected_undef_params | Array(params)
  self
end

Private Instance Methods

canonicalize_resource(resource) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 268
def canonicalize_resource(resource)
  res = resource_from_ref(resource_ref(resource))
  if res.nil?
    resource = Struct.new(:type, :title).new(*@catalogue.title_key_for_ref(resource)) if resource.is_a?(String)
    res = @catalogue.resource_keys.select { |type, name|
      type == resource.type
    }.map { |type, name|
      @catalogue.resource(type, name)
    }.compact.find { |cat_res|
      cat_res.builtin_type? && cat_res.uniqueness_key.first == resource.title
    }
  end
  res
end
canonicalize_resource_ref(ref) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 283
def canonicalize_resource_ref(ref)
  resource_ref(resource_from_ref(ref))
end
check_befores(catalogue, resource) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 228
def check_befores(catalogue, resource)
  @befores.each do |ref|
    unless precedes?(resource, canonicalize_resource(ref))
      @errors << BeforeRelationshipError.new(resource.to_ref, ref)
    end
  end
end
check_notifies(catalogue, resource) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 244
def check_notifies(catalogue, resource)
  @notifies.each do |ref|
    unless notifies?(resource, canonicalize_resource(ref))
      @errors << NotifyRelationshipError.new(resource.to_ref, ref)
    end
  end
end
check_params(resource, list, type) click to toggle source

@param resource [Hash<Symbol, Object>] The resource in the catalog @param list [Array<String, Object>] The expected values of the resource @param type [:should, :not] Whether the given parameters should/not match

# File lib/rspec-puppet/matchers/create_generic.rb, line 372
def check_params(resource, list, type)
  list.each do |param, value|
    param = param.to_sym

    if value.nil? then
      unless resource[param].nil?
        @errors << "#{param} undefined but it is set to #{resource[param].inspect}"
      end
    else
      m = ParameterMatcher.new(param, value, type)
      unless m.matches?(resource)
        @errors.concat m.errors
      end
    end
  end
end
check_requires(catalogue, resource) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 236
def check_requires(catalogue, resource)
  @requires.each do |ref|
    unless precedes?(canonicalize_resource(ref), resource)
      @errors << RequireRelationshipError.new(resource.to_ref, ref)
    end
  end
end
check_subscribes(catalogue, resource) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 252
def check_subscribes(catalogue, resource)
  @subscribes.each do |ref|
    unless notifies?(canonicalize_resource(ref), resource)
      @errors << SubscribeRelationshipError.new(resource.to_ref, ref)
    end
  end
end
errors() click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 206
def errors
  @errors.empty? ? "" : " with #{@errors.join(', and parameter ')}"
end
generate_param_list(list, type) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 210
def generate_param_list(list, type)
  output = []
  list.each do |param, value|
    if value.nil?
      output << "#{param.to_s} #{type == :not ? 'un' : ''}defined"
    else
      a = type == :not ? '!' : '='
      b = value.is_a?(Regexp) ? '~' : '>'
      if param.to_s == 'content' and value.is_a?( String )
        output << "#{param.to_s} #{type == :not ? 'not ' : ''} supplied string"
      else
        output << "#{param.to_s} #{a}#{b} #{value.inspect}"
      end
    end
  end
  output
end
notifies?(first, second) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 351
def notifies?(first, second)
  return false if first.nil? || second.nil?

  self_or_upstream(first).each do |u|
    self_or_upstream(second).each do |v|
      notify_refs = relationship_refs(u, :notify)
      subscribe_refs = relationship_refs(v, :subscribe)

      if notify_refs.include?(v.to_ref) || subscribe_refs.include?(u.to_ref)
        return true
      end
    end
  end

  # Nothing found
  return false
end
precedes?(first, second) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 333
def precedes?(first, second)
  return false if first.nil? || second.nil?

  self_or_upstream(first).each do |u|
    self_or_upstream(second).each do |v|
      before_refs = relationship_refs(u, :before) + relationship_refs(u, :notify)
      require_refs = relationship_refs(v, :require) + relationship_refs(u, :subscribe)

      if before_refs.include?(v.to_ref) || require_refs.include?(u.to_ref) || (before_refs & require_refs).any?
        return true
      end
    end
  end

  # Nothing found
  return false
end
referenced_type(type) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 202
def referenced_type(type)
  type.split('__').map { |r| r.capitalize }.join('::')
end
relationship_refs(resource, type, visited = Set.new) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 287
def relationship_refs(resource, type, visited = Set.new)
  resource = canonicalize_resource(resource)
  results = Set.new
  return results unless resource

  # guard to prevent infinite recursion
  if visited.include?(resource.object_id)
    return [canonicalize_resource_ref(resource)]
  else
    visited << resource.object_id
  end

  Array[resource[type]].flatten.compact.each do |r|
    results << canonicalize_resource_ref(r)
    results << relationship_refs(r, type, visited)

    res = canonicalize_resource(r)
    if res && res.builtin_type?
      results << res.to_ref
      results << "#{res.type.to_s.capitalize}[#{res.uniqueness_key.first}]"
    end
  end

  # Add auto* (autorequire etc) if any
  if [:before, :notify, :require, :subscribe].include?(type)
    func = "eachauto#{type}".to_sym
    if resource.resource_type.respond_to?(func)
      resource.resource_type.send(func) do |t, b|
        Array(resource.to_ral.instance_eval(&b)).each do |dep|
          res = "#{t.to_s.capitalize}[#{dep}]"
          if r = relationship_refs(res, type, visited)
            results << res
            results << r
          end
        end
      end
    end
  end

  results.flatten
end
resource_from_ref(ref) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 264
def resource_from_ref(ref)
  ref.is_a?(Puppet::Resource) ? ref : @catalogue.resource(ref)
end
resource_ref(resource) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 260
def resource_ref(resource)
  resource.respond_to?(:to_ref) ? resource.to_ref : resource
end
self_or_upstream(vertex) click to toggle source
# File lib/rspec-puppet/matchers/create_generic.rb, line 329
def self_or_upstream(vertex)
  [vertex] + @catalogue.upstream_from_vertex(vertex).keys
end