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 +4 -4
- data/README.md +107 -1
- data/lib/rspec-puppet.rb +2 -0
- data/lib/rspec-puppet/coverage.rb +107 -0
- data/lib/rspec-puppet/errors.rb +12 -12
- data/lib/rspec-puppet/example.rb +19 -15
- data/lib/rspec-puppet/example/class_example_group.rb +2 -2
- data/lib/rspec-puppet/example/define_example_group.rb +2 -2
- data/lib/rspec-puppet/example/function_example_group.rb +19 -9
- data/lib/rspec-puppet/example/host_example_group.rb +2 -2
- data/lib/rspec-puppet/matchers/compile.rb +46 -24
- data/lib/rspec-puppet/matchers/count_generic.rb +6 -4
- data/lib/rspec-puppet/matchers/create_generic.rb +62 -19
- data/lib/rspec-puppet/matchers/include_class.rb +11 -3
- data/lib/rspec-puppet/matchers/parameter_matcher.rb +2 -3
- data/lib/rspec-puppet/matchers/run.rb +37 -8
- data/lib/rspec-puppet/setup.rb +2 -2
- data/lib/rspec-puppet/support.rb +35 -6
- metadata +14 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddac12d8bb2638b92b4d2f009511b2d5bc144305
|
4
|
+
data.tar.gz: 2196abf53f49f3ece60c2c615fdf00214a768948
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
data/lib/rspec-puppet.rb
CHANGED
@@ -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
|
data/lib/rspec-puppet/errors.rb
CHANGED
@@ -5,16 +5,16 @@ module RSpec::Puppet
|
|
5
5
|
|
6
6
|
def initialize(param, expected, actual, negative)
|
7
7
|
@param = param
|
8
|
-
@expected = expected
|
9
|
-
@actual = actual
|
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
|
-
"
|
61
|
+
"that comes before #{to}"
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
65
|
class RequireRelationshipError < RelationshipError
|
66
66
|
def message
|
67
|
-
"
|
67
|
+
"that requires #{to}"
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
71
|
class NotifyRelationshipError < RelationshipError
|
72
72
|
def message
|
73
|
-
"
|
73
|
+
"that notifies #{to}"
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
77
|
class SubscribeRelationshipError < RelationshipError
|
78
78
|
def message
|
79
|
-
"
|
79
|
+
"that is subscribed to #{to}"
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
data/lib/rspec-puppet/example.rb
CHANGED
@@ -9,19 +9,23 @@ RSpec::configure do |c|
|
|
9
9
|
Regexp.compile(parts.join('[\\\/]'))
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
:
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
@@ -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
|
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
|
}
|
@@ -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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
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
|
54
|
+
def failure_message
|
31
55
|
unless @cycles.empty?
|
32
56
|
"dependency cycles found: #{@cycles.join('; ')}"
|
33
57
|
else
|
34
|
-
|
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
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
cat
|
102
|
-
|
103
|
-
|
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
|
-
|
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
|
52
|
+
def failure_message
|
51
53
|
"expected that the catalogue would " + description + " but it contains #{@actual_number}"
|
52
54
|
end
|
53
55
|
|
54
|
-
def
|
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
|
42
|
+
@notifies.concat(Array(resource))
|
43
43
|
self
|
44
44
|
end
|
45
45
|
|
46
46
|
def that_subscribes_to(resource)
|
47
|
-
@subscribes
|
47
|
+
@subscribes.concat(Array(resource))
|
48
48
|
self
|
49
49
|
end
|
50
50
|
|
51
51
|
def that_requires(resource)
|
52
|
-
@requires
|
52
|
+
@requires.concat(Array(resource))
|
53
53
|
self
|
54
54
|
end
|
55
55
|
|
56
56
|
def that_comes_before(resource)
|
57
|
-
@befores
|
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
|
-
|
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
|
108
|
+
def failure_message
|
107
109
|
"expected that the catalogue would contain #{@referenced_type}[#{@title}]#{errors}"
|
108
110
|
end
|
109
111
|
|
110
|
-
def
|
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 = "
|
154
|
+
value_str = " #{value_str_prefix} #{values.first}"
|
132
155
|
else
|
133
|
-
value_str = "
|
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
|
-
|
203
|
-
|
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
|
-
|
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
|
-
|
210
|
-
|
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
|
-
|
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
|
-
|
16
|
-
|
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)
|
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
|
-
|
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
|
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
|
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
|
74
|
-
func_name
|
75
|
-
|
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
|
|
data/lib/rspec-puppet/setup.rb
CHANGED
@@ -31,7 +31,7 @@ module RSpec::Puppet
|
|
31
31
|
|
32
32
|
safe_touch('spec/fixtures/manifests/site.pp')
|
33
33
|
|
34
|
-
|
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 "!! #{
|
125
|
+
$stderr.puts "!! #{target} already exists and is not a symlink"
|
126
126
|
end
|
127
127
|
else
|
128
128
|
FileUtils.ln_s(source, target)
|
data/lib/rspec-puppet/support.rb
CHANGED
@@ -3,13 +3,29 @@ module RSpec::Puppet
|
|
3
3
|
|
4
4
|
@@cache = {}
|
5
5
|
|
6
|
-
def
|
6
|
+
def subject
|
7
|
+
lambda { catalogue }
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_catalogue(type)
|
7
11
|
vardir = setup_puppet
|
8
12
|
|
9
|
-
|
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
|
-
|
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
|
-
'
|
84
|
-
'
|
85
|
-
'
|
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:
|
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:
|
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/
|
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.
|
74
|
+
rubygems_version: 2.2.2
|
74
75
|
signing_key:
|
75
76
|
specification_version: 4
|
76
77
|
summary: RSpec tests for your Puppet manifests
|