chefspec-chef 9.3.4

Sign up to get free protection for your applications and to get access to all the features.
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,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