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,183 @@
|
|
1
|
+
module ChefSpec
|
2
|
+
module API
|
3
|
+
module Stubs
|
4
|
+
#
|
5
|
+
# Stub a shell command to return a particular value without
|
6
|
+
# shelling out to the system.
|
7
|
+
#
|
8
|
+
# @example stubbing a command to return true
|
9
|
+
# stub_command('test -f /tmp/bacon').and_return(true)
|
10
|
+
#
|
11
|
+
# @example stubbing a block that is evaluated at runtime
|
12
|
+
# stub_command('test -f /tmp/bacon') { MyClass.check? }
|
13
|
+
#
|
14
|
+
# @example stubbing a command that matches a pattern
|
15
|
+
# stub_command(/test \-f/).and_return(true)
|
16
|
+
#
|
17
|
+
# @example stubbing a command that raises an exception
|
18
|
+
# stub_command('test -f /tmp/bacon').and_raise(SomeException)
|
19
|
+
#
|
20
|
+
#
|
21
|
+
# @param [String, Regexp] command
|
22
|
+
# the command to stub
|
23
|
+
#
|
24
|
+
# @return [ChefSpec::CommandStub]
|
25
|
+
#
|
26
|
+
def stub_command(command, &block)
|
27
|
+
ChefSpec::Stubs::CommandRegistry.register(ChefSpec::Stubs::CommandStub.new(command, &block))
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Stub a Chef call to load a data_bag.
|
32
|
+
#
|
33
|
+
# @example stubbing a data_bag to return an empty Array
|
34
|
+
# stub_data_bag('users').and_return([])
|
35
|
+
#
|
36
|
+
# @example stubbing a data_bag with a block that is evaluated at runtime
|
37
|
+
# stub_data_bag('users') { JSON.parse(File.read('fixtures/data_bag.json')) }
|
38
|
+
#
|
39
|
+
# @example stubbing a data_bag to return an Array of Hashes
|
40
|
+
# stub_data_bag('users').and_return([
|
41
|
+
# { id: 'bacon', comment: 'delicious' },
|
42
|
+
# { id: 'ham', comment: 'also good' }
|
43
|
+
# ])
|
44
|
+
#
|
45
|
+
# @example stubbing a data_bag to raise an exception
|
46
|
+
# stub_data_bag('users').and_raise(Chef::Exceptions::PrivateKeyMissing)
|
47
|
+
#
|
48
|
+
#
|
49
|
+
# @param [String, Symbol] bag
|
50
|
+
# the name of the data bag to stub
|
51
|
+
#
|
52
|
+
# @return [ChefSpec::DataBagStub]
|
53
|
+
#
|
54
|
+
def stub_data_bag(bag, &block)
|
55
|
+
ChefSpec::Stubs::DataBagRegistry.register(ChefSpec::Stubs::DataBagStub.new(bag, &block))
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Stub a Chef data_bag call.
|
60
|
+
#
|
61
|
+
# @example stubbing a data_bag_item to return a Hash of data
|
62
|
+
# stub_data_bag_item('users', 'svargo').and_return({
|
63
|
+
# id: 'svargo',
|
64
|
+
# name: 'Seth Vargo'
|
65
|
+
# })
|
66
|
+
#
|
67
|
+
# @example stubbing a data_bag_item with a block that is evaluated at runtime
|
68
|
+
# stub_data_bag_item('users', 'svargo') { JSON.parse(File.read('fixtures/data_bag_item.json')) }
|
69
|
+
#
|
70
|
+
# @example stubbing a data_bag_item to raise an exception
|
71
|
+
# stub_data_bag('users', 'svargo').and_raise(Chef::Exceptions::PrivateKeyMissing)
|
72
|
+
#
|
73
|
+
#
|
74
|
+
# @param [String, Symbol] bag
|
75
|
+
# the name of the data bag to find the item in
|
76
|
+
# @param [String] id
|
77
|
+
# the id of the data bag item to stub
|
78
|
+
#
|
79
|
+
# @return [ChefSpec::DataBagItemStub]
|
80
|
+
#
|
81
|
+
def stub_data_bag_item(bag, id, &block)
|
82
|
+
ChefSpec::Stubs::DataBagItemRegistry.register(ChefSpec::Stubs::DataBagItemStub.new(bag, id, &block))
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Creates a fake Chef::Node for use in testing.
|
87
|
+
#
|
88
|
+
# @example mocking a simple node
|
89
|
+
# stub_node('mynode.example')
|
90
|
+
#
|
91
|
+
# @example mocking a specific platform and version
|
92
|
+
# stub_node('mynode.example', platform: 'ubuntu', version: '18.04')
|
93
|
+
#
|
94
|
+
# @example overriding a specific ohai attribute
|
95
|
+
# stub_node('mynode.example', ohai: { ipaddress: '1.2.3.4' })
|
96
|
+
#
|
97
|
+
# @example stubbing a node based on a JSON fixture
|
98
|
+
# stub_node('mynode.example', path: File.join('fixtures', 'nodes', 'default.json'))
|
99
|
+
#
|
100
|
+
# @example setting specific attributes
|
101
|
+
# stub_node('mynode.example') do |node|
|
102
|
+
# node.default['attribute']['thing'] = 'value'
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
#
|
106
|
+
# @param [String] name
|
107
|
+
# the name of the node
|
108
|
+
# @param [Hash] options
|
109
|
+
# the list of options for the node
|
110
|
+
#
|
111
|
+
# @option options [Symbol] :platform
|
112
|
+
# the platform to mock
|
113
|
+
# @option options [Symbol] :version
|
114
|
+
# the version of the platform to mock
|
115
|
+
# @option options [Symbol] :path
|
116
|
+
# filepath to a JSON file to pull a node from
|
117
|
+
# @option options [Hash] :ohai
|
118
|
+
# a Hash of Ohai attributes to mock on the node
|
119
|
+
#
|
120
|
+
# @return [Chef::Node]
|
121
|
+
#
|
122
|
+
def stub_node(*args, &block)
|
123
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
124
|
+
name = args.first || "node.example"
|
125
|
+
|
126
|
+
fauxhai = Fauxhai.mock(options).data
|
127
|
+
fauxhai = fauxhai.merge(options[:ohai] || {})
|
128
|
+
fauxhai = Mash.new(fauxhai)
|
129
|
+
|
130
|
+
node = Chef::Node.new
|
131
|
+
node.name(name)
|
132
|
+
node.automatic_attrs = fauxhai
|
133
|
+
node.instance_eval(&block) if block_given?
|
134
|
+
node
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Stub a Chef search to return pre-defined data. When providing a value,
|
139
|
+
# the value is automatically mashified (to the best of ChefSpec's abilities)
|
140
|
+
# to ease in use.
|
141
|
+
#
|
142
|
+
# @example stubbing a search to return nothing
|
143
|
+
# stub_search(:node)
|
144
|
+
#
|
145
|
+
# @example stubbing a search with a query
|
146
|
+
# stub_search(:node, 'name:*')
|
147
|
+
#
|
148
|
+
# @example stubbing a search with a query as a regex
|
149
|
+
# stub_search(:node, /name:(.+)/)
|
150
|
+
#
|
151
|
+
# @example stubbing a search with a block that is evaluated at runtime
|
152
|
+
# stub_search(:node) { JSON.parse(File.read('fixtures/nodes.json')) }
|
153
|
+
#
|
154
|
+
# @example stubbing a search to return a fixed value
|
155
|
+
# stub_search(:node).and_return([ { a: 'b' } ])
|
156
|
+
#
|
157
|
+
# @example stubbing a search to raise an exception
|
158
|
+
# stub_search(:node).and_raise(Chef::Exceptions::PrivateKeyMissing)
|
159
|
+
#
|
160
|
+
#
|
161
|
+
# @param [String, Symbol] type
|
162
|
+
# the type to search to stub
|
163
|
+
# @param [String, Symbol, nil] query
|
164
|
+
# the query to stub
|
165
|
+
#
|
166
|
+
# @return [ChefSpec::SearchStub]
|
167
|
+
#
|
168
|
+
def stub_search(type, query = "*:*", &block)
|
169
|
+
ChefSpec::Stubs::SearchRegistry.register(ChefSpec::Stubs::SearchStub.new(type, query, &block))
|
170
|
+
end
|
171
|
+
|
172
|
+
# Automatically clear all stubs after each test.
|
173
|
+
extend RSpec::SharedContext
|
174
|
+
after(:each) do
|
175
|
+
ChefSpec::Stubs::CommandRegistry.reset!
|
176
|
+
ChefSpec::Stubs::DataBagRegistry.reset!
|
177
|
+
ChefSpec::Stubs::DataBagItemRegistry.reset!
|
178
|
+
ChefSpec::Stubs::SearchRegistry.reset!
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require "chef/version"
|
2
|
+
require "mixlib/shellout" unless defined?(Mixlib::ShellOut)
|
3
|
+
|
4
|
+
module ChefSpec
|
5
|
+
module API
|
6
|
+
module StubsFor
|
7
|
+
# Pull in the needed machinery to use `before` here.
|
8
|
+
extend RSpec::SharedContext
|
9
|
+
|
10
|
+
# Which version to use the shell_out_compacted hook on.
|
11
|
+
HAS_SHELLOUT_COMPACTED = Gem::Requirement.create("> 14.2")
|
12
|
+
|
13
|
+
# Hook used in the monkey patches to set up a place to inject stubs when
|
14
|
+
# needed for a resource or provider.
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
# @param object [Chef::Resource, Chef::Provider] Resource or provider to inject
|
18
|
+
# @param type [Symbol] Type of object to register stubs on
|
19
|
+
# @return [void]
|
20
|
+
def self.setup_stubs_for(object, type)
|
21
|
+
# This space left intentionally blank, real implementation is below.
|
22
|
+
end
|
23
|
+
|
24
|
+
# Place to hold any stubs which should be active for this example.
|
25
|
+
let(:_chefspec_stubs_for_registry) do
|
26
|
+
# Auto-vivify for things like _chefspec_stubs_for_registry[:resource]['my_resource'] == [].
|
27
|
+
Hash.new do |hash, key|
|
28
|
+
hash[key] = Hash.new do |inner_hash, inner_key|
|
29
|
+
inner_hash[inner_key] = []
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Take over from the default, no-op implementation of {.setup_stubs_for}.
|
35
|
+
before do
|
36
|
+
allow(StubsFor).to receive(:setup_stubs_for) do |object, type|
|
37
|
+
type_stubs = _chefspec_stubs_for_registry[type]
|
38
|
+
resource_object = object.respond_to?(:new_resource) ? object.new_resource : object
|
39
|
+
stubs = type_stubs[nil] + type_stubs[resource_object.resource_name.to_s] + type_stubs[resource_object.to_s]
|
40
|
+
stubs.each do |stub|
|
41
|
+
instance_exec(object, &stub)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Register stubs for resource objects.
|
47
|
+
#
|
48
|
+
# The `target` parameter can select either a resource string like `'package[foo]'`,
|
49
|
+
# a resource name like `'package'`, or `nil` for all resources.
|
50
|
+
#
|
51
|
+
# @example Setting method stub on a single resource
|
52
|
+
# it "does a thing" do
|
53
|
+
# stubs_for_resource("my_resource[foo]") do |res|
|
54
|
+
# expect(res).to receive_shell_out.with("my_command").and_return(stdout: "asdf")
|
55
|
+
# end
|
56
|
+
# expect(subject.some_method).to eq "asdf"
|
57
|
+
# end
|
58
|
+
# @param target [String, nil] Resource name to inject, or nil for all resources.
|
59
|
+
# @param current_value [Boolean] If true, also register stubs for current_value objects on the same target.
|
60
|
+
# @param block [Proc] A block taking the resource object as a parameter.
|
61
|
+
# @return [void]
|
62
|
+
def stubs_for_resource(target = nil, current_value: true, current_resource: true, &block)
|
63
|
+
current_value = false unless current_resource
|
64
|
+
_chefspec_stubs_for_registry[:resource][target] << block
|
65
|
+
stubs_for_current_value(target, &block) if current_value
|
66
|
+
end
|
67
|
+
|
68
|
+
# Register stubs for current_value objects.
|
69
|
+
#
|
70
|
+
# @see #stubs_for_resource
|
71
|
+
# @param target [String, nil] Resource name to inject, or nil for all resources.
|
72
|
+
# @param block [Proc] A block taking the resource object as a parameter.
|
73
|
+
# @return [void]
|
74
|
+
def stubs_for_current_value(target = nil, &block)
|
75
|
+
_chefspec_stubs_for_registry[:current_value][target] << block
|
76
|
+
end
|
77
|
+
alias_method :stubs_for_current_resource, :stubs_for_current_value
|
78
|
+
|
79
|
+
# Register stubs for provider objects.
|
80
|
+
#
|
81
|
+
# @see #stubs_for_resource
|
82
|
+
# @param target [String, nil] Resource name to inject, or nil for all providers.
|
83
|
+
# @param block [Proc] A block taking the resource object as a parameter.
|
84
|
+
# @return [void]
|
85
|
+
def stubs_for_provider(target = nil, &block)
|
86
|
+
_chefspec_stubs_for_registry[:provider][target] << block
|
87
|
+
end
|
88
|
+
|
89
|
+
def receive_shell_out(*cmd, stdout: "", stderr: "", exitstatus: 0, **opts)
|
90
|
+
# Ruby does not allow constructing an actual exitstatus object from Ruby code. Really.
|
91
|
+
fake_exitstatus = double(exitstatus: exitstatus)
|
92
|
+
fake_cmd = Mixlib::ShellOut.new(*cmd)
|
93
|
+
fake_cmd.define_singleton_method(:run_command) {} # Do nothing, just in case.
|
94
|
+
# Inject our canned data.
|
95
|
+
fake_cmd.instance_exec do
|
96
|
+
@stdout = stdout
|
97
|
+
@stderr = stderr
|
98
|
+
@status = fake_exitstatus
|
99
|
+
end
|
100
|
+
# On newer Chef, we can intercept using the new, better shell_out_compact hook point.
|
101
|
+
shell_out_method ||= if HAS_SHELLOUT_COMPACTED.satisfied_by?(Gem::Version.create(Chef::VERSION))
|
102
|
+
:shell_out_compacted
|
103
|
+
else
|
104
|
+
:shell_out
|
105
|
+
end
|
106
|
+
with_args = cmd + (opts.empty? ? [any_args] : [hash_including(opts)])
|
107
|
+
receive(shell_out_method).with(*with_args).and_return(fake_cmd)
|
108
|
+
end
|
109
|
+
|
110
|
+
module ClassMethods
|
111
|
+
# (see StubsFor#stubs_for_resource)
|
112
|
+
def stubs_for_resource(*args, **kwargs, &block)
|
113
|
+
before { stubs_for_resource(*args, **kwargs, &block) }
|
114
|
+
end
|
115
|
+
|
116
|
+
# (see StubsFor#stubs_for_current_value)
|
117
|
+
def stubs_for_current_value(*args, &block)
|
118
|
+
before { stubs_for_current_value(*args, &block) }
|
119
|
+
end
|
120
|
+
alias_method :stubs_for_current_resource, :stubs_for_current_value
|
121
|
+
|
122
|
+
# (see StubsFor#stubs_for_provider)
|
123
|
+
def stubs_for_provider(*args, &block)
|
124
|
+
before { stubs_for_provider(*args, &block) }
|
125
|
+
end
|
126
|
+
|
127
|
+
# @api private
|
128
|
+
def included(klass)
|
129
|
+
super
|
130
|
+
# Inject classmethods into the group.
|
131
|
+
klass.extend(ClassMethods)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
extend ClassMethods
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ChefSpec
|
2
|
+
module API
|
3
|
+
module Subscriptions
|
4
|
+
#
|
5
|
+
# Assert that a resource subscribes to another. Given a Chef Recipe that
|
6
|
+
# subscribes a template resource to restart apache:
|
7
|
+
#
|
8
|
+
# service 'apache2' do
|
9
|
+
# subscribes :create, 'template[/etc/apache2/config]'
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# The Examples section demonstrates the different ways to test a
|
13
|
+
# subscription on a resource with ChefSpec.
|
14
|
+
#
|
15
|
+
# @example Assert a basic subscription
|
16
|
+
# service = chef_run.service('apache2')
|
17
|
+
# expect(service).to subscribe_to('template[/etc/apache2/config]')
|
18
|
+
#
|
19
|
+
# @example Assert a subscription with specified action
|
20
|
+
# expect(service).to subscribe_to('template[/etc/apache2/config]').on(:restart)
|
21
|
+
#
|
22
|
+
# @example Assert a subscription with specified action and timing
|
23
|
+
# expect(service).to subscribe_to('template[/etc/apache2/config]').on(:restart).immediately
|
24
|
+
#
|
25
|
+
#
|
26
|
+
# @param [String] signature
|
27
|
+
# the signature of the notification to match
|
28
|
+
#
|
29
|
+
# @return [ChefSpec::Matchers::NotificationsMatcher]
|
30
|
+
#
|
31
|
+
def subscribe_to(signature)
|
32
|
+
ChefSpec::Matchers::SubscribesMatcher.new(signature)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
module ChefSpec
|
2
|
+
module API
|
3
|
+
module User
|
4
|
+
ChefSpec.define_matcher :user
|
5
|
+
|
6
|
+
#
|
7
|
+
# Assert that a +user+ resource exists in the Chef run with the
|
8
|
+
# action +:create+. Given a Chef Recipe that creates "apache2" as a
|
9
|
+
# +user+:
|
10
|
+
#
|
11
|
+
# user 'apache2' do
|
12
|
+
# action :create
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# The Examples section demonstrates the different ways to test a
|
16
|
+
# +user+ resource with ChefSpec.
|
17
|
+
#
|
18
|
+
# @example Assert that a +user+ was created
|
19
|
+
# expect(chef_run).to create_user('apache2')
|
20
|
+
#
|
21
|
+
# @example Assert that a +user+ was created with predicate matchers
|
22
|
+
# expect(chef_run).to create_user('apache2').with_uid(1234)
|
23
|
+
#
|
24
|
+
# @example Assert that a +user+ was created with attributes
|
25
|
+
# expect(chef_run).to create_user('apache2').with(uid: 1234)
|
26
|
+
#
|
27
|
+
# @example Assert that a +user+ was created using a regex
|
28
|
+
# expect(chef_run).to create_user('apache2').with(uid: /\d+/)
|
29
|
+
#
|
30
|
+
# @example Assert that a +user+ was _not_ created
|
31
|
+
# expect(chef_run).to_not create_user('apache2')
|
32
|
+
#
|
33
|
+
#
|
34
|
+
# @param [String, Regex] resource_name
|
35
|
+
# the name of the resource to match
|
36
|
+
#
|
37
|
+
# @return [ChefSpec::Matchers::ResourceMatcher]
|
38
|
+
#
|
39
|
+
def create_user(resource_name)
|
40
|
+
ChefSpec::Matchers::ResourceMatcher.new(:user, :create, resource_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Assert that a +user+ resource exists in the Chef run with the
|
45
|
+
# action +:remove+. Given a Chef Recipe that removes "apache2" as a
|
46
|
+
# +user+:
|
47
|
+
#
|
48
|
+
# user 'apache2' do
|
49
|
+
# action :remove
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# The Examples section demonstrates the different ways to test a
|
53
|
+
# +user+ resource with ChefSpec.
|
54
|
+
#
|
55
|
+
# @example Assert that a +user+ was remove
|
56
|
+
# expect(chef_run).to remove_user('apache2')
|
57
|
+
#
|
58
|
+
# @example Assert that a +user+ was remove with predicate matchers
|
59
|
+
# expect(chef_run).to remove_user('apache2').with_uid(1234)
|
60
|
+
#
|
61
|
+
# @example Assert that a +user+ was remove with attributes
|
62
|
+
# expect(chef_run).to remove_user('apache2').with(uid: 1234)
|
63
|
+
#
|
64
|
+
# @example Assert that a +user+ was remove using a regex
|
65
|
+
# expect(chef_run).to remove_user('apache2').with(uid: /\d+/)
|
66
|
+
#
|
67
|
+
# @example Assert that a +user+ was _not_ remove
|
68
|
+
# expect(chef_run).to_not remove_user('apache2')
|
69
|
+
#
|
70
|
+
#
|
71
|
+
# @param [String, Regex] resource_name
|
72
|
+
# the name of the resource to match
|
73
|
+
#
|
74
|
+
# @return [ChefSpec::Matchers::ResourceMatcher]
|
75
|
+
#
|
76
|
+
def remove_user(resource_name)
|
77
|
+
ChefSpec::Matchers::ResourceMatcher.new(:user, :remove, resource_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Assert that a +user+ resource exists in the Chef run with the
|
82
|
+
# action +:modify+. Given a Chef Recipe that modifies "apache2" as a
|
83
|
+
# +user+:
|
84
|
+
#
|
85
|
+
# user 'apache2' do
|
86
|
+
# action :modify
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# The Examples section demonstrates the different ways to test a
|
90
|
+
# +user+ resource with ChefSpec.
|
91
|
+
#
|
92
|
+
# @example Assert that a +user+ was modified
|
93
|
+
# expect(chef_run).to modify_user('apache2')
|
94
|
+
#
|
95
|
+
# @example Assert that a +user+ was modified with predicate matchers
|
96
|
+
# expect(chef_run).to modify_user('apache2').with_uid(1234)
|
97
|
+
#
|
98
|
+
# @example Assert that a +user+ was modified with attributes
|
99
|
+
# expect(chef_run).to modify_user('apache2').with(uid: 1234)
|
100
|
+
#
|
101
|
+
# @example Assert that a +user+ was modified using a regex
|
102
|
+
# expect(chef_run).to modify_user('apache2').with(uid: /\d+/)
|
103
|
+
#
|
104
|
+
# @example Assert that a +user+ was _not_ modified
|
105
|
+
# expect(chef_run).to_not modify_user('apache2')
|
106
|
+
#
|
107
|
+
#
|
108
|
+
# @param [String, Regex] resource_name
|
109
|
+
# the name of the resource to match
|
110
|
+
#
|
111
|
+
# @return [ChefSpec::Matchers::ResourceMatcher]
|
112
|
+
#
|
113
|
+
def modify_user(resource_name)
|
114
|
+
ChefSpec::Matchers::ResourceMatcher.new(:user, :modify, resource_name)
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Assert that a +user+ resource exists in the Chef run with the
|
119
|
+
# action +:manage+. Given a Chef Recipe that manages "apache2" as a
|
120
|
+
# +user+:
|
121
|
+
#
|
122
|
+
# user 'apache2' do
|
123
|
+
# action :manage
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# The Examples section demonstrates the different ways to test a
|
127
|
+
# +user+ resource with ChefSpec.
|
128
|
+
#
|
129
|
+
# @example Assert that a +user+ was managed
|
130
|
+
# expect(chef_run).to manage_user('apache2')
|
131
|
+
#
|
132
|
+
# @example Assert that a +user+ was managed with predicate matchers
|
133
|
+
# expect(chef_run).to manage_user('apache2').with_uid(1234)
|
134
|
+
#
|
135
|
+
# @example Assert that a +user+ was managed with attributes
|
136
|
+
# expect(chef_run).to manage_user('apache2').with(uid: 1234)
|
137
|
+
#
|
138
|
+
# @example Assert that a +user+ was managed using a regex
|
139
|
+
# expect(chef_run).to manage_user('apache2').with(uid: /\d+/)
|
140
|
+
#
|
141
|
+
# @example Assert that a +user+ was _not_ managed
|
142
|
+
# expect(chef_run).to_not manage_user('apache2')
|
143
|
+
#
|
144
|
+
#
|
145
|
+
# @param [String, Regex] resource_name
|
146
|
+
# the name of the resource to match
|
147
|
+
#
|
148
|
+
# @return [ChefSpec::Matchers::ResourceMatcher]
|
149
|
+
#
|
150
|
+
def manage_user(resource_name)
|
151
|
+
ChefSpec::Matchers::ResourceMatcher.new(:user, :manage, resource_name)
|
152
|
+
end
|
153
|
+
|
154
|
+
#
|
155
|
+
# Assert that a +user+ resource exists in the Chef run with the
|
156
|
+
# action +:lock+. Given a Chef Recipe that locks "apache2" as a
|
157
|
+
# +user+:
|
158
|
+
#
|
159
|
+
# user 'apache2' do
|
160
|
+
# action :lock
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# The Examples section demonstrates the different ways to test a
|
164
|
+
# +user+ resource with ChefSpec.
|
165
|
+
#
|
166
|
+
# @example Assert that a +user+ was locked
|
167
|
+
# expect(chef_run).to lock_user('apache2')
|
168
|
+
#
|
169
|
+
# @example Assert that a +user+ was locked with predicate matchers
|
170
|
+
# expect(chef_run).to lock_user('apache2').with_uid(1234)
|
171
|
+
#
|
172
|
+
# @example Assert that a +user+ was locked with attributes
|
173
|
+
# expect(chef_run).to lock_user('apache2').with(uid: 1234)
|
174
|
+
#
|
175
|
+
# @example Assert that a +user+ was locked using a regex
|
176
|
+
# expect(chef_run).to lock_user('apache2').with(uid: /\d+/)
|
177
|
+
#
|
178
|
+
# @example Assert that a +user+ was _not_ locked
|
179
|
+
# expect(chef_run).to_not lock_user('apache2')
|
180
|
+
#
|
181
|
+
#
|
182
|
+
# @param [String, Regex] resource_name
|
183
|
+
# the name of the resource to match
|
184
|
+
#
|
185
|
+
# @return [ChefSpec::Matchers::ResourceMatcher]
|
186
|
+
#
|
187
|
+
def lock_user(resource_name)
|
188
|
+
ChefSpec::Matchers::ResourceMatcher.new(:user, :lock, resource_name)
|
189
|
+
end
|
190
|
+
|
191
|
+
#
|
192
|
+
# Assert that a +user+ resource exists in the Chef run with the
|
193
|
+
# action +:unlock+. Given a Chef Recipe that unlocks "apache2" as a
|
194
|
+
# +user+:
|
195
|
+
#
|
196
|
+
# user 'apache2' do
|
197
|
+
# action :unlock
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# The Examples section demonstrates the different ways to test a
|
201
|
+
# +user+ resource with ChefSpec.
|
202
|
+
#
|
203
|
+
# @example Assert that a +user+ was unlocked
|
204
|
+
# expect(chef_run).to unlock_user('apache2')
|
205
|
+
#
|
206
|
+
# @example Assert that a +user+ was unlocked with predicate matchers
|
207
|
+
# expect(chef_run).to unlock_user('apache2').with_uid(1234)
|
208
|
+
#
|
209
|
+
# @example Assert that a +user+ was unlocked with attributes
|
210
|
+
# expect(chef_run).to unlock_user('apache2').with(uid: 1234)
|
211
|
+
#
|
212
|
+
# @example Assert that a +user+ was unlocked using a regex
|
213
|
+
# expect(chef_run).to unlock_user('apache2').with(uid: /\d+/)
|
214
|
+
#
|
215
|
+
# @example Assert that a +user+ was _not_ unlocked
|
216
|
+
# expect(chef_run).to_not unlock_user('apache2')
|
217
|
+
#
|
218
|
+
#
|
219
|
+
# @param [String, Regex] resource_name
|
220
|
+
# the name of the resource to match
|
221
|
+
#
|
222
|
+
# @return [ChefSpec::Matchers::ResourceMatcher]
|
223
|
+
#
|
224
|
+
def unlock_user(resource_name)
|
225
|
+
ChefSpec::Matchers::ResourceMatcher.new(:user, :unlock, resource_name)
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
data/lib/chefspec/api.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module ChefSpec
|
2
|
+
module API
|
3
|
+
autoload :Core, "chefspec/api/core"
|
4
|
+
autoload :Described, "chefspec/api/described"
|
5
|
+
autoload :DoNothing, "chefspec/api/do_nothing"
|
6
|
+
autoload :IncludeAnyRecipe, "chefspec/api/include_any_recipe"
|
7
|
+
autoload :IncludeRecipe, "chefspec/api/include_recipe"
|
8
|
+
autoload :Link, "chefspec/api/link"
|
9
|
+
autoload :Notifications, "chefspec/api/notifications"
|
10
|
+
autoload :Reboot, "chefspec/api/reboot"
|
11
|
+
autoload :RenderFile, "chefspec/api/render_file"
|
12
|
+
autoload :StateAttrs, "chefspec/api/state_attrs"
|
13
|
+
autoload :Stubs, "chefspec/api/stubs"
|
14
|
+
autoload :StubsFor, "chefspec/api/stubs_for"
|
15
|
+
autoload :Subscriptions, "chefspec/api/subscriptions"
|
16
|
+
autoload :User, "chefspec/api/user"
|
17
|
+
|
18
|
+
def self.included(klass)
|
19
|
+
# non-resources
|
20
|
+
klass.include(ChefSpec::API::Core)
|
21
|
+
klass.include(ChefSpec::API::Described)
|
22
|
+
klass.include(ChefSpec::API::DoNothing)
|
23
|
+
klass.include(ChefSpec::API::IncludeAnyRecipe)
|
24
|
+
klass.include(ChefSpec::API::IncludeRecipe)
|
25
|
+
klass.include(ChefSpec::API::DoNothing)
|
26
|
+
klass.include(ChefSpec::API::RenderFile)
|
27
|
+
klass.include(ChefSpec::API::StateAttrs)
|
28
|
+
klass.include(ChefSpec::API::Notifications)
|
29
|
+
klass.include(ChefSpec::API::Stubs)
|
30
|
+
klass.include(ChefSpec::API::StubsFor)
|
31
|
+
klass.include(ChefSpec::API::Subscriptions)
|
32
|
+
|
33
|
+
# hacks and sugar for resources that don't follow the normal pattern
|
34
|
+
klass.include(ChefSpec::API::User)
|
35
|
+
klass.include(ChefSpec::API::Link)
|
36
|
+
klass.include(ChefSpec::API::Reboot)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
begin
|
2
|
+
require "berkshelf"
|
3
|
+
rescue LoadError
|
4
|
+
raise ChefSpec::Error::GemLoadError.new(gem: "berkshelf", name: "Berkshelf")
|
5
|
+
end
|
6
|
+
|
7
|
+
module ChefSpec
|
8
|
+
class Berkshelf
|
9
|
+
class << self
|
10
|
+
extend Forwardable
|
11
|
+
def_delegators :instance, :setup!, :teardown!
|
12
|
+
end
|
13
|
+
|
14
|
+
include Singleton
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@tmpdir = Dir.mktmpdir
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Setup and install the necessary dependencies in the temporary directory.
|
22
|
+
#
|
23
|
+
def setup!
|
24
|
+
# Get the list of Berkshelf options
|
25
|
+
opts = RSpec.configuration.berkshelf_options
|
26
|
+
unless opts.is_a?(Hash)
|
27
|
+
raise InvalidBerkshelfOptions(value: opts.inspect)
|
28
|
+
end
|
29
|
+
|
30
|
+
berksfile = ::Berkshelf::Berksfile.from_options(opts)
|
31
|
+
|
32
|
+
# Grab a handle to tmpdir, since Berkshelf 2 modifies it a bit
|
33
|
+
tmpdir = File.join(@tmpdir, "cookbooks")
|
34
|
+
|
35
|
+
::Berkshelf.ui.mute do
|
36
|
+
if ::Berkshelf::Berksfile.method_defined?(:vendor)
|
37
|
+
# Berkshelf 3.0 requires the directory to not exist
|
38
|
+
FileUtils.rm_rf(tmpdir)
|
39
|
+
berksfile.vendor(tmpdir)
|
40
|
+
else
|
41
|
+
berksfile.install(path: tmpdir)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
filter = Coverage::BerkshelfFilter.new(berksfile)
|
46
|
+
Coverage.add_filter(filter)
|
47
|
+
|
48
|
+
::RSpec.configure { |config| config.cookbook_path = tmpdir }
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Destroy the installed Berkshelf at the temporary directory.
|
53
|
+
#
|
54
|
+
def teardown!
|
55
|
+
FileUtils.rm_rf(@tmpdir) if File.exist?(@tmpdir)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
RSpec.configure do |config|
|
61
|
+
config.before(:suite) { ChefSpec::Berkshelf.setup! }
|
62
|
+
config.after(:suite) { ChefSpec::Berkshelf.teardown! }
|
63
|
+
end
|