rspec-puppet 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9af746954c13b2c0fa5039fa066faef84c28e846
4
- data.tar.gz: 3616d46484994eabad338decfdb9c0b8b83cf48f
3
+ metadata.gz: ddac12d8bb2638b92b4d2f009511b2d5bc144305
4
+ data.tar.gz: 2196abf53f49f3ece60c2c615fdf00214a768948
5
5
  SHA512:
6
- metadata.gz: d86702faa57cca90162ed80d8a39bc2b5bc57452b7daa6defa507c1cd51bddd8e7a44e1a1bebb718997efdb3e1059625c3d14580762d611be6fe42a3680826e2
7
- data.tar.gz: 530a52d04ef1465361c34b5970d0b0da3f99742e0bd4867f9fd8ffbb7aa8d0fc4c124c890f0a384c003231c52ffaa17c17ac583aa684ea051a236571041cb73b
6
+ metadata.gz: 4449c6a9b781f5e8df545de27102e00c1034f7db550559b4b4fcf0aee75845de6e635f0ff87f658f713da7e1702054d69f257a941ace132011f6bd8732d11b47
7
+ data.tar.gz: 23bbc5facc62afbbf4f9b6c34d8ba9fe7260977790e07472bd7d338f1c3bdf282cea2b79d19a4555082b6df3c79240ac7051fc074d3ff0ede6cccfa257afd2e2
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RSpec tests for your Puppet manifests & modules
1
+ # RSpec tests for your Puppet manifests & modules [![Build Status](https://travis-ci.org/rodjek/rspec-puppet.png)](https://travis-ci.org/rodjek/rspec-puppet)
2
2
 
3
3
  ## Installation
4
4
 
@@ -172,6 +172,46 @@ least one class, the class under test. The same holds for defined types, the
172
172
  catalogue generated when testing a defined type will have at least one resource
173
173
  (the defined type itself).
174
174
 
175
+ #### Relationship matchers
176
+
177
+ The following methods will allow you to test the relationships between the resources in your catalogue, regardless of how the relationship is defined. This means that it doesn’t matter if you prefer to define your relationships with the metaparameters (**require**, **before**, **notify** and **subscribe**) or the chaining arrows (**->**, **~>**, **<-** and **<~**), they’re all tested the same.
178
+
179
+ ```ruby
180
+ it { should contain_file('foo').that_requires('File[bar]') }
181
+ it { should contain_file('foo').that_comes_before('File[bar]') }
182
+ it { should contain_file('foo').that_notifies('File[bar]') }
183
+ it { should contain_file('foo').that_subscribes_to('File[bar]') }
184
+ ```
185
+
186
+ An array can be used to test a resource for multiple relationships
187
+
188
+ ```ruby
189
+ it { should contain_file('foo').that_requires(['File[bar]', 'File[baz]']) }
190
+ it { should contain_file('foo').that_comes_before(['File[bar]','File[baz]']) }
191
+ it { should contain_file('foo').that_notifies(['File[bar]', 'File[baz]']) }
192
+ it { should contain_file('foo').that_subscribes_to(['File[bar]', 'File[baz]']) }
193
+ ```
194
+
195
+ You can also test the reverse direction of the relationship, so if you have the following bit of Puppet code
196
+
197
+ ```ruby
198
+ notify { 'foo': }
199
+ notify { 'bar':
200
+ before => Notify['foo'],
201
+ }
202
+ ```
203
+
204
+ You can test that **Notify[bar]** comes before **Notify[foo]**
205
+
206
+ ```ruby
207
+ it { should contain_notify('bar').that_comes_before('Notify[foo]') }
208
+ ```
209
+ Or, you can test that **Notify[foo]** requires **Notify[bar]**
210
+
211
+ ```ruby
212
+ it { should contain_notify('foo').that_requires('Notify[bar]') }
213
+ ```
214
+
175
215
  ### Writing tests
176
216
 
177
217
  #### Basic test structure
@@ -352,3 +392,69 @@ it 'something' do
352
392
  expect { subject.call(['a']) }.should_not raise_error(Puppet::ParseError)
353
393
  end
354
394
  ```
395
+
396
+ ## Hiera integration
397
+
398
+ ### Configuration
399
+
400
+ Set the hiera config symbol properly in your spec files:
401
+
402
+ ```ruby
403
+ let(:hiera_config) { 'spec/fixtures/hiera/hiera.yaml' }
404
+ hiera = Hiera.new(:config => 'spec/fixtures/hiera/hiera.yaml')
405
+ ```
406
+
407
+ Create your spec hiera files
408
+
409
+ spec/fixtures/hiera/hiera.yaml
410
+ ```ruby
411
+ ---
412
+ :backends:
413
+ - yaml
414
+ :hierarchy:
415
+ - test
416
+ :yaml:
417
+ :datadir: 'spec/fixtures/hiera'
418
+ ```
419
+
420
+ spec/fixtures/hiera/test.yaml
421
+ ```ruby
422
+ ---
423
+ ntpserver: ['ntp1.domain.com','ntpXX.domain.com']
424
+ user:
425
+ oneuser:
426
+ shell: '/bin/bash'
427
+ twouser:
428
+ shell: '/sbin/nologin'
429
+ ```
430
+
431
+ ### Use hiera in your tests
432
+
433
+ ```ruby
434
+ ntpserver = hiera.lookup('ntpserver', nil, nil)
435
+ let(:params) { :ntpserver => ntpserver }
436
+ ```
437
+
438
+ ### Enabling hiera lookups
439
+ If you just want to fetch values from hiera (e.g. because
440
+ you're testing code that uses explicit hiera lookups) just specify
441
+ the path to the hiera config in your `spec_helper.rb`
442
+
443
+ ```ruby
444
+ RSpec.configure do |c|
445
+ c.hiera_config = 'spec/fixtures/hiera/hiera.yaml'
446
+ end
447
+ ```
448
+
449
+ ## Producing coverage reports
450
+
451
+ You can output a basic resource coverage report with the following in
452
+ you spec file.
453
+
454
+ ```ruby
455
+ at_exit { RSpec::Puppet::Coverage.report! }
456
+ ```
457
+
458
+ This checks which Puppet resources have been explicitly checked as part
459
+ of the current test run and outputs both a coverage percentage and a
460
+ list of untouched resources.
@@ -6,6 +6,7 @@ require 'rspec-puppet/errors'
6
6
  require 'rspec-puppet/matchers'
7
7
  require 'rspec-puppet/example'
8
8
  require 'rspec-puppet/setup'
9
+ require 'rspec-puppet/coverage'
9
10
 
10
11
  begin
11
12
  require 'puppet/test/test_helper'
@@ -21,6 +22,7 @@ RSpec.configure do |c|
21
22
  c.add_setting :confdir, :default => '/etc/puppet'
22
23
  c.add_setting :default_facts, :default => {}
23
24
  c.add_setting :hiera_config, :default => '/dev/null'
25
+ c.add_setting :parser, :default => 'current'
24
26
 
25
27
  if defined?(Puppet::Test::TestHelper)
26
28
  begin
@@ -0,0 +1,107 @@
1
+ module RSpec::Puppet
2
+ class Coverage
3
+
4
+ attr_accessor :filters
5
+
6
+ class << self
7
+ extend Forwardable
8
+ def_delegators :instance, :add, :cover!, :report!, :filters
9
+ end
10
+
11
+ include Singleton
12
+
13
+ def initialize
14
+ @collection = {}
15
+ @filters = ['Stage[main]', 'Class[Settings]', 'Class[main]']
16
+ end
17
+
18
+ def add(resource)
19
+ if !exists?(resource) && !filtered?(resource)
20
+ @collection[resource.to_s] = ResourceWrapper.new(resource)
21
+ end
22
+ end
23
+
24
+ def filtered?(resource)
25
+ filters.include?(resource.to_s)
26
+ end
27
+
28
+ def cover!(resource)
29
+ if !filtered?(resource) && (wrapper = find(resource))
30
+ wrapper.touch!
31
+ end
32
+ end
33
+
34
+ def report!
35
+ report = {}
36
+
37
+ report[:total] = @collection.size
38
+ report[:touched] = @collection.count { |_, resource| resource.touched? }
39
+ report[:untouched] = report[:total] - report[:touched]
40
+ report[:coverage] = sprintf("%5.2f", ((report[:touched].to_f/report[:total].to_f)*100))
41
+
42
+ report[:detailed] = Hash[*@collection.map do |name, wrapper|
43
+ [name, wrapper.to_hash]
44
+ end.flatten]
45
+
46
+ puts <<-EOH.gsub(/^ {8}/, '')
47
+
48
+ Total resources: #{report[:total]}
49
+ Touched resources: #{report[:touched]}
50
+ Resource coverage: #{report[:coverage]}%
51
+ EOH
52
+
53
+ if report[:coverage] != "100.00"
54
+ puts <<-EOH.gsub(/^ {10}/, '')
55
+ Untouched resources:
56
+
57
+ #{
58
+ untouched_resources = report[:detailed].reject do |_,rsrc|
59
+ rsrc["touched"]
60
+ end
61
+ untouched_resources.inject([]) do |memo, (name,_)|
62
+ memo << " #{name}"
63
+ end.sort.join("\n")
64
+ }
65
+ EOH
66
+ end
67
+
68
+ end
69
+
70
+ private
71
+
72
+ def find(resource)
73
+ @collection[resource.to_s]
74
+ end
75
+
76
+ def exists?(resource)
77
+ !find(resource).nil?
78
+ end
79
+
80
+ class ResourceWrapper
81
+ attr_reader :resource
82
+
83
+ def initialize(resource = nil)
84
+ @resource = resource
85
+ end
86
+
87
+ def to_s
88
+ @resource.to_s
89
+ end
90
+
91
+ def to_hash
92
+ {
93
+ 'touched' => touched?,
94
+ }
95
+ end
96
+
97
+ def touch!
98
+ @touched = true
99
+ end
100
+
101
+ def touched?
102
+ !!@touched
103
+ end
104
+ end
105
+
106
+ end
107
+ end
@@ -5,16 +5,16 @@ module RSpec::Puppet
5
5
 
6
6
  def initialize(param, expected, actual, negative)
7
7
  @param = param
8
- @expected = expected.inspect
9
- @actual = actual.inspect
8
+ @expected = expected
9
+ @actual = actual
10
10
  @negative = negative
11
11
  end
12
12
 
13
13
  def message
14
14
  if negative == true
15
- "#{param} not set to #{expected} but it is set to #{actual}"
15
+ "#{param} not set to #{expected.inspect} but it is set to #{actual.inspect}"
16
16
  else
17
- "#{param} set to #{expected} but it is set to #{actual}"
17
+ "#{param} set to #{expected.inspect} but it is set to #{actual.inspect}"
18
18
  end
19
19
  end
20
20
 
@@ -26,9 +26,9 @@ module RSpec::Puppet
26
26
  class RegexpMatchError < MatchError
27
27
  def message
28
28
  if negative == true
29
- "#{param} not matching #{expected} but its value of #{actual} does"
29
+ "#{param} not matching #{expected.inspect} but its value of #{actual.inspect} does"
30
30
  else
31
- "#{param} matching #{expected} but its value of #{actual} does not"
31
+ "#{param} matching #{expected.inspect} but its value of #{actual.inspect} does not"
32
32
  end
33
33
  end
34
34
  end
@@ -36,9 +36,9 @@ module RSpec::Puppet
36
36
  class ProcMatchError < MatchError
37
37
  def message
38
38
  if negative == true
39
- "#{param} passed to the block would not return `#{expected}` but it did"
39
+ "#{param} passed to the block would not return `#{expected.inspect}` but it did"
40
40
  else
41
- "#{param} passed to the block would return `#{expected}` but it is `#{actual}`"
41
+ "#{param} passed to the block would return `#{expected.inspect}` but it is `#{actual.inspect}`"
42
42
  end
43
43
  end
44
44
  end
@@ -58,25 +58,25 @@ module RSpec::Puppet
58
58
 
59
59
  class BeforeRelationshipError < RelationshipError
60
60
  def message
61
- "#{from} to come before #{to} in the graph"
61
+ "that comes before #{to}"
62
62
  end
63
63
  end
64
64
 
65
65
  class RequireRelationshipError < RelationshipError
66
66
  def message
67
- "#{from} to require #{to} in the graph"
67
+ "that requires #{to}"
68
68
  end
69
69
  end
70
70
 
71
71
  class NotifyRelationshipError < RelationshipError
72
72
  def message
73
- "#{from} to notify #{to}"
73
+ "that notifies #{to}"
74
74
  end
75
75
  end
76
76
 
77
77
  class SubscribeRelationshipError < RelationshipError
78
78
  def message
79
- "#{from} to be subscribed to #{to}"
79
+ "that is subscribed to #{to}"
80
80
  end
81
81
  end
82
82
  end
@@ -9,19 +9,23 @@ RSpec::configure do |c|
9
9
  Regexp.compile(parts.join('[\\\/]'))
10
10
  end
11
11
 
12
- c.include RSpec::Puppet::DefineExampleGroup, :type => :define, :example_group => {
13
- :file_path => c.escaped_path(%w[spec defines])
14
- }
15
-
16
- c.include RSpec::Puppet::ClassExampleGroup, :type => :class, :example_group => {
17
- :file_path => c.escaped_path(%w[spec classes])
18
- }
19
-
20
- c.include RSpec::Puppet::FunctionExampleGroup, :type => :puppet_function, :example_group => {
21
- :file_path => c.escaped_path(%w[spec functions])
22
- }
23
-
24
- c.include RSpec::Puppet::HostExampleGroup, :type => :host, :example_group => {
25
- :file_path => c.escaped_path(%w[spec hosts])
26
- }
12
+ if RSpec::Version::STRING < '3'
13
+ c.include RSpec::Puppet::DefineExampleGroup, :type => :define, :example_group => {
14
+ :file_path => c.escaped_path(%w[spec defines])
15
+ }
16
+ c.include RSpec::Puppet::ClassExampleGroup, :type => :class, :example_group => {
17
+ :file_path => c.escaped_path(%w[spec classes])
18
+ }
19
+ c.include RSpec::Puppet::FunctionExampleGroup, :type => :puppet_function, :example_group => {
20
+ :file_path => c.escaped_path(%w[spec functions])
21
+ }
22
+ c.include RSpec::Puppet::HostExampleGroup, :type => :host, :example_group => {
23
+ :file_path => c.escaped_path(%w[spec hosts])
24
+ }
25
+ else
26
+ c.include RSpec::Puppet::DefineExampleGroup, :type => :define, :file_path => c.escaped_path(%w[spec defines])
27
+ c.include RSpec::Puppet::ClassExampleGroup, :type => :class, :file_path => c.escaped_path(%w[spec classes])
28
+ c.include RSpec::Puppet::FunctionExampleGroup, :type => :puppet_function, :file_path => c.escaped_path(%w[spec functions])
29
+ c.include RSpec::Puppet::HostExampleGroup, :type => :host, :file_path => c.escaped_path(%w[spec hosts])
30
+ end
27
31
  end
@@ -3,8 +3,8 @@ module RSpec::Puppet
3
3
  include RSpec::Puppet::ManifestMatchers
4
4
  include RSpec::Puppet::Support
5
5
 
6
- def subject
7
- @catalogue ||= catalogue(:class)
6
+ def catalogue
7
+ @catalogue ||= load_catalogue(:class)
8
8
  end
9
9
  end
10
10
  end
@@ -3,8 +3,8 @@ module RSpec::Puppet
3
3
  include RSpec::Puppet::ManifestMatchers
4
4
  include RSpec::Puppet::Support
5
5
 
6
- def subject
7
- @catalogue ||= catalogue(:define)
6
+ def catalogue
7
+ @catalogue ||= load_catalogue(:define)
8
8
  end
9
9
  end
10
10
  end
@@ -11,14 +11,6 @@ module RSpec::Puppet
11
11
 
12
12
  node_name = nodename(:function)
13
13
 
14
- facts_val = facts_hash(node_name)
15
-
16
- # if we specify a pre_condition, we should ensure that we compile that code
17
- # into a catalog that is accessible from the scope where the function is called
18
- Puppet[:code] = pre_cond
19
-
20
- compiler = build_compiler(node_name, facts_val)
21
-
22
14
  function_scope = scope(compiler, node_name)
23
15
 
24
16
  # Return the method instance for the function. This can be used with
@@ -28,8 +20,26 @@ module RSpec::Puppet
28
20
  function_scope.method("function_#{function_name}".intern)
29
21
  end
30
22
 
23
+ def catalogue
24
+ @catalogue ||= compiler.catalog
25
+ end
26
+
27
+ private
28
+
29
+ def compiler
30
+ @compiler ||= build_compiler
31
+ end
32
+
31
33
  # get a compiler with an attached compiled catalog
32
- def build_compiler(node_name, fact_values)
34
+ def build_compiler
35
+ node_name = nodename(:function)
36
+ fact_values = facts_hash(node_name)
37
+
38
+ # if we specify a pre_condition, we should ensure that we compile that
39
+ # code into a catalog that is accessible from the scope where the
40
+ # function is called
41
+ Puppet[:code] = pre_cond
42
+
33
43
  node_options = {
34
44
  :parameters => fact_values,
35
45
  }
@@ -3,8 +3,8 @@ module RSpec::Puppet
3
3
  include RSpec::Puppet::ManifestMatchers
4
4
  include RSpec::Puppet::Support
5
5
 
6
- def subject
7
- @catalogue ||= catalogue(:host)
6
+ def catalogue
7
+ @catalogue ||= load_catalogue(:host)
8
8
  end
9
9
  end
10
10
  end
@@ -5,6 +5,7 @@ module RSpec::Puppet
5
5
  @failed_resource = ""
6
6
  @check_deps = false
7
7
  @cycles = []
8
+ @error_msg = ""
8
9
  end
9
10
 
10
11
  def with_all_deps
@@ -12,30 +13,57 @@ module RSpec::Puppet
12
13
  self
13
14
  end
14
15
 
16
+ def and_raise_error(error)
17
+ @expected_error = error
18
+ self
19
+ end
20
+
15
21
  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
22
+ begin
23
+ @catalogue = catalogue.call
24
+
25
+ if cycles_found?
26
+ false
27
+ elsif @check_deps == true && missing_dependencies?
28
+ false
29
+ else
30
+ true
31
+ end
32
+ rescue Puppet::Error => e
33
+ @error_msg = e.message
34
+ if @expected_error.nil?
35
+ false
36
+ else
37
+ method = @expected_error.is_a?(Regexp) ? :=~ : :==
38
+ e.message.send(method, @expected_error)
39
+ end
23
40
  end
24
41
  end
25
42
 
26
43
  def description
27
- "compile the catalogue without cycles"
44
+ case @expected_error
45
+ when nil
46
+ "compile into a catalogue without dependency cycles"
47
+ when Regexp
48
+ "fail to compile and raise an error matching #{@expected_error.inspect}"
49
+ else
50
+ "fail to compile and raise the error #{@expected_error.inspect}"
51
+ end
28
52
  end
29
53
 
30
- def failure_message_for_should
54
+ def failure_message
31
55
  unless @cycles.empty?
32
56
  "dependency cycles found: #{@cycles.join('; ')}"
33
57
  else
34
- "expected that the catalogue would include #{@failed_resource}"
58
+ unless @error_msg.empty?
59
+ "error during compilation: #{@error_msg}"
60
+ else
61
+ "expected that the catalogue would include #{@failed_resource}"
62
+ end
35
63
  end
36
64
  end
37
65
 
38
- def failure_message_for_should_not
66
+ def failure_message_when_negated
39
67
  "expected that the catalogue would not compile but it does"
40
68
  end
41
69
 
@@ -95,20 +123,14 @@ module RSpec::Puppet
95
123
  end
96
124
 
97
125
  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
126
+ cat = @catalogue.to_ral.relationship_graph
127
+ cat.write_graph(:resources)
128
+ if cat.respond_to? :find_cycles_in_graph
129
+ find_cycles(cat)
130
+ else
131
+ find_cycles_legacy(cat)
110
132
  end
111
- retval
133
+ !@cycles.empty?
112
134
  end
113
135
 
114
136
  def find_cycles(catalogue)
@@ -12,15 +12,17 @@ module RSpec::Puppet
12
12
  end
13
13
 
14
14
  def matches?(catalogue)
15
+ @catalogue = catalogue.call
16
+
15
17
  if @type == "resource"
16
- @actual_number = catalogue.resources.count do |res|
18
+ @actual_number = @catalogue.resources.count do |res|
17
19
  !(['Class', 'Node'].include? res.type)
18
20
  end
19
21
 
20
22
  # Puppet automatically adds Stage[main]
21
23
  @actual_number = @actual_number - 1
22
24
  else
23
- @actual_number = catalogue.resources.count do |res|
25
+ @actual_number = @catalogue.resources.count do |res|
24
26
  res.type == @referenced_type
25
27
  end
26
28
 
@@ -47,11 +49,11 @@ module RSpec::Puppet
47
49
  desc.join(" ")
48
50
  end
49
51
 
50
- def failure_message_for_should
52
+ def failure_message
51
53
  "expected that the catalogue would " + description + " but it contains #{@actual_number}"
52
54
  end
53
55
 
54
- def failure_message_for_should_not
56
+ def failure_message_when_negated
55
57
  "expected that the catalogue would not " + description + " but it does"
56
58
  end
57
59
 
@@ -39,22 +39,22 @@ module RSpec::Puppet
39
39
  end
40
40
 
41
41
  def that_notifies(resource)
42
- @notifies << resource
42
+ @notifies.concat(Array(resource))
43
43
  self
44
44
  end
45
45
 
46
46
  def that_subscribes_to(resource)
47
- @subscribes << resource
47
+ @subscribes.concat(Array(resource))
48
48
  self
49
49
  end
50
50
 
51
51
  def that_requires(resource)
52
- @requires << resource
52
+ @requires.concat(Array(resource))
53
53
  self
54
54
  end
55
55
 
56
56
  def that_comes_before(resource)
57
- @befores << resource
57
+ @befores.concat(Array(resource))
58
58
  self
59
59
  end
60
60
 
@@ -79,11 +79,13 @@ module RSpec::Puppet
79
79
 
80
80
  def matches?(catalogue)
81
81
  ret = true
82
- resource = catalogue.resource(@referenced_type, @title)
82
+ @catalogue = catalogue.call
83
+ resource = @catalogue.resource(@referenced_type, @title)
83
84
 
84
85
  if resource.nil?
85
86
  false
86
87
  else
88
+ RSpec::Puppet::Coverage.cover!(resource)
87
89
  rsrc_hsh = resource.to_hash
88
90
  if @expected_params_count
89
91
  unless rsrc_hsh.size == @expected_params_count
@@ -94,25 +96,26 @@ module RSpec::Puppet
94
96
 
95
97
  check_params(rsrc_hsh, @expected_params, :should) if @expected_params.any?
96
98
  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?
99
+ check_befores(@catalogue, resource) if @befores.any?
100
+ check_requires(@catalogue, resource) if @requires.any?
101
+ check_notifies(@catalogue, resource) if @notifies.any?
102
+ check_subscribes(@catalogue, resource) if @subscribes.any?
101
103
 
102
104
  @errors.empty?
103
105
  end
104
106
  end
105
107
 
106
- def failure_message_for_should
108
+ def failure_message
107
109
  "expected that the catalogue would contain #{@referenced_type}[#{@title}]#{errors}"
108
110
  end
109
111
 
110
- def failure_message_for_should_not
112
+ def failure_message_when_negated
111
113
  "expected that the catalogue would not contain #{@referenced_type}[#{@title}]#{errors}"
112
114
  end
113
115
 
114
116
  def description
115
117
  values = []
118
+ value_str_prefix = "with"
116
119
 
117
120
  if @expected_params_count
118
121
  values << "exactly #{@expected_params_count} parameters"
@@ -126,17 +129,49 @@ module RSpec::Puppet
126
129
  values.concat(generate_param_list(@expected_undef_params, :not))
127
130
  end
128
131
 
132
+ if @notifies.any?
133
+ value_str_prefix = "that notifies"
134
+ values = @notifies
135
+ end
136
+
137
+ if @subscribes.any?
138
+ value_str_prefix = "that subscribes to"
139
+ values = @subscribes
140
+ end
141
+
142
+ if @requires.any?
143
+ value_str_prefix = "that requires"
144
+ values = @requires
145
+ end
146
+
147
+ if @befores.any?
148
+ value_str_prefix = "that comes before"
149
+ values = @befores
150
+ end
151
+
129
152
  unless values.empty?
130
153
  if values.length == 1
131
- value_str = " with #{values.first}"
154
+ value_str = " #{value_str_prefix} #{values.first}"
132
155
  else
133
- value_str = " with #{values[0..-2].join(", ")} and #{values[-1]}"
156
+ value_str = " #{value_str_prefix} #{values[0..-2].join(", ")} and #{values[-1]}"
134
157
  end
135
158
  end
136
159
 
137
160
  "contain #{@referenced_type}[#{@title}]#{value_str}"
138
161
  end
139
162
 
163
+ def diffable?
164
+ true
165
+ end
166
+
167
+ def expected
168
+ @errors.map {|e| e.expected if e.respond_to?(:expected)}.compact.join("\n\n")
169
+ end
170
+
171
+ def actual
172
+ @errors.map {|e| e.actual if e.respond_to?(:actual)}.compact.join("\n\n")
173
+ end
174
+
140
175
  private
141
176
  def referenced_type(type)
142
177
  type.split('__').map { |r| r.capitalize }.join('::')
@@ -199,17 +234,25 @@ module RSpec::Puppet
199
234
  end
200
235
 
201
236
  def precedes?(first, second)
202
- before_refs = relationship_refs(first[:before])
203
- require_refs = relationship_refs(second[:require])
237
+ if first.nil? || second.nil?
238
+ false
239
+ else
240
+ before_refs = relationship_refs(first[:before])
241
+ require_refs = relationship_refs(second[:require])
204
242
 
205
- before_refs.include?(second.to_ref) || require_refs.include?(first.to_ref)
243
+ before_refs.include?(second.to_ref) || require_refs.include?(first.to_ref)
244
+ end
206
245
  end
207
246
 
208
247
  def notifies?(first, second)
209
- notify_refs = relationship_refs(first[:notify])
210
- subscribe_refs = relationship_refs(second[:subscribe])
248
+ if first.nil? || second.nil?
249
+ false
250
+ else
251
+ notify_refs = relationship_refs(first[:notify])
252
+ subscribe_refs = relationship_refs(second[:subscribe])
211
253
 
212
- notify_refs.include?(second.to_ref) || subscribe_refs.include?(first.to_ref)
254
+ notify_refs.include?(second.to_ref) || subscribe_refs.include?(first.to_ref)
255
+ end
213
256
  end
214
257
 
215
258
  # @param resource [Hash<Symbol, Object>] The resource in the catalog
@@ -5,16 +5,24 @@ module RSpec::Puppet
5
5
  matcher :include_class do |expected_class|
6
6
  match do |catalogue|
7
7
  RSpec.deprecate(:include_class, :contain_class)
8
- catalogue.classes.include?(expected_class)
8
+ catalogue.call.classes.include?(expected_class)
9
9
  end
10
10
 
11
11
  description do
12
12
  "include Class[#{expected_class}]"
13
13
  end
14
14
 
15
- failure_message_for_should do |actual|
16
- "expected that the catalogue would include Class[#{expected_class}]"
15
+ if RSpec::Version::STRING < '3'
16
+ failure_message_for_should do |actual|
17
+ "expected that the catalogue would include Class[#{expected_class}]"
18
+ end
19
+ else
20
+ failure_message do |actual|
21
+ "expected that the catalogue would include Class[#{expected_class}]"
22
+ end
17
23
  end
24
+
18
25
  end
26
+
19
27
  end
20
28
  end
@@ -30,9 +30,7 @@ module RSpec::Puppet
30
30
  # Puppet flattens an array with a single value into just the value and
31
31
  # this can cause confusion when testing as people expect when you put
32
32
  # an array in, you'll get an array out.
33
- if expected.is_a?(Array) && expected.length == 1
34
- actual = Array[actual] unless actual.is_a?(Array)
35
- end
33
+ actual = [*actual] if expected.is_a?(Array)
36
34
 
37
35
  retval = check(expected, actual)
38
36
 
@@ -57,6 +55,7 @@ module RSpec::Puppet
57
55
  #
58
56
  # @return [true, false] If the resource matched
59
57
  def check(expected, actual)
58
+ return false if actual.nil? && !expected.nil?
60
59
  case expected
61
60
  when Proc
62
61
  check_proc(expected, actual)
@@ -2,6 +2,7 @@ module RSpec::Puppet
2
2
  module FunctionMatchers
3
3
  class Run
4
4
  def matches?(func_obj)
5
+ @func_obj = func_obj
5
6
  if @params
6
7
  @func = lambda { func_obj.call(@params) }
7
8
  else
@@ -29,7 +30,12 @@ module RSpec::Puppet
29
30
  else
30
31
  unless @expected_return.nil?
31
32
  @actual_return = @func.call
32
- @actual_return == @expected_return
33
+ case @expected_return
34
+ when Regexp
35
+ @actual_return =~ @expected_return
36
+ else
37
+ @actual_return == @expected_return
38
+ end
33
39
  else
34
40
  begin
35
41
  @func.call
@@ -48,6 +54,11 @@ module RSpec::Puppet
48
54
 
49
55
  def and_return(value)
50
56
  @expected_return = value
57
+ if value.is_a? Regexp
58
+ @desc = "match #{value.inspect}"
59
+ else
60
+ @desc = "return #{value.inspect}"
61
+ end
51
62
  self
52
63
  end
53
64
 
@@ -58,22 +69,40 @@ module RSpec::Puppet
58
69
  else
59
70
  @expected_error, @expected_error_message = error_or_message, message
60
71
  end
72
+
73
+ if @expected_error_message.is_a? Regexp
74
+ @desc = "raise an #{@expected_error} with the message matching #{@expected_error_message.inspect}"
75
+ else
76
+ @desc = "raise an #{@expected_error}"
77
+ unless @expected_error_message.nil?
78
+ @desc += "with the message #{@expected_error_message.inspect}"
79
+ end
80
+ end
61
81
  self
62
82
  end
63
83
 
64
- def failure_message_for_should(func_obj)
65
- failure_message_generic(:should, func_obj)
84
+ def failure_message
85
+ failure_message_generic(:should, @func_obj)
66
86
  end
67
87
 
68
- def failure_message_for_should_not(func_obj)
69
- failure_message_generic(:should_not, func_obj)
88
+ def failure_message_when_negated
89
+ failure_message_generic(:should_not, @func_obj)
90
+ end
91
+
92
+ def description
93
+ "run #{func_name}(#{func_params}) and #{@desc}"
70
94
  end
71
95
 
72
96
  private
73
- def failure_message_generic(type, func_obj)
74
- func_name = func_obj.name.gsub(/^function_/, '')
75
- func_params = @params.inspect[1..-2]
97
+ def func_name
98
+ @func_name ||= @func_obj.name.to_s.gsub(/^function_/, '')
99
+ end
76
100
 
101
+ def func_params
102
+ @func_args ||= @params.inspect[1..-2]
103
+ end
104
+
105
+ def failure_message_generic(type, func_obj)
77
106
  message = "expected #{func_name}(#{func_params}) to "
78
107
  message << "not " if type == :should_not
79
108
 
@@ -31,7 +31,7 @@ module RSpec::Puppet
31
31
 
32
32
  safe_touch('spec/fixtures/manifests/site.pp')
33
33
 
34
- ['manifests','lib','files','templates'].each do |dir|
34
+ %w(data manifests lib files templates).each do |dir|
35
35
  if File.exist? dir
36
36
  safe_make_symlink("../../../../#{dir}", "spec/fixtures/modules/#{module_name}/#{dir}")
37
37
  end
@@ -122,7 +122,7 @@ EOF
122
122
  def self.safe_make_symlink(source, target)
123
123
  if File.exists? target
124
124
  unless File.symlink? target
125
- $stderr.puts "!! #{file} already exists and is not a symlink"
125
+ $stderr.puts "!! #{target} already exists and is not a symlink"
126
126
  end
127
127
  else
128
128
  FileUtils.ln_s(source, target)
@@ -3,13 +3,29 @@ module RSpec::Puppet
3
3
 
4
4
  @@cache = {}
5
5
 
6
- def catalogue(type)
6
+ def subject
7
+ lambda { catalogue }
8
+ end
9
+
10
+ def load_catalogue(type)
7
11
  vardir = setup_puppet
8
12
 
9
- code = [import_str, pre_cond, test_manifest(type)].join("\n")
13
+ if Puppet[:parser] == 'future'
14
+ code = [pre_cond, test_manifest(type)].join("\n")
15
+ else
16
+ code = [import_str, pre_cond, test_manifest(type)].join("\n")
17
+ end
18
+
10
19
  node_name = nodename(type)
11
20
 
12
21
  catalogue = build_catalog(node_name, facts_hash(node_name), code)
22
+
23
+ RSpec::Puppet::Coverage.filters << "#{type.to_s.capitalize}[#{self.class.description.capitalize}]"
24
+
25
+ catalogue.to_a.each do |resource|
26
+ RSpec::Puppet::Coverage.add(resource)
27
+ end
28
+
13
29
  FileUtils.rm_rf(vardir) if File.directory?(vardir)
14
30
  catalogue
15
31
  end
@@ -58,8 +74,9 @@ module RSpec::Puppet
58
74
  end
59
75
 
60
76
  def nodename(type)
77
+ return node if self.respond_to?(:node)
61
78
  if [:class, :define, :function].include? type
62
- self.respond_to?(:node) ? node : Puppet[:certname]
79
+ Puppet[:certname]
63
80
  else
64
81
  self.class.top_level_description.downcase
65
82
  end
@@ -80,9 +97,12 @@ module RSpec::Puppet
80
97
 
81
98
  def facts_hash(node)
82
99
  facts_val = {
83
- 'hostname' => node.split('.').first,
84
- 'fqdn' => node,
85
- 'domain' => node.split('.', 2).last,
100
+ 'clientversion' => Puppet::PUPPETVERSION,
101
+ 'environment' => 'production',
102
+ 'hostname' => node.split('.').first,
103
+ 'fqdn' => node,
104
+ 'domain' => node.split('.', 2).last,
105
+ 'clientcert' => node
86
106
  }
87
107
 
88
108
  if RSpec.configuration.default_facts.any?
@@ -112,6 +132,7 @@ module RSpec::Puppet
112
132
  [:config, :config],
113
133
  [:confdir, :confdir],
114
134
  [:hiera_config, :hiera_config],
135
+ [:parser, :parser],
115
136
  ].each do |a, b|
116
137
  value = self.respond_to?(b) ? self.send(b) : RSpec.configuration.send(b)
117
138
  begin
@@ -182,5 +203,13 @@ module RSpec::Puppet
182
203
  opts.merge!({:environment => node_environment})
183
204
  Puppet::Node.new(name, opts)
184
205
  end
206
+
207
+ def rspec_compatibility
208
+ if RSpec::Version::STRING < '3'
209
+ # RSpec 2 compatibility:
210
+ alias_method :failure_message_for_should, :failure_message
211
+ alias_method :failure_message_for_should_not, :failure_message_when_negated
212
+ end
213
+ end
185
214
  end
186
215
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-puppet
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Sharpe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-06 00:00:00.000000000 Z
11
+ date: 2015-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '2.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '2.0'
27
27
  description: RSpec tests for your Puppet manifests
28
28
  email: tim@sharpe.id.au
29
29
  executables:
@@ -32,12 +32,16 @@ extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
34
  - README.md
35
+ - bin/rspec-puppet-init
36
+ - lib/rspec-puppet.rb
37
+ - lib/rspec-puppet/coverage.rb
35
38
  - lib/rspec-puppet/errors.rb
39
+ - lib/rspec-puppet/example.rb
36
40
  - lib/rspec-puppet/example/class_example_group.rb
37
41
  - lib/rspec-puppet/example/define_example_group.rb
38
42
  - lib/rspec-puppet/example/function_example_group.rb
39
43
  - lib/rspec-puppet/example/host_example_group.rb
40
- - lib/rspec-puppet/example.rb
44
+ - lib/rspec-puppet/matchers.rb
41
45
  - lib/rspec-puppet/matchers/compile.rb
42
46
  - lib/rspec-puppet/matchers/count_generic.rb
43
47
  - lib/rspec-puppet/matchers/create_generic.rb
@@ -45,11 +49,8 @@ files:
45
49
  - lib/rspec-puppet/matchers/include_class.rb
46
50
  - lib/rspec-puppet/matchers/parameter_matcher.rb
47
51
  - lib/rspec-puppet/matchers/run.rb
48
- - lib/rspec-puppet/matchers.rb
49
52
  - lib/rspec-puppet/setup.rb
50
53
  - lib/rspec-puppet/support.rb
51
- - lib/rspec-puppet.rb
52
- - bin/rspec-puppet-init
53
54
  homepage: https://github.com/rodjek/rspec-puppet/
54
55
  licenses:
55
56
  - MIT
@@ -60,17 +61,17 @@ require_paths:
60
61
  - lib
61
62
  required_ruby_version: !ruby/object:Gem::Requirement
62
63
  requirements:
63
- - - '>='
64
+ - - ">="
64
65
  - !ruby/object:Gem::Version
65
66
  version: '0'
66
67
  required_rubygems_version: !ruby/object:Gem::Requirement
67
68
  requirements:
68
- - - '>='
69
+ - - ">="
69
70
  - !ruby/object:Gem::Version
70
71
  version: '0'
71
72
  requirements: []
72
73
  rubyforge_project:
73
- rubygems_version: 2.0.3
74
+ rubygems_version: 2.2.2
74
75
  signing_key:
75
76
  specification_version: 4
76
77
  summary: RSpec tests for your Puppet manifests