rspec-puppet 2.7.5 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e33fa5eda2bf9349af429c677c62cd0c3456bbb81c77cf8ecaf03a1a4af31a14
4
- data.tar.gz: b7693cd9f85ba31b5c952d75c0afd04fdfe1f9670221d9379eadcb16870b4bc3
3
+ metadata.gz: 375cf91b75c36ee1bc0bbade72608205a7b85fce94a49d0fcfafb02fa6f8eddc
4
+ data.tar.gz: 732ce59e27a5fd9d15052262d21b2de6df85b090fb56152aca88da4480133ac1
5
5
  SHA512:
6
- metadata.gz: c277a6e3f6c6271e0d7e301a8dc973d77d7c8805101f64487e875bbb60e47ce791948c567a72dd57fc837ef8467fc9c043d14980f848a7d561772ece677c1115
7
- data.tar.gz: 386013fdab15bb3299e83d0a22504cf3d80c7dc5c0c1bc74233a5e8b3973f8f70f60d7f813405fb2013d1077297cb10cf4f23bdfd632f503e8b9380fb4144b2b
6
+ metadata.gz: 3f1b0bb5c052df4ff70981384ad81bdc2e75ec1f23f9a890f101a933a4f7f50d569acef001524c7c3a9f37f0cfffd2eeaf0a26a7d67f8a3abd80b7147005e758
7
+ data.tar.gz: 65f0fbe718948c13b1866bb8d71c6b03fe741927ed86e97d6eeefb39526e98671e3ac5ae98e14e939654988fa8c272b3e72f66c5b61e9c6321fac11d5b66e276
@@ -2,6 +2,72 @@
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.8.0]
6
+
7
+ ### Breaking Changes
8
+ * As of the 2.8.0 release, the `rspec-puppet` project no longer guarantees compatibility
9
+ with Puppet 2.x or 3.x (running under Ruby 1.8.7) or Puppet 4.x (running under Ruby 1.9.3).
10
+ * This release adds support for the [`Sensitive`](https://puppet.com/docs/puppet/latest/lang_data_sensitive.html)
11
+ data type, however existing tests that were expecting `String` content may need to be updated
12
+ to wrap the expected value in the new `sensitive` helper:
13
+
14
+ ```ruby
15
+ # Old
16
+ it { is_expected.to contain_file('/etc/mysecret.conf').with_content("top secret\n") }
17
+
18
+ # New
19
+ it { is_expected.to contain_file('/etc/mysecret.conf').with_content(sensitive("top secret\n")) }
20
+ ```
21
+
22
+ ### Added
23
+ * Added support for [trusted external fact data](https://github.com/rodjek/rspec-puppet#specifying-trusted-external-data).
24
+ * Added the ability to exclude resources from the coverage report calculations using a regular expression.
25
+ (See [documentation](https://rspec-puppet.com/documentation/coverage/#excluded-resources) for an example.
26
+ * Added `have_unique_values_for_all` matcher to assert a specific resource parameter value is unique across
27
+ the entire catalogue.
28
+ (See [documentation](https://rspec-puppet.com/documentation/classes/#test-resource-parameter-values-for-uniqueness).)
29
+ * Added ability to customize module-layer Hiera configuration via new settings. See
30
+ [documentation](https://rspec-puppet.com/documentation/configuration/#disable_module_hiera) for details.
31
+
32
+ ### Changed
33
+ * `RSpec::Puppet::Cache` now evicts least recently used entries when it reaches max size.
34
+ * `rspec-puppet`'s implementation of `match_manifests` will no longer look in `init.pp` for class
35
+ declarations if a manifest file exactly matching the class name exists.
36
+
37
+ ### Fixed
38
+ * Resolved compatibility issues with Ruby 2.7.x and added Ruby 2.7.x to the test matrix.
39
+ * Resolved issues calculating coverage and reporting results when there are 0 tested resources.
40
+ * Resolved compatibility issue with `rspec-expectations` 3.10.0.
41
+
42
+ ## [2.7.10]
43
+
44
+ ### Fixed
45
+ * Fix issues with removal of `default_env` method in Puppet 6.17.0.
46
+
47
+ ## [2.7.9]
48
+
49
+ This release had unintended breaking changes and was withdrawn.
50
+
51
+ ## [2.7.8]
52
+
53
+ ### Fixed
54
+ * Fix cross-platform testing for Puppet >= 6.9.0 when there is no `ipaddress6`
55
+ fact defined.
56
+
57
+ ## [2.7.7]
58
+
59
+ ### Fixed
60
+ * Fix the support for rspec-expectations >= 3.8.5.
61
+
62
+ ### Changed
63
+ * Remove the rspec-expectations dependency limit introduced in 2.7.6.
64
+
65
+ ## [2.7.6]
66
+
67
+ ### Changed
68
+ * Limit rspec-expectations dependency to < 3.8.5 due to an incompatible
69
+ change.
70
+
5
71
  ## [2.7.5]
6
72
 
7
73
  ### Fixed
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
@@ -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
@@ -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
@@ -61,6 +61,14 @@ module RSpec::Puppet
61
61
  "expected that the catalogue would not " + description + " but it does"
62
62
  end
63
63
 
64
+ def supports_block_expectations
65
+ true
66
+ end
67
+
68
+ def supports_value_expectations
69
+ true
70
+ end
71
+
64
72
  private
65
73
 
66
74
  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
@@ -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
@@ -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,47 @@
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
+ return unwrap == other.unwrap
33
+ else
34
+ super
35
+ end
36
+ end
37
+ end
38
+ else
39
+ #:nocov:
40
+ class Sensitive
41
+ def initialize(value)
42
+ raise 'The use of the Sensitive data type is not supported by this Puppet version'
43
+ end
44
+ end
45
+ #:nocov:
46
+ end
47
+ 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
 
@@ -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.5
4
+ version: 2.8.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-06-06 00:00:00.000000000 Z
11
+ date: 2020-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -57,14 +57,17 @@ files:
57
57
  - lib/rspec-puppet/matchers/dynamic_matchers.rb
58
58
  - lib/rspec-puppet/matchers/include_class.rb
59
59
  - lib/rspec-puppet/matchers/parameter_matcher.rb
60
+ - lib/rspec-puppet/matchers/raise_error.rb
60
61
  - lib/rspec-puppet/matchers/run.rb
61
62
  - lib/rspec-puppet/matchers/type_matchers.rb
63
+ - lib/rspec-puppet/matchers/unique_values.rb
62
64
  - lib/rspec-puppet/monkey_patches.rb
63
65
  - lib/rspec-puppet/monkey_patches/win32/registry.rb
64
66
  - lib/rspec-puppet/monkey_patches/win32/taskscheduler.rb
65
67
  - lib/rspec-puppet/monkey_patches/windows/taskschedulerconstants.rb
66
68
  - lib/rspec-puppet/rake_task.rb
67
69
  - lib/rspec-puppet/raw_string.rb
70
+ - lib/rspec-puppet/sensitive.rb
68
71
  - lib/rspec-puppet/setup.rb
69
72
  - lib/rspec-puppet/spec_helper.rb
70
73
  - lib/rspec-puppet/support.rb
@@ -88,8 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
91
  - !ruby/object:Gem::Version
89
92
  version: '0'
90
93
  requirements: []
91
- rubyforge_project:
92
- rubygems_version: 2.7.6.2
94
+ rubygems_version: 3.0.8
93
95
  signing_key:
94
96
  specification_version: 4
95
97
  summary: RSpec tests for your Puppet manifests