rspec-puppet 2.7.6 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: fa9949d6cb9b79c951ecd504f85344d100a40e70
4
- data.tar.gz: 2571507918bf8e77cc750501c6b24b0da1731c01
2
+ SHA256:
3
+ metadata.gz: 420f47c99ab3190a3e97c2b172e694694a5d997a56e70366bd168c8338c21fb9
4
+ data.tar.gz: 21f2d247e5169ba70e490d6b9c66fc8542a1c3c1cd99d1e5895a1322a9cfac1b
5
5
  SHA512:
6
- metadata.gz: a31eda49f0aa0933c87a9b90fdbd704103690b8f230e9eca9b9c5b785a889db60a2ccbbcba7c3d468c24dcd15b554c5f2bf27f3423e4b6b50782d3854e12b9d9
7
- data.tar.gz: bc3107e48272088eb124c8cca18cee099c44d0541ef8ebc9a323c93b1b158294be4f48e409d1a10d50403534b761799f37300efeae22449cf9a927eac5539c44
6
+ metadata.gz: a5b2ef185120131a07fc6e063a7ee2d2cb191db8a765bb5cdda1fce92081547c4d0aea83b386faaf1d20e8051d51d84ba05facc5f9deb74f3aecdeea67798b86
7
+ data.tar.gz: 4714dac2b13e5a4eb44a8c965a61e08cc7e2ded172271ea61f85bfae22d43f51dde5d609f0a1d3515844fb5924b48ad8441fbb071eef267c0af6f886c0e21c9c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,74 @@
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.9.0]
6
+
7
+ ### Added
8
+ * Allow users to disable app_management for Puppet 4
9
+ * Added support for regexp arguments to Sensitive
10
+ * Handle all auto*, not just autorequire
11
+ * Set up loaders so that 4.x functions resolve properly
12
+
13
+ ## [2.8.0]
14
+
15
+ ### Breaking Changes
16
+ * As of the 2.8.0 release, the `rspec-puppet` project no longer guarantees compatibility
17
+ with Puppet 2.x or 3.x (running under Ruby 1.8.7) or Puppet 4.x (running under Ruby 1.9.3).
18
+ * This release adds support for the [`Sensitive`](https://puppet.com/docs/puppet/latest/lang_data_sensitive.html)
19
+ data type, however existing tests that were expecting `String` content may need to be updated
20
+ to wrap the expected value in the new `sensitive` helper:
21
+
22
+ ```ruby
23
+ # Old
24
+ it { is_expected.to contain_file('/etc/mysecret.conf').with_content("top secret\n") }
25
+
26
+ # New
27
+ it { is_expected.to contain_file('/etc/mysecret.conf').with_content(sensitive("top secret\n")) }
28
+ ```
29
+
30
+ ### Added
31
+ * Added support for [trusted external fact data](https://github.com/rodjek/rspec-puppet#specifying-trusted-external-data).
32
+ * Added the ability to exclude resources from the coverage report calculations using a regular expression.
33
+ (See [documentation](https://rspec-puppet.com/documentation/coverage/#excluded-resources) for an example.
34
+ * Added `have_unique_values_for_all` matcher to assert a specific resource parameter value is unique across
35
+ the entire catalogue.
36
+ (See [documentation](https://rspec-puppet.com/documentation/classes/#test-resource-parameter-values-for-uniqueness).)
37
+ * Added ability to customize module-layer Hiera configuration via new settings. See
38
+ [documentation](https://rspec-puppet.com/documentation/configuration/#disable_module_hiera) for details.
39
+
40
+ ### Changed
41
+ * `RSpec::Puppet::Cache` now evicts least recently used entries when it reaches max size.
42
+ * `rspec-puppet`'s implementation of `match_manifests` will no longer look in `init.pp` for class
43
+ declarations if a manifest file exactly matching the class name exists.
44
+
45
+ ### Fixed
46
+ * Resolved compatibility issues with Ruby 2.7.x and added Ruby 2.7.x to the test matrix.
47
+ * Resolved issues calculating coverage and reporting results when there are 0 tested resources.
48
+ * Resolved compatibility issue with `rspec-expectations` 3.10.0.
49
+
50
+ ## [2.7.10]
51
+
52
+ ### Fixed
53
+ * Fix issues with removal of `default_env` method in Puppet 6.17.0.
54
+
55
+ ## [2.7.9]
56
+
57
+ This release had unintended breaking changes and was withdrawn.
58
+
59
+ ## [2.7.8]
60
+
61
+ ### Fixed
62
+ * Fix cross-platform testing for Puppet >= 6.9.0 when there is no `ipaddress6`
63
+ fact defined.
64
+
65
+ ## [2.7.7]
66
+
67
+ ### Fixed
68
+ * Fix the support for rspec-expectations >= 3.8.5.
69
+
70
+ ### Changed
71
+ * Remove the rspec-expectations dependency limit introduced in 2.7.6.
72
+
5
73
  ## [2.7.6]
6
74
 
7
75
  ### Changed
@@ -486,7 +554,36 @@ Thanks to Adrien Thebo, Alex Harvey, Brian, Dan Bode, Dominic Cleal, Javier Pala
486
554
  ## 1.0.1 and earlier
487
555
  For changelog of versions 1.0.1 and earlier, see http://rspec-puppet.com/changelog/
488
556
 
489
- [2.x]: https://github.com/rodjek/rspec-puppet/compare/v2.5.0...master
557
+ [2.x]: https://github.com/rodjek/rspec-puppet/compare/v2.9.0...master
558
+ [2.9.0]: https://github.com/rodjek/rspec-puppet/compare/v2.8.0...v2.9.0
559
+ [2.8.0]: https://github.com/rodjek/rspec-puppet/compare/v2.7.10...v2.8.0
560
+ [2.7.10]: https://github.com/rodjek/rspec-puppet/compare/v2.7.9...v2.7.10
561
+ [2.7.9]: https://github.com/rodjek/rspec-puppet/compare/v2.7.8...v2.7.9
562
+ [2.7.8]: https://github.com/rodjek/rspec-puppet/compare/v2.7.7...v2.7.8
563
+ [2.7.7]: https://github.com/rodjek/rspec-puppet/compare/v2.7.6...v2.7.7
564
+ [2.7.6]: https://github.com/rodjek/rspec-puppet/compare/v2.7.5...v2.7.6
565
+ [2.7.5]: https://github.com/rodjek/rspec-puppet/compare/v2.7.4...v2.7.5
566
+ [2.7.4]: https://github.com/rodjek/rspec-puppet/compare/v2.7.3...v2.7.4
567
+ [2.7.3]: https://github.com/rodjek/rspec-puppet/compare/v2.7.2...v2.7.3
568
+ [2.7.2]: https://github.com/rodjek/rspec-puppet/compare/v2.7.1...v2.7.2
569
+ [2.7.1]: https://github.com/rodjek/rspec-puppet/compare/v2.7.0...v2.7.1
570
+ [2.7.0]: https://github.com/rodjek/rspec-puppet/compare/v2.6.15...v2.7.0
571
+ [2.6.15]: https://github.com/rodjek/rspec-puppet/compare/v2.6.14...v2.6.15
572
+ [2.6.14]: https://github.com/rodjek/rspec-puppet/compare/v2.6.13...v2.6.14
573
+ [2.6.13]: https://github.com/rodjek/rspec-puppet/compare/v2.6.12...v2.6.13
574
+ [2.6.12]: https://github.com/rodjek/rspec-puppet/compare/v2.6.11...v2.6.12
575
+ [2.6.11]: https://github.com/rodjek/rspec-puppet/compare/v2.6.10...v2.6.11
576
+ [2.6.10]: https://github.com/rodjek/rspec-puppet/compare/v2.6.9...v2.6.10
577
+ [2.6.9]: https://github.com/rodjek/rspec-puppet/compare/v2.6.8...v2.6.9
578
+ [2.6.8]: https://github.com/rodjek/rspec-puppet/compare/v2.6.7...v2.6.8
579
+ [2.6.7]: https://github.com/rodjek/rspec-puppet/compare/v2.6.6...v2.6.7
580
+ [2.6.6]: https://github.com/rodjek/rspec-puppet/compare/v2.6.5...v2.6.6
581
+ [2.6.5]: https://github.com/rodjek/rspec-puppet/compare/v2.6.4...v2.6.5
582
+ [2.6.4]: https://github.com/rodjek/rspec-puppet/compare/v2.6.3...v2.6.4
583
+ [2.6.3]: https://github.com/rodjek/rspec-puppet/compare/v2.6.2...v2.6.3
584
+ [2.6.2]: https://github.com/rodjek/rspec-puppet/compare/v2.6.1...v2.6.2
585
+ [2.6.1]: https://github.com/rodjek/rspec-puppet/compare/v2.6.0...v2.6.1
586
+ [2.6.0]: https://github.com/rodjek/rspec-puppet/compare/2.5.0...v2.6.0
490
587
  [2.5.0]: https://github.com/rodjek/rspec-puppet/compare/v2.4.0...v2.5.0
491
588
  [2.4.0]: https://github.com/rodjek/rspec-puppet/compare/v2.3.2...v2.4.0
492
589
  [2.3.2]: https://github.com/rodjek/rspec-puppet/compare/v2.3.1...v2.3.2
data/README.md CHANGED
@@ -2,6 +2,20 @@
2
2
  [![Build Status](https://travis-ci.org/rodjek/rspec-puppet.svg?branch=master)](https://travis-ci.org/rodjek/rspec-puppet)
3
3
  [![Coverage Status](https://coveralls.io/repos/rodjek/rspec-puppet/badge.svg?branch=master)](https://coveralls.io/r/rodjek/rspec-puppet?branch=master)
4
4
 
5
+ #### Table of Contents
6
+
7
+ * [Installation](#installation)
8
+ * [Starting out with a new module](#starting-out-with-a-new-module)
9
+ * [Configure manifests for Puppet 4](#configure-manifests-for-puppet-4)
10
+ * [Configuration](#configuration)
11
+ * [Naming conventions](#naming-conventions)
12
+ * [Example groups](#example-groups)
13
+ * [Defined Types, Classes & Applications](#defined-types-classes--applications)
14
+ * [Functions](#functions)
15
+ * [Hiera integration](#hiera-integration)
16
+ * [Producing coverage reports](#producing-coverage-reports)
17
+ * [Related projects](#related-projects)
18
+
5
19
  ## Installation
6
20
 
7
21
  gem install rspec-puppet
@@ -759,6 +773,30 @@ RSpec.configure do |c|
759
773
  end
760
774
  ```
761
775
 
776
+ #### Specifying trusted external data
777
+
778
+ When testing with Puppet >= 6.14, the trusted facts hash will have an additional `external`
779
+ key for trusted external data.
780
+
781
+ By default, the test environment contains no trusted external data (as usually obtained from
782
+ trusted external commands and found in the `external` key). If you need to test against specific
783
+ trusted external data you can set those with a hash. The hash will then be available in
784
+ `$trusted['external']`
785
+
786
+ ```ruby
787
+ let(:trusted_external_data) { {'foo' => 'bar'} }
788
+ ```
789
+
790
+ You can also create a set of default trusted external data provided to all specs in your spec_helper:
791
+
792
+ ```ruby
793
+ RSpec.configure do |c|
794
+ c.default_trusted_external_data = {
795
+ 'foo' => 'bar'
796
+ }
797
+ end
798
+ ```
799
+
762
800
  #### Testing Exported Resources
763
801
 
764
802
  You can test if a resource was exported from the catalogue by using the
data/lib/rspec-puppet.rb CHANGED
@@ -42,6 +42,7 @@ RSpec.configure do |c|
42
42
  c.add_setting :default_facts, :default => {}
43
43
  c.add_setting :default_node_params, :default => {}
44
44
  c.add_setting :default_trusted_facts, :default => {}
45
+ c.add_setting :default_trusted_external_data, :default => {}
45
46
  c.add_setting :hiera_config, :default => Puppet::Util::Platform.actually_windows? ? 'c:/nul/' : '/dev/null'
46
47
  c.add_setting :parser, :default => 'current'
47
48
  c.add_setting :trusted_node_data, :default => false
@@ -54,6 +55,10 @@ RSpec.configure do |c|
54
55
  c.add_setting :platform, :default => Puppet::Util::Platform.actual_platform
55
56
  c.add_setting :vendormoduledir, :default => Puppet::Util::Platform.actually_windows? ? 'c:/nul/' : '/dev/null'
56
57
  c.add_setting :basemodulepath, :default => Puppet::Util::Platform.actually_windows? ? 'c:/nul/' : '/dev/null'
58
+ c.add_setting :disable_module_hiera, :default => false
59
+ c.add_setting :fixture_hiera_configs, :default => {}
60
+ c.add_setting :use_fixture_spec_hiera, :default => false
61
+ c.add_setting :fallback_to_default_hiera, :default => true
57
62
 
58
63
  c.instance_eval do
59
64
  def trusted_server_facts
@@ -139,7 +139,8 @@ module RSpec::Puppet
139
139
  Puppet.push_context(
140
140
  {
141
141
  :environments => loader,
142
- :current_environment => env
142
+ :current_environment => env,
143
+ :loaders => (Puppet::Pops::Loaders.new(env) if Gem::Version.new(Puppet.version) >= Gem::Version.new('6.0.0')),
143
144
  },
144
145
  "Setup rspec-puppet environments"
145
146
  )
@@ -11,12 +11,17 @@ module RSpec::Puppet
11
11
  end
12
12
 
13
13
  def get(*args, &blk)
14
- # decouple the hash key from whatever the blk might do to it
15
14
  key = Marshal.load(Marshal.dump(args))
16
- if !@cache.has_key? key
17
- @cache[key] = (blk || @default_proc).call(*args)
18
- @lra << key
15
+ if @cache.has_key?(key)
16
+ # Cache hit
17
+ # move that entry last to make it "most recenty used"
18
+ @lra.insert(-1, @lra.delete_at(@lra.index(args)))
19
+ else
20
+ # Cache miss
21
+ # Ensure room by evicting least recently used if no space left
19
22
  expire!
23
+ @cache[args] = (blk || @default_proc).call(*args)
24
+ @lra << args
20
25
  end
21
26
 
22
27
  @cache[key]
@@ -25,8 +30,8 @@ module RSpec::Puppet
25
30
  private
26
31
 
27
32
  def expire!
28
- expired = @lra.slice!(0, @lra.size - MAX_ENTRIES)
29
- expired.each { |key| @cache.delete(key) } if expired
33
+ # delete one entry (the oldest) when there is no room in cache
34
+ @cache.delete(@lra.shift) if @cache.size == MAX_ENTRIES
30
35
  end
31
36
  end
32
37
  end
@@ -17,13 +17,25 @@ end
17
17
  module RSpec::Puppet
18
18
  class Coverage
19
19
 
20
- attr_accessor :filters
20
+ attr_accessor :filters, :filters_regex
21
21
 
22
22
  class << self
23
23
  extend Forwardable
24
- def_delegators(:instance, :add, :cover!, :report!,
25
- :filters, :add_filter, :add_from_catalog,
26
- :results)
24
+
25
+ delegated_methods = [
26
+ :instance,
27
+ :add,
28
+ :cover!,
29
+ :report!,
30
+ :filters,
31
+ :filters_regex,
32
+ :add_filter,
33
+ :add_filter_regex,
34
+ :add_from_catalog,
35
+ :results,
36
+ ]
37
+
38
+ def_delegators(*delegated_methods)
27
39
 
28
40
  attr_writer :instance
29
41
 
@@ -35,6 +47,7 @@ module RSpec::Puppet
35
47
  def initialize
36
48
  @collection = {}
37
49
  @filters = ['Stage[main]', 'Class[Settings]', 'Class[main]', 'Node[default]']
50
+ @filters_regex = []
38
51
  end
39
52
 
40
53
  def save_results
@@ -42,6 +55,9 @@ module RSpec::Puppet
42
55
  File.open(File.join(Dir.tmpdir, "rspec-puppet-filter-#{slug}"), 'w+') do |f|
43
56
  f.puts @filters.to_json
44
57
  end
58
+ File.open(File.join(Dir.tmpdir, "rspec-puppet-filter_regex-#{slug}"), 'w+') do |f|
59
+ f.puts @filters_regex.to_json
60
+ end
45
61
  File.open(File.join(Dir.tmpdir, "rspec-puppet-coverage-#{slug}"), 'w+') do |f|
46
62
  f.puts @collection.to_json
47
63
  end
@@ -57,10 +73,17 @@ module RSpec::Puppet
57
73
 
58
74
  def merge_filters
59
75
  pattern = File.join(Dir.tmpdir, "rspec-puppet-filter-#{Digest::MD5.hexdigest(Dir.pwd)}-*")
76
+ regex_filter_pattern = File.join(Dir.tmpdir, "rspec-puppet-filter_regex-#{Digest::MD5.hexdigest(Dir.pwd)}-*")
77
+
60
78
  Dir[pattern].each do |result_file|
61
79
  load_filters(result_file)
62
80
  FileUtils.rm(result_file)
63
81
  end
82
+
83
+ Dir[regex_filter_pattern].each do |result_file|
84
+ load_filters_regex(result_file)
85
+ FileUtils.rm(result_file)
86
+ end
64
87
  end
65
88
 
66
89
  def load_results(path)
@@ -79,6 +102,15 @@ module RSpec::Puppet
79
102
  end
80
103
  end
81
104
 
105
+ def load_filters_regex(path)
106
+ saved_regex_filters = JSON.parse(File.read(path))
107
+ saved_regex_filters.each do |pattern|
108
+ regex = Regexp.new(pattern)
109
+ @filters_regex << regex
110
+ @collection.delete_if { |resource, _| resource =~ regex }
111
+ end
112
+ end
113
+
82
114
  def add(resource)
83
115
  if !exists?(resource) && !filtered?(resource)
84
116
  @collection[resource.to_s] = ResourceWrapper.new(resource)
@@ -86,11 +118,8 @@ module RSpec::Puppet
86
118
  end
87
119
 
88
120
  def add_filter(type, title)
89
- def capitalize_name(name)
90
- name.split('::').map { |subtitle| subtitle.capitalize }.join('::')
91
- end
92
-
93
121
  type = capitalize_name(type)
122
+
94
123
  if type == 'Class'
95
124
  title = capitalize_name(title)
96
125
  end
@@ -98,6 +127,34 @@ module RSpec::Puppet
98
127
  @filters << "#{type}[#{title}]"
99
128
  end
100
129
 
130
+ def add_filter_regex(type, pattern)
131
+ raise ArgumentError.new('pattern argument must be a Regexp') unless pattern.is_a?(Regexp)
132
+
133
+ type = capitalize_name(type)
134
+
135
+ # avoid recompiling the regular expression during processing
136
+ src = pattern.source
137
+
138
+ # switch from anchors to wildcards since it is embedded into a larger pattern
139
+ src = if src.start_with?('\\A', '^')
140
+ src.gsub(/\A(?:\\A|\^)/, '')
141
+ else
142
+ # no anchor at the start
143
+ ".*#{src}"
144
+ end
145
+
146
+ # match an even number of backslashes before the anchor - this indicates that the anchor was not escaped
147
+ # note the necessity for the negative lookbehind `(?<!)` to assert that there is no backslash before this
148
+ src = if src.match(/(?<!\\)(\\\\)*(?:\\[zZ]|\$)\z/)
149
+ src.gsub(/(?:\\[zZ]|\$)\z/, '')
150
+ else
151
+ # no anchor at the end
152
+ "#{src}.*"
153
+ end
154
+
155
+ @filters_regex << /\A#{Regexp.escape(type)}\[#{src}\]\z/
156
+ end
157
+
101
158
  # add all resources from catalog declared in module test_module
102
159
  def add_from_catalog(catalog, test_module)
103
160
  coverable_resources = catalog.to_a.reject { |resource| !test_module.nil? && filter_resource?(resource, test_module) }
@@ -107,7 +164,10 @@ module RSpec::Puppet
107
164
  end
108
165
 
109
166
  def filtered?(resource)
110
- filters.include?(resource.to_s)
167
+ return true if filters.include?(resource.to_s)
168
+ return true if filters_regex.any? { |f| resource.to_s =~ f }
169
+
170
+ false
111
171
  end
112
172
 
113
173
  def cover!(resource)
@@ -136,7 +196,7 @@ module RSpec::Puppet
136
196
  end
137
197
 
138
198
  def run_report(coverage_desired = nil)
139
- if ENV['TEST_ENV_NUMBER']
199
+ if parallel_tests?
140
200
  merge_filters
141
201
  merge_results
142
202
  end
@@ -145,7 +205,7 @@ module RSpec::Puppet
145
205
 
146
206
  coverage_test(coverage_desired, report)
147
207
 
148
- puts report[:text]
208
+ puts "\n\nCoverage Report:\n\n#{report[:text]}"
149
209
  end
150
210
 
151
211
  def coverage_test(coverage_desired, report)
@@ -187,7 +247,9 @@ module RSpec::Puppet
187
247
  report[:total] = @collection.size
188
248
  report[:touched] = @collection.count { |_, resource| resource.touched? }
189
249
  report[:untouched] = report[:total] - report[:touched]
190
- report[:coverage] = "%5.2f" % ((report[:touched].to_f / report[:total].to_f) * 100)
250
+
251
+ coverage = report[:total].to_f > 0 ? ((report[:touched].to_f / report[:total].to_f) * 100) : 100.0
252
+ report[:coverage] = "%5.2f" % coverage
191
253
 
192
254
  report[:resources] = Hash[*@collection.map do |name, wrapper|
193
255
  [name, wrapper.to_hash]
@@ -226,7 +288,7 @@ module RSpec::Puppet
226
288
  # @param test_module [String] The name of the module under test
227
289
  # @return [true, false]
228
290
  def filter_resource?(resource, test_module)
229
- if @filters.include?(resource.to_s)
291
+ if filtered?(resource)
230
292
  return true
231
293
  end
232
294
 
@@ -267,6 +329,10 @@ module RSpec::Puppet
267
329
  !find(resource).nil?
268
330
  end
269
331
 
332
+ def capitalize_name(name)
333
+ name.split('::').map { |subtitle| subtitle.capitalize }.join('::')
334
+ end
335
+
270
336
  class ResourceWrapper
271
337
  attr_reader :resource
272
338
 
@@ -6,3 +6,5 @@ require 'rspec-puppet/matchers/count_generic'
6
6
  require 'rspec-puppet/matchers/dynamic_matchers'
7
7
  require 'rspec-puppet/matchers/type_matchers'
8
8
  require 'rspec-puppet/matchers/allow_value'
9
+ require 'rspec-puppet/matchers/raise_error'
10
+ require 'rspec-puppet/matchers/unique_values'
@@ -34,6 +34,14 @@ module RSpec::Puppet
34
34
  def failure_message_when_negated
35
35
  "expected that the type alias would not " + description + " but it does"
36
36
  end
37
+
38
+ def supports_block_expectations
39
+ true
40
+ end
41
+
42
+ def supports_value_expectations
43
+ true
44
+ end
37
45
  end
38
46
 
39
47
  def allow_value(*values)
@@ -78,6 +78,14 @@ module RSpec::Puppet
78
78
  end
79
79
  end
80
80
 
81
+ def supports_block_expectations
82
+ true
83
+ end
84
+
85
+ def supports_value_expectations
86
+ true
87
+ end
88
+
81
89
  private
82
90
  def missing_dependencies?
83
91
  retval = false
@@ -7,13 +7,15 @@ module RSpec::Puppet
7
7
  'Stage[main]',
8
8
  ].freeze
9
9
 
10
+ attr_reader :resource_type
11
+
10
12
  def initialize(type, count, *method)
11
13
  if type.nil?
12
14
  @type = method[0].to_s.gsub(/^have_(.+)_resource_count$/, '\1')
13
15
  else
14
16
  @type = type
15
17
  end
16
- @referenced_type = referenced_type(@type)
18
+ @resource_type = referenced_type(@type)
17
19
  @expected_number = count.to_i
18
20
  end
19
21
 
@@ -30,7 +32,7 @@ module RSpec::Puppet
30
32
  end
31
33
  else
32
34
  resources.count do |res|
33
- res.type == @referenced_type
35
+ res.type == @resource_type
34
36
  end
35
37
  end
36
38
 
@@ -45,7 +47,7 @@ module RSpec::Puppet
45
47
  desc << "#{@expected_number == 1 ? "class" : "classes" }"
46
48
  else
47
49
  unless @type == "resource"
48
- desc << "#{@referenced_type}"
50
+ desc << "#{@resource_type}"
49
51
  end
50
52
  desc << "#{@expected_number == 1 ? "resource" : "resources" }"
51
53
  end
@@ -61,6 +63,14 @@ module RSpec::Puppet
61
63
  "expected that the catalogue would not " + description + " but it does"
62
64
  end
63
65
 
66
+ def supports_block_expectations
67
+ true
68
+ end
69
+
70
+ def supports_value_expectations
71
+ true
72
+ end
73
+
64
74
  private
65
75
 
66
76
  def referenced_type(type)
@@ -89,6 +89,11 @@ module RSpec::Puppet
89
89
  else
90
90
  RSpec::Puppet::Coverage.cover!(resource)
91
91
  rsrc_hsh = resource.to_hash
92
+ if resource.respond_to?(:sensitive_parameters)
93
+ resource.sensitive_parameters.each do |s_param|
94
+ rsrc_hsh[s_param] = ::Puppet::Pops::Types::PSensitiveType::Sensitive.new(rsrc_hsh[s_param])
95
+ end
96
+ end
92
97
 
93
98
  if resource.builtin_type?
94
99
  namevar = resource.resource_type.key_attributes.first.to_s
@@ -177,6 +182,14 @@ module RSpec::Puppet
177
182
  true
178
183
  end
179
184
 
185
+ def supports_block_expectations
186
+ true
187
+ end
188
+
189
+ def supports_value_expectations
190
+ true
191
+ end
192
+
180
193
  def expected
181
194
  @errors.map {|e| e.expected if e.respond_to?(:expected)}.compact.join("\n\n")
182
195
  end
@@ -294,14 +307,17 @@ module RSpec::Puppet
294
307
  end
295
308
  end
296
309
 
297
- # Add autorequires if any
298
- if type == :require and resource.resource_type.respond_to? :eachautorequire
299
- resource.resource_type.eachautorequire do |t, b|
300
- Array(resource.to_ral.instance_eval(&b)).each do |dep|
301
- res = "#{t.to_s.capitalize}[#{dep}]"
302
- if r = relationship_refs(res, type, visited)
303
- results << res
304
- results << r
310
+ # Add auto* (autorequire etc) if any
311
+ if [:before, :notify, :require, :subscribe].include?(type)
312
+ func = "eachauto#{type}".to_sym
313
+ if resource.resource_type.respond_to?(func)
314
+ resource.resource_type.send(func) do |t, b|
315
+ Array(resource.to_ral.instance_eval(&b)).each do |dep|
316
+ res = "#{t.to_s.capitalize}[#{dep}]"
317
+ if r = relationship_refs(res, type, visited)
318
+ results << res
319
+ results << r
320
+ end
305
321
  end
306
322
  end
307
323
  end
@@ -4,7 +4,7 @@ module RSpec::Puppet
4
4
 
5
5
  matcher :include_class do |expected_class|
6
6
  match do |catalogue|
7
- RSpec.deprecate(:include_class, :replacement => :contain_class)
7
+ RSpec.deprecate('include_class()', :replacement => 'contain_class()')
8
8
  catalogue.call.classes.include?(expected_class)
9
9
  end
10
10
 
@@ -22,6 +22,13 @@ module RSpec::Puppet
22
22
  end
23
23
  end
24
24
 
25
+ def supports_block_expectations
26
+ true
27
+ end
28
+
29
+ def supports_value_expectations
30
+ true
31
+ end
25
32
  end
26
33
 
27
34
  end
@@ -21,10 +21,7 @@ module RSpec::Puppet
21
21
  #
22
22
  # @return [true, false]
23
23
  def matches?(resource)
24
-
25
- @resource = resource
26
-
27
- actual = @resource[@parameter]
24
+ actual = resource[@parameter]
28
25
  expected = @value
29
26
 
30
27
  # Puppet flattens an array with a single value into just the value and
@@ -65,6 +62,8 @@ module RSpec::Puppet
65
62
  check_hash(expected, actual)
66
63
  when Array
67
64
  check_array(expected, actual)
65
+ when RSpec::Puppet::Sensitive
66
+ expected == actual
68
67
  else
69
68
  check_string(expected, actual)
70
69
  end
@@ -0,0 +1,23 @@
1
+ module RSpec::Puppet
2
+ module GenericMatchers
3
+ # Due to significant code base depending on the
4
+ #
5
+ # is_expected.to raise_error Puppet::Error
6
+ #
7
+ # syntax, and removal of this syntax from RSpec, extend RSpec's built-in
8
+ # `raise_error` matcher to accept a value target, e.g. a subject defined
9
+ # as a lambda, e.g.:
10
+ #
11
+ # subject(:catalogue) { lambda { load_catalogue } }
12
+ #
13
+ class RaiseError < RSpec::Matchers::BuiltIn::RaiseError
14
+ def supports_value_expectations?
15
+ true
16
+ end
17
+ end
18
+
19
+ def raise_error(error=defined?(RSpec::Matchers::BuiltIn::RaiseError::UndefinedValue) ? RSpec::Matchers::BuiltIn::RaiseError::UndefinedValue : nil, message=nil, &block)
20
+ RaiseError.new(error, message, &block)
21
+ end
22
+ end
23
+ end
@@ -104,6 +104,14 @@ module RSpec::Puppet
104
104
  end
105
105
  end
106
106
 
107
+ def supports_block_expectations
108
+ true
109
+ end
110
+
111
+ def supports_value_expectations
112
+ true
113
+ end
114
+
107
115
  private
108
116
  def func_name
109
117
  @func_obj.func_name
@@ -52,9 +52,6 @@ module RSpec::Puppet
52
52
  self
53
53
  end
54
54
 
55
- #def with_autorequires(autorequires))
56
- #end
57
-
58
55
  #
59
56
  # this is the method that drives all of the validation
60
57
  #
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module Puppet
5
+ module ManifestMatchers
6
+ extend RSpec::Matchers::DSL
7
+
8
+ matcher :have_unique_values_for_all do |type, attribute|
9
+ match do |catalogue|
10
+ catalogue.call.resources.select { |rsrc| rsrc.type == type.capitalize }
11
+ .group_by { |rsrc| rsrc[attribute.to_sym] }
12
+ .all? { |_, group| group.size == 1 }
13
+ end
14
+
15
+ description do
16
+ "have unique attribute values for #{type.capitalize}[#{attribute.to_sym}]"
17
+ end
18
+
19
+ if RSpec::Version::STRING < '3'
20
+ failure_message_for_should do |_actual|
21
+ "expected that the catalogue would have no duplicate values for #{type.capitalize}[#{attribute.to_sym}]"
22
+ end
23
+ else
24
+ failure_message do |_actual|
25
+ "expected that the catalogue would have no duplicate values for #{type.capitalize}[#{attribute.to_sym}]"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -132,6 +132,16 @@ module Puppet
132
132
  end
133
133
 
134
134
  module Util
135
+ # Fix for removal of default_env function
136
+ # Bug: https://github.com/rodjek/rspec-puppet/issues/796
137
+ # Upstream: https://github.com/puppetlabs/puppet/commit/94df3c1a3992d89b2d7d5db8a70373c135bdd86b
138
+ if !respond_to?(:default_env)
139
+ def default_env()
140
+ DEFAULT_ENV
141
+ end
142
+ module_function :default_env
143
+ end
144
+
135
145
  if respond_to?(:get_env)
136
146
  alias :old_get_env :get_env
137
147
  module_function :old_get_env
@@ -258,6 +268,40 @@ module Puppet
258
268
  end
259
269
  end
260
270
  end
271
+
272
+ if Puppet::Util::Package.versioncmp(Puppet.version, '4.9.0') >= 0
273
+ class Module
274
+ old_hiera_conf_file = instance_method(:hiera_conf_file)
275
+ define_method(:hiera_conf_file) do
276
+ if RSpec::Puppet.rspec_puppet_example?
277
+ if RSpec.configuration.disable_module_hiera
278
+ return nil
279
+ elsif RSpec.configuration.fixture_hiera_configs.key?(name)
280
+ config = RSpec.configuration.fixture_hiera_configs[name]
281
+ config = File.absolute_path(config, path) unless config.nil?
282
+ return config
283
+ elsif RSpec.configuration.use_fixture_spec_hiera
284
+ config = RSpec::Puppet.current_example.fixture_spec_hiera_conf(self)
285
+ return config unless config.nil? && RSpec.configuration.fallback_to_default_hiera
286
+ end
287
+ end
288
+ old_hiera_conf_file.bind(self).call
289
+ end
290
+ end
291
+
292
+ class Pops::Lookup::ModuleDataProvider
293
+ old_configuration_path = instance_method(:configuration_path)
294
+ define_method(:configuration_path) do |lookup_invocation|
295
+ if RSpec::Puppet.rspec_puppet_example?
296
+ env = lookup_invocation.scope.environment
297
+ mod = env.module(module_name)
298
+ raise Puppet::DataBinding::LookupError, _("Environment '%{env}', cannot find module '%{module_name}'") % { :env => env.name, :module_name => module_name } unless mod
299
+ return Pathname.new(mod.hiera_conf_file)
300
+ end
301
+ old_configuration_path.bind(self).call(lookup_invocation)
302
+ end
303
+ end
304
+ end
261
305
  end
262
306
 
263
307
  class Pathname
@@ -292,6 +336,24 @@ class Pathname
292
336
  end
293
337
  end
294
338
 
339
+ # Puppet loads init.pp, then foo.pp, to find class "mod::foo". If
340
+ # class "mod" has been mocked using pre_condition when testing
341
+ # "mod::foo", this causes duplicate declaration for "mod".
342
+ # This monkey patch only loads "init.pp" if "foo.pp" does not exist.
343
+ class Puppet::Module
344
+ if [:match_manifests, 'match_manifests'].any? { |r| instance_methods.include?(r) }
345
+ old_match_manifests = instance_method(:match_manifests)
346
+
347
+ define_method(:match_manifests) do |rest|
348
+ result = old_match_manifests.bind(self).call(rest)
349
+ if result.length > 1 && File.basename(result[0]) == 'init.pp'
350
+ result.shift
351
+ end
352
+ result
353
+ end
354
+ end
355
+ end
356
+
295
357
  # Prevent the File type from munging paths (which uses File.expand_path to
296
358
  # normalise paths, which does very bad things to *nix paths on Windows.
297
359
  file_path_munge = Puppet::Type.type(:file).paramclass(:path).instance_method(:unsafe_munge)
@@ -0,0 +1,51 @@
1
+ module RSpec::Puppet
2
+ if defined?(::Puppet::Pops::Types::PSensitiveType::Sensitive)
3
+ # A wrapper representing Sensitive data type, eg. in class params.
4
+ class Sensitive < ::Puppet::Pops::Types::PSensitiveType::Sensitive
5
+ # Create a new Sensitive object
6
+ # @param [Object] value to wrap
7
+ def initialize(value)
8
+ @value = value
9
+ end
10
+
11
+ # @return the wrapped value
12
+ def unwrap
13
+ @value
14
+ end
15
+
16
+ # @return true
17
+ def sensitive?
18
+ true
19
+ end
20
+
21
+ # @return inspect of the wrapped value, inside Sensitive()
22
+ def inspect
23
+ "Sensitive(#{@value.inspect})"
24
+ end
25
+
26
+ # Check for equality with another value.
27
+ # If compared to Puppet Sensitive type, it compares the wrapped values.
28
+
29
+ # @param other [#unwrap, Object] value to compare to
30
+ def == other
31
+ if other.respond_to? :unwrap
32
+ if unwrap.kind_of?(Regexp)
33
+ return unwrap =~ other.unwrap
34
+ else
35
+ return unwrap == other.unwrap
36
+ end
37
+ else
38
+ super
39
+ end
40
+ end
41
+ end
42
+ else
43
+ #:nocov:
44
+ class Sensitive
45
+ def initialize(value)
46
+ raise 'The use of the Sensitive data type is not supported by this Puppet version'
47
+ end
48
+ end
49
+ #:nocov:
50
+ end
51
+ end
@@ -1,10 +1,14 @@
1
1
  require 'rspec-puppet/cache'
2
2
  require 'rspec-puppet/adapters'
3
3
  require 'rspec-puppet/raw_string'
4
+ require 'rspec-puppet/sensitive'
4
5
 
5
6
  module RSpec::Puppet
6
7
  module Support
8
+ include GenericMatchers
9
+
7
10
  @@cache = RSpec::Puppet::Cache.new
11
+ @@fixture_hiera_configs = Hash.new { |h, k| h[k] = nil }
8
12
 
9
13
  def subject
10
14
  lambda { catalogue }
@@ -84,10 +88,29 @@ module RSpec::Puppet
84
88
  hiera_config_value = self.respond_to?(:hiera_config) ? hiera_config : nil
85
89
  hiera_data_value = self.respond_to?(:hiera_data) ? hiera_data : nil
86
90
 
91
+ rspec_config_values = [
92
+ :trusted_server_facts,
93
+ :disable_module_hiera,
94
+ :use_fixture_spec_hiera,
95
+ :fixture_hiera_configs,
96
+ :fallback_to_default_hiera,
97
+ ].map { |setting| RSpec.configuration.send(setting) }
98
+
87
99
  build_facts = facts_hash(node_name)
88
- catalogue = build_catalog(node_name, build_facts, trusted_facts_hash(node_name), hiera_config_value,
89
- build_code(type, manifest_opts), exported, node_params_hash, hiera_data_value,
90
- RSpec.configuration.trusted_server_facts)
100
+ catalogue = build_catalog(
101
+ nodename: node_name,
102
+ facts_val: build_facts,
103
+ trusted_facts_val: trusted_facts_hash(node_name),
104
+ hiera_config_val: hiera_config_value,
105
+ code: build_code(type, manifest_opts),
106
+ exported: exported,
107
+ node_params: node_params_hash,
108
+ trusted_external_data: trusted_external_data_hash,
109
+ ignored_cache_params: {
110
+ hiera_data_value: hiera_data_value,
111
+ rspec_config_values: rspec_config_values,
112
+ }
113
+ )
91
114
 
92
115
  test_module = type == :host ? nil : class_name.split('::').first
93
116
  if type == :define
@@ -224,10 +247,11 @@ module RSpec::Puppet
224
247
  }
225
248
 
226
249
  node_facts = {
227
- 'hostname' => node.split('.').first,
228
- 'fqdn' => node,
229
- 'domain' => node.split('.', 2).last,
230
- 'clientcert' => node,
250
+ 'hostname' => node.split('.').first,
251
+ 'fqdn' => node,
252
+ 'domain' => node.split('.', 2).last,
253
+ 'clientcert' => node,
254
+ 'ipaddress6' => 'FE80:0000:0000:0000:AAAA:AAAA:AAAA',
231
255
  }
232
256
 
233
257
  networking_facts = {
@@ -290,6 +314,19 @@ module RSpec::Puppet
290
314
  extensions
291
315
  end
292
316
 
317
+ def trusted_external_data_hash
318
+ return {} unless Puppet::Util::Package.versioncmp(Puppet.version, '6.14.0') >= 0
319
+
320
+ external_data = {}
321
+
322
+ if RSpec.configuration.default_trusted_external_data.any?
323
+ external_data.merge!(munge_facts(RSpec.configuration.default_trusted_external_data))
324
+ end
325
+
326
+ external_data.merge!(munge_facts(trusted_external_data)) if self.respond_to?(:trusted_external_data)
327
+ external_data
328
+ end
329
+
293
330
  def server_facts_hash
294
331
  server_facts = {}
295
332
 
@@ -355,7 +392,7 @@ module RSpec::Puppet
355
392
 
356
393
  # Enable app_management by default for Puppet versions that support it
357
394
  if Puppet::Util::Package.versioncmp(Puppet.version, '4.3.0') >= 0 && Puppet.version.to_i < 5
358
- Puppet[:app_management] = true
395
+ Puppet[:app_management] = ENV.include?('PUPPET_NOAPP_MANAGMENT') ? false : true
359
396
  end
360
397
 
361
398
  adapter.modulepath.map do |d|
@@ -377,6 +414,29 @@ module RSpec::Puppet
377
414
  end
378
415
 
379
416
  def build_catalog_without_cache(nodename, facts_val, trusted_facts_val, hiera_config_val, code, exported, node_params, *_)
417
+ build_catalog_without_cache_v2({
418
+ nodename: nodename,
419
+ facts_val: facts_val,
420
+ trusted_facts_val: trusted_facts_val,
421
+ hiera_config_val: hiera_config_val,
422
+ code: code,
423
+ exported: exported,
424
+ node_params: node_params,
425
+ trusted_external: {},
426
+ })
427
+ end
428
+
429
+ def build_catalog_without_cache_v2(
430
+ nodename: nil,
431
+ facts_val: nil,
432
+ trusted_facts_val: nil,
433
+ hiera_config_val: nil,
434
+ code: nil,
435
+ exported: nil,
436
+ node_params: nil,
437
+ trusted_external_data: nil,
438
+ ignored_cache_params: {}
439
+ )
380
440
 
381
441
  # If we're going to rebuild the catalog, we should clear the cached instance
382
442
  # of Hiera that Puppet is using. This opens the possibility of the catalog
@@ -398,10 +458,14 @@ module RSpec::Puppet
398
458
 
399
459
  node_obj = Puppet::Node.new(nodename, { :parameters => node_params, :facts => node_facts })
400
460
 
461
+ trusted_info = ['remote', nodename, trusted_facts_val]
462
+ if Puppet::Util::Package.versioncmp(Puppet.version, '6.14.0') >= 0
463
+ trusted_info.push(trusted_external_data)
464
+ end
401
465
  if Puppet::Util::Package.versioncmp(Puppet.version, '4.3.0') >= 0
402
466
  Puppet.push_context(
403
467
  {
404
- :trusted_information => Puppet::Context::TrustedInformation.new('remote', nodename, trusted_facts_val)
468
+ :trusted_information => Puppet::Context::TrustedInformation.new(*trusted_info)
405
469
  },
406
470
  "Context for spec trusted hash"
407
471
  )
@@ -420,7 +484,11 @@ module RSpec::Puppet
420
484
 
421
485
  def build_catalog(*args)
422
486
  @@cache.get(*args) do |*args|
423
- build_catalog_without_cache(*args)
487
+ if args.length == 1 && args.first.is_a?(Hash)
488
+ build_catalog_without_cache_v2(args.first)
489
+ else
490
+ build_catalog_without_cache(*args)
491
+ end
424
492
  end
425
493
  end
426
494
 
@@ -437,8 +505,7 @@ module RSpec::Puppet
437
505
  end
438
506
 
439
507
  def escape_special_chars(string)
440
- string.gsub!(/\$/, "\\$")
441
- string
508
+ string.gsub(/\$/, "\\$")
442
509
  end
443
510
 
444
511
  def rspec_compatibility
@@ -449,6 +516,23 @@ module RSpec::Puppet
449
516
  end
450
517
  end
451
518
 
519
+ def fixture_spec_hiera_conf(mod)
520
+ return @@fixture_hiera_configs[mod.name] if @@fixture_hiera_configs.key?(mod.name)
521
+
522
+ path = Pathname.new(mod.path)
523
+ if path.join('spec').exist?
524
+ path.join('spec').find do |file|
525
+ Find.prune if %w[modules work-dir].any? do |dir|
526
+ file.relative_path_from(path).to_s.start_with?("spec/fixtures/#{dir}")
527
+ end
528
+ if file.basename.to_s.eql?(Puppet::Pops::Lookup::HieraConfig::CONFIG_FILE_NAME)
529
+ return @@fixture_hiera_configs[mod.name] = file.to_s
530
+ end
531
+ end
532
+ end
533
+ @@fixture_hiera_configs[mod.name]
534
+ end
535
+
452
536
  # Helper to return a resource/node reference, so it gets translated in params to a raw string
453
537
  # without quotes.
454
538
  #
@@ -459,6 +543,14 @@ module RSpec::Puppet
459
543
  return RSpec::Puppet::RawString.new("#{type}['#{title}']")
460
544
  end
461
545
 
546
+ # Helper to return value wrapped in Sensitive type.
547
+ #
548
+ # @param [Object] value to wrap
549
+ # @return [RSpec::Puppet::Sensitive] a new Sensitive wrapper with the new value
550
+ def sensitive(value)
551
+ return RSpec::Puppet::Sensitive.new(value)
552
+ end
553
+
462
554
  # @!attribute [r] adapter
463
555
  # @api private
464
556
  # @return [Class < RSpec::Puppet::Adapters::Base]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-puppet
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.6
4
+ version: 2.9.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: 2019-10-04 00:00:00.000000000 Z
11
+ date: 2021-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: rspec-expectations
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "<"
32
- - !ruby/object:Gem::Version
33
- version: 3.8.5
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "<"
39
- - !ruby/object:Gem::Version
40
- version: 3.8.5
41
27
  description: RSpec tests for your Puppet manifests
42
28
  email: tim@sharpe.id.au
43
29
  executables:
@@ -71,14 +57,17 @@ files:
71
57
  - lib/rspec-puppet/matchers/dynamic_matchers.rb
72
58
  - lib/rspec-puppet/matchers/include_class.rb
73
59
  - lib/rspec-puppet/matchers/parameter_matcher.rb
60
+ - lib/rspec-puppet/matchers/raise_error.rb
74
61
  - lib/rspec-puppet/matchers/run.rb
75
62
  - lib/rspec-puppet/matchers/type_matchers.rb
63
+ - lib/rspec-puppet/matchers/unique_values.rb
76
64
  - lib/rspec-puppet/monkey_patches.rb
77
65
  - lib/rspec-puppet/monkey_patches/win32/registry.rb
78
66
  - lib/rspec-puppet/monkey_patches/win32/taskscheduler.rb
79
67
  - lib/rspec-puppet/monkey_patches/windows/taskschedulerconstants.rb
80
68
  - lib/rspec-puppet/rake_task.rb
81
69
  - lib/rspec-puppet/raw_string.rb
70
+ - lib/rspec-puppet/sensitive.rb
82
71
  - lib/rspec-puppet/setup.rb
83
72
  - lib/rspec-puppet/spec_helper.rb
84
73
  - lib/rspec-puppet/support.rb
@@ -102,8 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
91
  - !ruby/object:Gem::Version
103
92
  version: '0'
104
93
  requirements: []
105
- rubyforge_project:
106
- rubygems_version: 2.6.14.4
94
+ rubygems_version: 3.2.5
107
95
  signing_key:
108
96
  specification_version: 4
109
97
  summary: RSpec tests for your Puppet manifests