rspec-puppet 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,10 +11,18 @@ module RSpec::Puppet
11
11
  end
12
12
 
13
13
  def message
14
- if negative == true
15
- "#{param} not set to #{expected.inspect} but it is set to #{actual.inspect}"
14
+ if @param.to_s == 'content' and expected.is_a?( String )
15
+ if negative == true
16
+ "#{param} not set to supplied string"
17
+ else
18
+ "#{param} set to supplied string"
19
+ end
16
20
  else
17
- "#{param} set to #{expected.inspect} but it is set to #{actual.inspect}"
21
+ if negative == true
22
+ "#{param} not set to #{expected.inspect} but it is set to #{actual.inspect}"
23
+ else
24
+ "#{param} set to #{expected.inspect} but it is set to #{actual.inspect}"
25
+ end
18
26
  end
19
27
  end
20
28
 
@@ -6,5 +6,9 @@ module RSpec::Puppet
6
6
  def catalogue
7
7
  @catalogue ||= load_catalogue(:class)
8
8
  end
9
+
10
+ def rspec_puppet_cleanup
11
+ @catalogue = nil
12
+ end
9
13
  end
10
14
  end
@@ -6,5 +6,9 @@ module RSpec::Puppet
6
6
  def catalogue
7
7
  @catalogue ||= load_catalogue(:define)
8
8
  end
9
+
10
+ def rspec_puppet_cleanup
11
+ @catalogue = nil
12
+ end
9
13
  end
10
14
  end
@@ -10,7 +10,7 @@ module RSpec::Puppet
10
10
  vardir = setup_puppet
11
11
 
12
12
  if Puppet.version.to_f >= 4.0
13
- env = Puppet::Node::Environment.create(environment, [File.join(Puppet[:environmentpath],'fixtures','modules')], File.join(Puppet[:environmentpath],'fixtures','manifests'))
13
+ env = build_4x_environment(environment)
14
14
  loader = Puppet::Pops::Loaders.new(env)
15
15
  func = loader.private_environment_loader.load(:function,function_name)
16
16
  return func if func
@@ -35,6 +35,12 @@ module RSpec::Puppet
35
35
  @catalogue ||= compiler.catalog
36
36
  end
37
37
 
38
+ def rspec_puppet_cleanup
39
+ @catalogue = nil
40
+ @compiler = nil
41
+ @scope = nil
42
+ end
43
+
38
44
  private
39
45
 
40
46
  def compiler
@@ -63,5 +69,31 @@ module RSpec::Puppet
63
69
  compiler.compile
64
70
  compiler
65
71
  end
72
+
73
+ def build_scope(compiler, node_name)
74
+ if Puppet.version =~ /^2\.[67]/
75
+ # loadall should only be necessary prior to 3.x
76
+ # Please note, loadall needs to happen first when creating a scope, otherwise
77
+ # you might receive undefined method `function_*' errors
78
+ Puppet::Parser::Functions.autoloader.loadall
79
+ scope = Puppet::Parser::Scope.new(:compiler => compiler)
80
+ else
81
+ scope = Puppet::Parser::Scope.new(compiler)
82
+ end
83
+
84
+ scope.source = Puppet::Resource::Type.new(:node, node_name)
85
+ scope.parent = compiler.topscope
86
+ scope
87
+ end
88
+
89
+ def build_node(name, opts = {})
90
+ if Puppet.version.to_f >= 4.0
91
+ node_environment = build_4x_environment(environment)
92
+ else
93
+ node_environment = Puppet::Node::Environment.new(environment)
94
+ end
95
+ opts.merge!({:environment => node_environment})
96
+ Puppet::Node.new(name, opts)
97
+ end
66
98
  end
67
99
  end
@@ -6,5 +6,9 @@ module RSpec::Puppet
6
6
  def catalogue
7
7
  @catalogue ||= load_catalogue(:host)
8
8
  end
9
+
10
+ def rspec_puppet_cleanup
11
+ @catalogue = nil
12
+ end
9
13
  end
10
14
  end
@@ -0,0 +1,4 @@
1
+ module RSpec::Puppet
2
+ module ProviderExampleGroup
3
+ end
4
+ end
@@ -0,0 +1,26 @@
1
+ module RSpec::Puppet
2
+ module TypeExampleGroup
3
+ include RSpec::Puppet::TypeMatchers
4
+ include RSpec::Puppet::Support
5
+
6
+ def subject
7
+ @type_and_resource ||= begin
8
+ setup_puppet
9
+ type_name = self.class.top_level_description.downcase
10
+ my_params = self.respond_to?(:params) ? params : {}
11
+ [
12
+ Puppet::Type.type(type_name),
13
+ # I don't want to create the resource here, so I have
14
+ # to pass all of the bits form the current scope
15
+ # required to create it
16
+ title,
17
+ my_params
18
+ ]
19
+ end
20
+ end
21
+
22
+ def rspec_puppet_cleanup
23
+ @type_and_resource = nil
24
+ end
25
+ end
26
+ end
@@ -3,8 +3,11 @@ require 'rspec-puppet/example/define_example_group'
3
3
  require 'rspec-puppet/example/class_example_group'
4
4
  require 'rspec-puppet/example/function_example_group'
5
5
  require 'rspec-puppet/example/host_example_group'
6
+ require 'rspec-puppet/example/type_example_group'
7
+ require 'rspec-puppet/example/provider_example_group'
6
8
 
7
9
  RSpec::configure do |c|
10
+
8
11
  def c.escaped_path(*parts)
9
12
  Regexp.compile(parts.join('[\\\/]'))
10
13
  end
@@ -21,11 +24,23 @@ RSpec::configure do |c|
21
24
  }
22
25
  c.include RSpec::Puppet::HostExampleGroup, :type => :host, :example_group => {
23
26
  :file_path => c.escaped_path(%w[spec hosts])
24
- }
27
+ }
28
+ c.include RSpec::Puppet::TypeExampleGroup, :type => :type, :example_group => {
29
+ :file_path => c.escaped_path(%w[spec types])
30
+ }
31
+ c.include RSpec::Puppet::ProviderExampleGroup, :type => :provider, :example_group => {
32
+ :file_path => c.escaped_path(%w[spec providers])
33
+ }
25
34
  else
26
35
  c.include RSpec::Puppet::DefineExampleGroup, :type => :define, :file_path => c.escaped_path(%w[spec defines])
27
36
  c.include RSpec::Puppet::ClassExampleGroup, :type => :class, :file_path => c.escaped_path(%w[spec classes])
28
37
  c.include RSpec::Puppet::FunctionExampleGroup, :type => :puppet_function, :file_path => c.escaped_path(%w[spec functions])
29
38
  c.include RSpec::Puppet::HostExampleGroup, :type => :host, :file_path => c.escaped_path(%w[spec hosts])
39
+ c.include RSpec::Puppet::TypeExampleGroup, :type => :type, :file_path => c.escaped_path(%w[spec types])
40
+ c.include RSpec::Puppet::ProviderExampleGroup, :type => :provider, :file_path => c.escaped_path(%w[spec providers])
30
41
  end
42
+
43
+ # Hook for each example group type to remove any caches or instance variables, since they will remain
44
+ # and cause a memory leak. Can't be assigned per type by :file_path, so check for its presence.
45
+ c.after(:each) { rspec_puppet_cleanup if respond_to?(:rspec_puppet_cleanup) }
31
46
  end
@@ -27,7 +27,7 @@ module RSpec::Puppet
27
27
  elsif @check_deps == true && missing_dependencies?
28
28
  false
29
29
  else
30
- true
30
+ @expected_error.nil?
31
31
  end
32
32
  rescue Puppet::Error => e
33
33
  @error_msg = e.message
@@ -58,13 +58,24 @@ module RSpec::Puppet
58
58
  unless @error_msg.empty?
59
59
  "error during compilation: #{@error_msg}"
60
60
  else
61
- "expected that the catalogue would include #{@failed_resource}"
61
+ case @expected_error
62
+ when nil
63
+ "expected that the catalogue would include #{@failed_resource}"
64
+ when Regexp
65
+ "expected that the catalogue would fail to compile and raise an error matching #{@expected_error.inspect}"
66
+ else
67
+ "expected that the catalogue would fail to compile and raise the error #{@expected_error.inspect}"
68
+ end
62
69
  end
63
70
  end
64
71
  end
65
72
 
66
73
  def failure_message_when_negated
67
- "expected that the catalogue would not compile but it does"
74
+ if @expected_error.nil?
75
+ "expected that the catalogue would not compile but it does"
76
+ else
77
+ "expected that the catalogue would compile but it does not"
78
+ end
68
79
  end
69
80
 
70
81
  private
@@ -189,7 +189,11 @@ module RSpec::Puppet
189
189
  else
190
190
  a = type == :not ? '!' : '='
191
191
  b = value.is_a?(Regexp) ? '~' : '>'
192
- output << "#{param.to_s} #{a}#{b} #{value.inspect}"
192
+ if param.to_s == 'content' and value.is_a?( String )
193
+ output << "#{param.to_s} #{type == :not ? 'not ' : ''} supplied string"
194
+ else
195
+ output << "#{param.to_s} #{a}#{b} #{value.inspect}"
196
+ end
193
197
  end
194
198
  end
195
199
  output
@@ -227,32 +231,84 @@ module RSpec::Puppet
227
231
  end
228
232
  end
229
233
 
230
- def relationship_refs(array)
231
- Array[array].flatten.map do |resource|
232
- resource.respond_to?(:to_ref) ? resource.to_ref : resource
234
+ def resource_ref(resource)
235
+ resource.respond_to?(:to_ref) ? resource.to_ref : resource
236
+ end
237
+
238
+ def resource_from_ref(ref)
239
+ ref.is_a?(Puppet::Resource) ? ref : @catalogue.resource(ref)
240
+ end
241
+
242
+ def canonicalize_resource(resource)
243
+ resource_from_ref(resource_ref(resource))
244
+ end
245
+
246
+ def canonicalize_resource_ref(ref)
247
+ resource_ref(resource_from_ref(ref))
248
+ end
249
+
250
+ def relationship_refs(resource, type)
251
+ resource = canonicalize_resource(resource)
252
+ results = []
253
+ return results unless resource
254
+ Array[resource[type]].flatten.compact.each do |r|
255
+ results << canonicalize_resource_ref(r)
256
+ results << relationship_refs(r, type)
233
257
  end
258
+
259
+ # Add autorequires if any
260
+ if type == :require and resource.resource_type.respond_to? :eachautorequire
261
+ resource.resource_type.eachautorequire do |t, b|
262
+ Array(resource.to_ral.instance_eval(&b)).each do |dep|
263
+ res = "#{t.to_s.capitalize}[#{dep}]"
264
+ if r = relationship_refs(res, type)
265
+ results << res
266
+ results << r
267
+ end
268
+ end
269
+ end
270
+ end
271
+ results.flatten
272
+ end
273
+
274
+ def self_or_upstream(vertex)
275
+ [vertex] + @catalogue.upstream_from_vertex(vertex).keys
234
276
  end
235
277
 
236
278
  def precedes?(first, second)
237
- if first.nil? || second.nil?
238
- false
239
- else
240
- before_refs = relationship_refs(first[:before])
241
- require_refs = relationship_refs(second[:require])
279
+ return false if first.nil? || second.nil?
280
+
281
+ self_or_upstream(first).each do |u|
282
+ self_or_upstream(second).each do |v|
283
+ before_refs = relationship_refs(u, :before)
284
+ require_refs = relationship_refs(v, :require)
242
285
 
243
- before_refs.include?(second.to_ref) || require_refs.include?(first.to_ref)
286
+ if before_refs.include?(v.to_ref) || require_refs.include?(u.to_ref) || (before_refs & require_refs).any?
287
+ return true
288
+ end
289
+ end
244
290
  end
291
+
292
+ # Nothing found
293
+ return false
245
294
  end
246
295
 
247
296
  def notifies?(first, second)
248
- if first.nil? || second.nil?
249
- false
250
- else
251
- notify_refs = relationship_refs(first[:notify])
252
- subscribe_refs = relationship_refs(second[:subscribe])
297
+ return false if first.nil? || second.nil?
298
+
299
+ self_or_upstream(first).each do |u|
300
+ self_or_upstream(second).each do |v|
301
+ notify_refs = relationship_refs(u, :notify)
302
+ subscribe_refs = relationship_refs(v, :subscribe)
253
303
 
254
- notify_refs.include?(second.to_ref) || subscribe_refs.include?(first.to_ref)
304
+ if notify_refs.include?(v.to_ref) || subscribe_refs.include?(u.to_ref)
305
+ return true
306
+ end
307
+ end
255
308
  end
309
+
310
+ # Nothing found
311
+ return false
256
312
  end
257
313
 
258
314
  # @param resource [Hash<Symbol, Object>] The resource in the catalog
@@ -14,4 +14,11 @@ module RSpec::Puppet
14
14
  super
15
15
  end
16
16
  end
17
+
18
+ module TypeMatchers
19
+ def method_missing(method, *args, &block)
20
+ return RSpec::Puppet::TypeMatchers::CreateGeneric.new(method, *args, &block) if method == :be_valid_type
21
+ super
22
+ end
23
+ end
17
24
  end
@@ -4,7 +4,7 @@ module RSpec::Puppet
4
4
 
5
5
  matcher :include_class do |expected_class|
6
6
  match do |catalogue|
7
- RSpec.deprecate(:include_class, :contain_class)
7
+ RSpec.deprecate(:include_class, :replacement => :contain_class)
8
8
  catalogue.call.classes.include?(expected_class)
9
9
  end
10
10
 
@@ -17,6 +17,7 @@ module RSpec::Puppet
17
17
  end
18
18
  end
19
19
 
20
+ @has_returned = false
20
21
  begin
21
22
  @actual_return = @func.call
22
23
  @has_returned = true
@@ -32,7 +33,7 @@ module RSpec::Puppet
32
33
  when nil
33
34
  return true
34
35
  when Regexp
35
- return @actual_error.message =~ @expected_error_message
36
+ return !!(@actual_error.message =~ @expected_error_message)
36
37
  else
37
38
  return @actual_error.message == @expected_error_message
38
39
  end
@@ -45,7 +46,7 @@ module RSpec::Puppet
45
46
  else
46
47
  case @expected_return
47
48
  when Regexp
48
- return @actual_return =~ @expected_return
49
+ return !!(@actual_return =~ @expected_return)
49
50
  else
50
51
  return @actual_return == @expected_return
51
52
  end
@@ -0,0 +1,161 @@
1
+ module RSpec::Puppet
2
+ module TypeMatchers
3
+
4
+ class CreateGeneric
5
+
6
+ def initialize(*args, &block)
7
+
8
+ @exp_provider = nil
9
+ @exp_parameters = []
10
+ @exp_properties = []
11
+ @exp_features = []
12
+ @exp_defaults = {}
13
+ @params_with_values = {}
14
+ @errors = []
15
+ end
16
+
17
+ # specifies a provider to validate
18
+ def with_provider(name)
19
+ @exp_provider = name
20
+ self
21
+ end
22
+
23
+ # ensures the listed properties are valid
24
+ def with_properties(props)
25
+ @exp_properties = @exp_properties | Array(props)
26
+ self
27
+ end
28
+
29
+ # ensures the listed parameters are valid
30
+ def with_parameters(params)
31
+ @exp_parameters = @exp_parameters | Array(params)
32
+ self
33
+ end
34
+
35
+ # ensure the type has the list of features
36
+ def with_features(features)
37
+ @exp_features = @exp_features | Array(features)
38
+ self
39
+ end
40
+
41
+ #
42
+ # ensures that the specified parameters with their values
43
+ # results in a valid resource
44
+ #
45
+ def with_set_attributes(params)
46
+ @params_with_values.merge!(params)
47
+ self
48
+ end
49
+
50
+ def with_defaults(defaults_hash)
51
+ @exp_defaults.merge!(defaults_hash)
52
+ self
53
+ end
54
+
55
+ #def with_autorequires(autorequires))
56
+ #end
57
+
58
+ #
59
+ # this is the method that drives all of the validation
60
+ #
61
+ def matches?(type_title_and_params)
62
+ type = type_title_and_params[0]
63
+ title = type_title_and_params[1]
64
+ params = type_title_and_params[2]
65
+ unless match_params(type) && match_props(type) && match_features(type)
66
+ return false
67
+ end
68
+ if @params_with_values != {} || @exp_provider
69
+ # only build a resource if we are validating provider or setting
70
+ # additional parameters
71
+ resource = be_valid_resource(type, title, params.merge(@params_with_values))
72
+ match_default_provider(resource) and match_default_values(resource)
73
+ else
74
+ true
75
+ end
76
+ end
77
+
78
+ # checks that the specified params exist
79
+ def match_params(type)
80
+ match_attrs(type, @exp_parameters, :parameter)
81
+ end
82
+
83
+ # checks that the specified properties exist
84
+ def match_props(type)
85
+ match_attrs(type, @exp_properties, :property)
86
+ end
87
+
88
+ # checks that the specified features exist
89
+ def match_features(type)
90
+ match_attrs(type, @exp_features, :feature)
91
+ end
92
+
93
+ # builds the resource with the specified param values
94
+ def be_valid_resource(type, title, params)
95
+ params[:name] ||= title
96
+ type.new(params)
97
+ end
98
+
99
+ #
100
+ # checks that the expected provider is set
101
+ #
102
+ def match_default_provider(resource)
103
+ if @exp_provider
104
+ if resource[:provider] == @exp_provider
105
+ return true
106
+ else
107
+ @errors.push("Expected provider: #{@exp_provider} does not match: #{resource[:provider]}")
108
+ return false
109
+ end
110
+ else
111
+ return true
112
+ end
113
+ end
114
+
115
+ def match_default_values(resource)
116
+ # TODO FINISH
117
+ true
118
+ end
119
+
120
+ def description
121
+ "be a valid type"
122
+ end
123
+
124
+ def failure_message
125
+ "Not a valid type #{@errors.inspect}"
126
+ end
127
+
128
+ private
129
+
130
+ def match_attrs(type, attrs, attr_type)
131
+ baddies = []
132
+ attrs.each do |param|
133
+ param = param.to_sym
134
+ if attr_type == :feature
135
+ unless type.provider_feature(param)
136
+ baddies.push(param)
137
+ end
138
+ elsif ! type.send("valid#{attr_type}?".to_sym, param)
139
+ baddies.push(param)
140
+ end
141
+ end
142
+ if baddies.size > 0
143
+ @errors.push("Invalid #{pluralize(attr_type)}: #{baddies.join(',')}")
144
+ false
145
+ else
146
+ true
147
+ end
148
+ end
149
+
150
+ def pluralize(name)
151
+ if name == :property
152
+ "properties"
153
+ else
154
+ "#{name}s"
155
+ end
156
+ end
157
+
158
+ end
159
+
160
+ end
161
+ end
@@ -4,3 +4,4 @@ require 'rspec-puppet/matchers/compile'
4
4
  require 'rspec-puppet/matchers/run'
5
5
  require 'rspec-puppet/matchers/count_generic'
6
6
  require 'rspec-puppet/matchers/dynamic_matchers'
7
+ require 'rspec-puppet/matchers/type_matchers'
@@ -0,0 +1,20 @@
1
+ require 'rake'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run all RSpec code examples"
5
+ RSpec::Core::RakeTask.new(:rspec) do |t|
6
+ File.exist?('spec/spec.opts') ? opts = File.read("spec/spec.opts").chomp : opts = ""
7
+ t.rspec_opts = opts
8
+ end
9
+
10
+ SPEC_SUITES = (Dir.entries('spec') - ['.', '..','fixtures']).select {|e| File.directory? "spec/#{e}" }
11
+ namespace :rspec do
12
+ SPEC_SUITES.each do |suite|
13
+ desc "Run #{suite} RSpec code examples"
14
+ RSpec::Core::RakeTask.new(suite) do |t|
15
+ t.pattern = "spec/#{suite}/**/*_spec.rb"
16
+ File.exist?('spec/spec.opts') ? opts = File.read("spec/spec.opts").chomp : opts = ""
17
+ t.rspec_opts = opts
18
+ end
19
+ end
20
+ end
@@ -55,6 +55,8 @@ module RSpec::Puppet
55
55
  end
56
56
 
57
57
  def self.get_module_name_from_file(file)
58
+ # FIXME: see discussion at
59
+ # https://github.com/rodjek/rspec-puppet/issues/290
58
60
  if Puppet.version.to_f >= 4.0
59
61
  p = Puppet::Pops::Parser::Lexer2.new
60
62
  else
@@ -113,18 +115,8 @@ module RSpec::Puppet
113
115
  end
114
116
 
115
117
  def self.safe_create_spec_helper
116
- content = <<-EOF
117
- require 'rspec-puppet'
118
-
119
- fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures'))
120
-
121
- RSpec.configure do |c|
122
- c.module_path = File.join(fixture_path, 'modules')
123
- c.manifest_dir = File.join(fixture_path, 'manifests')
124
- c.environmentpath = File.join(Dir.pwd, 'spec')
125
- end
126
- EOF
127
- safe_create_file('spec/spec_helper.rb', content)
118
+ content = "require 'rspec-puppet/spec_helper'\n"
119
+ safe_create_file('spec/spec_helper.rb', content)
128
120
  end
129
121
 
130
122
  def self.safe_make_symlink(source, target)
@@ -140,27 +132,7 @@ EOF
140
132
 
141
133
  def self.safe_create_rakefile
142
134
  content = <<-'EOF'
143
- require 'rake'
144
- require 'rspec/core/rake_task'
145
-
146
- desc "Run all RSpec code examples"
147
- RSpec::Core::RakeTask.new(:rspec) do |t|
148
- File.exist?('spec/spec.opts') ? opts = File.read("spec/spec.opts").chomp : opts = ""
149
- t.rspec_opts = opts
150
- end
151
-
152
- SPEC_SUITES = (Dir.entries('spec') - ['.', '..','fixtures']).select {|e| File.directory? "spec/#{e}" }
153
- namespace :rspec do
154
- SPEC_SUITES.each do |suite|
155
- desc "Run #{suite} RSpec code examples"
156
- RSpec::Core::RakeTask.new(suite) do |t|
157
- t.pattern = "spec/#{suite}/**/*_spec.rb"
158
- File.exist?('spec/spec.opts') ? opts = File.read("spec/spec.opts").chomp : opts = ""
159
- t.rspec_opts = opts
160
- end
161
- end
162
- end
163
- task :default => :rspec
135
+ require 'rspec-puppet/rake_task'
164
136
 
165
137
  begin
166
138
  if Gem::Specification::find_by_name('puppet-lint')
@@ -169,9 +141,10 @@ begin
169
141
  task :default => [:rspec, :lint]
170
142
  end
171
143
  rescue Gem::LoadError
144
+ task :default => :rspec
172
145
  end
173
146
  EOF
174
- safe_create_file('Rakefile', content)
147
+ safe_create_file('Rakefile', content)
175
148
  end
176
149
  end
177
150
  end
@@ -0,0 +1,9 @@
1
+ require 'rspec-puppet'
2
+
3
+ fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures'))
4
+
5
+ RSpec.configure do |c|
6
+ c.module_path = File.join(fixture_path, 'modules')
7
+ c.manifest_dir = File.join(fixture_path, 'manifests')
8
+ c.environmentpath = File.join(Dir.pwd, 'spec')
9
+ end