chefspec-chef 9.3.4

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.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +30 -0
  3. data/LICENSE +22 -0
  4. data/Rakefile +85 -0
  5. data/chefspec-chef.gemspec +30 -0
  6. data/lib/chefspec/api/core.rb +217 -0
  7. data/lib/chefspec/api/described.rb +53 -0
  8. data/lib/chefspec/api/do_nothing.rb +26 -0
  9. data/lib/chefspec/api/include_any_recipe.rb +24 -0
  10. data/lib/chefspec/api/include_recipe.rb +28 -0
  11. data/lib/chefspec/api/link.rb +28 -0
  12. data/lib/chefspec/api/notifications.rb +40 -0
  13. data/lib/chefspec/api/reboot.rb +14 -0
  14. data/lib/chefspec/api/render_file.rb +37 -0
  15. data/lib/chefspec/api/state_attrs.rb +30 -0
  16. data/lib/chefspec/api/stubs.rb +183 -0
  17. data/lib/chefspec/api/stubs_for.rb +139 -0
  18. data/lib/chefspec/api/subscriptions.rb +37 -0
  19. data/lib/chefspec/api/user.rb +230 -0
  20. data/lib/chefspec/api.rb +39 -0
  21. data/lib/chefspec/berkshelf.rb +63 -0
  22. data/lib/chefspec/cacher.rb +64 -0
  23. data/lib/chefspec/coverage/filters.rb +82 -0
  24. data/lib/chefspec/coverage.rb +247 -0
  25. data/lib/chefspec/deprecations.rb +46 -0
  26. data/lib/chefspec/errors.rb +48 -0
  27. data/lib/chefspec/expect_exception.rb +51 -0
  28. data/lib/chefspec/extensions/chef/client.rb +21 -0
  29. data/lib/chefspec/extensions/chef/conditional.rb +16 -0
  30. data/lib/chefspec/extensions/chef/cookbook/gem_installer.rb +33 -0
  31. data/lib/chefspec/extensions/chef/cookbook_loader.rb +14 -0
  32. data/lib/chefspec/extensions/chef/cookbook_uploader.rb +12 -0
  33. data/lib/chefspec/extensions/chef/data_query.rb +49 -0
  34. data/lib/chefspec/extensions/chef/lwrp_base.rb +29 -0
  35. data/lib/chefspec/extensions/chef/provider.rb +39 -0
  36. data/lib/chefspec/extensions/chef/resource/freebsd_package.rb +17 -0
  37. data/lib/chefspec/extensions/chef/resource.rb +188 -0
  38. data/lib/chefspec/extensions/chef/run_context/cookbook_compiler.rb +84 -0
  39. data/lib/chefspec/extensions/chef/securable.rb +19 -0
  40. data/lib/chefspec/extensions/ohai/system.rb +11 -0
  41. data/lib/chefspec/extensions.rb +21 -0
  42. data/lib/chefspec/file_cache_path_proxy.rb +15 -0
  43. data/lib/chefspec/formatter.rb +282 -0
  44. data/lib/chefspec/librarian.rb +51 -0
  45. data/lib/chefspec/matchers/do_nothing_matcher.rb +52 -0
  46. data/lib/chefspec/matchers/include_any_recipe_matcher.rb +51 -0
  47. data/lib/chefspec/matchers/include_recipe_matcher.rb +46 -0
  48. data/lib/chefspec/matchers/link_to_matcher.rb +37 -0
  49. data/lib/chefspec/matchers/notifications_matcher.rb +143 -0
  50. data/lib/chefspec/matchers/render_file_matcher.rb +140 -0
  51. data/lib/chefspec/matchers/resource_matcher.rb +175 -0
  52. data/lib/chefspec/matchers/state_attrs_matcher.rb +71 -0
  53. data/lib/chefspec/matchers/subscribes_matcher.rb +72 -0
  54. data/lib/chefspec/matchers.rb +13 -0
  55. data/lib/chefspec/mixins/normalize.rb +22 -0
  56. data/lib/chefspec/policyfile.rb +69 -0
  57. data/lib/chefspec/renderer.rb +145 -0
  58. data/lib/chefspec/rspec.rb +21 -0
  59. data/lib/chefspec/runner.rb +8 -0
  60. data/lib/chefspec/server.rb +4 -0
  61. data/lib/chefspec/server_methods.rb +173 -0
  62. data/lib/chefspec/server_runner.rb +76 -0
  63. data/lib/chefspec/solo_runner.rb +516 -0
  64. data/lib/chefspec/stubs/command_registry.rb +11 -0
  65. data/lib/chefspec/stubs/command_stub.rb +37 -0
  66. data/lib/chefspec/stubs/data_bag_item_registry.rb +13 -0
  67. data/lib/chefspec/stubs/data_bag_item_stub.rb +25 -0
  68. data/lib/chefspec/stubs/data_bag_registry.rb +13 -0
  69. data/lib/chefspec/stubs/data_bag_stub.rb +23 -0
  70. data/lib/chefspec/stubs/registry.rb +32 -0
  71. data/lib/chefspec/stubs/search_registry.rb +13 -0
  72. data/lib/chefspec/stubs/search_stub.rb +25 -0
  73. data/lib/chefspec/stubs/stub.rb +38 -0
  74. data/lib/chefspec/util.rb +58 -0
  75. data/lib/chefspec/version.rb +3 -0
  76. data/lib/chefspec/zero_server.rb +142 -0
  77. data/lib/chefspec.rb +75 -0
  78. data/spec/spec_helper.rb +12 -0
  79. data/spec/support/hash.rb +35 -0
  80. data/spec/unit/cacher_spec.rb +70 -0
  81. data/spec/unit/coverage/filters_spec.rb +60 -0
  82. data/spec/unit/deprecations_spec.rb +52 -0
  83. data/spec/unit/errors_spec.rb +57 -0
  84. data/spec/unit/expect_exception_spec.rb +32 -0
  85. data/spec/unit/macros_spec.rb +119 -0
  86. data/spec/unit/matchers/do_nothing_matcher.rb +5 -0
  87. data/spec/unit/matchers/include_any_recipe_matcher_spec.rb +52 -0
  88. data/spec/unit/matchers/include_recipe_matcher_spec.rb +38 -0
  89. data/spec/unit/matchers/link_to_matcher_spec.rb +55 -0
  90. data/spec/unit/matchers/notifications_matcher_spec.rb +39 -0
  91. data/spec/unit/matchers/render_file_matcher_spec.rb +68 -0
  92. data/spec/unit/matchers/resource_matcher_spec.rb +5 -0
  93. data/spec/unit/matchers/state_attrs_matcher_spec.rb +68 -0
  94. data/spec/unit/matchers/subscribes_matcher_spec.rb +63 -0
  95. data/spec/unit/renderer_spec.rb +69 -0
  96. data/spec/unit/server_runner_spec.rb +28 -0
  97. data/spec/unit/solo_runner_spec.rb +171 -0
  98. data/spec/unit/stubs/command_registry_spec.rb +27 -0
  99. data/spec/unit/stubs/command_stub_spec.rb +61 -0
  100. data/spec/unit/stubs/data_bag_item_registry_spec.rb +39 -0
  101. data/spec/unit/stubs/data_bag_item_stub_spec.rb +36 -0
  102. data/spec/unit/stubs/data_bag_registry_spec.rb +39 -0
  103. data/spec/unit/stubs/data_bag_stub_spec.rb +35 -0
  104. data/spec/unit/stubs/registry_spec.rb +29 -0
  105. data/spec/unit/stubs/search_registry_spec.rb +39 -0
  106. data/spec/unit/stubs/search_stub_spec.rb +36 -0
  107. data/spec/unit/stubs/stub_spec.rb +64 -0
  108. data/templates/coverage/human.erb +22 -0
  109. data/templates/coverage/json.erb +8 -0
  110. data/templates/coverage/table.erb +14 -0
  111. data/templates/errors/cookbook_path_not_found.erb +3 -0
  112. data/templates/errors/erb_template_parse_error.erb +5 -0
  113. data/templates/errors/gem_load_error.erb +7 -0
  114. data/templates/errors/invalid_berkshelf_options.erb +4 -0
  115. data/templates/errors/may_need_to_specify_platform.erb +25 -0
  116. data/templates/errors/no_conversion_error.erb +1 -0
  117. data/templates/errors/not_stubbed.erb +7 -0
  118. data/templates/errors/shell_out_not_stubbed.erb +10 -0
  119. data/templates/errors/template_not_found.erb +9 -0
  120. metadata +221 -0
@@ -0,0 +1,64 @@
1
+ module ChefSpec
2
+ #
3
+ # The cacher module allows for ultra-fast tests by caching the results of a
4
+ # CCR in memory across an example group. In testing, this can reduce the
5
+ # total testing time by a factor of 10x. This strategy is _not_ the default
6
+ # behavior, because it has implications surrounding stubbing and is _not_
7
+ # threadsafe!
8
+ #
9
+ # The credit for this approach and code belongs to Juri Timošin (DracoAter).
10
+ # Please see his original blog post below for an in-depth explanation of how
11
+ # and why this approach is faster.
12
+ #
13
+ # @example Using the Cacher module
14
+ # First, require the Cacher module in your +spec_helper.rb+:
15
+ #
16
+ # RSpec.configure do |config|
17
+ # config.extend(ChefSpec::Cacher)
18
+ # end
19
+ #
20
+ # Next, change your +let+ blocks to +cached+ blocks:
21
+ #
22
+ # let(:chef_run) { ... } #=> cached(:chef_run) { ... }
23
+ #
24
+ # Finally, celebrate!
25
+ #
26
+ # @warn
27
+ # This strategy is only recommended for advanced users, as it makes
28
+ # stubbing slightly more difficult and indirect!
29
+ #
30
+ # @see http://dracoater.blogspot.com/2013/12/testing-chef-cookbooks-part-25-speeding.html
31
+ #
32
+ module Cacher
33
+ @@cache = {}
34
+ FINALIZER = lambda { |id| @@cache.delete(id) }
35
+
36
+ def cached(name, &block)
37
+ location = ancestors.first.metadata[:location]
38
+ unless location.nil?
39
+ location += ancestors.first.metadata[:description] unless ancestors.first.metadata[:description].nil?
40
+ location += ancestors.first.metadata[:scoped_id] unless ancestors.first.metadata[:scoped_id].nil?
41
+ end
42
+ location ||= ancestors.first.metadata[:parent_example_group][:location]
43
+
44
+ define_method(name) do
45
+ key = [location, name.to_s].join(".")
46
+ unless @@cache.key?(Thread.current.object_id)
47
+ ObjectSpace.define_finalizer(Thread.current, FINALIZER)
48
+ end
49
+ @@cache[Thread.current.object_id] ||= {}
50
+ @@cache[Thread.current.object_id][key] ||= instance_eval(&block)
51
+ end
52
+ end
53
+
54
+ def cached!(name, &block)
55
+ cached(name, &block)
56
+
57
+ before { send(name) }
58
+ end
59
+ end
60
+ end
61
+
62
+ RSpec.configure do |config|
63
+ config.extend(ChefSpec::Cacher)
64
+ end
@@ -0,0 +1,82 @@
1
+ module ChefSpec
2
+ class Coverage
3
+ class Filter
4
+ def initialize(filter)
5
+ @filter = filter
6
+ end
7
+
8
+ def matches?
9
+ raise "Must override Filter#matches?"
10
+ end
11
+ end
12
+
13
+ #
14
+ # @example Match resources based on a regular expression.
15
+ # add_filter /^test/
16
+ #
17
+ class RegexpFilter < Filter
18
+ def matches?(resource)
19
+ return true if resource.source_line.nil?
20
+
21
+ @filter =~ resource.source_line
22
+ end
23
+ end
24
+
25
+ #
26
+ # @example Match resources based on a regular expression.
27
+ # add_filter 'test/bar/zip'
28
+ #
29
+ class StringFilter < RegexpFilter
30
+ def initialize(filter)
31
+ super(Regexp.new("^#{filter}"))
32
+ end
33
+ end
34
+
35
+ #
36
+ # @example Match resources based on a custom block.
37
+ # # Ignore internal cookbooks
38
+ # add_filter do |resource|
39
+ # resource.name =~ /^acme-(.+)/
40
+ # end
41
+ #
42
+ class BlockFilter < Filter
43
+ def matches?(resource)
44
+ return true if resource.source_line.nil?
45
+
46
+ @filter.call(resource)
47
+ end
48
+ end
49
+
50
+ #
51
+ # @example Ignore dependent cookbooks (via Berkshelf)
52
+ # add_filter BerkshelfFilter.new(berksfile)
53
+ #
54
+ class BerkshelfFilter < Filter
55
+ def initialize(berksfile)
56
+ @berksfile = berksfile
57
+
58
+ @metadatas = if berksfile.respond_to?(:dependencies)
59
+ berksfile.dependencies
60
+ .select(&:metadata?)
61
+ .map(&:name)
62
+ else
63
+ berksfile.sources.collect do |source|
64
+ location = source.location
65
+ if location.respond_to?(:metadata?) && location.metadata?
66
+ source
67
+ else
68
+ nil
69
+ end
70
+ end.compact.map(&:name)
71
+ end
72
+ end
73
+
74
+ def matches?(resource)
75
+ return true if resource.source_line.nil?
76
+
77
+ normalized_source_line = resource.source_line.gsub("\\", "/")
78
+ normalized_source_line =~ %r{cookbooks/(?!#{@metadatas.join('|')})}
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,247 @@
1
+ require_relative "coverage/filters"
2
+
3
+ module ChefSpec
4
+ class Coverage
5
+ EXIT_FAILURE = 1
6
+ EXIT_SUCCESS = 0
7
+
8
+ class << self
9
+ def method_added(name)
10
+ # Only delegate public methods
11
+ if method_defined?(name)
12
+ instance_eval <<-EOH, __FILE__, __LINE__ + 1
13
+ def #{name}(*args, &block)
14
+ instance.public_send(:#{name}, *args, &block)
15
+ end
16
+ EOH
17
+ end
18
+ end
19
+ end
20
+
21
+ include Singleton
22
+
23
+ attr_reader :filters
24
+
25
+ #
26
+ # Create a new coverage object singleton.
27
+ #
28
+ def initialize
29
+ @collection = {}
30
+ @filters = {}
31
+ @outputs = []
32
+ add_output do |report|
33
+
34
+ erb = Erubis::Eruby.new(File.read(@template))
35
+ puts erb.evaluate(report)
36
+ rescue NameError => e
37
+ raise Error::ErbTemplateParseError.new(original_error: e.message)
38
+
39
+ end
40
+ @template = ChefSpec.root.join("templates", "coverage", "human.erb")
41
+ end
42
+
43
+ #
44
+ # Start the coverage reporting analysis. This method also adds the the
45
+ # +at_exit+ handler for printing the coverage report.
46
+ #
47
+ def start!(&block)
48
+ warn("ChefSpec's coverage reporting is deprecated and will be removed in a future version")
49
+ instance_eval(&block) if block
50
+ at_exit { ChefSpec::Coverage.report! }
51
+ end
52
+
53
+ #
54
+ # Add a filter to the coverage analysis.
55
+ #
56
+ # @param [Filter, String, Regexp] filter
57
+ # the filter to add
58
+ # @param [Proc] block
59
+ # the block to use as a filter
60
+ #
61
+ # @return [true]
62
+ #
63
+ def add_filter(filter = nil, &block)
64
+ id = "#{filter.inspect}/#{block.inspect}".hash
65
+
66
+ @filters[id] = if filter.is_a?(Filter)
67
+ filter
68
+ elsif filter.is_a?(String)
69
+ StringFilter.new(filter)
70
+ elsif filter.is_a?(Regexp)
71
+ RegexpFilter.new(filter)
72
+ elsif block
73
+ BlockFilter.new(block)
74
+ else
75
+ raise ArgumentError, "Please specify either a string, " \
76
+ "filter, or block to filter source files with!"
77
+ end
78
+
79
+ true
80
+ end
81
+
82
+ #
83
+ # Add an output to send the coverage results to.
84
+ # @param [Proc] block
85
+ # the block to use as the output
86
+ #
87
+ # @return [true]
88
+ #
89
+ def add_output(&block)
90
+ @outputs << block
91
+ end
92
+
93
+ #
94
+ # Change the template for reporting of converage analysis.
95
+ #
96
+ # @param [string] path
97
+ # The template file to use for the output of the report
98
+ #
99
+ # @return [true]
100
+ #
101
+ def set_template(file = "human.erb")
102
+ @template = [
103
+ ChefSpec.root.join("templates", "coverage", file),
104
+ File.expand_path(file, Dir.pwd),
105
+ ].find { |f| File.exist?(f) }
106
+ raise Error::TemplateNotFound.new(path: file) unless @template
107
+ end
108
+
109
+ #
110
+ # Add a resource to the resource collection. Only new resources are added
111
+ # and only resources that match the given filter are covered (which is *
112
+ # by default).
113
+ #
114
+ # @param [Chef::Resource] resource
115
+ #
116
+ def add(resource)
117
+ if !exists?(resource) && !filtered?(resource)
118
+ @collection[resource.to_s] = ResourceWrapper.new(resource)
119
+ end
120
+ end
121
+
122
+ #
123
+ # Called when a resource is matched to indicate it has been tested.
124
+ #
125
+ # @param [Chef::Resource] resource
126
+ #
127
+ def cover!(resource)
128
+ wrapper = find(resource)
129
+ wrapper.touch! if wrapper
130
+ end
131
+
132
+ #
133
+ # Called to check if a resource belongs to a cookbook from the specified
134
+ # directories.
135
+ #
136
+ # @param [Chef::Resource] resource
137
+ #
138
+ def filtered?(resource)
139
+ filters.any? { |_, filter| filter.matches?(resource) }
140
+ end
141
+
142
+ #
143
+ # Generate a coverage report. This report **must** be generated +at_exit+
144
+ # or else the entire resource collection may not be complete!
145
+ #
146
+ # @example Generating a report
147
+ #
148
+ # ChefSpec::Coverage.report!
149
+ #
150
+ def report!
151
+ # Borrowed from simplecov#41
152
+ #
153
+ # If an exception is thrown that isn't a "SystemExit", we need to capture
154
+ # that error and re-raise.
155
+ if $!
156
+ exit_status = $!.is_a?(SystemExit) ? $!.status : EXIT_FAILURE
157
+ else
158
+ exit_status = EXIT_SUCCESS
159
+ end
160
+
161
+ report = {}.tap do |h|
162
+ h[:total] = @collection.size
163
+ h[:touched] = @collection.count { |_, resource| resource.touched? }
164
+ h[:coverage] = ((h[:touched] / h[:total].to_f) * 100).round(2)
165
+ end
166
+
167
+ report[:untouched_resources] = @collection.collect do |_, resource|
168
+ resource unless resource.touched?
169
+ end.compact
170
+ report[:all_resources] = @collection.values
171
+
172
+ @outputs.each do |block|
173
+ instance_exec(report, &block)
174
+ end
175
+
176
+ # Ensure we exit correctly (#351)
177
+ Kernel.exit(exit_status) if exit_status && exit_status > 0
178
+ end
179
+
180
+ private
181
+
182
+ def find(resource)
183
+ @collection[resource.to_s]
184
+ end
185
+
186
+ def exists?(resource)
187
+ !find(resource).nil?
188
+ end
189
+
190
+ class ResourceWrapper
191
+ attr_reader :resource
192
+
193
+ def initialize(resource = nil)
194
+ @resource = resource
195
+ end
196
+
197
+ def to_s
198
+ @resource.to_s
199
+ end
200
+
201
+ def to_json
202
+ {
203
+ "source_file" => source_file,
204
+ "source_line" => source_line,
205
+ "touched" => touched?,
206
+ "resource" => to_s,
207
+ }.to_json
208
+ end
209
+
210
+ def source_file
211
+ @source_file ||= if @resource.source_line
212
+ shortname(@resource.source_line.split(":").first)
213
+ else
214
+ "Unknown"
215
+ end
216
+ end
217
+
218
+ def source_line
219
+ @source_line ||= if @resource.source_line
220
+ @resource.source_line.split(":", 2).last.to_i
221
+ else
222
+ "Unknown"
223
+ end
224
+ end
225
+
226
+ def touch!
227
+ @touched = true
228
+ end
229
+
230
+ def touched?
231
+ !!@touched
232
+ end
233
+
234
+ private
235
+
236
+ def shortname(file)
237
+ if file.include?(Dir.pwd)
238
+ file.split(Dir.pwd, 2).last
239
+ elsif file.include?("cookbooks")
240
+ file.split("cookbooks/", 2).last
241
+ else
242
+ file
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,46 @@
1
+ module Kernel
2
+ # Kernel extension to print deprecation notices.
3
+ #
4
+ # @example printing a deprecation warning
5
+ # deprecated 'no longer in use' #=> "[DEPRECATION] no longer in use"
6
+ #
7
+ # @param [Array<String>] messages
8
+ def deprecated(*messages)
9
+ messages.each do |message|
10
+ calling_spec = caller.find { |line| line =~ %r{(/spec)|(_spec\.rb)} }
11
+ if calling_spec
12
+ calling_spec = "spec/" + calling_spec.split("/spec/").last
13
+ warn "[DEPRECATION] #{message} (called from #{calling_spec})"
14
+ else
15
+ warn "[DEPRECATION] #{message}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ module ChefSpec
22
+ class SoloRunner
23
+ # @deprecated {ChefSpec::Runner.define_runner_method} is deprecated. Please
24
+ # use {ChefSpec.define_matcher} instead.
25
+ def self.define_runner_method(resource_name)
26
+ deprecated "`ChefSpec::Runner.define_runner_method' is deprecated." \
27
+ " It is being used in the #{resource_name} resource matcher." \
28
+ " Please use `ChefSpec.define_matcher' instead."
29
+
30
+ ChefSpec.define_matcher(resource_name)
31
+ end
32
+ end
33
+
34
+ class Server
35
+ def self.method_missing(m, *args, &block)
36
+ deprecated "`ChefSpec::Server.#{m}' is deprecated. There is no longer" \
37
+ " a global Chef Server instance. Please use a ChefSpec::SoloRunner" \
38
+ " instead. More documentation can be found in the ChefSpec README."
39
+ raise ChefSpec::Error::NoConversionError
40
+ end
41
+ end
42
+ end
43
+
44
+ module ChefSpec::Error
45
+ class NoConversionError < ChefSpecError; end
46
+ end
@@ -0,0 +1,48 @@
1
+ module ChefSpec
2
+ module Error
3
+ class ChefSpecError < StandardError
4
+ def initialize(options = {})
5
+ class_name = self.class.to_s.split("::").last
6
+ filename = options.delete(:_template) || Util.underscore(class_name)
7
+ template = ChefSpec.root.join("templates", "errors", "#{filename}.erb")
8
+
9
+ erb = Erubis::Eruby.new(File.read(template))
10
+ super erb.evaluate(options)
11
+ end
12
+ end
13
+
14
+ class NotStubbed < ChefSpecError
15
+ def initialize(options = {})
16
+ name = self.class.name.to_s.split("::").last
17
+ type = Util.underscore(name).gsub("_not_stubbed", "")
18
+ klass = Stubs.const_get(name.gsub("NotStubbed", "") + "Stub")
19
+ stub = klass.new(*options[:args]).and_return("...").signature
20
+
21
+ signature = "#{type}(#{options[:args].map(&:inspect).join(", ")})"
22
+
23
+ super({
24
+ type: type,
25
+ signature: signature,
26
+ stub: stub,
27
+ _template: :not_stubbed,
28
+ }.merge(options))
29
+ end
30
+ end
31
+
32
+ class CommandNotStubbed < NotStubbed; end
33
+ class SearchNotStubbed < NotStubbed; end
34
+ class DataBagNotStubbed < NotStubbed; end
35
+ class DataBagItemNotStubbed < NotStubbed; end
36
+ class ShellOutNotStubbed < ChefSpecError; end
37
+
38
+ class CookbookPathNotFound < ChefSpecError; end
39
+ class GemLoadError < ChefSpecError; end
40
+
41
+ class MayNeedToSpecifyPlatform < ChefSpecError; end
42
+
43
+ class InvalidBerkshelfOptions < ChefSpecError; end
44
+
45
+ class TemplateNotFound < ChefSpecError; end
46
+ class ErbTemplateParseError < ChefSpecError; end
47
+ end
48
+ end
@@ -0,0 +1,51 @@
1
+ class RSpec::Matchers::BuiltIn::RaiseError
2
+ class << self
3
+ attr_accessor :last_run
4
+ end
5
+
6
+ attr_reader :expected_message
7
+
8
+ def last_error_for_chefspec
9
+ @expected_error
10
+ end
11
+
12
+ alias_method :old_matches?, :matches?
13
+ def matches?(*args)
14
+ self.class.last_run = self
15
+ old_matches?(*args)
16
+ end
17
+ end
18
+
19
+ module ChefSpec
20
+ class ExpectException
21
+ def initialize(formatter_exception, formatter_message = nil)
22
+ @formatter_exception = formatter_exception
23
+ @formatter_message = formatter_message
24
+ @matcher = RSpec::Matchers::BuiltIn::RaiseError.last_run
25
+ end
26
+
27
+ def expected?
28
+ return false if @matcher.nil?
29
+
30
+ exception_matched? && message_matched?
31
+ end
32
+
33
+ private
34
+
35
+ def exception_matched?
36
+ @formatter_exception == @matcher.last_error_for_chefspec ||
37
+ @matcher.last_error_for_chefspec === @formatter_exception
38
+ end
39
+
40
+ def message_matched?
41
+ case @formatter_message
42
+ when nil
43
+ true
44
+ when Regexp
45
+ @matcher.expected_message =~ @formatter_message
46
+ else
47
+ @matcher.expected_message == @formatter_message
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ # Force loading Chef Config to fix a bad dependency tree. See
2
+ # https://github.com/opscode/chef/issues/2703 for more information.
3
+ require "chef/config"
4
+
5
+ # Providers has to be included before client... probably a weird
6
+ # include missing in Chef-land, but we can make sure we get it right anyway.
7
+ require "chef/providers"
8
+ require "chef/client"
9
+
10
+ # @private
11
+ Chef::Client.prepend(Module.new do
12
+ #
13
+ # Don't actually run ohai (we have fake data for that)
14
+ #
15
+ # @see Chef::Client#run_ohai
16
+ #
17
+ def run_ohai
18
+ return super unless $CHEFSPEC_MODE
19
+ # noop
20
+ end
21
+ end)
@@ -0,0 +1,16 @@
1
+ require "chef/resource/conditional"
2
+
3
+ Chef::Resource::Conditional.prepend(Module.new do
4
+ # @see Chef::Resource::Conditional#evaluate_command
5
+ def evaluate_command
6
+ return super unless $CHEFSPEC_MODE
7
+
8
+ stub = ChefSpec::Stubs::CommandRegistry.stub_for(@command)
9
+
10
+ if stub.nil?
11
+ raise ChefSpec::Error::CommandNotStubbed.new(args: [@command])
12
+ end
13
+
14
+ stub.result
15
+ end
16
+ end)
@@ -0,0 +1,33 @@
1
+ require "chef/cookbook/gem_installer"
2
+
3
+ Chef::Cookbook::GemInstaller.prepend(Module.new do
4
+ # Installs the gems into the omnibus gemset.
5
+ def install
6
+ return super unless $CHEFSPEC_MODE
7
+
8
+ cookbook_gems = Hash.new { |h, k| h[k] = [] }
9
+
10
+ cookbook_collection.each do |cookbook_name, cookbook_version|
11
+ cookbook_version.metadata.gems.each do |args|
12
+ cookbook_gems[args.first] += args[1..-1]
13
+ end
14
+ end
15
+
16
+ events.cookbook_gem_start(cookbook_gems)
17
+ cookbook_gems.each { |gem_name, gem_requirements| locate_gem(gem_name, gem_requirements) }
18
+ events.cookbook_gem_finished
19
+ end
20
+
21
+ private
22
+
23
+ def locate_gem(gem_name, gem_requirements)
24
+ ::Gem::Specification.find_by_name(gem_name, gem_requirements)
25
+ rescue ::Gem::MissingSpecError
26
+ gem_cmd = "gem install #{gem_name} --version '#{gem_requirements.join(", ")}'"
27
+ gemfile_line = "gem '#{[gem_name, *gem_requirements].join("', '")}'"
28
+ warn "No matching version found for '#{gem_name}' in your gem environment.\n" \
29
+ " - if you are using Chef Workstation, run the following command: \"chef #{gem_cmd}\"\n" \
30
+ " - if you are using bundler, append \"#{gemfile_line}\" to your Gemfile and run \"bundle install\"\n" \
31
+ " - otherwise run: \"#{gem_cmd}\""
32
+ end
33
+ end)
@@ -0,0 +1,14 @@
1
+ require "chef/cookbook_loader"
2
+
3
+ Chef::CookbookLoader.prepend(Module.new do
4
+ def all_directories_in_repo_paths
5
+ return super unless $CHEFSPEC_MODE
6
+
7
+ if Chef::Config[:chefspec_cookbook_root]
8
+ # Hax.
9
+ [Chef::Config[:chefspec_cookbook_root]]
10
+ else
11
+ super
12
+ end
13
+ end
14
+ end)
@@ -0,0 +1,12 @@
1
+ require "chef/cookbook_uploader"
2
+
3
+ Chef::CookbookUploader.prepend(Module.new do |variable|
4
+ #
5
+ # Don't validate uploaded cookbooks. Validating a cookbook takes *forever*
6
+ # to complete. It's just not worth it...
7
+ #
8
+ def validate_cookbooks
9
+ return super unless $CHEFSPEC_MODE
10
+ # noop
11
+ end
12
+ end)
@@ -0,0 +1,49 @@
1
+ require "chef/dsl/data_query"
2
+
3
+ Chef::DSL::DataQuery.prepend(Module.new do
4
+ # @see Chef::DSL::DataQuery#search
5
+ def search(*args, &block)
6
+ return super unless Chef::Config[:solo] && $CHEFSPEC_MODE
7
+
8
+ type = args[0]
9
+ query = args[1] || "*:*"
10
+ stub = ChefSpec::Stubs::SearchRegistry.stub_for(type, query)
11
+
12
+ if stub.nil?
13
+ raise ChefSpec::Error::SearchNotStubbed.new(args: [type, query])
14
+ end
15
+
16
+ if block
17
+ Array(stub.result).each { |r| block.call(r) }
18
+ true
19
+ else
20
+ stub.result
21
+ end
22
+ end
23
+
24
+ # @see Chef::DSL::DataQuery#data_bag
25
+ def data_bag(bag)
26
+ return super unless Chef::Config[:solo] && $CHEFSPEC_MODE
27
+
28
+ stub = ChefSpec::Stubs::DataBagRegistry.stub_for(bag)
29
+
30
+ if stub.nil?
31
+ raise ChefSpec::Error::DataBagNotStubbed.new(args: [bag])
32
+ end
33
+
34
+ stub.result
35
+ end
36
+
37
+ # @see Chef::DSL::DataQuery#data_bag_item
38
+ def data_bag_item(bag, id, secret = nil)
39
+ return super unless Chef::Config[:solo] && $CHEFSPEC_MODE
40
+
41
+ stub = ChefSpec::Stubs::DataBagItemRegistry.stub_for(bag, id)
42
+
43
+ if stub.nil?
44
+ raise ChefSpec::Error::DataBagItemNotStubbed.new(args: [bag, id])
45
+ end
46
+
47
+ stub.result
48
+ end
49
+ end)