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,282 @@
|
|
1
|
+
require "chef/formatters/base"
|
2
|
+
require "chef/formatters/error_mapper"
|
3
|
+
|
4
|
+
module ChefSpec
|
5
|
+
class ChefFormatter < Chef::Formatters::Base
|
6
|
+
cli_name :chefspec
|
7
|
+
|
8
|
+
# Called at the very start of a Chef Run
|
9
|
+
def run_start(version); end
|
10
|
+
|
11
|
+
def run_started(run_status); end
|
12
|
+
|
13
|
+
# Called at the end a successful Chef run.
|
14
|
+
def run_completed(node); end
|
15
|
+
|
16
|
+
# Called at the end of a failed Chef run.
|
17
|
+
def run_failed(exception); end
|
18
|
+
|
19
|
+
# Called right after ohai runs.
|
20
|
+
def ohai_completed(node); end
|
21
|
+
|
22
|
+
# Already have a client key, assuming this node has registered.
|
23
|
+
def skipping_registration(node_name, config); end
|
24
|
+
|
25
|
+
# About to attempt to register as +node_name+
|
26
|
+
def registration_start(node_name, config); end
|
27
|
+
|
28
|
+
def registration_completed; end
|
29
|
+
|
30
|
+
# Failed to register this client with the server.
|
31
|
+
def registration_failed(node_name, exception, config)
|
32
|
+
expecting_exception(exception) do
|
33
|
+
description = Chef::Formatters::ErrorMapper.registration_failed(node_name, exception, config)
|
34
|
+
display_error(description)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Called before Chef client loads the node data from the server
|
39
|
+
def node_load_start(node_name, config); end
|
40
|
+
|
41
|
+
# Failed to load node data from the server
|
42
|
+
def node_load_failed(node_name, exception, config)
|
43
|
+
expecting_exception(exception) do
|
44
|
+
description = Chef::Formatters::ErrorMapper.node_load_failed(node_name, exception, config)
|
45
|
+
display_error(description)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Error expanding the run list
|
50
|
+
def run_list_expand_failed(node, exception)
|
51
|
+
expecting_exception(exception) do
|
52
|
+
description = Chef::Formatters::ErrorMapper.run_list_expand_failed(node, exception)
|
53
|
+
display_error(description)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Called after Chef client has loaded the node data.
|
58
|
+
# Default and override attrs from roles have been computed, but not yet applied.
|
59
|
+
# Normal attrs from JSON have been added to the node.
|
60
|
+
def node_load_completed(node, expanded_run_list, config); end
|
61
|
+
|
62
|
+
# Called before the cookbook collection is fetched from the server.
|
63
|
+
def cookbook_resolution_start(expanded_run_list); end
|
64
|
+
|
65
|
+
# Called when there is an error getting the cookbook collection from the
|
66
|
+
# server.
|
67
|
+
def cookbook_resolution_failed(expanded_run_list, exception)
|
68
|
+
expecting_exception(exception) do
|
69
|
+
description = Chef::Formatters::ErrorMapper.cookbook_resolution_failed(expanded_run_list, exception)
|
70
|
+
display_error(description)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Called when the cookbook collection is returned from the server.
|
75
|
+
def cookbook_resolution_complete(cookbook_collection); end
|
76
|
+
|
77
|
+
# Called before unneeded cookbooks are removed
|
78
|
+
def cookbook_clean_start; end
|
79
|
+
|
80
|
+
# Called after the file at +path+ is removed. It may be removed if the
|
81
|
+
# cookbook containing it was removed from the run list, or if the file was
|
82
|
+
# removed from the cookbook.
|
83
|
+
def removed_cookbook_file(path); end
|
84
|
+
|
85
|
+
# Called when cookbook cleaning is finished.
|
86
|
+
def cookbook_clean_complete; end
|
87
|
+
|
88
|
+
# Called before cookbook sync starts
|
89
|
+
def cookbook_sync_start(cookbook_count); end
|
90
|
+
|
91
|
+
# Called when cookbook +cookbook_name+ has been sync'd
|
92
|
+
def synchronized_cookbook(cookbook_name); end
|
93
|
+
|
94
|
+
# Called when an individual file in a cookbook has been updated
|
95
|
+
def updated_cookbook_file(cookbook_name, path); end
|
96
|
+
|
97
|
+
# Called when an error occurs during cookbook sync
|
98
|
+
def cookbook_sync_failed(cookbooks, exception)
|
99
|
+
expecting_exception(exception) do
|
100
|
+
description = Chef::Formatters::ErrorMapper.cookbook_sync_failed(cookbooks, exception)
|
101
|
+
display_error(description)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Called after all cookbooks have been sync'd.
|
106
|
+
def cookbook_sync_complete; end
|
107
|
+
|
108
|
+
# Called when library file loading starts
|
109
|
+
def library_load_start(file_count); end
|
110
|
+
|
111
|
+
# Called when library file has been loaded
|
112
|
+
def library_file_loaded(path); end
|
113
|
+
|
114
|
+
# Called when a library file has an error on load.
|
115
|
+
def library_file_load_failed(path, exception)
|
116
|
+
file_load_failed(path, exception)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Called when library file loading has finished
|
120
|
+
def library_load_complete; end
|
121
|
+
|
122
|
+
# Called when LWRP loading starts
|
123
|
+
def lwrp_load_start(lwrp_file_count); end
|
124
|
+
|
125
|
+
# Called after a LWR or LWP has been loaded
|
126
|
+
def lwrp_file_loaded(path); end
|
127
|
+
|
128
|
+
# Called after a LWR or LWP file errors on load
|
129
|
+
def lwrp_file_load_failed(path, exception)
|
130
|
+
file_load_failed(path, exception)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Called when LWRPs are finished loading
|
134
|
+
def lwrp_load_complete; end
|
135
|
+
|
136
|
+
# Called when an ohai plugin file loading starts
|
137
|
+
def ohai_plugin_load_start(file_count); end
|
138
|
+
|
139
|
+
# Called when an ohai plugin file has been loaded
|
140
|
+
def ohai_plugin_file_loaded(path); end
|
141
|
+
|
142
|
+
# Called when an ohai plugin file has an error on load.
|
143
|
+
def ohai_plugin_file_load_failed(path, exception); end
|
144
|
+
|
145
|
+
# Called when an ohai plugin file loading has finished
|
146
|
+
def ohai_plugin_load_complete; end
|
147
|
+
|
148
|
+
# Called before attribute files are loaded
|
149
|
+
def attribute_load_start(attribute_file_count); end
|
150
|
+
|
151
|
+
# Called after the attribute file is loaded
|
152
|
+
def attribute_file_loaded(path); end
|
153
|
+
|
154
|
+
# Called when an attribute file fails to load.
|
155
|
+
def attribute_file_load_failed(path, exception)
|
156
|
+
file_load_failed(path, exception)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Called when attribute file loading is finished
|
160
|
+
def attribute_load_complete; end
|
161
|
+
|
162
|
+
# Called before resource definitions are loaded
|
163
|
+
def definition_load_start(definition_file_count); end
|
164
|
+
|
165
|
+
# Called when a resource definition has been loaded
|
166
|
+
def definition_file_loaded(path); end
|
167
|
+
|
168
|
+
# Called when a resource definition file fails to load
|
169
|
+
def definition_file_load_failed(path, exception)
|
170
|
+
file_load_failed(path, exception)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Called when resource defintions are done loading
|
174
|
+
def definition_load_complete; end
|
175
|
+
|
176
|
+
# Called before recipes are loaded
|
177
|
+
def recipe_load_start(recipe_count); end
|
178
|
+
|
179
|
+
# Called after the recipe has been loaded
|
180
|
+
def recipe_file_loaded(path); end
|
181
|
+
|
182
|
+
# Called after a recipe file fails to load
|
183
|
+
def recipe_file_load_failed(path, exception)
|
184
|
+
file_load_failed(path, exception)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Called when a recipe cannot be resolved
|
188
|
+
def recipe_not_found(exception)
|
189
|
+
expecting_exception(exception) do
|
190
|
+
description = Chef::Formatters::ErrorMapper.file_load_failed(nil, exception)
|
191
|
+
display_error(description)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Called when recipes have been loaded.
|
196
|
+
def recipe_load_complete; end
|
197
|
+
|
198
|
+
# Called before convergence starts
|
199
|
+
def converge_start(run_context); end
|
200
|
+
|
201
|
+
# Called when the converge phase is finished.
|
202
|
+
def converge_complete; end
|
203
|
+
|
204
|
+
# Called before action is executed on a resource.
|
205
|
+
def resource_action_start(resource, action, notification_type = nil, notifier = nil); end
|
206
|
+
|
207
|
+
# Called when a resource fails, but will retry.
|
208
|
+
def resource_failed_retriable(resource, action, retry_count, exception); end
|
209
|
+
|
210
|
+
# Called when a resource fails and will not be retried.
|
211
|
+
def resource_failed(resource, action, exception)
|
212
|
+
expecting_exception(exception) do
|
213
|
+
description = Chef::Formatters::ErrorMapper.resource_failed(resource, action, exception)
|
214
|
+
display_error(description)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Called when a resource action has been skipped b/c of a conditional
|
219
|
+
def resource_skipped(resource, action, conditional); end
|
220
|
+
|
221
|
+
# Called when a resource action has been completed
|
222
|
+
def resource_completed(resource); end
|
223
|
+
|
224
|
+
# Called after #load_current_resource has run.
|
225
|
+
def resource_current_state_loaded(resource, action, current_resource); end
|
226
|
+
|
227
|
+
# Called when resource current state load is skipped due to the provider
|
228
|
+
# not supporting whyrun mode.
|
229
|
+
def resource_current_state_load_bypassed(resource, action, current_resource); end
|
230
|
+
|
231
|
+
# Called when evaluating a resource that does not support whyrun in whyrun mode
|
232
|
+
def resource_bypassed(resource, action, current_resource); end
|
233
|
+
|
234
|
+
# Called when a resource has no converge actions, e.g., it was already correct.
|
235
|
+
def resource_up_to_date(resource, action); end
|
236
|
+
|
237
|
+
# Called when a change has been made to a resource. May be called multiple
|
238
|
+
# times per resource, e.g., a file may have its content updated, and then
|
239
|
+
# its permissions updated.
|
240
|
+
def resource_update_applied(resource, action, update); end
|
241
|
+
|
242
|
+
# Called after a resource has been completely converged, but only if
|
243
|
+
# modifications were made.
|
244
|
+
def resource_updated(resource, action); end
|
245
|
+
|
246
|
+
# Called before handlers run
|
247
|
+
def handlers_start(handler_count); end
|
248
|
+
|
249
|
+
# Called after an individual handler has run
|
250
|
+
def handler_executed(handler); end
|
251
|
+
|
252
|
+
# Called after all handlers have executed
|
253
|
+
def handlers_completed; end
|
254
|
+
|
255
|
+
# Called when an assertion declared by a provider fails
|
256
|
+
def provider_requirement_failed(action, resource, exception, message); end
|
257
|
+
|
258
|
+
# Called when a provider makes an assumption after a failed assertion
|
259
|
+
# in whyrun mode, in order to allow execution to continue
|
260
|
+
def whyrun_assumption(action, resource, message); end
|
261
|
+
|
262
|
+
# An uncategorized message. This supports the case that a user needs to
|
263
|
+
# pass output that doesn't fit into one of the callbacks above. Note that
|
264
|
+
# there's no semantic information about the content or importance of the
|
265
|
+
# message. That means that if you're using this too often, you should add a
|
266
|
+
# callback for it.
|
267
|
+
def msg(message); end
|
268
|
+
|
269
|
+
private
|
270
|
+
|
271
|
+
def file_load_failed(path, exception)
|
272
|
+
expecting_exception(exception) do
|
273
|
+
description = Chef::Formatters::ErrorMapper.file_load_failed(path, exception)
|
274
|
+
display_error(description)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def expecting_exception(exception, &block)
|
279
|
+
yield unless ChefSpec::ExpectException.new(exception).expected?
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
begin
|
2
|
+
require "librarian/chef/environment"
|
3
|
+
require "librarian/action/resolve"
|
4
|
+
require "librarian/action/install"
|
5
|
+
rescue LoadError
|
6
|
+
raise ChefSpec::Error::GemLoadError.new(
|
7
|
+
gem: "librarian-chef", name: "Librarian"
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ChefSpec
|
12
|
+
class Librarian
|
13
|
+
class << self
|
14
|
+
extend Forwardable
|
15
|
+
def_delegators :instance, :setup!, :teardown!
|
16
|
+
end
|
17
|
+
|
18
|
+
include Singleton
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@tmpdir = Dir.mktmpdir
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Setup and install the necessary dependencies in the temporary directory.
|
26
|
+
#
|
27
|
+
def setup!
|
28
|
+
env = ::Librarian::Chef::Environment.new(project_path: Dir.pwd)
|
29
|
+
@originalpath, env.config_db.local["path"] = env.config_db.local["path"], @tmpdir
|
30
|
+
::Librarian::Action::Resolve.new(env).run
|
31
|
+
::Librarian::Action::Install.new(env).run
|
32
|
+
|
33
|
+
::RSpec.configure { |config| config.cookbook_path = @tmpdir }
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Remove the temporary directory and restore the librarian-chef cookbook path.
|
38
|
+
#
|
39
|
+
def teardown!
|
40
|
+
env = ::Librarian::Chef::Environment.new(project_path: Dir.pwd)
|
41
|
+
env.config_db.local["path"] = @originalpath
|
42
|
+
|
43
|
+
FileUtils.rm_rf(@tmpdir) if File.exist?(@tmpdir)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
RSpec.configure do |config|
|
49
|
+
config.before(:suite) { ChefSpec::Librarian.setup! }
|
50
|
+
config.after(:suite) { ChefSpec::Librarian.teardown! }
|
51
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ChefSpec::Matchers
|
2
|
+
class DoNothingMatcher
|
3
|
+
def matches?(resource)
|
4
|
+
@resource = resource
|
5
|
+
|
6
|
+
if @resource
|
7
|
+
ChefSpec::Coverage.cover!(@resource)
|
8
|
+
|
9
|
+
actions = @resource.performed_actions
|
10
|
+
actions.empty? || actions == [:nothing]
|
11
|
+
else
|
12
|
+
false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def description
|
17
|
+
"do nothing"
|
18
|
+
end
|
19
|
+
|
20
|
+
def failure_message
|
21
|
+
if @resource
|
22
|
+
message = %{expected #{@resource} to do nothing, but the following }
|
23
|
+
message << %{actions were performed:}
|
24
|
+
message << %{\n\n}
|
25
|
+
@resource.performed_actions.each do |action|
|
26
|
+
message << %{ :#{action}}
|
27
|
+
end
|
28
|
+
message
|
29
|
+
else
|
30
|
+
message = %{expected _something_ to do nothing, but the _something_ }
|
31
|
+
message << %{you gave me was nil! If you are running a test like:}
|
32
|
+
message << %{\n\n}
|
33
|
+
message << %{ expect(_something_).to do_nothing}
|
34
|
+
message << %{\n\n}
|
35
|
+
message << %{make sure that `_something_` exists, because I got nil!}
|
36
|
+
message
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def failure_message_when_negated
|
41
|
+
if @resource
|
42
|
+
message = %{expected #{@resource} to do something, but no actions }
|
43
|
+
message << %{were performed.}
|
44
|
+
message
|
45
|
+
else
|
46
|
+
message = %{expected _something_ to do something, but no actions }
|
47
|
+
message << %{were performed.}
|
48
|
+
message
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ChefSpec::Matchers
|
2
|
+
class IncludeAnyRecipeMatcher
|
3
|
+
def matches?(runner)
|
4
|
+
@runner = runner
|
5
|
+
!(loaded_recipes - run_list_recipes).empty?
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
"include any recipe"
|
10
|
+
end
|
11
|
+
|
12
|
+
def failure_message
|
13
|
+
"expected to include any recipe"
|
14
|
+
end
|
15
|
+
|
16
|
+
def failure_message_when_negated
|
17
|
+
"expected not to include any recipes"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
#
|
23
|
+
# The list of run_list recipes on the Chef run (normalized)
|
24
|
+
#
|
25
|
+
# @return [Array<String>]
|
26
|
+
#
|
27
|
+
def run_list_recipes
|
28
|
+
@runner.run_context.node.run_list.run_list_items.map { |x| with_default(x.name) }
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Automatically appends "+::default+" to recipes that need them.
|
33
|
+
#
|
34
|
+
# @param [String] name
|
35
|
+
#
|
36
|
+
# @return [String]
|
37
|
+
#
|
38
|
+
def with_default(name)
|
39
|
+
name.include?("::") ? name : "#{name}::default"
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# The list of loaded recipes on the Chef run (normalized)
|
44
|
+
#
|
45
|
+
# @return [Array<String>]
|
46
|
+
#
|
47
|
+
def loaded_recipes
|
48
|
+
@runner.run_context.loaded_recipes.map { |name| with_default(name) }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ChefSpec::Matchers
|
2
|
+
class IncludeRecipeMatcher
|
3
|
+
def initialize(recipe_name)
|
4
|
+
@recipe_name = with_default(recipe_name)
|
5
|
+
end
|
6
|
+
|
7
|
+
def matches?(runner)
|
8
|
+
@runner = runner
|
9
|
+
loaded_recipes.include?(@recipe_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def description
|
13
|
+
%Q{include recipe "#{@recipe_name}"}
|
14
|
+
end
|
15
|
+
|
16
|
+
def failure_message
|
17
|
+
%Q{expected #{loaded_recipes.inspect} to include "#{@recipe_name}"}
|
18
|
+
end
|
19
|
+
|
20
|
+
def failure_message_when_negated
|
21
|
+
%Q{expected "#{@recipe_name}" to not be included}
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
#
|
27
|
+
# Automatically appends "+::default+" to recipes that need them.
|
28
|
+
#
|
29
|
+
# @param [String] name
|
30
|
+
#
|
31
|
+
# @return [String]
|
32
|
+
#
|
33
|
+
def with_default(name)
|
34
|
+
name.include?("::") ? name : "#{name}::default"
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# The list of loaded recipes on the Chef run (normalized)
|
39
|
+
#
|
40
|
+
# @return [Array<String>]
|
41
|
+
#
|
42
|
+
def loaded_recipes
|
43
|
+
@runner.run_context.loaded_recipes.map { |name| with_default(name) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ChefSpec::Matchers
|
2
|
+
class LinkToMatcher
|
3
|
+
def initialize(path)
|
4
|
+
@path = path
|
5
|
+
end
|
6
|
+
|
7
|
+
def matches?(link)
|
8
|
+
@link = link
|
9
|
+
|
10
|
+
if @link
|
11
|
+
ChefSpec::Coverage.cover!(@link)
|
12
|
+
|
13
|
+
@link.is_a?(Chef::Resource::Link) &&
|
14
|
+
@link.performed_action?(:create) &&
|
15
|
+
@path === @link.to
|
16
|
+
else
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def description
|
22
|
+
%Q{link to "#{@path}"}
|
23
|
+
end
|
24
|
+
|
25
|
+
def failure_message
|
26
|
+
if @link.nil?
|
27
|
+
%Q{expected "link[#{@path}]" with action :create to be in Chef run}
|
28
|
+
else
|
29
|
+
%Q{expected "#{@link}" to link to "#{@path}" but was "#{@link.to}"}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def failure_message_when_negated
|
34
|
+
%Q{expected "#{@link}" to not link to "#{@path}"}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module ChefSpec::Matchers
|
2
|
+
class NotificationsMatcher
|
3
|
+
include ChefSpec::Normalize
|
4
|
+
|
5
|
+
def initialize(signature)
|
6
|
+
signature.match(/^([^\[]*)\[(.*)\]$/)
|
7
|
+
@expected_resource_type = $1
|
8
|
+
@expected_resource_name = $2
|
9
|
+
end
|
10
|
+
|
11
|
+
def matches?(resource)
|
12
|
+
@resource = resource
|
13
|
+
|
14
|
+
if @resource
|
15
|
+
block = Proc.new do |notified|
|
16
|
+
resource_name(notified.resource).to_s == @expected_resource_type &&
|
17
|
+
(@expected_resource_name === notified.resource.identity.to_s || @expected_resource_name === notified.resource.name.to_s) &&
|
18
|
+
matches_action?(notified)
|
19
|
+
end
|
20
|
+
|
21
|
+
if @immediately
|
22
|
+
immediate_notifications.any?(&block)
|
23
|
+
elsif @delayed
|
24
|
+
delayed_notifications.any?(&block)
|
25
|
+
elsif @before
|
26
|
+
before_notifications.any?(&block)
|
27
|
+
else
|
28
|
+
all_notifications.any?(&block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def to(action)
|
34
|
+
@action = action.to_sym
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def immediately
|
39
|
+
@immediately = true
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def delayed
|
44
|
+
@delayed = true
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def before
|
49
|
+
@before = true
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def description
|
54
|
+
message = %Q{notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
|
55
|
+
message << " with action :#{@action}" if @action
|
56
|
+
message << " immediately" if @immediately
|
57
|
+
message << " delayed" if @delayed
|
58
|
+
message << " before" if @before
|
59
|
+
message
|
60
|
+
end
|
61
|
+
|
62
|
+
def failure_message
|
63
|
+
if @resource
|
64
|
+
message = %Q{expected "#{@resource}" to notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
|
65
|
+
message << " with action :#{@action}" if @action
|
66
|
+
message << " immediately" if @immediately
|
67
|
+
message << " delayed" if @delayed
|
68
|
+
message << " before" if @before
|
69
|
+
message << ", but did not."
|
70
|
+
message << "\n\n"
|
71
|
+
message << "Other notifications were:\n\n#{format_notifications}"
|
72
|
+
message << "\n "
|
73
|
+
message
|
74
|
+
else
|
75
|
+
message = %Q{expected _something_ to notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
|
76
|
+
message << " with action :#{@action}" if @action
|
77
|
+
message << " immediately" if @immediately
|
78
|
+
message << " delayed" if @delayed
|
79
|
+
message << " before" if @before
|
80
|
+
message << ", but the _something_ you gave me was nil! If you are running a test like:"
|
81
|
+
message << "\n\n"
|
82
|
+
message << " expect(_something_).to notify('...')"
|
83
|
+
message << "\n\n"
|
84
|
+
message << "Make sure that `_something_` exists, because I got nil"
|
85
|
+
message << "\n "
|
86
|
+
message
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def failure_message_when_negated
|
91
|
+
if @resource
|
92
|
+
message = %Q{expected "#{@resource}" to not notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
|
93
|
+
message << ", but it did."
|
94
|
+
message
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def all_notifications
|
101
|
+
immediate_notifications + delayed_notifications + before_notifications
|
102
|
+
end
|
103
|
+
|
104
|
+
def immediate_notifications
|
105
|
+
@resource.immediate_notifications
|
106
|
+
end
|
107
|
+
|
108
|
+
def delayed_notifications
|
109
|
+
@resource.delayed_notifications
|
110
|
+
end
|
111
|
+
|
112
|
+
def before_notifications
|
113
|
+
@resource.before_notifications
|
114
|
+
end
|
115
|
+
|
116
|
+
def matches_action?(notification)
|
117
|
+
return true if @action.nil?
|
118
|
+
|
119
|
+
@action == notification.action.to_sym
|
120
|
+
end
|
121
|
+
|
122
|
+
def format_notification(notification)
|
123
|
+
notifying_resource = notification.notifying_resource
|
124
|
+
resource = notification.resource
|
125
|
+
|
126
|
+
if notifying_resource.immediate_notifications.include?(notification)
|
127
|
+
type = :immediately
|
128
|
+
elsif notifying_resource.before_notifications.include?(notification)
|
129
|
+
type = :before
|
130
|
+
else
|
131
|
+
type = :delayed
|
132
|
+
end
|
133
|
+
|
134
|
+
%Q{ "#{notifying_resource}" notifies "#{resource_name(resource)}[#{resource.name}]" to :#{notification.action}, :#{type}}
|
135
|
+
end
|
136
|
+
|
137
|
+
def format_notifications
|
138
|
+
all_notifications.map do |notification|
|
139
|
+
" " + format_notification(notification)
|
140
|
+
end.join("\n")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|