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,516 @@
1
+ require "fauxhai"
2
+ require "chef/client"
3
+ require "chef/cookbook/metadata"
4
+ require "chef/mash"
5
+ require "chef/providers"
6
+ require "chef/resources"
7
+
8
+ module ChefSpec
9
+ class SoloRunner
10
+ #
11
+ # Handy class method for just converging a runner if you do not care about
12
+ # initializing the runner with custom options.
13
+ #
14
+ # @example
15
+ # ChefSpec::SoloRunner.converge('cookbook::recipe')
16
+ #
17
+ def self.converge(*recipe_names)
18
+ new.tap do |instance|
19
+ instance.converge(*recipe_names)
20
+ end
21
+ end
22
+
23
+ include ChefSpec::Normalize
24
+
25
+ # @return [Hash]
26
+ attr_reader :options
27
+
28
+ # @return [Chef::RunContext]
29
+ attr_reader :run_context
30
+
31
+ #
32
+ # Instantiate a new SoloRunner to run examples with.
33
+ #
34
+ # @example Instantiate a new Runner
35
+ # ChefSpec::SoloRunner.new
36
+ #
37
+ # @example Specifying the platform and version
38
+ # ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '18.04')
39
+ #
40
+ # @example Specifying the cookbook path
41
+ # ChefSpec::SoloRunner.new(cookbook_path: ['/cookbooks'])
42
+ #
43
+ # @example Specifying the log level
44
+ # ChefSpec::SoloRunner.new(log_level: :info)
45
+ #
46
+ #
47
+ # @param [Hash] options
48
+ # The options for the new runner
49
+ #
50
+ # @option options [Symbol] :log_level
51
+ # The log level to use (default is :warn)
52
+ # @option options [String] :platform
53
+ # The platform to load Ohai attributes from (must be present in fauxhai)
54
+ # @option options [String] :version
55
+ # The version of the platform to load Ohai attributes from (must be present in fauxhai)
56
+ # @option options [String] :path
57
+ # Path of a json file that will be passed to fauxhai as :path option
58
+ # @option options [Array<String>] :step_into
59
+ # The list of LWRPs to evaluate
60
+ # @option options String] :file_cache_path
61
+ # File caching path, if absent ChefSpec will use a temporary directory generated on the fly
62
+ #
63
+ # @yield [node] Configuration block for Chef::Node
64
+ #
65
+ def initialize(options = {})
66
+ @options = with_default_options(options)
67
+ apply_chef_config!
68
+ yield node if block_given?
69
+ end
70
+
71
+ #
72
+ # Execute the given `run_list` on the node, without actually converging
73
+ # the node. Each time {#converge} is called, the `run_list` is reset to the
74
+ # new value (it is **not** additive).
75
+ #
76
+ # @example Converging a single recipe
77
+ # chef_run.converge('example::default')
78
+ #
79
+ # @example Converging multiple recipes
80
+ # chef_run.converge('example::default', 'example::secondary')
81
+ #
82
+ #
83
+ # @param [Array] recipe_names
84
+ # The names of the recipe or recipes to converge
85
+ #
86
+ # @return [ChefSpec::SoloRunner]
87
+ # A reference to the calling Runner (for chaining purposes)
88
+ #
89
+ def converge(*recipe_names)
90
+ # Re-apply the Chef config before converging in case something else
91
+ # called Config.reset too.
92
+ apply_chef_config!
93
+ @converging = false
94
+ node.run_list.reset!
95
+ recipe_names.each { |recipe_name| node.run_list.add(recipe_name) }
96
+
97
+ return self if dry_run?
98
+
99
+ # Expand the run_list
100
+ expand_run_list!
101
+
102
+ # Merge in provided node attributes. Default and override use the role_
103
+ # levels so they win over the relevant bits from cookbooks since otherwise
104
+ # they would not and that would be confusing.
105
+ node.attributes.role_default = Chef::Mixin::DeepMerge.merge(node.attributes.role_default, options[:default_attributes]) if options[:default_attributes]
106
+ node.attributes.normal = Chef::Mixin::DeepMerge.merge(node.attributes.normal, options[:normal_attributes]) if options[:normal_attributes]
107
+ node.attributes.role_override = Chef::Mixin::DeepMerge.merge(node.attributes.role_override, options[:override_attributes]) if options[:override_attributes]
108
+ node.attributes.automatic = Chef::Mixin::DeepMerge.merge(node.attributes.automatic, options[:automatic_attributes]) if options[:automatic_attributes]
109
+
110
+ # Setup the run_context, rescuing the exception that happens when a
111
+ # resource is not defined on a particular platform
112
+ begin
113
+ @run_context = client.setup_run_context
114
+ rescue Chef::Exceptions::NoSuchResourceType => e
115
+ raise Error::MayNeedToSpecifyPlatform.new(original_error: e.message)
116
+ end
117
+
118
+ # Allow stubbing/mocking after the cookbook has been compiled but before the converge
119
+ yield node if block_given?
120
+
121
+ @converging = true
122
+ converge_val = @client.converge(@run_context)
123
+ if converge_val.is_a?(Exception)
124
+ raise converge_val
125
+ end
126
+
127
+ self
128
+ end
129
+
130
+ #
131
+ # Execute a block of recipe code.
132
+ #
133
+ # @param [Proc] block
134
+ # A block containing Chef recipe code
135
+ #
136
+ # @return [ChefSpec::SoloRunner]
137
+ #
138
+ def converge_block(&block)
139
+ converge do
140
+ recipe = Chef::Recipe.new(cookbook_name, "_test", run_context)
141
+ recipe.instance_exec(&block)
142
+ end
143
+ end
144
+
145
+ #
146
+ # Run a static preload of the cookbook under test. This will load libraries
147
+ # and resources, but not attributes or recipes.
148
+ #
149
+ # @return [void]
150
+ #
151
+ def preload!
152
+ # Flag to disable preloading for situations where it doesn't make sense.
153
+ return if ENV["CHEFSPEC_NO_PRELOAD"]
154
+
155
+ begin
156
+ old_preload = $CHEFSPEC_PRELOAD
157
+ $CHEFSPEC_PRELOAD = true
158
+ converge("recipe[#{cookbook_name}]")
159
+ node.run_list.reset!
160
+ ensure
161
+ $CHEFSPEC_PRELOAD = old_preload
162
+ end
163
+ end
164
+
165
+ #
166
+ # The +Chef::Node+ corresponding to this Runner.
167
+ #
168
+ # @return [Chef::Node]
169
+ #
170
+ def node
171
+ runner = self
172
+ @node ||= begin
173
+ apply_chef_config!
174
+ client.build_node.tap do |node|
175
+ node.define_singleton_method(:runner) { runner }
176
+ end
177
+ end
178
+ end
179
+
180
+ #
181
+ # The full collection of resources for this Runner.
182
+ #
183
+ # @return [Hash<String, Chef::Resource>]
184
+ #
185
+ def resource_collection
186
+ @resource_collection ||= @run_context.resource_collection
187
+ end
188
+
189
+ #
190
+ # Find the resource with the declared type and resource name, and optionally match a performed action.
191
+ #
192
+ # If multiples match it returns the last (which more or less matches the chef last-inserter-wins semantics)
193
+ #
194
+ # @example Find a template at `/etc/foo`
195
+ # chef_run.find_resource(:template, '/etc/foo') #=> #<Chef::Resource::Template>
196
+ #
197
+ #
198
+ # @param [Symbol] type
199
+ # The type of resource (sometimes called `resource_name`) such as `file`
200
+ # or `directory`.
201
+ # @param [String, Regexp] name
202
+ # The value of the name attribute or identity attribute for the resource.
203
+ # @param [Symbol] action
204
+ # (optional) match only resources that performed the action.
205
+ #
206
+ # @return [Chef::Resource, nil]
207
+ # The matching resource, or nil if one is not found
208
+ #
209
+ def find_resource(type, name, action = nil)
210
+ resource_collection.all_resources.reverse_each.find do |resource|
211
+ resource.declared_type == type.to_sym && (name === resource.name || name === resource.identity) && (action.nil? || resource.performed_action?(action))
212
+ end
213
+ end
214
+
215
+ #
216
+ # Find the resource with the declared type.
217
+ #
218
+ # @example Find all template resources
219
+ # chef_run.find_resources(:template) #=> [#<Chef::Resource::Template>, #...]
220
+ #
221
+ #
222
+ # @param [Symbol] type
223
+ # The type of resource such as `:file` or `:directory`.
224
+ #
225
+ # @return [Array<Chef::Resource>]
226
+ # The matching resources
227
+ #
228
+ def find_resources(type)
229
+ resource_collection.all_resources.select do |resource|
230
+ resource_name(resource) == type.to_sym
231
+ end
232
+ end
233
+
234
+ #
235
+ # Boolean method to determine the current phase of the Chef run (compiling
236
+ # or converging)
237
+ #
238
+ # @return [true, false]
239
+ #
240
+ def compiling?
241
+ !@converging
242
+ end
243
+
244
+ #
245
+ # Determines if the runner should step into the given resource. The
246
+ # +step_into+ option takes a string, but this method coerces everything
247
+ # to symbols for safety.
248
+ #
249
+ # This method also substitutes any dashes (+-+) with underscores (+_+),
250
+ # because that's what Chef does under the hood. (See GitHub issue #254
251
+ # for more background)
252
+ #
253
+ # @param [Chef::Resource] resource
254
+ # the Chef resource to try and step in to
255
+ #
256
+ # @return [true, false]
257
+ #
258
+ def step_into?(resource)
259
+ key = resource_name(resource)
260
+ Array(options[:step_into]).map(&method(:resource_name)).include?(key)
261
+ end
262
+
263
+ #
264
+ # Boolean method to determine if this Runner is in `dry_run` mode.
265
+ #
266
+ # @return [true, false]
267
+ #
268
+ def dry_run?
269
+ !!options[:dry_run]
270
+ end
271
+
272
+ #
273
+ # This runner as a string.
274
+ #
275
+ # @return [String] Currently includes the run_list. Format of the string
276
+ # may change between versions of this gem.
277
+ #
278
+ def to_s
279
+ "#<#{self.class.name} run_list: [#{node.run_list}]>"
280
+ end
281
+
282
+ #
283
+ # The runner as a String with helpful output.
284
+ #
285
+ # @return [String]
286
+ #
287
+ def inspect
288
+ "#<#{self.class.name}" \
289
+ " options: #{options.inspect}," \
290
+ " run_list: [#{node.run_list}]>"
291
+ end
292
+
293
+ #
294
+ # Respond to custom matchers defined by the user.
295
+ #
296
+ def method_missing(m, *args, &block)
297
+ block = ChefSpec.matchers[resource_name(m.to_sym)]
298
+ if block
299
+ instance_exec(args.first, &block)
300
+ else
301
+ super
302
+ end
303
+ end
304
+
305
+ #
306
+ # Inform Ruby that we respond to methods that are defined as custom
307
+ # matchers.
308
+ #
309
+ def respond_to_missing?(m, include_private = false)
310
+ ChefSpec.matchers.key?(m.to_sym) || super
311
+ end
312
+
313
+ private
314
+
315
+ #
316
+ # The path to cache files on disk. This value is created using
317
+ # {Dir.mktmpdir}. The method adds a {Kernel.at_exit} handler to ensure the
318
+ # temporary directory is deleted when the system exits.
319
+ #
320
+ # **This method creates a new temporary directory on each call!** As such,
321
+ # you should cache the result to a variable inside you system.
322
+ #
323
+ def file_cache_path
324
+ path = Dir.mktmpdir
325
+ at_exit { FileUtils.rm_rf(path) }
326
+ path
327
+ end
328
+
329
+ #
330
+ # Set the default options, with the given options taking precedence.
331
+ #
332
+ # @param [Hash] options
333
+ # the list of options to take precedence
334
+ #
335
+ # @return [Hash] options
336
+ #
337
+ def with_default_options(options)
338
+ config = RSpec.configuration
339
+
340
+ {
341
+ cookbook_root: config.cookbook_root || calling_cookbook_root(options, caller),
342
+ cookbook_path: config.cookbook_path || calling_cookbook_path(options, caller),
343
+ role_path: config.role_path || default_role_path,
344
+ environment_path: config.environment_path || default_environment_path,
345
+ file_cache_path: config.file_cache_path,
346
+ log_level: config.log_level,
347
+ path: config.path,
348
+ platform: config.platform,
349
+ version: config.version,
350
+ }.merge(options)
351
+ end
352
+
353
+ #
354
+ # The inferred cookbook root from the calling spec.
355
+ #
356
+ # @param [Hash<Symbol, Object>] options
357
+ # initial runner options
358
+ # @param [Array<String>] kaller
359
+ # the calling trace
360
+ #
361
+ # @return [String]
362
+ #
363
+ def calling_cookbook_root(options, kaller)
364
+ calling_spec = options[:spec_declaration_locations] || kaller.find { |line| line =~ %r{/spec} }
365
+ raise Error::CookbookPathNotFound if calling_spec.nil?
366
+
367
+ bits = calling_spec.split(/:[0-9]/, 2).first.split(File::SEPARATOR)
368
+ spec_dir = bits.index("spec") || 0
369
+
370
+ File.join(bits.slice(0, spec_dir))
371
+ end
372
+
373
+ #
374
+ # The inferred path from the calling spec.
375
+ #
376
+ # @param [Hash<Symbol, Object>] options
377
+ # initial runner options
378
+ # @param [Array<String>] kaller
379
+ # the calling trace
380
+ #
381
+ # @return [String]
382
+ #
383
+ def calling_cookbook_path(options, kaller)
384
+ File.expand_path(File.join(calling_cookbook_root(options, kaller), ".."))
385
+ end
386
+
387
+ #
388
+ # The inferred path to roles.
389
+ #
390
+ # @return [String, nil]
391
+ #
392
+ def default_role_path
393
+ Pathname.new(Dir.pwd).ascend do |path|
394
+ possible = File.join(path, "roles")
395
+ return possible if File.exist?(possible)
396
+ end
397
+
398
+ nil
399
+ end
400
+
401
+ #
402
+ # The inferred path to environments.
403
+ #
404
+ # @return [String, nil]
405
+ #
406
+ def default_environment_path
407
+ Pathname.new(Dir.pwd).ascend do |path|
408
+ possible = File.join(path, "environments")
409
+ return possible if File.exist?(possible)
410
+ end
411
+
412
+ nil
413
+ end
414
+
415
+ #
416
+ # The +Chef::Client+ for this runner.
417
+ #
418
+ # @return [Chef::Runner]
419
+ #
420
+ def client
421
+ return @client if @client
422
+
423
+ @client = Chef::Client.new
424
+ @client.ohai.data = Mash.from_hash(Fauxhai.mock(options).data)
425
+ @client.load_node
426
+ @client.build_node
427
+ @client.save_updated_node
428
+ @client
429
+ end
430
+
431
+ #
432
+ # We really need a way to just expand the run_list, but that's done by
433
+ # +Chef::Client#build_node+. However, that same method also resets the
434
+ # automatic attributes, making it impossible to mock them. So we are
435
+ # stuck +instance_eval+ing against the client and manually expanding
436
+ # the mode object.
437
+ #
438
+ # @todo Remove in Chef 13
439
+ #
440
+ def expand_run_list!
441
+ # Recent versions of Chef include a method to expand the +run_list+,
442
+ # setting the correct instance variables on the policy builder. We use
443
+ # that, unless the user is running an older version of Chef which
444
+ # doesn't include this method.
445
+ if client.respond_to?(:expanded_run_list)
446
+ client.expanded_run_list
447
+ else
448
+ # Sadly, if we got this far, it means that the current Chef version
449
+ # does not include the +expanded_run_list+ method, so we need to
450
+ # manually expand the +run_list+. The following code has been known
451
+ # to make kittens cry, so please read with extreme caution.
452
+ client.instance_eval do
453
+ @run_list_expansion = expand_run_list
454
+ @expanded_run_list_with_versions = @run_list_expansion.recipes.with_version_constraints_strings
455
+ end
456
+ end
457
+ end
458
+
459
+ #
460
+ # Try to load the cookbook metadata for the cookbook under test.
461
+ #
462
+ # @return [Chef::Cookbook::Metadata]
463
+ #
464
+ def cookbook
465
+ @cookbook ||= Chef::Cookbook::Metadata.new.tap { |m| m.from_file("#{options[:cookbook_root]}/metadata.rb") }
466
+ end
467
+
468
+ #
469
+ # Try to figure out the name for the cookbook under test.
470
+ #
471
+ # @return [String]
472
+ #
473
+ def cookbook_name
474
+ # Try to figure out the name of this cookbook, pretending this block
475
+ # is in the name context as the cookbook under test.
476
+
477
+ cookbook.name
478
+ rescue IOError
479
+ # Old cookbook, has no metadata, use the folder name I guess.
480
+ File.basename(options[:cookbook_root])
481
+ end
482
+
483
+ #
484
+ # Apply the required options to {Chef::Config}.
485
+ #
486
+ # @api private
487
+ # @return [void]
488
+ #
489
+ def apply_chef_config!
490
+ Chef::Log.level = @options[:log_level]
491
+
492
+ Chef::Config.reset!
493
+ Chef::Config.formatters.clear
494
+ Chef::Config.add_formatter("chefspec")
495
+ Chef::Config[:cache_type] = "Memory"
496
+ Chef::Config[:client_key] = nil
497
+ Chef::Config[:client_name] = nil
498
+ Chef::Config[:node_name] = nil
499
+ Chef::Config[:file_cache_path] = @options[:file_cache_path] || file_cache_path
500
+ Chef::Config[:cookbook_path] = Array(@options[:cookbook_path])
501
+ # If the word cookbook is in the folder name, treat it as the path. Otherwise
502
+ # it's probably not a cookbook path and so we activate the gross hack mode.
503
+ if Chef::Config[:cookbook_path].size == 1 && Chef::Config[:cookbook_path].first !~ /cookbook/
504
+ Chef::Config[:chefspec_cookbook_root] = @options[:cookbook_root]
505
+ end
506
+ Chef::Config[:no_lazy_load] = true
507
+ Chef::Config[:role_path] = Array(@options[:role_path])
508
+ Chef::Config[:force_logger] = true
509
+ Chef::Config[:solo] = true
510
+ Chef::Config[:solo_legacy_mode] = true
511
+ Chef::Config[:use_policyfile] = false
512
+ Chef::Config[:environment_path] = @options[:environment_path]
513
+ end
514
+
515
+ end
516
+ end
@@ -0,0 +1,11 @@
1
+ require_relative "registry"
2
+
3
+ module ChefSpec
4
+ module Stubs
5
+ class CommandRegistry < Registry
6
+ def stub_for(command)
7
+ @stubs.find { |stub| stub.command === command }
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ require_relative "stub"
2
+
3
+ module ChefSpec
4
+ module Stubs
5
+ class CommandStub < Stub
6
+ attr_reader :block
7
+ attr_reader :command
8
+ attr_reader :value
9
+
10
+ def initialize(command, &block)
11
+ @command = command
12
+ @block = block
13
+ end
14
+
15
+ def and_return(value)
16
+ @value = value
17
+ self
18
+ end
19
+
20
+ def result
21
+ if @block
22
+ @block.call
23
+ else
24
+ @value
25
+ end
26
+ end
27
+
28
+ def signature
29
+ if @block
30
+ "stub_command(#{@command.inspect}) { # Ruby code }"
31
+ else
32
+ "stub_command(#{@command.inspect}).and_return(#{@value})"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+ require_relative "registry"
2
+
3
+ module ChefSpec
4
+ module Stubs
5
+ class DataBagItemRegistry < Registry
6
+ def stub_for(bag, id)
7
+ @stubs.find do |stub|
8
+ stub.bag.to_s == bag.to_s && stub.id === id
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ require_relative "stub"
2
+
3
+ module ChefSpec
4
+ module Stubs
5
+ class DataBagItemStub < Stub
6
+ attr_reader :block
7
+ attr_reader :id
8
+ attr_reader :bag
9
+
10
+ def initialize(bag, id, &block)
11
+ @bag = bag.to_s
12
+ @id = id
13
+ @block = block
14
+ end
15
+
16
+ def signature
17
+ if @block
18
+ "stub_data_bag_item(#{@bag.inspect}, #{@id.inspect}) { # Ruby code }"
19
+ else
20
+ "stub_data_bag_item(#{@bag.inspect}, #{@id.inspect}).and_return(#{@value})"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ require_relative "registry"
2
+
3
+ module ChefSpec
4
+ module Stubs
5
+ class DataBagRegistry < Registry
6
+ def stub_for(bag)
7
+ @stubs.find do |stub|
8
+ stub.bag.to_s == bag.to_s
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ require_relative "stub"
2
+
3
+ module ChefSpec
4
+ module Stubs
5
+ class DataBagStub < Stub
6
+ attr_reader :block
7
+ attr_reader :bag
8
+
9
+ def initialize(bag, &block)
10
+ @bag = bag.to_s
11
+ @block = block
12
+ end
13
+
14
+ def signature
15
+ if @block
16
+ "stub_data_bag(#{@bag.inspect}) { # Ruby code }"
17
+ else
18
+ "stub_data_bag(#{@bag.inspect}).and_return(#{@value})"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ module ChefSpec
2
+ module Stubs
3
+ class Registry
4
+ class << self
5
+ extend Forwardable
6
+ def_delegators :instance, :reset!, :register, :stubs, :stubs=, :stub_for
7
+ end
8
+
9
+ include Singleton
10
+
11
+ # @return [Hash<Symbol, Array<SearchStub>>]
12
+ attr_accessor :stubs
13
+
14
+ def initialize
15
+ reset!
16
+ end
17
+
18
+ def reset!
19
+ @stubs = []
20
+ end
21
+
22
+ def register(stub)
23
+ @stubs.insert(0, stub)
24
+ stub
25
+ end
26
+
27
+ def stub_for(*args)
28
+ raise ArgumentError, "#stub_for is an abstract function"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ require_relative "registry"
2
+
3
+ module ChefSpec
4
+ module Stubs
5
+ class SearchRegistry < Registry
6
+ def stub_for(type, query = "*:*")
7
+ @stubs.find do |stub|
8
+ stub.type.to_s == type.to_s && stub.query === query
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end