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