rspec-puppet 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -0
- data/README.md +195 -39
- data/lib/rspec-puppet/cache.rb +30 -0
- data/lib/rspec-puppet/coverage.rb +76 -47
- data/lib/rspec-puppet/errors.rb +11 -3
- data/lib/rspec-puppet/example/class_example_group.rb +4 -0
- data/lib/rspec-puppet/example/define_example_group.rb +4 -0
- data/lib/rspec-puppet/example/function_example_group.rb +33 -1
- data/lib/rspec-puppet/example/host_example_group.rb +4 -0
- data/lib/rspec-puppet/example/provider_example_group.rb +4 -0
- data/lib/rspec-puppet/example/type_example_group.rb +26 -0
- data/lib/rspec-puppet/example.rb +16 -1
- data/lib/rspec-puppet/matchers/compile.rb +14 -3
- data/lib/rspec-puppet/matchers/create_generic.rb +72 -16
- data/lib/rspec-puppet/matchers/dynamic_matchers.rb +7 -0
- data/lib/rspec-puppet/matchers/include_class.rb +1 -1
- data/lib/rspec-puppet/matchers/run.rb +3 -2
- data/lib/rspec-puppet/matchers/type_matchers.rb +161 -0
- data/lib/rspec-puppet/matchers.rb +1 -0
- data/lib/rspec-puppet/rake_task.rb +20 -0
- data/lib/rspec-puppet/setup.rb +7 -34
- data/lib/rspec-puppet/spec_helper.rb +9 -0
- data/lib/rspec-puppet/support.rb +55 -60
- data/lib/rspec-puppet.rb +1 -1
- metadata +17 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9c6c2a6e166342f02c503667f8312efd1d411e5
|
4
|
+
data.tar.gz: 407d9b0fc06f52c63428320819fd634eee4cf049
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1c780ece606028d4a134797cec9740204529a597b2b4a14400dd4dbf623c8b689ba067726d4ebcac7f09ce792c7867ab47860b8d5fbd82304e1204be46c5b74
|
7
|
+
data.tar.gz: c5b11365c94d792e111b804c87a225583461f68570b5e29be03bb1e87a621cef66b08283c04e5bf42eb120c50afaae57375a8483a0be33f1ea569eaf2fa71617
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,46 @@
|
|
2
2
|
All notable changes to this project will be documented in this file. This
|
3
3
|
project adheres to [Semantic Versioning](http://semver.org/).
|
4
4
|
|
5
|
+
## [2.3.0]
|
6
|
+
|
7
|
+
Rspec-puppet now supports testing custom types, `:undef` values in params, structured facts, and checks resource dependencies recursively.
|
8
|
+
|
9
|
+
The settings in `module_path` and `manifest` are now respected throughout the code base. The former default for `module_path` (`'/etc/puppet/modules'`) was dropped to avoid accidentally poisoning the test environment with unrelated code.
|
10
|
+
|
11
|
+
To reduce the maintenance overhead of boilerplate code, rspec-puppet now provides some of the code that rspec-puppet-init deployed in helper files that you can just `require` instead.
|
12
|
+
|
13
|
+
This release also reduces memory usage on bigger testsuites drastically by reducing the caching of compiled catalogs.
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
- Limit the catalogue cache to 16 entries. Significant memory savings and reduced runtime were observed in testing this.
|
17
|
+
- Prevent Puppet 3's \_timestamp fact from invalidating cache.
|
18
|
+
- Extracted catalog cache from RSpec::Puppet::Support.
|
19
|
+
- Updated README to use the rspec 3 syntax, and additional explanations.
|
20
|
+
- `contain_file(...).with_content(...)` will now only show the diff and not the full contents of the file.
|
21
|
+
|
22
|
+
### Added
|
23
|
+
- Custom type testing example group and matcher.
|
24
|
+
- before/require/subscribe/notify checking now searches recursively through all dependencies. `File[a] -> File[b] -> File[c]` is now matched by `contain_file('a').that_comes_before('File[c]')`, whereas earlier versions would have missed that.
|
25
|
+
- `let(:params)` now allows `:undef` to pass a literal undef value through to the subject.
|
26
|
+
- Support structured facts with keys as symbols or strings (\#295).
|
27
|
+
- rspec-puppet-init now creates smaller files, using rspec-puppet helpers, instead of pasting code into the module.
|
28
|
+
- Added a list of related projects to the README.
|
29
|
+
|
30
|
+
### Fixed
|
31
|
+
- Fix #276: `compile.and_raise_error` now correctly considers successful compilation an error
|
32
|
+
- Puppet's `modulepath` can now contain multiple entries and rspec-puppet will configure puppet to load code from all of them
|
33
|
+
- Support running with rspec 2.99 again
|
34
|
+
- non-class resources are now covered by the coverage code
|
35
|
+
- Fix #323/MODULES-2374: autorequires checking doesn't abort on "undefined method \`[]' for nil:NilClass"
|
36
|
+
- improved documentation for hiera integration, added example spec
|
37
|
+
- document the `scope` property
|
38
|
+
|
39
|
+
### Credits
|
40
|
+
|
41
|
+
Thanks to Adrien Thebo, Alex Harvey, Brian, Dan Bode, Dominic Cleal, Javier Palacios, Jeff McCune, Jordan Moldow, Peter van Zetten, Raphaël Pinson, Simon Kohlmeyer, and Tristan Colgate for their contributions to this release.
|
42
|
+
|
43
|
+
-- David Schmitt
|
44
|
+
|
5
45
|
## [2.2.0]
|
6
46
|
### Added
|
7
47
|
- Settings for ordering, strict_variables, stringify_facts, and trusted_node_data
|
@@ -47,6 +87,9 @@ project adheres to [Semantic Versioning](http://semver.org/).
|
|
47
87
|
## 1.0.1 and earlier
|
48
88
|
For changelog of versions 1.0.1 and earlier, see http://rspec-puppet.com/changelog/
|
49
89
|
|
90
|
+
[2.x]: https://github.com/rodjek/rspec-puppet/compare/v2.3.0...master
|
91
|
+
[2.3.0]: https://github.com/rodjek/rspec-puppet/compare/v2.2.0...v2.3.0
|
92
|
+
[2.2.0]: https://github.com/rodjek/rspec-puppet/compare/v2.1.0...v2.2.0
|
50
93
|
[2.1.0]: https://github.com/rodjek/rspec-puppet/compare/v2.0.1...v2.1.0
|
51
94
|
[2.0.1]: https://github.com/rodjek/rspec-puppet/compare/v2.0.0...v2.0.1
|
52
95
|
[2.0.0]: https://github.com/rodjek/rspec-puppet/compare/v1.0.1...v2.0.0
|
data/README.md
CHANGED
@@ -1,9 +1,19 @@
|
|
1
|
-
# RSpec tests for your Puppet manifests & modules
|
1
|
+
# RSpec tests for your Puppet manifests & modules
|
2
|
+
[![Build Status](https://travis-ci.org/rodjek/rspec-puppet.png)](https://travis-ci.org/rodjek/rspec-puppet)
|
3
|
+
[![Coverage Status](https://coveralls.io/repos/rodjek/rspec-puppet/badge.svg?branch=master)](https://coveralls.io/r/rodjek/rspec-puppet?branch=master)
|
2
4
|
|
3
5
|
## Installation
|
4
6
|
|
5
7
|
gem install rspec-puppet
|
6
8
|
|
9
|
+
> Note for ruby 1.8 users: while rspec-puppet itself supports ruby 1.8, you'll
|
10
|
+
> need to pin rspec itself to `~> 3.1.0`, as later rspec versions do not work
|
11
|
+
> on old rubies anymore.
|
12
|
+
|
13
|
+
## Starting out with a new module
|
14
|
+
|
15
|
+
When you start out on a new module, run `rspec-puppet-init` to create the necessary files to configure rspec-puppet for your module's tests.
|
16
|
+
|
7
17
|
## Naming conventions
|
8
18
|
|
9
19
|
For clarity and consistency, I recommend that you use the following directory
|
@@ -31,6 +41,10 @@ structure and naming convention.
|
|
31
41
|
| |
|
32
42
|
| +-- <function_name>_spec.rb
|
33
43
|
|
|
44
|
+
+-- types
|
45
|
+
| |
|
46
|
+
| +-- <type_name>_spec.rb
|
47
|
+
|
|
34
48
|
+-- hosts
|
35
49
|
|
|
36
50
|
+-- <host_name>_spec.rb
|
@@ -54,6 +68,10 @@ describe 'myfunction', :type => :puppet_function do
|
|
54
68
|
...
|
55
69
|
end
|
56
70
|
|
71
|
+
describe 'mytype', :type => :type do
|
72
|
+
...
|
73
|
+
end
|
74
|
+
|
57
75
|
describe 'myhost.example.com', :type => :host do
|
58
76
|
...
|
59
77
|
end
|
@@ -63,76 +81,96 @@ end
|
|
63
81
|
|
64
82
|
### Matchers
|
65
83
|
|
84
|
+
#### Checking if the catalog compiles
|
85
|
+
|
86
|
+
You can test whether the subject catalog compiles cleanly with `compile`.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
it { is_expected.to compile }
|
90
|
+
```
|
91
|
+
|
92
|
+
To check the error messages of your class, you can check for raised error messages.
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
it { is_expected.to compile.and_raise_error(/error message match/) }
|
96
|
+
```
|
97
|
+
|
66
98
|
#### Checking if a resource exists
|
67
99
|
|
68
100
|
You can test if a resource exists in the catalogue with the generic
|
69
101
|
`contain_<resource type>` matcher.
|
70
102
|
|
71
103
|
```ruby
|
72
|
-
it {
|
104
|
+
it { is_expected.to contain_augeas('bleh') }
|
73
105
|
```
|
74
106
|
|
75
107
|
You can also test if a class has been included in the catalogue with the
|
76
108
|
same matcher.
|
77
109
|
|
78
110
|
```ruby
|
79
|
-
it {
|
111
|
+
it { is_expected.to contain_class('foo') }
|
80
112
|
```
|
81
113
|
|
114
|
+
Note that rspec-puppet does none of the class name parsing and lookup that the puppet parser would do for you. The matcher only accepts fully qualified classnames without any leading colons. That is a class `foo::bar` will only be matched by `foo::bar`, but not by `::foo::bar`, or `bar` alone.
|
115
|
+
|
82
116
|
If your resource type includes :: (e.g.
|
83
117
|
`foo::bar` simply replace the :: with __ (two underscores).
|
84
118
|
|
85
119
|
```ruby
|
86
|
-
it {
|
120
|
+
it { is_expected.to contain_foo__bar('baz') }
|
87
121
|
```
|
88
122
|
|
89
123
|
You can further test the parameters that have been passed to the resources with
|
90
124
|
the generic `with_<parameter>` chains.
|
91
125
|
|
92
126
|
```ruby
|
93
|
-
it {
|
127
|
+
it { is_expected.to contain_package('mysql-server').with_ensure('present') }
|
94
128
|
```
|
95
129
|
|
96
130
|
If you want to specify that the given parameters should be the only ones passed
|
97
131
|
to the resource, use the `only_with_<parameter>` chains.
|
98
132
|
|
99
133
|
```ruby
|
100
|
-
it {
|
134
|
+
it { is_expected.to contain_package('httpd').only_with_ensure('latest') }
|
101
135
|
```
|
102
136
|
|
103
137
|
You can use the `with` method to verify the value of multiple parameters.
|
104
138
|
|
105
139
|
```ruby
|
106
|
-
it do
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
140
|
+
it do
|
141
|
+
is_expected.to contain_service('keystone').with(
|
142
|
+
'ensure' => 'running',
|
143
|
+
'enable' => 'true',
|
144
|
+
'hasstatus' => 'true',
|
145
|
+
'hasrestart' => 'true'
|
146
|
+
)
|
147
|
+
end
|
112
148
|
```
|
113
149
|
|
114
150
|
The same holds for the `only_with` method, which in addition verifies the exact
|
115
151
|
set of parameters and values for the resource in the catalogue.
|
116
152
|
|
117
153
|
```ruby
|
118
|
-
it do
|
119
|
-
|
120
|
-
|
121
|
-
|
154
|
+
it do
|
155
|
+
is_expected.to contain_user('luke').only_with(
|
156
|
+
'ensure' => 'present',
|
157
|
+
'uid' => '501'
|
158
|
+
)
|
159
|
+
end
|
122
160
|
```
|
123
161
|
|
124
162
|
You can also test that specific parameters have been left undefined with the
|
125
163
|
generic `without_<parameter>` chains.
|
126
164
|
|
127
165
|
```ruby
|
128
|
-
it {
|
166
|
+
it { is_expected.to contain_file('/foo/bar').without_mode }
|
129
167
|
```
|
130
168
|
|
131
169
|
You can use the without method to verify that a list of parameters have not been
|
132
170
|
defined
|
133
171
|
|
134
172
|
```ruby
|
135
|
-
it {
|
173
|
+
it { is_expected.to contain_service('keystone').without(
|
136
174
|
['restart', 'status']
|
137
175
|
)}
|
138
176
|
```
|
@@ -143,28 +181,28 @@ You can test the number of resources in the catalogue with the
|
|
143
181
|
`have_resource_count` matcher.
|
144
182
|
|
145
183
|
```ruby
|
146
|
-
it {
|
184
|
+
it { is_expected.to have_resource_count(2) }
|
147
185
|
```
|
148
186
|
|
149
187
|
The number of classes in the catalogue can be checked with the
|
150
188
|
`have_class_count` matcher.
|
151
189
|
|
152
190
|
```ruby
|
153
|
-
it {
|
191
|
+
it { is_expected.to have_class_count(2) }
|
154
192
|
```
|
155
193
|
|
156
194
|
You can also test the number of a specific resource type, by using the generic
|
157
195
|
`have_<resource type>_resource_count` matcher.
|
158
196
|
|
159
197
|
```ruby
|
160
|
-
it {
|
198
|
+
it { is_expected.to have_exec_resource_count(1) }
|
161
199
|
```
|
162
200
|
|
163
201
|
This last matcher also works for defined types. If the resource type contains
|
164
202
|
::, you can replace it with __ (two underscores).
|
165
203
|
|
166
204
|
```ruby
|
167
|
-
it {
|
205
|
+
it { is_expected.to have_logrotate__rule_resource_count(3) }
|
168
206
|
```
|
169
207
|
|
170
208
|
*NOTE*: when testing a class, the catalogue generated will always contain at
|
@@ -177,19 +215,19 @@ catalogue generated when testing a defined type will have at least one resource
|
|
177
215
|
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
216
|
|
179
217
|
```ruby
|
180
|
-
it {
|
181
|
-
it {
|
182
|
-
it {
|
183
|
-
it {
|
218
|
+
it { is_expected.to contain_file('foo').that_requires('File[bar]') }
|
219
|
+
it { is_expected.to contain_file('foo').that_comes_before('File[bar]') }
|
220
|
+
it { is_expected.to contain_file('foo').that_notifies('File[bar]') }
|
221
|
+
it { is_expected.to contain_file('foo').that_subscribes_to('File[bar]') }
|
184
222
|
```
|
185
223
|
|
186
224
|
An array can be used to test a resource for multiple relationships
|
187
225
|
|
188
226
|
```ruby
|
189
|
-
it {
|
190
|
-
it {
|
191
|
-
it {
|
192
|
-
it {
|
227
|
+
it { is_expected.to contain_file('foo').that_requires(['File[bar]', 'File[baz]']) }
|
228
|
+
it { is_expected.to contain_file('foo').that_comes_before(['File[bar]','File[baz]']) }
|
229
|
+
it { is_expected.to contain_file('foo').that_notifies(['File[bar]', 'File[baz]']) }
|
230
|
+
it { is_expected.to contain_file('foo').that_subscribes_to(['File[bar]', 'File[baz]']) }
|
193
231
|
```
|
194
232
|
|
195
233
|
You can also test the reverse direction of the relationship, so if you have the following bit of Puppet code
|
@@ -204,14 +242,83 @@ notify { 'bar':
|
|
204
242
|
You can test that **Notify[bar]** comes before **Notify[foo]**
|
205
243
|
|
206
244
|
```ruby
|
207
|
-
it {
|
245
|
+
it { is_expected.to contain_notify('bar').that_comes_before('Notify[foo]') }
|
208
246
|
```
|
209
247
|
Or, you can test that **Notify[foo]** requires **Notify[bar]**
|
210
248
|
|
211
249
|
```ruby
|
212
|
-
it {
|
250
|
+
it { is_expected.to contain_notify('foo').that_requires('Notify[bar]') }
|
251
|
+
```
|
252
|
+
|
253
|
+
##### Match target syntax
|
254
|
+
|
255
|
+
Note that this notation does not support any of the features you're used from the puppet language. Only a single resource with a single, unquoted title can be referenced here. Class names need to be always fully qualified and not have the leading `::`. It currently does not support inline arrays or quoting.
|
256
|
+
|
257
|
+
These work
|
258
|
+
* `Notify[foo]`
|
259
|
+
* `Class[profile::apache]`
|
260
|
+
|
261
|
+
These will not work
|
262
|
+
* `Notify['foo']`
|
263
|
+
* `Notify[foo, bar]`
|
264
|
+
* `Class[::profile::apache]`
|
265
|
+
|
266
|
+
##### Recursive dependencies
|
267
|
+
|
268
|
+
The relationship matchers are recursive in two directions:
|
269
|
+
|
270
|
+
* vertical recursion, which checks for dependencies with parents of the resource
|
271
|
+
(i.e. the resource is contained, directly or not, in the class involved in the relationship).
|
272
|
+
E.g. where `Package['foo']` comes before `File['/foo']`:
|
273
|
+
|
274
|
+
```puppet
|
275
|
+
class { 'foo::install': } ->
|
276
|
+
class { 'foo::config': }
|
277
|
+
|
278
|
+
class foo::install {
|
279
|
+
package { 'foo': }
|
280
|
+
}
|
281
|
+
|
282
|
+
class foo::config {
|
283
|
+
file { '/foo': }
|
284
|
+
}
|
213
285
|
```
|
214
286
|
|
287
|
+
* horizontal recursion, which follows indirect dependencies (dependencies of dependencies).
|
288
|
+
E.g. where `Yumrepo['foo']` comes before `File['/foo']`:
|
289
|
+
|
290
|
+
```puppet
|
291
|
+
class { 'foo::repo': } ->
|
292
|
+
class { 'foo::install': } ->
|
293
|
+
class { 'foo::config': }
|
294
|
+
|
295
|
+
class foo::repo {
|
296
|
+
yumrepo { 'foo': }
|
297
|
+
}
|
298
|
+
|
299
|
+
class foo::install {
|
300
|
+
package { 'foo': }
|
301
|
+
}
|
302
|
+
|
303
|
+
class foo::config {
|
304
|
+
file { '/foo': }
|
305
|
+
}
|
306
|
+
```
|
307
|
+
|
308
|
+
##### Autorequires
|
309
|
+
|
310
|
+
Autorequires are considered in dependency checks.
|
311
|
+
|
312
|
+
#### Type matcher
|
313
|
+
|
314
|
+
When testing custom types, the `be_valid_type` matcher provides a range of expectations:
|
315
|
+
|
316
|
+
* `with_provider(<provider_name>)`: check that the right provider was selected
|
317
|
+
* `with_properties(<property_list>)`: check that the specified properties are available
|
318
|
+
* `with_parameters(<parameter_list>)`: check that the specified parameters are available
|
319
|
+
* `with_features(<feature_list>)`: check that the specified features are available
|
320
|
+
* `with_set_attributes(<param_value_hash>)`: check that the specified attributes are set
|
321
|
+
|
215
322
|
### Writing tests
|
216
323
|
|
217
324
|
#### Basic test structure
|
@@ -235,7 +342,7 @@ describe 'sysctl' do
|
|
235
342
|
let(:title) { 'baz' }
|
236
343
|
let(:params) { { :value => 'foo' } }
|
237
344
|
|
238
|
-
it {
|
345
|
+
it { is_expected.to contain_exec('sysctl/reload').with_command("/sbin/sysctl -p /etc/sysctl.conf") }
|
239
346
|
end
|
240
347
|
```
|
241
348
|
|
@@ -245,12 +352,18 @@ end
|
|
245
352
|
let(:title) { 'foo' }
|
246
353
|
```
|
247
354
|
|
248
|
-
#### Specifying the parameters to pass to a resources or
|
355
|
+
#### Specifying the parameters to pass to a resources or parameterised class
|
249
356
|
|
250
357
|
```ruby
|
251
358
|
let(:params) { {:ensure => 'present', ...} }
|
252
359
|
```
|
253
360
|
|
361
|
+
##### Passing Puppet's `undef` as a parameter value
|
362
|
+
|
363
|
+
```ruby
|
364
|
+
let(:params) { {:user => :undef, ...} }
|
365
|
+
```
|
366
|
+
|
254
367
|
#### Specifying the FQDN of the test node
|
255
368
|
|
256
369
|
If the manifest you're testing expects to run on host with a particular name,
|
@@ -278,6 +391,14 @@ You can set them with a hash
|
|
278
391
|
let(:facts) { {:operatingsystem => 'Debian', :kernel => 'Linux', ...} }
|
279
392
|
```
|
280
393
|
|
394
|
+
Facts may be expressed as a value (shown in the previous example) or a structure. Fact keys
|
395
|
+
may be expressed as either symbols or strings. A key will be converted to a lower case
|
396
|
+
string to align with the Facter standard
|
397
|
+
|
398
|
+
```ruby
|
399
|
+
let(:facts) { {:os => { :family => 'RedHat', :release => { :major => '7', :minor => '1', :full => '7.1.1503' } } } }
|
400
|
+
```
|
401
|
+
|
281
402
|
You can also create a set of default facts provided to all specs in your spec_helper:
|
282
403
|
|
283
404
|
``` ruby
|
@@ -325,7 +446,7 @@ For your convenience though, a `run` matcher exists to provide easier to
|
|
325
446
|
understand test cases.
|
326
447
|
|
327
448
|
```ruby
|
328
|
-
it {
|
449
|
+
it { is_expected.to run.with_params('foo').and_return('bar') }
|
329
450
|
```
|
330
451
|
|
331
452
|
### Writing tests
|
@@ -354,7 +475,7 @@ You can specify the arguments to pass to your function during the test(s) using
|
|
354
475
|
either the `with_params` chain method in the `run` matcher
|
355
476
|
|
356
477
|
```ruby
|
357
|
-
it {
|
478
|
+
it { is_expected.to run.with_params('foo', 'bar', ['baz']) }
|
358
479
|
```
|
359
480
|
|
360
481
|
Or by using the `call` method on the subject directly
|
@@ -371,7 +492,7 @@ You can test the result of a function (if it produces one) using either the
|
|
371
492
|
`and_returns` chain method in the `run` matcher
|
372
493
|
|
373
494
|
```ruby
|
374
|
-
it {
|
495
|
+
it { is_expected.to run.with_params('foo').and_return('bar') }
|
375
496
|
```
|
376
497
|
|
377
498
|
Or by using any of the existing RSpec matchers on the subject directly
|
@@ -389,8 +510,8 @@ You can test whether the function throws an exception using either the
|
|
389
510
|
`and_raises_error` chain method in the `run` matcher
|
390
511
|
|
391
512
|
```ruby
|
392
|
-
it {
|
393
|
-
it {
|
513
|
+
it { is_expected.to run.with_params('a', 'b').and_raise_error(Puppet::ParseError) }
|
514
|
+
it { is_expected.not_to run.with_params('a').and_raise_error(Puppet::ParseError) }
|
394
515
|
```
|
395
516
|
|
396
517
|
Or by using the existing `raises_error` RSpec matcher
|
@@ -402,6 +523,16 @@ it 'something' do
|
|
402
523
|
end
|
403
524
|
```
|
404
525
|
|
526
|
+
#### Accessing the parser scope where the function is running
|
527
|
+
|
528
|
+
Some complex functions require access to the current parser's scope, e.g. for
|
529
|
+
stubbing other parts of the system.
|
530
|
+
|
531
|
+
```ruby
|
532
|
+
before(:each) { scope.expects(:lookupvar).with('some_variable').returns('some_value') }
|
533
|
+
it { is_expected.to run.with_params('...').and_return('...') }
|
534
|
+
```
|
535
|
+
|
405
536
|
## Hiera integration
|
406
537
|
|
407
538
|
### Configuration
|
@@ -455,6 +586,18 @@ RSpec.configure do |c|
|
|
455
586
|
end
|
456
587
|
```
|
457
588
|
|
589
|
+
spec/fixtures/hiera/hiera.yaml
|
590
|
+
```yaml
|
591
|
+
---
|
592
|
+
:backends:
|
593
|
+
- yaml
|
594
|
+
:yaml:
|
595
|
+
:datadir: spec/fixtures/hieradata
|
596
|
+
:hierarchy:
|
597
|
+
- common
|
598
|
+
```
|
599
|
+
|
600
|
+
|
458
601
|
## Producing coverage reports
|
459
602
|
|
460
603
|
You can output a basic resource coverage report with the following in
|
@@ -467,3 +610,16 @@ at_exit { RSpec::Puppet::Coverage.report! }
|
|
467
610
|
This checks which Puppet resources have been explicitly checked as part
|
468
611
|
of the current test run and outputs both a coverage percentage and a
|
469
612
|
list of untouched resources.
|
613
|
+
|
614
|
+
## Related projects
|
615
|
+
|
616
|
+
* [puppetlabs_spec_helper](https://github.com/puppetlabs/puppetlabs_spec_helper): shared spec helpers to setup puppet
|
617
|
+
* [rspec-puppet-augeas](https://github.com/domcleal/rspec-puppet-augeas): RSpec tests for Augeas resources inside Puppet manifests
|
618
|
+
* [jimdo-rspec-puppet-helpers](https://github.com/Jimdo/jimdo-rspec-puppet-helpers): Tests the contents of a file with a source
|
619
|
+
|
620
|
+
* Fact providers
|
621
|
+
* [rspec-puppet-facts](https://github.com/mcanevet/rspec-puppet-facts): Simplify your unit tests by looping on every supported Operating System and populating facts.
|
622
|
+
* [rspec-puppet-osmash](https://github.com/Aethylred/rspec-puppet-osmash): Provides Operation System hashes and validations for rspec-puppet
|
623
|
+
* [puppet_spec_facts](https://github.com/danieldreier/puppet_spec_facts): Gem to provide puppet fact hashes for rspec-puppet testing
|
624
|
+
|
625
|
+
For a list of other module development tools see https://puppet.community/plugins/
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RSpec::Puppet
|
2
|
+
class Cache
|
3
|
+
|
4
|
+
MAX_ENTRIES = 16
|
5
|
+
|
6
|
+
# @param [Proc] default_proc The default proc to use to fetch objects on cache miss
|
7
|
+
def initialize(&default_proc)
|
8
|
+
@default_proc = default_proc
|
9
|
+
@cache = {}
|
10
|
+
@lra = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(*args, &blk)
|
14
|
+
if !@cache.has_key? args
|
15
|
+
@cache[args] = (blk || @default_proc).call(*args)
|
16
|
+
@lra << args
|
17
|
+
expire!
|
18
|
+
end
|
19
|
+
|
20
|
+
@cache[args]
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def expire!
|
26
|
+
expired = @lra.slice!(0, @lra.size - MAX_ENTRIES)
|
27
|
+
expired.each { |key| @cache.delete(key) } if expired
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -28,31 +28,8 @@ module RSpec::Puppet
|
|
28
28
|
|
29
29
|
# add all resources from catalog declared in module test_module
|
30
30
|
def add_from_catalog(catalog, test_module)
|
31
|
-
catalog.to_a.
|
32
|
-
|
33
|
-
next if @filters.include?(resource.to_s)
|
34
|
-
if resource.type == 'Class'
|
35
|
-
# if the resource is a class, make sure the class belongs to
|
36
|
-
# module test_module
|
37
|
-
module_name = resource.title.split('::').first.downcase
|
38
|
-
next if module_name != test_module
|
39
|
-
elsif resource.file
|
40
|
-
# otherwise, the source file should be available, so make
|
41
|
-
# sure the manifest declaring the resource is in
|
42
|
-
# test_module's directory tree or the site manifest(s)
|
43
|
-
if Puppet.version.to_f >= 4.0
|
44
|
-
paths = [
|
45
|
-
(Pathname.new(Puppet[:environmentpath]) + 'fixtures' + test_module + 'manifests').to_s,
|
46
|
-
(Pathname.new(Puppet[:environmentpath]) + 'fixtures' + 'manifests' + 'site.pp').to_s
|
47
|
-
]
|
48
|
-
else
|
49
|
-
paths = Puppet[:modulepath].split(File::PATH_SEPARATOR).map do |dir|
|
50
|
-
(Pathname.new(dir) + test_module + 'manifests').to_s
|
51
|
-
end
|
52
|
-
paths << Puppet[:manifest]
|
53
|
-
end
|
54
|
-
next unless paths.any? { |path| resource.file.include?(path) }
|
55
|
-
end
|
31
|
+
coverable_resources = catalog.to_a.select { |resource| !filter_resource?(resource, test_module) }
|
32
|
+
coverable_resources.each do |resource|
|
56
33
|
add(resource)
|
57
34
|
end
|
58
35
|
end
|
@@ -105,39 +82,91 @@ module RSpec::Puppet
|
|
105
82
|
|
106
83
|
private
|
107
84
|
|
108
|
-
|
109
|
-
|
85
|
+
# Should this resource be excluded from coverage reports?
|
86
|
+
#
|
87
|
+
# The resource is not included in coverage reports if any of the conditions hold:
|
88
|
+
#
|
89
|
+
# * The resource has been explicitly filtered out.
|
90
|
+
# * Examples: autogenerated resources such as 'Stage[main]'
|
91
|
+
# * The resource is a class but does not belong to the module under test.
|
92
|
+
# * Examples: Class dependencies included from a fixture module
|
93
|
+
# * The resource was declared in a file outside of the test module or site.pp
|
94
|
+
# * Examples: Resources declared in a dependency of this module.
|
95
|
+
#
|
96
|
+
# @param resource [Puppet::Resource] The resource that may be filtered
|
97
|
+
# @param test_module [String] The name of the module under test
|
98
|
+
# @return [true, false]
|
99
|
+
def filter_resource?(resource, test_module)
|
100
|
+
if @filters.include?(resource.to_s)
|
101
|
+
return true
|
110
102
|
end
|
111
103
|
|
112
|
-
|
113
|
-
|
104
|
+
if resource.type == 'Class'
|
105
|
+
module_name = resource.title.split('::').first.downcase
|
106
|
+
if module_name != test_module
|
107
|
+
return true
|
108
|
+
end
|
114
109
|
end
|
115
110
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
@resource = resource
|
111
|
+
if resource.file
|
112
|
+
paths = module_paths(test_module)
|
113
|
+
unless paths.any? { |path| resource.file.include?(path) }
|
114
|
+
return true
|
121
115
|
end
|
116
|
+
end
|
122
117
|
|
123
|
-
|
124
|
-
|
125
|
-
end
|
118
|
+
return false
|
119
|
+
end
|
126
120
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
121
|
+
# Find all paths that may contain testable resources for a module.
|
122
|
+
#
|
123
|
+
# @return [Array<String>]
|
124
|
+
def module_paths(test_module)
|
125
|
+
if Puppet.version.to_f >= 4.0
|
126
|
+
modulepath = RSpec.configuration.module_path || File.join(Puppet[:environmentpath], 'fixtures', 'modules')
|
127
|
+
manifest = RSpec.configuration.manifest || File.join(Puppet[:environmentpath], 'fixtures', 'manifests', 'site.pp')
|
128
|
+
paths = [File.join(modulepath, test_module, 'manifests'), manifest]
|
129
|
+
else
|
130
|
+
paths = Puppet[:modulepath].split(File::PATH_SEPARATOR).map do |dir|
|
131
|
+
File.join(dir, test_module, 'manifests')
|
131
132
|
end
|
133
|
+
paths << Puppet[:manifest]
|
134
|
+
end
|
135
|
+
paths
|
136
|
+
end
|
132
137
|
|
133
|
-
|
134
|
-
|
135
|
-
|
138
|
+
def find(resource)
|
139
|
+
@collection[resource.to_s]
|
140
|
+
end
|
136
141
|
|
137
|
-
|
138
|
-
|
139
|
-
|
142
|
+
def exists?(resource)
|
143
|
+
!find(resource).nil?
|
144
|
+
end
|
145
|
+
|
146
|
+
class ResourceWrapper
|
147
|
+
attr_reader :resource
|
148
|
+
|
149
|
+
def initialize(resource = nil)
|
150
|
+
@resource = resource
|
151
|
+
end
|
152
|
+
|
153
|
+
def to_s
|
154
|
+
@resource.to_s
|
140
155
|
end
|
141
156
|
|
157
|
+
def to_hash
|
158
|
+
{
|
159
|
+
'touched' => touched?,
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
def touch!
|
164
|
+
@touched = true
|
165
|
+
end
|
166
|
+
|
167
|
+
def touched?
|
168
|
+
!!@touched
|
169
|
+
end
|
170
|
+
end
|
142
171
|
end
|
143
172
|
end
|