rspec-puppet 0.1.6 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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