chefspec-chef 9.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,25 @@
|
|
|
1
|
+
require_relative "stub"
|
|
2
|
+
|
|
3
|
+
module ChefSpec
|
|
4
|
+
module Stubs
|
|
5
|
+
class SearchStub < Stub
|
|
6
|
+
attr_reader :block
|
|
7
|
+
attr_reader :query
|
|
8
|
+
attr_reader :type
|
|
9
|
+
|
|
10
|
+
def initialize(type, query = "*:*", &block)
|
|
11
|
+
@type = type.to_s
|
|
12
|
+
@query = query
|
|
13
|
+
@block = block
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def signature
|
|
17
|
+
if @block
|
|
18
|
+
"stub_search(#{@type.inspect}, #{@query.inspect}) { # Ruby code }"
|
|
19
|
+
else
|
|
20
|
+
"stub_search(#{@type.inspect}, #{@query.inspect}).and_return(#{@value})"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module ChefSpec
|
|
2
|
+
module Stubs
|
|
3
|
+
class Stub
|
|
4
|
+
attr_reader :value
|
|
5
|
+
|
|
6
|
+
def and_return(value)
|
|
7
|
+
@value = value
|
|
8
|
+
self
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def and_raise(exception)
|
|
12
|
+
@block = Proc.new { raise exception }
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def result
|
|
17
|
+
if @block
|
|
18
|
+
recursively_mashify(@block.call)
|
|
19
|
+
else
|
|
20
|
+
recursively_mashify(@value)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def recursively_mashify(thing)
|
|
27
|
+
case thing
|
|
28
|
+
when Array
|
|
29
|
+
thing.collect { |item| recursively_mashify(item) }
|
|
30
|
+
when Hash
|
|
31
|
+
Mash.from_hash(thing)
|
|
32
|
+
else
|
|
33
|
+
thing
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module ChefSpec
|
|
2
|
+
module Util
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# Covert the given CaMelCaSeD string to under_score. Graciously borrowed
|
|
7
|
+
# from http://stackoverflow.com/questions/1509915.
|
|
8
|
+
#
|
|
9
|
+
# @param [String] string
|
|
10
|
+
# the string to use for transformation
|
|
11
|
+
#
|
|
12
|
+
# @return [String]
|
|
13
|
+
#
|
|
14
|
+
def underscore(string)
|
|
15
|
+
string
|
|
16
|
+
.to_s
|
|
17
|
+
.gsub(/::/, "/")
|
|
18
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
19
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
20
|
+
.tr("-", "_")
|
|
21
|
+
.downcase
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
# Convert an underscored string to it's camelcase equivalent constant.
|
|
26
|
+
#
|
|
27
|
+
# @param [String] string
|
|
28
|
+
# the string to convert
|
|
29
|
+
#
|
|
30
|
+
# @return [String]
|
|
31
|
+
#
|
|
32
|
+
def camelize(string)
|
|
33
|
+
string
|
|
34
|
+
.to_s
|
|
35
|
+
.split("_")
|
|
36
|
+
.map(&:capitalize)
|
|
37
|
+
.join
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
#
|
|
41
|
+
# Truncate the given string to a certain number of characters.
|
|
42
|
+
#
|
|
43
|
+
# @param [String] string
|
|
44
|
+
# the string to truncate
|
|
45
|
+
# @param [Hash] options
|
|
46
|
+
# the list of options (such as +length+)
|
|
47
|
+
#
|
|
48
|
+
def truncate(string, options = {})
|
|
49
|
+
length = options[:length] || 30
|
|
50
|
+
|
|
51
|
+
if string.length > length
|
|
52
|
+
string[0..length - 3] + "..."
|
|
53
|
+
else
|
|
54
|
+
string
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require "chef_zero/server"
|
|
2
|
+
|
|
3
|
+
module ChefSpec
|
|
4
|
+
# Rather than create a ChefZero instance per test case, simply create one
|
|
5
|
+
# ChefZero instance and reset it for every test case.
|
|
6
|
+
class ZeroServer
|
|
7
|
+
class << self
|
|
8
|
+
extend Forwardable
|
|
9
|
+
def_delegators :instance, :setup!, :teardown!, :reset!, :upload_cookbooks!, :server, :load_data, :nuke!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
include Singleton
|
|
13
|
+
|
|
14
|
+
attr_reader :server
|
|
15
|
+
|
|
16
|
+
# Create the ChefZero Server
|
|
17
|
+
def initialize
|
|
18
|
+
nuke!
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
# Start the ChefZero Server
|
|
23
|
+
#
|
|
24
|
+
def setup!
|
|
25
|
+
@server.start_background unless @server.running?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# Remove all the data we just loaded from the ChefZero server
|
|
30
|
+
#
|
|
31
|
+
def reset!
|
|
32
|
+
if RSpec.configuration.server_runner_clear_cookbooks
|
|
33
|
+
@server.clear_data
|
|
34
|
+
@cookbooks_uploaded = false
|
|
35
|
+
else
|
|
36
|
+
# If we don't want to do a full clear, iterate through each value that we
|
|
37
|
+
# set and manually remove it.
|
|
38
|
+
@data_loaded.each do |key, names|
|
|
39
|
+
if key == "data"
|
|
40
|
+
names.each { |n| @server.data_store.delete_dir(["organizations", "chef", key, n]) }
|
|
41
|
+
else
|
|
42
|
+
names.each { |n| @server.data_store.delete(["organizations", "chef", key, n]) }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
@data_loaded = {}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
# Really reset everything and reload the configuration
|
|
51
|
+
#
|
|
52
|
+
def nuke!
|
|
53
|
+
@server = ChefZero::Server.new(
|
|
54
|
+
# Set the log level from RSpec, defaulting to warn
|
|
55
|
+
log_level: RSpec.configuration.log_level || :warn,
|
|
56
|
+
port: RSpec.configuration.server_runner_port,
|
|
57
|
+
|
|
58
|
+
# Set the data store
|
|
59
|
+
data_store: data_store(RSpec.configuration.server_runner_data_store)
|
|
60
|
+
)
|
|
61
|
+
@cookbooks_uploaded = false
|
|
62
|
+
@data_loaded = {}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
#
|
|
66
|
+
# Teardown the ChefZero Server
|
|
67
|
+
#
|
|
68
|
+
def teardown!
|
|
69
|
+
@server.stop if @server.running?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
#
|
|
73
|
+
# Upload the cookbooks to the Chef Server.
|
|
74
|
+
#
|
|
75
|
+
def upload_cookbooks!
|
|
76
|
+
return if @cookbooks_uploaded
|
|
77
|
+
|
|
78
|
+
loader = Chef::CookbookLoader.new(Chef::Config[:cookbook_path])
|
|
79
|
+
loader.load_cookbooks
|
|
80
|
+
cookbook_uploader_for(loader).upload_cookbooks
|
|
81
|
+
@cookbooks_uploaded = true
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#
|
|
85
|
+
# Load (and track) data sent to the server
|
|
86
|
+
#
|
|
87
|
+
# @param [String] name
|
|
88
|
+
# the name or id of the item to load
|
|
89
|
+
# @param [String, Symbol] key
|
|
90
|
+
# the key to load
|
|
91
|
+
# @param [Hash] data
|
|
92
|
+
# the data for the object, which will be converted to JSON and uploaded
|
|
93
|
+
# to the server
|
|
94
|
+
#
|
|
95
|
+
def load_data(name, key, data)
|
|
96
|
+
@data_loaded[key] ||= []
|
|
97
|
+
@data_loaded[key] << name
|
|
98
|
+
@server.load_data({ key => { name => data } })
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
#
|
|
104
|
+
# The uploader for the cookbooks.
|
|
105
|
+
#
|
|
106
|
+
# @param [Chef::CookbookLoader] loader
|
|
107
|
+
# the Chef cookbook loader
|
|
108
|
+
#
|
|
109
|
+
# @return [Chef::CookbookUploader]
|
|
110
|
+
#
|
|
111
|
+
def cookbook_uploader_for(loader)
|
|
112
|
+
Chef::CookbookUploader.new(loader.cookbooks)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
#
|
|
116
|
+
# Generate the DataStore object to be passed in to the ChefZero::Server object
|
|
117
|
+
#
|
|
118
|
+
def data_store(option)
|
|
119
|
+
require "chef_zero/data_store/default_facade"
|
|
120
|
+
|
|
121
|
+
store = case option
|
|
122
|
+
when :in_memory
|
|
123
|
+
require "chef_zero/data_store/memory_store_v2"
|
|
124
|
+
ChefZero::DataStore::MemoryStoreV2.new
|
|
125
|
+
when :on_disk
|
|
126
|
+
require "tmpdir" unless defined?(Dir.mktmpdir)
|
|
127
|
+
require "chef_zero/data_store/raw_file_store"
|
|
128
|
+
ChefZero::DataStore::RawFileStore.new(Dir.mktmpdir)
|
|
129
|
+
else
|
|
130
|
+
raise ArgumentError, ":#{option} is not a valid server_runner_data_store option. Please use either :in_memory or :on_disk."
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
ChefZero::DataStore::DefaultFacade.new(store, "chef", true)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
RSpec.configure do |config|
|
|
139
|
+
config.before(:suite) { ChefSpec::ZeroServer.setup! }
|
|
140
|
+
config.after(:each) { ChefSpec::ZeroServer.reset! }
|
|
141
|
+
config.after(:suite) { ChefSpec::ZeroServer.teardown! }
|
|
142
|
+
end
|
data/lib/chefspec.rb
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require "rspec"
|
|
2
|
+
|
|
3
|
+
module ChefSpec
|
|
4
|
+
#
|
|
5
|
+
# Defines a new runner method on the Chef runner.
|
|
6
|
+
#
|
|
7
|
+
# @param [Symbol] resource_name
|
|
8
|
+
# the name of the resource to define a method
|
|
9
|
+
#
|
|
10
|
+
# @return [self]
|
|
11
|
+
#
|
|
12
|
+
def define_matcher(resource_name)
|
|
13
|
+
matchers[resource_name.to_sym] = Proc.new do |identity|
|
|
14
|
+
find_resource(resource_name, identity)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
module_function :define_matcher
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
# The source root of the ChefSpec gem. This is useful when requiring files
|
|
23
|
+
# that are relative to the root of the project.
|
|
24
|
+
#
|
|
25
|
+
# @return [Pathname]
|
|
26
|
+
#
|
|
27
|
+
def root
|
|
28
|
+
@root ||= Pathname.new(File.expand_path("..", __dir__))
|
|
29
|
+
end
|
|
30
|
+
module_function :root
|
|
31
|
+
|
|
32
|
+
protected
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# The list of custom defined matchers.
|
|
36
|
+
#
|
|
37
|
+
# @return [Hash<String, Proc>]
|
|
38
|
+
#
|
|
39
|
+
def matchers
|
|
40
|
+
@matchers ||= {}
|
|
41
|
+
end
|
|
42
|
+
module_function :matchers
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
require_relative "chefspec/extensions"
|
|
46
|
+
|
|
47
|
+
require_relative "chefspec/mixins/normalize"
|
|
48
|
+
|
|
49
|
+
require_relative "chefspec/stubs/command_registry"
|
|
50
|
+
require_relative "chefspec/stubs/command_stub"
|
|
51
|
+
require_relative "chefspec/stubs/data_bag_item_registry"
|
|
52
|
+
require_relative "chefspec/stubs/data_bag_item_stub"
|
|
53
|
+
require_relative "chefspec/stubs/data_bag_registry"
|
|
54
|
+
require_relative "chefspec/stubs/data_bag_stub"
|
|
55
|
+
require_relative "chefspec/stubs/registry"
|
|
56
|
+
require_relative "chefspec/stubs/stub"
|
|
57
|
+
require_relative "chefspec/stubs/search_registry"
|
|
58
|
+
require_relative "chefspec/stubs/search_stub"
|
|
59
|
+
|
|
60
|
+
require_relative "chefspec/api"
|
|
61
|
+
require_relative "chefspec/cacher"
|
|
62
|
+
require_relative "chefspec/coverage"
|
|
63
|
+
require_relative "chefspec/errors"
|
|
64
|
+
require_relative "chefspec/expect_exception"
|
|
65
|
+
require_relative "chefspec/formatter"
|
|
66
|
+
require_relative "chefspec/matchers"
|
|
67
|
+
require_relative "chefspec/renderer"
|
|
68
|
+
require_relative "chefspec/rspec"
|
|
69
|
+
require_relative "chefspec/server_runner"
|
|
70
|
+
require_relative "chefspec/solo_runner"
|
|
71
|
+
require_relative "chefspec/runner"
|
|
72
|
+
require_relative "chefspec/util"
|
|
73
|
+
require_relative "chefspec/version"
|
|
74
|
+
|
|
75
|
+
require_relative "chefspec/deprecations"
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require "chefspec"
|
|
2
|
+
require "support/hash"
|
|
3
|
+
|
|
4
|
+
ChefSpec::Coverage.start! do
|
|
5
|
+
set_template "table.erb"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
RSpec.configure do |config|
|
|
9
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
|
10
|
+
config.filter_run(focus: true)
|
|
11
|
+
config.run_all_when_everything_filtered = true
|
|
12
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#
|
|
2
|
+
# An extension of a Hash that allows mash-style like lookups.
|
|
3
|
+
#
|
|
4
|
+
# @private
|
|
5
|
+
#
|
|
6
|
+
class Hash
|
|
7
|
+
# Like, seriously Windows?
|
|
8
|
+
undef_method(:timeout) if method_defined?(:timeout)
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# Monkey-patch to allow mash-style look ups for tests
|
|
12
|
+
#
|
|
13
|
+
def method_missing(m, *args, &block)
|
|
14
|
+
if key?(m.to_sym)
|
|
15
|
+
self[m.to_sym]
|
|
16
|
+
elsif key?(m.to_s)
|
|
17
|
+
self[m.to_s]
|
|
18
|
+
else
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# Monkey-patch to stdlib Hash to correspond to Mash-style lookup
|
|
25
|
+
#
|
|
26
|
+
# @see Hash#respond_to?
|
|
27
|
+
#
|
|
28
|
+
def respond_to?(m, include_private = false)
|
|
29
|
+
if key?(m.to_sym) || key?(m.to_s)
|
|
30
|
+
true
|
|
31
|
+
else
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "chefspec/cacher"
|
|
3
|
+
|
|
4
|
+
describe ChefSpec::Cacher do
|
|
5
|
+
let(:klass) do
|
|
6
|
+
Class.new(RSpec::Core::ExampleGroup) do
|
|
7
|
+
extend ChefSpec::Cacher
|
|
8
|
+
|
|
9
|
+
def self.metadata
|
|
10
|
+
{ parent_example_group: { location: "spec" } }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
let(:cache) { described_class.class_variable_get(:@@cache) }
|
|
16
|
+
let(:preserve_cache) { false }
|
|
17
|
+
|
|
18
|
+
before(:each) { described_class.class_variable_set(:@@cache, {}) unless preserve_cache }
|
|
19
|
+
|
|
20
|
+
describe "cached" do
|
|
21
|
+
it "lazily defines the results for the cache" do
|
|
22
|
+
klass.cached(:chef_run)
|
|
23
|
+
expect(klass).to be_method_defined(:chef_run)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "adds the item to the cache when called" do
|
|
27
|
+
runner = double(:runner)
|
|
28
|
+
klass.cached(:chef_run) { runner }
|
|
29
|
+
klass.new.chef_run
|
|
30
|
+
|
|
31
|
+
expect(cache[Thread.current.object_id]).to have_key("spec.chef_run")
|
|
32
|
+
expect(cache[Thread.current.object_id]["spec.chef_run"]).to eq(runner)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context "when multithreaded environment" do
|
|
36
|
+
it "is thread safe" do
|
|
37
|
+
(1..2).each do |n|
|
|
38
|
+
Thread.new do
|
|
39
|
+
klass.cached(:chef_run) { n }
|
|
40
|
+
expect(klass.new.chef_run).to eq(n)
|
|
41
|
+
end.join
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context "when example groups are defined by looping" do
|
|
47
|
+
let(:preserve_cache) { true }
|
|
48
|
+
|
|
49
|
+
%w{first second third}.each do |iteration|
|
|
50
|
+
context "on the #{iteration} iteration" do
|
|
51
|
+
context "in caching context" do
|
|
52
|
+
cached(:cached_iteration) { iteration }
|
|
53
|
+
it "caches the iteration for this context" do
|
|
54
|
+
expect(cached_iteration).to eq iteration
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe "cached!" do
|
|
63
|
+
it "loads the value at runtime" do
|
|
64
|
+
expect(klass).to receive(:cached).with(:chef_run).once
|
|
65
|
+
expect(klass).to receive(:before).once
|
|
66
|
+
|
|
67
|
+
klass.cached!(:chef_run) {}
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
# Note: These specs don't use Berkshelf code directly as this project doesn't
|
|
4
|
+
# have a direct dependency on Berkshelf and loading it would impact the
|
|
5
|
+
# perfance of these specs. While not ideal, the test doubles provide enough of
|
|
6
|
+
# a standin for Berkshelf to exercise the `#matches?` behavior.
|
|
7
|
+
describe ChefSpec::Coverage::BerkshelfFilter do
|
|
8
|
+
let(:dependencies) do
|
|
9
|
+
[double("Berkshelf::Dependency", metadata?: true, name: "cookbookery")]
|
|
10
|
+
end
|
|
11
|
+
let(:berksfile) { double("Berkshelf::Berksfile", dependencies: dependencies) }
|
|
12
|
+
let(:resource) { Chef::Resource.new("theone") }
|
|
13
|
+
subject { described_class.new(berksfile) }
|
|
14
|
+
|
|
15
|
+
describe "#matches?" do
|
|
16
|
+
it "returns truthy if resource source_line is nil" do
|
|
17
|
+
expect(subject.matches?(resource)).to be_truthy
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context "when resource#source_line is under target cookbook" do
|
|
21
|
+
it "normal unix path returns truthy" do
|
|
22
|
+
resource.source_line =
|
|
23
|
+
"/path/to/cookbooks/nope/recipes/default.rb:22"
|
|
24
|
+
expect(subject.matches?(resource)).to be_truthy
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "normal windows path returns truthy" do
|
|
28
|
+
resource.source_line =
|
|
29
|
+
'C:\\path\\to\\cookbooks\\nope\\recipes\\default.rb:22'
|
|
30
|
+
expect(subject.matches?(resource)).to be_truthy
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "mixed windows path returns truthy" do
|
|
34
|
+
resource.source_line =
|
|
35
|
+
'C:\\path\\to\\cookbooks/nope/recipes/default.rb:22'
|
|
36
|
+
expect(subject.matches?(resource)).to be_truthy
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context "when resource#source_line is not under target cookbook" do
|
|
41
|
+
it "normal unix path returns falsey" do
|
|
42
|
+
resource.source_line =
|
|
43
|
+
"/path/to/cookbooks/cookbookery/recipes/default.rb:22"
|
|
44
|
+
expect(subject.matches?(resource)).to be_falsey
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "normal windows path returns falsey" do
|
|
48
|
+
resource.source_line =
|
|
49
|
+
'C:\\path\\to\\cookbooks\\cookbookery\\recipes\\default.rb:22'
|
|
50
|
+
expect(subject.matches?(resource)).to be_falsey
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "mixed windows path returns falsey" do
|
|
54
|
+
resource.source_line =
|
|
55
|
+
'C:\\path\\to\\cookbooks/cookbookery/recipes/default.rb:22'
|
|
56
|
+
expect(subject.matches?(resource)).to be_falsey
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe ChefSpec::Runner do
|
|
4
|
+
before do
|
|
5
|
+
allow_any_instance_of(ChefSpec::SoloRunner)
|
|
6
|
+
.to receive(:dry_run?)
|
|
7
|
+
.and_return(true)
|
|
8
|
+
allow(ChefSpec::Runner).to receive(:deprecated)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe "#define_runner_method" do
|
|
12
|
+
before do
|
|
13
|
+
allow(ChefSpec).to receive(:define_matcher)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "prints a deprecation" do
|
|
17
|
+
expect(ChefSpec::Runner).to receive(:deprecated)
|
|
18
|
+
.with("`ChefSpec::Runner.define_runner_method' is deprecated."\
|
|
19
|
+
" It is being used in the my_custom_resource resource matcher." \
|
|
20
|
+
" Please use `ChefSpec.define_matcher' instead.")
|
|
21
|
+
ChefSpec::Runner.define_runner_method(:my_custom_resource)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "calls ChefSpec#define_matcher" do
|
|
25
|
+
expect(ChefSpec).to receive(:define_matcher).with(:my_custom_resource).once
|
|
26
|
+
ChefSpec::Runner.define_runner_method(:my_custom_resource)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe ChefSpec::Server do
|
|
33
|
+
before do
|
|
34
|
+
allow(ChefSpec::Server).to receive(:deprecated)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "prints a deprecation for any method called" do
|
|
38
|
+
expect(ChefSpec::Server).to receive(:deprecated)
|
|
39
|
+
.with("`ChefSpec::Server.any_method' is deprecated. There is no longer" \
|
|
40
|
+
" a global Chef Server instance. Please use a ChefSpec::SoloRunner" \
|
|
41
|
+
" instead. More documentation can be found in the ChefSpec README.")
|
|
42
|
+
expect {
|
|
43
|
+
ChefSpec::Server.any_method
|
|
44
|
+
}.to raise_error(ChefSpec::Error::NoConversionError)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "raises non-conversion error for any method called" do
|
|
48
|
+
expect { ChefSpec::Server.any_method }
|
|
49
|
+
.to raise_error(ChefSpec::Error::NoConversionError)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
module ChefSpec::Error
|
|
4
|
+
describe CommandNotStubbed do
|
|
5
|
+
let(:instance) { described_class.new(args: ["cat"]) }
|
|
6
|
+
|
|
7
|
+
it "raises an exception with the correct message" do
|
|
8
|
+
instance
|
|
9
|
+
expect { raise instance }.to( raise_error { |error|
|
|
10
|
+
expect(error).to be_a(described_class)
|
|
11
|
+
expect(error.message).to eq <<-EOH.gsub(/^ {10}/, "")
|
|
12
|
+
Executing a real command is disabled. Unregistered command:
|
|
13
|
+
|
|
14
|
+
command("cat")
|
|
15
|
+
|
|
16
|
+
You can stub this command with:
|
|
17
|
+
|
|
18
|
+
stub_command("cat").and_return(...)
|
|
19
|
+
EOH
|
|
20
|
+
})
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe CookbookPathNotFound do
|
|
25
|
+
let(:instance) { described_class.new }
|
|
26
|
+
|
|
27
|
+
it "raises an exception with the correct message" do
|
|
28
|
+
expect { raise instance }.to(raise_error { |error|
|
|
29
|
+
expect(error).to be_a(described_class)
|
|
30
|
+
expect(error.message).to eq <<-EOH.gsub(/^ {10}/, "")
|
|
31
|
+
I could not find or infer a cookbook_path from your current working directory.
|
|
32
|
+
Please make sure you put your specs (tests) under a directory named 'spec' or
|
|
33
|
+
manually set the cookbook path in the RSpec configuration.
|
|
34
|
+
EOH
|
|
35
|
+
})
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe GemLoadError do
|
|
40
|
+
let(:instance) { described_class.new(gem: "bacon", name: "bacon") }
|
|
41
|
+
|
|
42
|
+
it "raises an exception with the correct message" do
|
|
43
|
+
expect { raise instance }.to( raise_error { |error|
|
|
44
|
+
expect(error).to be_a(described_class)
|
|
45
|
+
expect(error.message).to eq <<-EOH.gsub(/^ {10}/, "")
|
|
46
|
+
I could not load the 'bacon' gem! You must have the gem installed
|
|
47
|
+
on your local system before you can use the bacon plugin.
|
|
48
|
+
You can install bacon by running:
|
|
49
|
+
|
|
50
|
+
gem install bacon
|
|
51
|
+
|
|
52
|
+
or add bacon to your Gemfile and run the `bundle` command to install.
|
|
53
|
+
EOH
|
|
54
|
+
})
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe ChefSpec::ExpectException do
|
|
4
|
+
context "when there have been no `raise_error` matchers" do
|
|
5
|
+
subject { described_class.new(Exception) }
|
|
6
|
+
|
|
7
|
+
it "does not match" do
|
|
8
|
+
allow(RSpec::Matchers::BuiltIn::RaiseError).to receive(:last_run).and_return(nil)
|
|
9
|
+
expect(subject.expected?).to be_falsy
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
context "when the last error does not match the expected type" do
|
|
14
|
+
subject { described_class.new(RuntimeError) }
|
|
15
|
+
|
|
16
|
+
it "does not match" do
|
|
17
|
+
last_error = double("last error", last_error_for_chefspec: ArgumentError)
|
|
18
|
+
allow(RSpec::Matchers::BuiltIn::RaiseError).to receive(:last_run).and_return(last_error)
|
|
19
|
+
expect(subject.expected?).to be_falsy
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context "when the last error matches the expected type" do
|
|
24
|
+
subject { described_class.new(RuntimeError) }
|
|
25
|
+
|
|
26
|
+
it "does not match" do
|
|
27
|
+
last_error = double("last error", last_error_for_chefspec: RuntimeError)
|
|
28
|
+
allow(RSpec::Matchers::BuiltIn::RaiseError).to receive(:last_run).and_return(last_error)
|
|
29
|
+
expect(subject.expected?).to be_truthy
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|