rspec-puppet 0.1.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +71 -6
  3. data/lib/rspec-puppet.rb +45 -0
  4. data/lib/rspec-puppet/errors.rb +83 -0
  5. data/lib/rspec-puppet/example/class_example_group.rb +1 -55
  6. data/lib/rspec-puppet/example/define_example_group.rb +1 -57
  7. data/lib/rspec-puppet/example/function_example_group.rb +21 -35
  8. data/lib/rspec-puppet/example/host_example_group.rb +1 -26
  9. data/lib/rspec-puppet/matchers.rb +3 -1
  10. data/lib/rspec-puppet/matchers/compile.rb +133 -0
  11. data/lib/rspec-puppet/matchers/count_generic.rb +73 -0
  12. data/lib/rspec-puppet/matchers/create_generic.rb +155 -52
  13. data/lib/rspec-puppet/matchers/dynamic_matchers.rb +17 -0
  14. data/lib/rspec-puppet/matchers/include_class.rb +2 -1
  15. data/lib/rspec-puppet/matchers/parameter_matcher.rb +110 -0
  16. data/lib/rspec-puppet/matchers/run.rb +49 -31
  17. data/lib/rspec-puppet/setup.rb +57 -34
  18. data/lib/rspec-puppet/support.rb +154 -7
  19. metadata +18 -32
  20. data/LICENSE +0 -20
  21. data/Rakefile +0 -7
  22. data/lib/rspec-puppet/matchers/create_resource.rb +0 -53
  23. data/rspec-puppet.gemspec +0 -47
  24. data/spec/classes/boolean_regexp_spec.rb +0 -10
  25. data/spec/classes/boolean_spec.rb +0 -11
  26. data/spec/classes/sysctl_common_spec.rb +0 -40
  27. data/spec/defines/sysctl_before_spec.rb +0 -26
  28. data/spec/defines/sysctl_spec.rb +0 -14
  29. data/spec/fixtures/manifests/site.pp +0 -7
  30. data/spec/fixtures/modules/boolean/manifests/init.pp +0 -12
  31. data/spec/fixtures/modules/sysctl/manifests/init.pp +0 -39
  32. data/spec/functions/split_spec.rb +0 -17
  33. data/spec/hosts/foo_spec.rb +0 -6
  34. data/spec/hosts/testhost_spec.rb +0 -5
  35. data/spec/spec_helper.rb +0 -6
@@ -4,32 +4,7 @@ module RSpec::Puppet
4
4
  include RSpec::Puppet::Support
5
5
 
6
6
  def subject
7
- @catalogue ||= catalogue
8
- end
9
-
10
- def catalogue
11
- vardir = Dir.mktmpdir
12
- Puppet[:vardir] = vardir
13
- Puppet[:hiera_config] = File.join(vardir, "hiera.yaml") if Puppet[:hiera_config] == File.expand_path("/dev/null")
14
- Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
15
- Puppet[:manifestdir] = self.respond_to?(:manifest_dir) ? manifest_dir : RSpec.configuration.manifest_dir
16
- Puppet[:manifest] = self.respond_to?(:manifest) ? manifest : RSpec.configuration.manifest
17
- Puppet[:templatedir] = self.respond_to?(:template_dir) ? template_dir : RSpec.configuration.template_dir
18
- Puppet[:config] = self.respond_to?(:config) ? config : RSpec.configuration.config
19
- code = ""
20
-
21
- nodename = self.class.top_level_description.downcase
22
-
23
- facts_val = {
24
- 'hostname' => nodename.split('.').first,
25
- 'fqdn' => nodename,
26
- 'domain' => nodename.split('.').last,
27
- }
28
- facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
29
-
30
- catalogue = build_catalog(nodename, facts_val, code)
31
- FileUtils.rm_rf(vardir) if File.directory?(vardir)
32
- catalogue
7
+ @catalogue ||= catalogue(:host)
33
8
  end
34
9
  end
35
10
  end
@@ -1,4 +1,6 @@
1
1
  require 'rspec-puppet/matchers/create_generic'
2
- require 'rspec-puppet/matchers/create_resource'
3
2
  require 'rspec-puppet/matchers/include_class'
3
+ require 'rspec-puppet/matchers/compile'
4
4
  require 'rspec-puppet/matchers/run'
5
+ require 'rspec-puppet/matchers/count_generic'
6
+ require 'rspec-puppet/matchers/dynamic_matchers'
@@ -0,0 +1,133 @@
1
+ module RSpec::Puppet
2
+ module ManifestMatchers
3
+ class Compile
4
+ def initialize
5
+ @failed_resource = ""
6
+ @check_deps = false
7
+ @cycles = []
8
+ end
9
+
10
+ def with_all_deps
11
+ @check_deps = true
12
+ self
13
+ end
14
+
15
+ def matches?(catalogue)
16
+ @catalogue = catalogue
17
+ if cycles_found?
18
+ false
19
+ elsif @check_deps == true && missing_dependencies?
20
+ false
21
+ else
22
+ true
23
+ end
24
+ end
25
+
26
+ def description
27
+ "compile the catalogue without cycles"
28
+ end
29
+
30
+ def failure_message_for_should
31
+ unless @cycles.empty?
32
+ "dependency cycles found: #{@cycles.join('; ')}"
33
+ else
34
+ "expected that the catalogue would include #{@failed_resource}"
35
+ end
36
+ end
37
+
38
+ def failure_message_for_should_not
39
+ "expected that the catalogue would not compile but it does"
40
+ end
41
+
42
+ private
43
+ def missing_dependencies?
44
+ retval = false
45
+
46
+ resource_vertices = @catalogue.vertices.select { |v| v.is_a? Puppet::Resource }
47
+ resource_vertices.each do |vertex|
48
+ vertex.each do |param,value|
49
+ if [:require, :subscribe, :notify, :before].include? param
50
+ value = Array[value] unless value.is_a? Array
51
+ value.each do |val|
52
+ if val.is_a? Puppet::Resource
53
+ retval = true unless resource_exists?(val, vertex)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ retval
61
+ end
62
+
63
+ def resource_hash
64
+ @resource_hash ||= Proc.new do
65
+ res_hash = {}
66
+ @catalogue.vertices.each do |vertex|
67
+ if vertex.is_a? Puppet::Resource
68
+ res_hash[vertex.ref] = 1
69
+ if vertex[:alias]
70
+ res_hash["#{vertex.type.to_s}[#{vertex[:alias]}]"] = 1
71
+ end
72
+ end
73
+ end
74
+ res_hash
75
+ end.call
76
+ end
77
+
78
+ def check_resource(res)
79
+ if resource_hash[res.ref]
80
+ true
81
+ elsif res[:alias] && resource_hash["#{res.type.to_s}[#{res[:alias]}]"]
82
+ true
83
+ else
84
+ false
85
+ end
86
+ end
87
+
88
+ def resource_exists?(res, vertex)
89
+ unless check_resource(res)
90
+ @failed_resource = "#{res.ref} used at #{vertex.file}:#{vertex.line} in #{vertex.ref}"
91
+ false
92
+ else
93
+ true
94
+ end
95
+ end
96
+
97
+ def cycles_found?
98
+ retval = false
99
+ begin
100
+ cat = @catalogue.to_ral.relationship_graph
101
+ cat.write_graph(:resources)
102
+ if cat.respond_to? :find_cycles_in_graph
103
+ find_cycles(cat)
104
+ else
105
+ find_cycles_legacy(cat)
106
+ end
107
+ retval = true unless @cycles.empty?
108
+ rescue Puppet::Error
109
+ retval = true
110
+ end
111
+ retval
112
+ end
113
+
114
+ def find_cycles(catalogue)
115
+ cycles = catalogue.find_cycles_in_graph
116
+ if cycles.length > 0
117
+ cycles.each do |cycle|
118
+ paths = catalogue.paths_in_cycle(cycle)
119
+ @cycles << (paths.map{ |path| '(' + path.join(" => ") + ')'}.join("\n") + "\n")
120
+ end
121
+ end
122
+ end
123
+
124
+ def find_cycles_legacy(catalogue)
125
+ begin
126
+ catalogue.topsort
127
+ rescue Puppet::Error => e
128
+ @cycles = [e.message.rpartition(';').first.partition(':').last]
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,73 @@
1
+ module RSpec::Puppet
2
+ module ManifestMatchers
3
+ class CountGeneric
4
+ def initialize(type, count, *method)
5
+ if type.nil?
6
+ @type = method[0].to_s.gsub(/^have_(.+)_resource_count$/, '\1')
7
+ else
8
+ @type = type
9
+ end
10
+ @referenced_type = referenced_type(@type)
11
+ @expected_number = count.to_i
12
+ end
13
+
14
+ def matches?(catalogue)
15
+ if @type == "resource"
16
+ @actual_number = catalogue.resources.count do |res|
17
+ !(['Class', 'Node'].include? res.type)
18
+ end
19
+
20
+ # Puppet automatically adds Stage[main]
21
+ @actual_number = @actual_number - 1
22
+ else
23
+ @actual_number = catalogue.resources.count do |res|
24
+ res.type == @referenced_type
25
+ end
26
+
27
+ # Puppet automatically adds Class[main] and Class[Settings]
28
+ @actual_number = @actual_number - 2 if @type == "class"
29
+ end
30
+
31
+ @actual_number == @expected_number
32
+ end
33
+
34
+ def description
35
+ desc = []
36
+
37
+ desc << "contain exactly #{@expected_number}"
38
+ if @type == "class"
39
+ desc << "#{@expected_number == 1 ? "class" : "classes" }"
40
+ else
41
+ unless @type == "resource"
42
+ desc << "#{@referenced_type}"
43
+ end
44
+ desc << "#{@expected_number == 1 ? "resource" : "resources" }"
45
+ end
46
+
47
+ desc.join(" ")
48
+ end
49
+
50
+ def failure_message_for_should
51
+ "expected that the catalogue would " + description + " but it contains #{@actual_number}"
52
+ end
53
+
54
+ def failure_message_for_should_not
55
+ "expected that the catalogue would not " + description + " but it does"
56
+ end
57
+
58
+ private
59
+
60
+ def referenced_type(type)
61
+ type.split('__').map { |r| r.capitalize }.join('::')
62
+ end
63
+ end
64
+
65
+ def have_class_count(count)
66
+ RSpec::Puppet::ManifestMatchers::CountGeneric.new('class', count)
67
+ end
68
+
69
+ def have_resource_count(count)
70
+ RSpec::Puppet::ManifestMatchers::CountGeneric.new('resource', count)
71
+ end
72
+ end
73
+ end
@@ -1,34 +1,76 @@
1
+ require 'rspec-puppet/matchers/parameter_matcher'
1
2
  module RSpec::Puppet
2
3
  module ManifestMatchers
3
4
  class CreateGeneric
5
+ include RSpec::Puppet::Errors
6
+
4
7
  def initialize(*args, &block)
5
8
  @exp_resource_type = args.shift.to_s.gsub(/^(create|contain)_/, '')
6
9
  @args = args
7
10
  @block = block
8
11
  @referenced_type = referenced_type(@exp_resource_type)
9
12
  @title = args[0]
13
+
14
+ @errors = []
15
+ @expected_params = []
16
+ @expected_undef_params = []
17
+ @notifies = []
18
+ @subscribes = []
19
+ @requires = []
20
+ @befores = []
10
21
  end
11
22
 
12
23
  def with(*args, &block)
13
24
  params = args.shift
14
- @expected_params = (@expected_params || []) | params.to_a
25
+ @expected_params = @expected_params | params.to_a
15
26
  self
16
27
  end
17
28
 
29
+ def only_with(*args, &block)
30
+ params = args.shift
31
+ @expected_params_count = (@expected_params_count || 0) + params.size
32
+ self.with(params, &block)
33
+ end
34
+
18
35
  def without(*args, &block)
19
36
  params = args.shift
20
- @expected_undef_params = (@expected_undef_params || []) | Array(params)
37
+ @expected_undef_params = @expected_undef_params | Array(params)
38
+ self
39
+ end
40
+
41
+ def that_notifies(resource)
42
+ @notifies << resource
43
+ self
44
+ end
45
+
46
+ def that_subscribes_to(resource)
47
+ @subscribes << resource
48
+ self
49
+ end
50
+
51
+ def that_requires(resource)
52
+ @requires << resource
53
+ self
54
+ end
55
+
56
+ def that_comes_before(resource)
57
+ @befores << resource
21
58
  self
22
59
  end
23
60
 
24
61
  def method_missing(method, *args, &block)
25
62
  if method.to_s =~ /^with_/
26
63
  param = method.to_s.gsub(/^with_/, '')
27
- (@expected_params ||= []) << [param, args[0]]
64
+ @expected_params << [param, args[0]]
65
+ self
66
+ elsif method.to_s =~ /^only_with_/
67
+ param = method.to_s.gsub(/^only_with_/, '')
68
+ @expected_params_count = (@expected_params_count || 0) + 1
69
+ @expected_params << [param, args[0]]
28
70
  self
29
71
  elsif method.to_s =~ /^without_/
30
72
  param = method.to_s.gsub(/^without_/, '')
31
- (@expected_undef_params ||= []) << param
73
+ @expected_undef_params << [param, args[0]]
32
74
  self
33
75
  else
34
76
  super
@@ -40,41 +82,25 @@ module RSpec::Puppet
40
82
  resource = catalogue.resource(@referenced_type, @title)
41
83
 
42
84
  if resource.nil?
43
- ret = false
85
+ false
44
86
  else
45
87
  rsrc_hsh = resource.to_hash
46
- if @expected_params
47
- @expected_params.each do |name, value|
48
- if value.kind_of?(Regexp) then
49
- unless rsrc_hsh[name.to_sym].to_s =~ value
50
- ret = false
51
- (@errors ||= []) << "#{name.to_s} matching `#{value.inspect}` but its value of `#{rsrc_hsh[name.to_sym].inspect}` does not"
52
- end
53
- elsif value.kind_of?(Array) then
54
- unless Array(rsrc_hsh[name.to_sym]).flatten.join == value.flatten.join
55
- ret = false
56
- (@errors ||= []) << "#{name.to_s} set to `#{value.inspect}` but it is set to `#{rsrc_hsh[name.to_sym].inspect}` in the catalogue"
57
- end
58
- else
59
- unless rsrc_hsh[name.to_sym].to_s == value.to_s
60
- ret = false
61
- (@errors ||= []) << "#{name.to_s} set to `#{value.inspect}` but it is set to `#{rsrc_hsh[name.to_sym].inspect}` in the catalogue"
62
- end
63
- end
88
+ if @expected_params_count
89
+ unless rsrc_hsh.size == @expected_params_count
90
+ ret = false
91
+ (@errors ||= []) << "exactly #{@expected_params_count} parameters but the catalogue contains #{rsrc_hsh.size}"
64
92
  end
65
93
  end
66
94
 
67
- if @expected_undef_params
68
- @expected_undef_params.each do |name|
69
- unless resource.send(:parameters)[name.to_sym].nil?
70
- ret = false
71
- (@errors ||= []) << "#{name.to_s} undefined"
72
- end
73
- end
74
- end
75
- end
95
+ check_params(rsrc_hsh, @expected_params, :should) if @expected_params.any?
96
+ check_params(rsrc_hsh, @expected_undef_params, :not) if @expected_undef_params.any?
97
+ check_befores(catalogue, resource) if @befores.any?
98
+ check_requires(catalogue, resource) if @requires.any?
99
+ check_notifies(catalogue, resource) if @notifies.any?
100
+ check_subscribes(catalogue, resource) if @subscribes.any?
76
101
 
77
- ret
102
+ @errors.empty?
103
+ end
78
104
  end
79
105
 
80
106
  def failure_message_for_should
@@ -87,20 +113,17 @@ module RSpec::Puppet
87
113
 
88
114
  def description
89
115
  values = []
90
- if @expected_params
91
- @expected_params.each do |name, value|
92
- if value.kind_of?(Regexp)
93
- values << "#{name.to_s} matching #{value.inspect}"
94
- else
95
- values << "#{name.to_s} => #{value.inspect}"
96
- end
97
- end
116
+
117
+ if @expected_params_count
118
+ values << "exactly #{@expected_params_count} parameters"
98
119
  end
99
120
 
100
- if @expected_undef_params
101
- @expected_undef_params.each do |name, value|
102
- values << "#{name.to_s} undefined"
103
- end
121
+ if @expected_params.any?
122
+ values.concat(generate_param_list(@expected_params, :should))
123
+ end
124
+
125
+ if @expected_undef_params.any?
126
+ values.concat(generate_param_list(@expected_undef_params, :not))
104
127
  end
105
128
 
106
129
  unless values.empty?
@@ -114,20 +137,100 @@ module RSpec::Puppet
114
137
  "contain #{@referenced_type}[#{@title}]#{value_str}"
115
138
  end
116
139
 
117
- private
118
-
140
+ private
119
141
  def referenced_type(type)
120
142
  type.split('__').map { |r| r.capitalize }.join('::')
121
143
  end
122
144
 
123
145
  def errors
124
- @errors.nil? ? "" : " with #{@errors.join(', and parameter ')}"
146
+ @errors.empty? ? "" : " with #{@errors.join(', and parameter ')}"
125
147
  end
126
- end
127
148
 
128
- def method_missing(method, *args, &block)
129
- return RSpec::Puppet::ManifestMatchers::CreateGeneric.new(method, *args, &block) if method.to_s =~ /^(create|contain)_/
130
- super
149
+ def generate_param_list(list, type)
150
+ output = []
151
+ list.each do |param, value|
152
+ if value.nil?
153
+ output << "#{param.to_s} #{type == :not ? 'un' : ''}defined"
154
+ else
155
+ a = type == :not ? '!' : '='
156
+ b = value.is_a?(Regexp) ? '~' : '>'
157
+ output << "#{param.to_s} #{a}#{b} #{value.inspect}"
158
+ end
159
+ end
160
+ output
161
+ end
162
+
163
+ def check_befores(catalogue, resource)
164
+ @befores.each do |ref|
165
+ unless precedes?(resource, catalogue.resource(ref))
166
+ @errors << BeforeRelationshipError.new(resource.to_ref, ref)
167
+ end
168
+ end
169
+ end
170
+
171
+ def check_requires(catalogue, resource)
172
+ @requires.each do |ref|
173
+ unless precedes?(catalogue.resource(ref), resource)
174
+ @errors << RequireRelationshipError.new(resource.to_ref, ref)
175
+ end
176
+ end
177
+ end
178
+
179
+ def check_notifies(catalogue, resource)
180
+ @notifies.each do |ref|
181
+ unless notifies?(resource, catalogue.resource(ref))
182
+ @errors << NotifyRelationshipError.new(resource.to_ref, ref)
183
+ end
184
+ end
185
+ end
186
+
187
+ def check_subscribes(catalogue, resource)
188
+ @subscribes.each do |ref|
189
+ unless notifies?(catalogue.resource(ref), resource)
190
+ @errors << SubscribeRelationshipError.new(resource.to_ref, ref)
191
+ end
192
+ end
193
+ end
194
+
195
+ def relationship_refs(array)
196
+ Array[array].flatten.map do |resource|
197
+ resource.respond_to?(:to_ref) ? resource.to_ref : resource
198
+ end
199
+ end
200
+
201
+ def precedes?(first, second)
202
+ before_refs = relationship_refs(first[:before])
203
+ require_refs = relationship_refs(second[:require])
204
+
205
+ before_refs.include?(second.to_ref) || require_refs.include?(first.to_ref)
206
+ end
207
+
208
+ def notifies?(first, second)
209
+ notify_refs = relationship_refs(first[:notify])
210
+ subscribe_refs = relationship_refs(second[:subscribe])
211
+
212
+ notify_refs.include?(second.to_ref) || subscribe_refs.include?(first.to_ref)
213
+ end
214
+
215
+ # @param resource [Hash<Symbol, Object>] The resource in the catalog
216
+ # @param list [Array<String, Object>] The expected values of the resource
217
+ # @param type [:should, :not] Whether the given parameters should/not match
218
+ def check_params(resource, list, type)
219
+ list.each do |param, value|
220
+ param = param.to_sym
221
+
222
+ if value.nil? then
223
+ unless resource[param].nil?
224
+ @errors << "#{param} undefined"
225
+ end
226
+ else
227
+ m = ParameterMatcher.new(param, value, type)
228
+ unless m.matches?(resource)
229
+ @errors.concat m.errors
230
+ end
231
+ end
232
+ end
233
+ end
131
234
  end
132
235
  end
133
236
  end