chefspec-chef 9.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +30 -0
- data/LICENSE +22 -0
- data/Rakefile +85 -0
- data/chefspec-chef.gemspec +30 -0
- data/lib/chefspec/api/core.rb +217 -0
- data/lib/chefspec/api/described.rb +53 -0
- data/lib/chefspec/api/do_nothing.rb +26 -0
- data/lib/chefspec/api/include_any_recipe.rb +24 -0
- data/lib/chefspec/api/include_recipe.rb +28 -0
- data/lib/chefspec/api/link.rb +28 -0
- data/lib/chefspec/api/notifications.rb +40 -0
- data/lib/chefspec/api/reboot.rb +14 -0
- data/lib/chefspec/api/render_file.rb +37 -0
- data/lib/chefspec/api/state_attrs.rb +30 -0
- data/lib/chefspec/api/stubs.rb +183 -0
- data/lib/chefspec/api/stubs_for.rb +139 -0
- data/lib/chefspec/api/subscriptions.rb +37 -0
- data/lib/chefspec/api/user.rb +230 -0
- data/lib/chefspec/api.rb +39 -0
- data/lib/chefspec/berkshelf.rb +63 -0
- data/lib/chefspec/cacher.rb +64 -0
- data/lib/chefspec/coverage/filters.rb +82 -0
- data/lib/chefspec/coverage.rb +247 -0
- data/lib/chefspec/deprecations.rb +46 -0
- data/lib/chefspec/errors.rb +48 -0
- data/lib/chefspec/expect_exception.rb +51 -0
- data/lib/chefspec/extensions/chef/client.rb +21 -0
- data/lib/chefspec/extensions/chef/conditional.rb +16 -0
- data/lib/chefspec/extensions/chef/cookbook/gem_installer.rb +33 -0
- data/lib/chefspec/extensions/chef/cookbook_loader.rb +14 -0
- data/lib/chefspec/extensions/chef/cookbook_uploader.rb +12 -0
- data/lib/chefspec/extensions/chef/data_query.rb +49 -0
- data/lib/chefspec/extensions/chef/lwrp_base.rb +29 -0
- data/lib/chefspec/extensions/chef/provider.rb +39 -0
- data/lib/chefspec/extensions/chef/resource/freebsd_package.rb +17 -0
- data/lib/chefspec/extensions/chef/resource.rb +188 -0
- data/lib/chefspec/extensions/chef/run_context/cookbook_compiler.rb +84 -0
- data/lib/chefspec/extensions/chef/securable.rb +19 -0
- data/lib/chefspec/extensions/ohai/system.rb +11 -0
- data/lib/chefspec/extensions.rb +21 -0
- data/lib/chefspec/file_cache_path_proxy.rb +15 -0
- data/lib/chefspec/formatter.rb +282 -0
- data/lib/chefspec/librarian.rb +51 -0
- data/lib/chefspec/matchers/do_nothing_matcher.rb +52 -0
- data/lib/chefspec/matchers/include_any_recipe_matcher.rb +51 -0
- data/lib/chefspec/matchers/include_recipe_matcher.rb +46 -0
- data/lib/chefspec/matchers/link_to_matcher.rb +37 -0
- data/lib/chefspec/matchers/notifications_matcher.rb +143 -0
- data/lib/chefspec/matchers/render_file_matcher.rb +140 -0
- data/lib/chefspec/matchers/resource_matcher.rb +175 -0
- data/lib/chefspec/matchers/state_attrs_matcher.rb +71 -0
- data/lib/chefspec/matchers/subscribes_matcher.rb +72 -0
- data/lib/chefspec/matchers.rb +13 -0
- data/lib/chefspec/mixins/normalize.rb +22 -0
- data/lib/chefspec/policyfile.rb +69 -0
- data/lib/chefspec/renderer.rb +145 -0
- data/lib/chefspec/rspec.rb +21 -0
- data/lib/chefspec/runner.rb +8 -0
- data/lib/chefspec/server.rb +4 -0
- data/lib/chefspec/server_methods.rb +173 -0
- data/lib/chefspec/server_runner.rb +76 -0
- data/lib/chefspec/solo_runner.rb +516 -0
- data/lib/chefspec/stubs/command_registry.rb +11 -0
- data/lib/chefspec/stubs/command_stub.rb +37 -0
- data/lib/chefspec/stubs/data_bag_item_registry.rb +13 -0
- data/lib/chefspec/stubs/data_bag_item_stub.rb +25 -0
- data/lib/chefspec/stubs/data_bag_registry.rb +13 -0
- data/lib/chefspec/stubs/data_bag_stub.rb +23 -0
- data/lib/chefspec/stubs/registry.rb +32 -0
- data/lib/chefspec/stubs/search_registry.rb +13 -0
- data/lib/chefspec/stubs/search_stub.rb +25 -0
- data/lib/chefspec/stubs/stub.rb +38 -0
- data/lib/chefspec/util.rb +58 -0
- data/lib/chefspec/version.rb +3 -0
- data/lib/chefspec/zero_server.rb +142 -0
- data/lib/chefspec.rb +75 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/hash.rb +35 -0
- data/spec/unit/cacher_spec.rb +70 -0
- data/spec/unit/coverage/filters_spec.rb +60 -0
- data/spec/unit/deprecations_spec.rb +52 -0
- data/spec/unit/errors_spec.rb +57 -0
- data/spec/unit/expect_exception_spec.rb +32 -0
- data/spec/unit/macros_spec.rb +119 -0
- data/spec/unit/matchers/do_nothing_matcher.rb +5 -0
- data/spec/unit/matchers/include_any_recipe_matcher_spec.rb +52 -0
- data/spec/unit/matchers/include_recipe_matcher_spec.rb +38 -0
- data/spec/unit/matchers/link_to_matcher_spec.rb +55 -0
- data/spec/unit/matchers/notifications_matcher_spec.rb +39 -0
- data/spec/unit/matchers/render_file_matcher_spec.rb +68 -0
- data/spec/unit/matchers/resource_matcher_spec.rb +5 -0
- data/spec/unit/matchers/state_attrs_matcher_spec.rb +68 -0
- data/spec/unit/matchers/subscribes_matcher_spec.rb +63 -0
- data/spec/unit/renderer_spec.rb +69 -0
- data/spec/unit/server_runner_spec.rb +28 -0
- data/spec/unit/solo_runner_spec.rb +171 -0
- data/spec/unit/stubs/command_registry_spec.rb +27 -0
- data/spec/unit/stubs/command_stub_spec.rb +61 -0
- data/spec/unit/stubs/data_bag_item_registry_spec.rb +39 -0
- data/spec/unit/stubs/data_bag_item_stub_spec.rb +36 -0
- data/spec/unit/stubs/data_bag_registry_spec.rb +39 -0
- data/spec/unit/stubs/data_bag_stub_spec.rb +35 -0
- data/spec/unit/stubs/registry_spec.rb +29 -0
- data/spec/unit/stubs/search_registry_spec.rb +39 -0
- data/spec/unit/stubs/search_stub_spec.rb +36 -0
- data/spec/unit/stubs/stub_spec.rb +64 -0
- data/templates/coverage/human.erb +22 -0
- data/templates/coverage/json.erb +8 -0
- data/templates/coverage/table.erb +14 -0
- data/templates/errors/cookbook_path_not_found.erb +3 -0
- data/templates/errors/erb_template_parse_error.erb +5 -0
- data/templates/errors/gem_load_error.erb +7 -0
- data/templates/errors/invalid_berkshelf_options.erb +4 -0
- data/templates/errors/may_need_to_specify_platform.erb +25 -0
- data/templates/errors/no_conversion_error.erb +1 -0
- data/templates/errors/not_stubbed.erb +7 -0
- data/templates/errors/shell_out_not_stubbed.erb +10 -0
- data/templates/errors/template_not_found.erb +9 -0
- 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)
|