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,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
@@ -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