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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fc2b092d4a84ad47ceff22b0123d01b8171ff612
4
+ data.tar.gz: eb32c18c64ad5ae96e69f53fc8d2999665784f24
5
+ SHA512:
6
+ metadata.gz: 4f6909ee0c338d72e1251590bccc3d6a5b34fcc8279158ce1a39a26f3905396e3e6a06284cb92392c7b7aecf8f4c3b7e1a5960189ab3f5d7796afe3c131f49e1
7
+ data.tar.gz: ffd7a6073b9afc2d660d061c42450381ff631c77df9f953289c62875e8f12aa5d05889f7a23eafd01a9741e931b6007da82fc17a5cb76538090adcb42ce7b541
data/README.md CHANGED
@@ -96,6 +96,13 @@ the generic `with_<parameter>` chains.
96
96
  it { should contain_package('mysql-server').with_ensure('present') }
97
97
  ```
98
98
 
99
+ If you want to specify that the given parameters should be the only ones passed
100
+ to the resource, use the `only_with_<parameter>` chains.
101
+
102
+ ```ruby
103
+ it { should contain_package('httpd').only_with_ensure('latest') }
104
+ ```
105
+
99
106
  You can use the `with` method to verify the value of multiple parameters.
100
107
 
101
108
  ```ruby
@@ -107,6 +114,16 @@ it do should contain_service('keystone').with(
107
114
  ) end
108
115
  ```
109
116
 
117
+ The same holds for the `only_with` method, which in addition verifies the exact
118
+ set of parameters and values for the resource in the catalogue.
119
+
120
+ ```ruby
121
+ it do should contain_user('luke').only_with(
122
+ 'ensure' => 'present',
123
+ 'uid' => '501'
124
+ ) end
125
+ ```
126
+
110
127
  You can also test that specific parameters have been left undefined with the
111
128
  generic `without_<parameter>` chains.
112
129
 
@@ -123,6 +140,41 @@ it { should contain_service('keystone').without(
123
140
  )}
124
141
  ```
125
142
 
143
+ #### Checking the number of resources
144
+
145
+ You can test the number of resources in the catalogue with the
146
+ `have_resource_count` matcher.
147
+
148
+ ```ruby
149
+ it { should have_resource_count(2) }
150
+ ```
151
+
152
+ The number of classes in the catalogue can be checked with the
153
+ `have_class_count` matcher.
154
+
155
+ ```ruby
156
+ it { should have_class_count(2) }
157
+ ```
158
+
159
+ You can also test the number of a specific resource type, by using the generic
160
+ `have_<resource type>_resource_count` matcher.
161
+
162
+ ```ruby
163
+ it { should have_exec_resource_count(1) }
164
+ ```
165
+
166
+ This last matcher also works for defined types. If the resource type contains
167
+ ::, you can replace it with __ (two underscores).
168
+
169
+ ```ruby
170
+ it { should have_logrotate__rule_resource_count(3) }
171
+ ```
172
+
173
+ *NOTE*: when testing a class, the catalogue generated will always contain at
174
+ least one class, the class under test. The same holds for defined types, the
175
+ catalogue generated when testing a defined type will have at least one resource
176
+ (the defined type itself).
177
+
126
178
  ### Writing tests
127
179
 
128
180
  #### Basic test structure
@@ -180,6 +232,19 @@ You can set them with a hash
180
232
  let(:facts) { {:operatingsystem => 'Debian', :kernel => 'Linux', ...} }
181
233
  ```
182
234
 
235
+ You can also create a set of default facts provided to all specs in your spec_helper:
236
+
237
+ ``` ruby
238
+ RSpec.configure do |c|
239
+ c.default_facts = {
240
+ :operatingsystem => 'Ubuntu'
241
+ }
242
+ end
243
+ ```
244
+
245
+ Any facts you provide with `let(:facts)` in a spec will automatically be merged on top
246
+ of the default facts.
247
+
183
248
  #### Specifying the path to find your modules
184
249
 
185
250
  I recommend setting a default module path by adding the following code to your
@@ -206,7 +271,7 @@ Puppet functions.
206
271
 
207
272
  ```ruby
208
273
  it 'should be able to do something' do
209
- subject.call('foo') == 'bar'
274
+ subject.call(['foo']) == 'bar'
210
275
  end
211
276
  ```
212
277
 
@@ -250,7 +315,7 @@ Or by using the `call` method on the subject directly
250
315
 
251
316
  ```ruby
252
317
  it 'something' do
253
- subject.call('foo', 'bar', ['baz'])
318
+ subject.call(['foo', 'bar', ['baz']])
254
319
  end
255
320
  ```
256
321
 
@@ -267,8 +332,8 @@ Or by using any of the existing RSpec matchers on the subject directly
267
332
 
268
333
  ```ruby
269
334
  it 'something' do
270
- subject.call('foo') == 'bar'
271
- subject.call('baz').should be_an Array
335
+ subject.call(['foo']) == 'bar'
336
+ subject.call(['baz']).should be_an Array
272
337
  end
273
338
  ```
274
339
 
@@ -286,7 +351,7 @@ Or by using the existing `raises_error` RSpec matcher
286
351
 
287
352
  ```ruby
288
353
  it 'something' do
289
- expect { subject.call('a', 'b') }.should raise_error(Puppet::ParseError)
290
- expect { subject.call('a') }.should_not raise_error(Puppet::ParseError)
354
+ expect { subject.call(['a', 'b']) }.should raise_error(Puppet::ParseError)
355
+ expect { subject.call(['a']) }.should_not raise_error(Puppet::ParseError)
291
356
  end
292
357
  ```
data/lib/rspec-puppet.rb CHANGED
@@ -2,14 +2,59 @@ require 'puppet'
2
2
  require 'rspec'
3
3
  require 'fileutils'
4
4
  require 'tmpdir'
5
+ require 'rspec-puppet/errors'
5
6
  require 'rspec-puppet/matchers'
6
7
  require 'rspec-puppet/example'
7
8
  require 'rspec-puppet/setup'
8
9
 
10
+ begin
11
+ require 'puppet/test/test_helper'
12
+ rescue LoadError
13
+ end
14
+
9
15
  RSpec.configure do |c|
10
16
  c.add_setting :module_path, :default => '/etc/puppet/modules'
11
17
  c.add_setting :manifest_dir, :default => nil
12
18
  c.add_setting :manifest, :default => nil
13
19
  c.add_setting :template_dir, :default => nil
14
20
  c.add_setting :config, :default => nil
21
+ c.add_setting :confdir, :default => '/etc/puppet'
22
+ c.add_setting :default_facts, :default => {}
23
+ c.add_setting :hiera_config, :default => '/dev/null'
24
+
25
+ if defined?(Puppet::Test::TestHelper)
26
+ begin
27
+ Puppet::Test::TestHelper.initialize
28
+ rescue NoMethodError
29
+ Puppet::Test::TestHelper.before_each_test
30
+ end
31
+
32
+ c.before :all do
33
+ begin
34
+ Puppet::Test::TestHelper.before_all_tests
35
+ rescue
36
+ end
37
+ end
38
+
39
+ c.after :all do
40
+ begin
41
+ Puppet::Test::TestHelper.after_all_tests
42
+ rescue
43
+ end
44
+ end
45
+
46
+ c.before :each do
47
+ begin
48
+ Puppet::Test::TestHelper.before_each_test
49
+ rescue
50
+ end
51
+ end
52
+
53
+ c.after :each do
54
+ begin
55
+ Puppet::Test::TestHelper.after_each_test
56
+ rescue
57
+ end
58
+ end
59
+ end
15
60
  end
@@ -0,0 +1,83 @@
1
+ module RSpec::Puppet
2
+ module Errors
3
+ class MatchError < StandardError
4
+ attr_reader :param, :expected, :actual, :negative
5
+
6
+ def initialize(param, expected, actual, negative)
7
+ @param = param
8
+ @expected = expected.inspect
9
+ @actual = actual.inspect
10
+ @negative = negative
11
+ end
12
+
13
+ def message
14
+ if negative == true
15
+ "#{param} not set to #{expected} but it is set to #{actual}"
16
+ else
17
+ "#{param} set to #{expected} but it is set to #{actual}"
18
+ end
19
+ end
20
+
21
+ def to_s
22
+ message
23
+ end
24
+ end
25
+
26
+ class RegexpMatchError < MatchError
27
+ def message
28
+ if negative == true
29
+ "#{param} not matching #{expected} but its value of #{actual} does"
30
+ else
31
+ "#{param} matching #{expected} but its value of #{actual} does not"
32
+ end
33
+ end
34
+ end
35
+
36
+ class ProcMatchError < MatchError
37
+ def message
38
+ if negative == true
39
+ "#{param} passed to the block would not return `#{expected}` but it did"
40
+ else
41
+ "#{param} passed to the block would return `#{expected}` but it is `#{actual}`"
42
+ end
43
+ end
44
+ end
45
+
46
+ class RelationshipError < StandardError
47
+ attr_reader :from, :to
48
+
49
+ def initialize(from, to)
50
+ @from = from
51
+ @to = to
52
+ end
53
+
54
+ def to_s
55
+ message
56
+ end
57
+ end
58
+
59
+ class BeforeRelationshipError < RelationshipError
60
+ def message
61
+ "#{from} to come before #{to} in the graph"
62
+ end
63
+ end
64
+
65
+ class RequireRelationshipError < RelationshipError
66
+ def message
67
+ "#{from} to require #{to} in the graph"
68
+ end
69
+ end
70
+
71
+ class NotifyRelationshipError < RelationshipError
72
+ def message
73
+ "#{from} to notify #{to}"
74
+ end
75
+ end
76
+
77
+ class SubscribeRelationshipError < RelationshipError
78
+ def message
79
+ "#{from} to be subscribed to #{to}"
80
+ end
81
+ end
82
+ end
83
+ end
@@ -4,61 +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
-
20
- klass_name = self.class.top_level_description.downcase
21
-
22
- # If we're testing a standalone module (i.e. one that's outside of a
23
- # puppet tree), the autoloader won't work, so we need to fudge it a bit.
24
- if File.exists?(File.join(Puppet[:modulepath], 'manifests', 'init.pp'))
25
- path_to_manifest = File.join([Puppet[:modulepath], 'manifests', klass_name.split('::')[1..-1]].flatten)
26
- import_str = "import '#{Puppet[:modulepath]}/manifests/init.pp'\nimport '#{path_to_manifest}.pp'\n"
27
- elsif File.exists?(Puppet[:modulepath])
28
- import_str = "import '#{Puppet[:manifest]}'\n"
29
- else
30
- import_str = ""
31
- end
32
-
33
- if self.respond_to? :pre_condition
34
- if pre_condition.kind_of?(Array)
35
- pre_cond = pre_condition.join("\n")
36
- else
37
- pre_cond = pre_condition
38
- end
39
- else
40
- pre_cond = ''
41
- end
42
-
43
- if !self.respond_to?(:params) || params == {}
44
- code = import_str + "include #{klass_name}"
45
- else
46
- code = import_str + 'class' + " { \"" + klass_name + "\": " + params.keys.map { |r| "#{r.to_s} => #{params[r].inspect}"
47
- }.join(',' ) + " }"
48
- end
49
- code = pre_cond + "\n" + code
50
-
51
- nodename = self.respond_to?(:node) ? node : Puppet[:certname]
52
- facts_val = {
53
- 'hostname' => nodename.split('.').first,
54
- 'fqdn' => nodename,
55
- 'domain' => nodename.split('.').last,
56
- }
57
- facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
58
-
59
- catalogue = build_catalog(nodename, facts_val, code)
60
- FileUtils.rm_rf(vardir) if File.directory?(vardir)
61
- catalogue
7
+ @catalogue ||= catalogue(:class)
62
8
  end
63
9
  end
64
10
  end
@@ -4,63 +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
- define_name = self.class.top_level_description.downcase
12
-
13
- vardir = Dir.mktmpdir
14
- Puppet[:vardir] = vardir
15
- Puppet[:hiera_config] = File.join(vardir, "hiera.yaml") if Puppet[:hiera_config] == File.expand_path("/dev/null")
16
- Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
17
- Puppet[:manifestdir] = self.respond_to?(:manifest_dir) ? manifest_dir : RSpec.configuration.manifest_dir
18
- Puppet[:manifest] = self.respond_to?(:manifest) ? manifest : RSpec.configuration.manifest
19
- Puppet[:templatedir] = self.respond_to?(:template_dir) ? template_dir : RSpec.configuration.template_dir
20
- Puppet[:config] = self.respond_to?(:config) ? config : RSpec.configuration.config
21
-
22
- # If we're testing a standalone module (i.e. one that's outside of a
23
- # puppet tree), the autoloader won't work, so we need to fudge it a bit.
24
- if File.exists?(File.join(Puppet[:modulepath], 'manifests', 'init.pp'))
25
- path_to_manifest = File.join([Puppet[:modulepath], 'manifests', define_name.split('::')[1..-1]].flatten)
26
- import_str = "import '#{Puppet[:modulepath]}/manifests/init.pp'\nimport '#{path_to_manifest}.pp'\n"
27
- elsif File.exists?(Puppet[:modulepath])
28
- import_str = "import '#{Puppet[:manifest]}'\n"
29
- else
30
- import_str = ""
31
- end
32
-
33
- if self.respond_to? :params
34
- param_str = params.keys.map { |r|
35
- "#{r.to_s} => #{params[r].inspect}"
36
- }.join(', ')
37
- else
38
- param_str = ""
39
- end
40
-
41
- if self.respond_to? :pre_condition
42
- if pre_condition.kind_of?(Array)
43
- pre_cond = pre_condition.join("\n")
44
- else
45
- pre_cond = pre_condition
46
- end
47
- else
48
- pre_cond = ""
49
- end
50
-
51
- code = pre_cond + "\n" + import_str + define_name + " { \"" + title + "\": " + param_str + " }"
52
-
53
- nodename = self.respond_to?(:node) ? node : Puppet[:certname]
54
- facts_val = {
55
- 'hostname' => nodename.split('.').first,
56
- 'fqdn' => nodename,
57
- 'domain' => nodename.split('.', 2).last,
58
- }
59
- facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
60
-
61
- catalogue = build_catalog(nodename, facts_val, code)
62
- FileUtils.rm_rf(vardir) if File.directory?(vardir)
63
- catalogue
7
+ @catalogue ||= catalogue(:define)
64
8
  end
65
9
  end
66
10
  end
@@ -1,58 +1,44 @@
1
- require 'puppetlabs_spec_helper/puppetlabs_spec/puppet_internals'
2
-
3
1
  module RSpec::Puppet
4
2
  module FunctionExampleGroup
5
3
  include RSpec::Puppet::FunctionMatchers
6
- PuppetInternals = PuppetlabsSpec::PuppetInternals
4
+ include RSpec::Puppet::ManifestMatchers
5
+ include RSpec::Puppet::Support
7
6
 
8
7
  def subject
9
8
  function_name = self.class.top_level_description.downcase
10
9
 
11
- Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
12
- Puppet[:libdir] = Dir["#{Puppet[:modulepath]}/*/lib"].entries.join(File::PATH_SEPARATOR)
10
+ vardir = setup_puppet
13
11
 
14
- nodename = self.respond_to?(:node) ? node : Puppet[:certname]
15
- facts_val = {
16
- 'hostname' => nodename.split('.').first,
17
- 'fqdn' => nodename,
18
- 'domain' => nodename.split('.').last,
19
- }
20
- facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
21
- facts_val.each { |k, v| Facter.add(k) { setcode { v } } }
12
+ node_name = nodename(:function)
13
+
14
+ facts_val = facts_hash(node_name)
22
15
 
23
16
  # if we specify a pre_condition, we should ensure that we compile that code
24
17
  # into a catalog that is accessible from the scope where the function is called
25
- if self.respond_to? :pre_condition
26
- if pre_condition.kind_of?(Array)
27
- Puppet[:code] = pre_condition.join("\n")
28
- else
29
- Puppet[:code] = pre_condition
30
- end
31
- # we need to get a compiler, b/c we can attach that to a scope
32
- @compiler = build_compiler(nodename, facts_val)
33
- else
34
- @compiler = PuppetInternals.compiler
35
- end
36
-
37
- scope = PuppetInternals.scope(:compiler => @compiler)
18
+ Puppet[:code] = pre_cond
19
+
20
+ compiler = build_compiler(node_name, facts_val)
21
+
22
+ function_scope = scope(compiler, node_name)
38
23
 
39
24
  # Return the method instance for the function. This can be used with
40
25
  # method.call
41
- method = PuppetInternals.function_method(function_name, :scope => scope)
42
- end
43
-
44
- def compiler
45
- @compiler
26
+ return nil unless Puppet::Parser::Functions.function(function_name)
27
+ FileUtils.rm_rf(vardir) if File.directory?(vardir)
28
+ function_scope.method("function_#{function_name}".intern)
46
29
  end
47
30
 
48
31
  # get a compiler with an attached compiled catalog
49
32
  def build_compiler(node_name, fact_values)
50
33
  node_options = {
51
- :name => node_name,
52
- :options => { :parameters => fact_values },
34
+ :parameters => fact_values,
53
35
  }
54
- node = PuppetInternals.node(node_options)
55
- compiler = PuppetInternals.compiler(:node => node)
36
+
37
+ stub_facts! fact_values
38
+
39
+ node = build_node(node_name, node_options)
40
+
41
+ compiler = Puppet::Parser::Compiler.new(node)
56
42
  compiler.compile
57
43
  compiler
58
44
  end