File: parameter_matcher.rb

package info (click to toggle)
ruby-rspec-puppet 4.0.2%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,444 kB
  • sloc: ruby: 6,377; makefile: 6
file content (124 lines) | stat: -rw-r--r-- 3,811 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# frozen_string_literal: true

module RSpec::Puppet
  module ManifestMatchers
    class ParameterMatcher
      include RSpec::Puppet::Errors

      # @param parameter [Symbol] The specific parameter to check
      # @param value [Object] The expected data to match the parameter against
      # @param type [:should, :not] Whether the given parameter should match
      def initialize(parameter, value, type)
        @parameter = parameter
        @value = value
        @type = type

        @should_match = (type == :should)

        @errors = []
      end

      # Ensure that the actual parameter matches the expected parameter.
      #
      # @param resource [Hash<Symbol, Object>] A hash representing a Puppet
      #   resource in the catalog
      #
      # @return [true, false]
      def matches?(resource)
        actual = resource[@parameter]
        expected = @value

        # Puppet flattens an array with a single value into just the value and
        # this can cause confusion when testing as people expect when you put
        # an array in, you'll get an array out.
        actual = [actual] if expected.is_a?(Array) && !actual.is_a?(Array)

        retval = check(expected, actual)

        @errors << MatchError.new(@parameter, expected, actual, !@should_match) unless retval

        retval
      end

      # @!attribute [r] errors
      #   @return [Array<Object < StandardError>] All expectation errors
      #     generated on this parameter.
      attr_reader :errors

      private

      # Recursively check that the `expected` and `actual` data structures match
      #
      # @param expected [Object] The expected value of the given resource param
      # @param actual [Object] The value of the resource as found in the catalogue
      #
      # @return [true, false] If the resource matched
      def check(expected, actual)
        return false if !expected.is_a?(Proc) && actual.nil? && !expected.nil?

        case expected
        when Proc
          check_proc(expected, actual)
        when Regexp
          check_regexp(expected, actual)
        when Hash
          check_hash(expected, actual)
        when Array
          check_array(expected, actual)
        when RSpec::Puppet::Sensitive
          expected == actual
        else
          check_string(expected, actual)
        end
      end

      def check_proc(expected, actual)
        expected_return = @should_match
        actual_return   = expected.call(actual)

        actual_return == expected_return
      end

      def check_regexp(expected, actual)
        !!(actual.to_s.match expected) == @should_match
      end

      # Ensure that two hashes have the same number of keys, and that for each
      # key in the expected hash, there's a stringified key in the actual hash
      # with a matching value.
      def check_hash(expected, actual)
        op = @should_match ? :'==' : :'!='

        unless actual.class.send(op, expected.class)
          @errors << MatchError.new(@parameter, expected, actual, !@should_match)
          return false
        end

        return false unless expected.keys.size.send(op, actual.keys.size)

        expected.keys.all? do |key|
          check(expected[key], actual[key])
        end
      end

      def check_array(expected, actual)
        op = @should_match ? :'==' : :'!='

        unless actual.class.send(op, expected.class)
          @errors << MatchError.new(@parameter, expected, actual, !@should_match)
          return false
        end

        return false unless expected.size.send(op, actual.size)

        (0...expected.size).all? do |index|
          check(expected[index], actual[index])
        end
      end

      def check_string(expected, actual)
        (expected.to_s == actual.to_s) == @should_match
      end
    end
  end
end