chef-umami 0.0.3
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/CHANGELOG.md +4 -0
- data/CONTRIBUTING.md +54 -0
- data/LICENSE.md +201 -0
- data/README.md +163 -0
- data/bin/umami +7 -0
- data/lib/chef-umami.rb +16 -0
- data/lib/chef-umami/client.rb +50 -0
- data/lib/chef-umami/exceptions.rb +20 -0
- data/lib/chef-umami/helpers.rb +18 -0
- data/lib/chef-umami/helpers/filetools.rb +46 -0
- data/lib/chef-umami/helpers/inspec.rb +153 -0
- data/lib/chef-umami/helpers/os.rb +71 -0
- data/lib/chef-umami/logger.rb +24 -0
- data/lib/chef-umami/policyfile.rb +18 -0
- data/lib/chef-umami/policyfile/exporter.rb +107 -0
- data/lib/chef-umami/policyfile/uploader.rb +92 -0
- data/lib/chef-umami/runner.rb +112 -0
- data/lib/chef-umami/server.rb +36 -0
- data/lib/chef-umami/test.rb +63 -0
- data/lib/chef-umami/test/integration.rb +95 -0
- data/lib/chef-umami/test/unit.rb +108 -0
- data/lib/chef-umami/version.rb +17 -0
- data/spec/runner_spec.rb +22 -0
- data/spec/spec_helper.rb +103 -0
- data/support/umami.pem +27 -0
- metadata +115 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
# Copyright 2017 Bloomberg Finance, L.P.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'chef-dk/authenticated_http'
|
16
|
+
require 'chef-dk/policyfile/storage_config'
|
17
|
+
require 'chef-dk/policyfile/uploader'
|
18
|
+
require 'chef-dk/policyfile_lock'
|
19
|
+
require 'chef-dk/ui'
|
20
|
+
|
21
|
+
module Umami
|
22
|
+
class Policyfile
|
23
|
+
class Uploader
|
24
|
+
|
25
|
+
attr_reader :http_client
|
26
|
+
attr_reader :policyfile_lock
|
27
|
+
attr_reader :policyfile_lock_file
|
28
|
+
attr_reader :policyfile_uploader
|
29
|
+
attr_reader :storage_config
|
30
|
+
attr_reader :ui
|
31
|
+
def initialize(policyfile_lock_file = nil)
|
32
|
+
@http_client = http_client
|
33
|
+
@policyfile_lock_file = policyfile_lock_file
|
34
|
+
@policyfile_lock = policyfile_lock
|
35
|
+
@policyfile_uploader = policyfile_uploader
|
36
|
+
@storage_config = storage_config
|
37
|
+
@ui = ui
|
38
|
+
end
|
39
|
+
|
40
|
+
def storage_config
|
41
|
+
@storage_config ||= ChefDK::Policyfile::StorageConfig.new.use_policyfile(policyfile_lock_file)
|
42
|
+
end
|
43
|
+
|
44
|
+
def ui
|
45
|
+
@ui ||= ChefDK::UI.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def policyfile_lock_content
|
49
|
+
IO.read(policyfile_lock_file)
|
50
|
+
end
|
51
|
+
|
52
|
+
def lock_data
|
53
|
+
FFI_Yajl::Parser.new.parse(policyfile_lock_content)
|
54
|
+
end
|
55
|
+
|
56
|
+
def policyfile_lock
|
57
|
+
@policyfile_lock ||= ChefDK::PolicyfileLock.new(
|
58
|
+
storage_config,
|
59
|
+
ui: ui
|
60
|
+
).build_from_lock_data(lock_data)
|
61
|
+
end
|
62
|
+
|
63
|
+
def http_client
|
64
|
+
@http_client ||= ChefDK::AuthenticatedHTTP.new(Chef::Config['chef_server_url'])
|
65
|
+
end
|
66
|
+
|
67
|
+
def policy_group
|
68
|
+
Chef::Config['policy_group']
|
69
|
+
end
|
70
|
+
|
71
|
+
def policy_document_native_api
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def policyfile_uploader
|
76
|
+
@policyfile_uploader ||= ChefDK::Policyfile::Uploader.new(
|
77
|
+
policyfile_lock,
|
78
|
+
policy_group,
|
79
|
+
ui: ui,
|
80
|
+
http_client: http_client,
|
81
|
+
policy_document_native_api: policy_document_native_api
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Push the policy, including all dependent cookbooks.
|
86
|
+
def upload
|
87
|
+
policyfile_uploader.upload
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# Copyright 2017 Bloomberg Finance, L.P.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'chef'
|
16
|
+
require 'chef-umami/exceptions'
|
17
|
+
require 'chef-umami/client'
|
18
|
+
require 'chef-umami/logger'
|
19
|
+
require 'chef-umami/server'
|
20
|
+
require 'chef-umami/policyfile/exporter'
|
21
|
+
require 'chef-umami/policyfile/uploader'
|
22
|
+
require 'chef-umami/test/unit'
|
23
|
+
require 'chef-umami/test/integration'
|
24
|
+
|
25
|
+
module Umami
|
26
|
+
class Runner
|
27
|
+
|
28
|
+
include Umami::Logger
|
29
|
+
|
30
|
+
attr_reader :cookbook_dir
|
31
|
+
attr_reader :policyfile_lock_file
|
32
|
+
attr_reader :policyfile
|
33
|
+
# TODO: Build the ability to specify a custom policy lock file name.
|
34
|
+
def initialize(policyfile_lock_file = nil, policyfile = nil)
|
35
|
+
@cookbook_dir = Dir.pwd
|
36
|
+
@policyfile_lock_file = 'Policyfile.lock.json'
|
37
|
+
@policyfile = policyfile || 'Policyfile.rb'
|
38
|
+
@exporter = exporter
|
39
|
+
@chef_zero_server = chef_zero_server
|
40
|
+
# If we load the uploader or client now, they won't see the updated
|
41
|
+
# Chef config!
|
42
|
+
@uploader = nil
|
43
|
+
@chef_client = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_lock_file!
|
47
|
+
unless policyfile_lock_file.end_with?("lock.json")
|
48
|
+
raise InvalidPolicyfileLockFilename, "Policyfile lock files must end in '.lock.json'. I received '#{policyfile_lock_file}'."
|
49
|
+
end
|
50
|
+
|
51
|
+
unless File.exist?(policyfile_lock_file)
|
52
|
+
raise InvalidPolicyfileLockFilename, "Unable to locate '#{policyfile_lock_file}' You may need to run `chef install` to generate it."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def exporter
|
57
|
+
@exporter ||= Umami::Policyfile::Exporter.new(policyfile_lock_file, cookbook_dir, policyfile)
|
58
|
+
end
|
59
|
+
|
60
|
+
def uploader
|
61
|
+
@uploader ||= Umami::Policyfile::Uploader.new(policyfile_lock_file)
|
62
|
+
end
|
63
|
+
|
64
|
+
def chef_zero_server
|
65
|
+
@chef_zero_server ||= Umami::Server.new
|
66
|
+
end
|
67
|
+
|
68
|
+
def chef_client
|
69
|
+
@chef_client ||= Umami::Client.new
|
70
|
+
end
|
71
|
+
|
72
|
+
def run
|
73
|
+
validate_lock_file!
|
74
|
+
puts "\nExporting the policy, related cookbooks, and a valid client configuration..."
|
75
|
+
exporter.export
|
76
|
+
Chef::Config.from_file("#{exporter.chef_config_file}")
|
77
|
+
chef_zero_server.start
|
78
|
+
puts "\nUploading the policy and related cookbooks..."
|
79
|
+
uploader.upload
|
80
|
+
puts "\nExecuting chef-client compile phase..."
|
81
|
+
# Define Chef::Config['config_file'] lest Ohai complain.
|
82
|
+
Chef::Config['config_file'] = exporter.chef_config_file
|
83
|
+
chef_client.compile
|
84
|
+
# Build a hash of all the recipes' resources, keyed by the canonical
|
85
|
+
# name of the recipe (i.e. ohai::default).
|
86
|
+
recipe_resources = {}
|
87
|
+
chef_client.resource_collection.each do |resource|
|
88
|
+
canonical_recipe = "#{resource.cookbook_name}::#{resource.recipe_name}"
|
89
|
+
if recipe_resources.key?(canonical_recipe)
|
90
|
+
recipe_resources[canonical_recipe] << resource
|
91
|
+
else
|
92
|
+
recipe_resources[canonical_recipe] = [resource]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Remove the temporary directory using a naive guard to ensure we're
|
97
|
+
# deleting what we expect.
|
98
|
+
re_export_path = Regexp.new('/tmp/umami')
|
99
|
+
FileUtils.rm_rf(exporter.export_root) if exporter.export_root.match(re_export_path)
|
100
|
+
|
101
|
+
puts "\nGenerating a set of unit tests..."
|
102
|
+
unit_tester = Umami::Test::Unit.new
|
103
|
+
unit_tester.generate(recipe_resources)
|
104
|
+
|
105
|
+
puts "\nGenerating a set of integration tests..."
|
106
|
+
integration_tester = Umami::Test::Integration.new
|
107
|
+
integration_tester.generate(recipe_resources)
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright 2017 Bloomberg Finance, L.P.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'chef_zero/server'
|
16
|
+
|
17
|
+
module Umami
|
18
|
+
class Server
|
19
|
+
def initialize
|
20
|
+
@server = server
|
21
|
+
end
|
22
|
+
|
23
|
+
def server
|
24
|
+
@server ||= ChefZero::Server.new(port: 8889)
|
25
|
+
end
|
26
|
+
|
27
|
+
def start
|
28
|
+
server.start_background
|
29
|
+
end
|
30
|
+
|
31
|
+
def stop
|
32
|
+
server.stop
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Copyright 2017 Bloomberg Finance, L.P.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Umami
|
16
|
+
class Test
|
17
|
+
|
18
|
+
attr_reader :root_dir
|
19
|
+
def initialize
|
20
|
+
@root_dir = 'spec'
|
21
|
+
end
|
22
|
+
|
23
|
+
# All subclasses should implement the following methods.
|
24
|
+
|
25
|
+
# #framework should return a string describing the framework it's
|
26
|
+
# expected to write tests for.
|
27
|
+
# Examples:
|
28
|
+
# "chefspec"
|
29
|
+
# "serverspec"
|
30
|
+
# "inspec"
|
31
|
+
def framework
|
32
|
+
raise NoMethodError, "#{self.class} needs to implement the ##{__method__} method! Refer to Umami::Test."
|
33
|
+
end
|
34
|
+
|
35
|
+
# #preamble should return a string (with newlines) that will appear at
|
36
|
+
# the top of a test file.
|
37
|
+
# Expects a string representing the recipe name, at least.
|
38
|
+
# Example:
|
39
|
+
# "# #{test_root}/#{recipe}_spec.rb\n" \
|
40
|
+
# "\n" \
|
41
|
+
# "require '#{framework}'\n" \
|
42
|
+
# "\n" \
|
43
|
+
# "describe '#{recipe}' do\n" \
|
44
|
+
# " let(:chef_run) { ChefSpec::ServerRunner.converge(described_recipe) }"
|
45
|
+
def preamble(recipe = '')
|
46
|
+
raise NoMethodError, "#{self.class} needs to implement the ##{__method__} method! Refer to Umami::Test."
|
47
|
+
end
|
48
|
+
|
49
|
+
# #write_test should write a single, discreet test for a given resource.
|
50
|
+
# Return as a string with newlines.
|
51
|
+
# Expects a Chef::Resource object.
|
52
|
+
def write_test(resource = nil)
|
53
|
+
raise NoMethodError, "#{self.class} needs to implement the ##{__method__} method! Refer to Umami::Test."
|
54
|
+
end
|
55
|
+
|
56
|
+
# Performs the necessary steps to generate one or more tests within a
|
57
|
+
# test file.
|
58
|
+
def generate
|
59
|
+
raise NoMethodError, "#{self.class} needs to implement the ##{__method__} method! Refer to Umami::Test."
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# Copyright 2017 Bloomberg Finance, L.P.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'chef-umami/test'
|
16
|
+
require 'chef-umami/helpers/inspec'
|
17
|
+
require 'chef-umami/helpers/filetools'
|
18
|
+
|
19
|
+
module Umami
|
20
|
+
class Test
|
21
|
+
class Integration < Umami::Test
|
22
|
+
|
23
|
+
include Umami::Helper::InSpec
|
24
|
+
include Umami::Helper::FileTools
|
25
|
+
|
26
|
+
attr_reader :test_root
|
27
|
+
def initialize
|
28
|
+
super
|
29
|
+
@test_root = File.join(self.root_dir, 'umami', 'integration')
|
30
|
+
end
|
31
|
+
|
32
|
+
# InSpec doesn't need a require statement to use its tests.
|
33
|
+
# We define #framework here for completeness.
|
34
|
+
def framework
|
35
|
+
"inspec"
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_file(cookbook = '', recipe = '')
|
39
|
+
"#{test_root}/#{cookbook}_#{recipe}_spec.rb"
|
40
|
+
end
|
41
|
+
|
42
|
+
def preamble(cookbook = '', recipe = '')
|
43
|
+
"# #{test_file(cookbook, recipe)} - Originally written by Umami!"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Call on the apprpriate method from the Umami::Helper::InSpec
|
47
|
+
# module to generate our test.
|
48
|
+
def write_test(resource = nil)
|
49
|
+
if resource.action.is_a? Array
|
50
|
+
return if resource.action.include?(:delete)
|
51
|
+
end
|
52
|
+
return if resource.action == :delete
|
53
|
+
"\n" + send("test_#{resource.declared_type}", resource)
|
54
|
+
end
|
55
|
+
|
56
|
+
# If the test framework's helper module doesn't provide support for a
|
57
|
+
# given test-related method, return a friendly message.
|
58
|
+
# Raise NoMethodError for any other failed calls.
|
59
|
+
def method_missing(m, *args, &block)
|
60
|
+
case m
|
61
|
+
when /^test_/
|
62
|
+
"# #{m} is not currently defined. Stay tuned for updates."
|
63
|
+
else
|
64
|
+
raise NoMethodError
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate(recipe_resources = {})
|
69
|
+
test_files_written = []
|
70
|
+
recipe_resources.each do |canonical_recipe, resources|
|
71
|
+
(cookbook, recipe) = canonical_recipe.split('::')
|
72
|
+
content = [preamble(cookbook, recipe)]
|
73
|
+
resources.each do |resource|
|
74
|
+
content << write_test(resource)
|
75
|
+
end
|
76
|
+
test_file_name = test_file(cookbook, recipe)
|
77
|
+
test_file_content = content.join("\n") + "\n"
|
78
|
+
write_file(test_file_name, test_file_content)
|
79
|
+
test_files_written << test_file_name
|
80
|
+
end
|
81
|
+
|
82
|
+
enforce_styling(test_root)
|
83
|
+
|
84
|
+
unless test_files_written.empty?
|
85
|
+
puts "Wrote the following integration tests:"
|
86
|
+
test_files_written.each do |f|
|
87
|
+
puts "\t#{f}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# Copyright 2017 Bloomberg Finance, L.P.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'chef-umami/test'
|
16
|
+
require 'chef-umami/helpers/os'
|
17
|
+
require 'chef-umami/helpers/filetools'
|
18
|
+
|
19
|
+
module Umami
|
20
|
+
class Test
|
21
|
+
class Unit < Umami::Test
|
22
|
+
|
23
|
+
include Umami::Helper::OS
|
24
|
+
include Umami::Helper::FileTools
|
25
|
+
|
26
|
+
attr_reader :test_root
|
27
|
+
attr_reader :tested_cookbook # This cookbook.
|
28
|
+
def initialize
|
29
|
+
super
|
30
|
+
@test_root = File.join(self.root_dir, 'umami', 'unit', 'recipes')
|
31
|
+
@tested_cookbook = File.basename(Dir.pwd)
|
32
|
+
end
|
33
|
+
|
34
|
+
def framework
|
35
|
+
"chefspec"
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_file(recipe = '')
|
39
|
+
"#{test_root}/#{recipe}_spec.rb"
|
40
|
+
end
|
41
|
+
|
42
|
+
def preamble(cookbook = '', recipe = '')
|
43
|
+
"# #{test_file(recipe)} - Originally written by Umami!\n" \
|
44
|
+
"\n" \
|
45
|
+
"require '#{framework}'\n" \
|
46
|
+
"require '#{framework}/policyfile'\n" \
|
47
|
+
"\n" \
|
48
|
+
"describe '#{cookbook}::#{recipe}' do\n" \
|
49
|
+
"let(:chef_run) { ChefSpec::ServerRunner.new(platform: '#{os[:platform]}', version: '#{os[:version]}').converge(described_recipe) }"
|
50
|
+
end
|
51
|
+
|
52
|
+
def write_test(resource = nil)
|
53
|
+
state_attrs = [] # Attribute hash to be used with #with()
|
54
|
+
resource.state.each do |attr, value|
|
55
|
+
next if value.nil? or (value.respond_to?(:empty) and value.empty?)
|
56
|
+
if value.is_a? String
|
57
|
+
value = value.gsub("'", "\\\\'") # Escape any single quotes in the value.
|
58
|
+
end
|
59
|
+
state_attrs << "#{attr}: '#{value}'"
|
60
|
+
end
|
61
|
+
action = ''
|
62
|
+
if resource.action.is_a? Array
|
63
|
+
action = resource.action.first
|
64
|
+
else
|
65
|
+
action = resource.action
|
66
|
+
end
|
67
|
+
resource_name = resource.name.gsub("'", "\\\\'") # Escape any single quotes in the resource name.
|
68
|
+
test_output = ["\nit '#{action}s #{resource.declared_type} \"#{resource_name}\"' do"]
|
69
|
+
if state_attrs.empty?
|
70
|
+
test_output << "expect(chef_run).to #{action}_#{resource.declared_type}('#{resource_name}')"
|
71
|
+
else
|
72
|
+
test_output << "expect(chef_run).to #{action}_#{resource.declared_type}('#{resource_name}').with(#{state_attrs.join(', ')})"
|
73
|
+
end
|
74
|
+
test_output << "end\n"
|
75
|
+
test_output.join("\n")
|
76
|
+
end
|
77
|
+
|
78
|
+
def generate(recipe_resources = {})
|
79
|
+
test_files_written = []
|
80
|
+
recipe_resources.each do |canonical_recipe, resources|
|
81
|
+
(cookbook, recipe) = canonical_recipe.split('::')
|
82
|
+
# Only write unit tests for the cookbook we're in.
|
83
|
+
next unless cookbook == tested_cookbook
|
84
|
+
content = [preamble(cookbook, recipe)]
|
85
|
+
resources.each do |resource|
|
86
|
+
content << write_test(resource)
|
87
|
+
end
|
88
|
+
content << "end"
|
89
|
+
test_file_name = test_file(recipe)
|
90
|
+
test_file_content = content.join("\n") + "\n"
|
91
|
+
write_file(test_file_name, test_file_content)
|
92
|
+
test_files_written << test_file_name
|
93
|
+
end
|
94
|
+
|
95
|
+
enforce_styling(test_root)
|
96
|
+
|
97
|
+
unless test_files_written.empty?
|
98
|
+
puts "Wrote the following unit test files:"
|
99
|
+
test_files_written.each do |f|
|
100
|
+
puts "\t#{f}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|