minitest-chef-handler 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -1
- data/LICENSE +1 -0
- data/README.md +58 -2
- data/Rakefile +15 -0
- data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/README.md +0 -0
- data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/attributes/default.rb +0 -0
- data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/files/default/handlers/README +0 -0
- data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/metadata.json +0 -0
- data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/metadata.rb +0 -0
- data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/providers/default.rb +0 -0
- data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/recipes/default.rb +0 -0
- data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/recipes/json_file.rb +0 -0
- data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/recipes/minitest.rb +0 -0
- data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/resources/default.rb +0 -0
- data/examples/{cookbooks → simple-solo/cookbooks}/foo/recipes/default.rb +0 -0
- data/examples/{cookbooks → simple-solo/cookbooks}/foo/test/test_foo.rb +0 -0
- data/examples/{dna.json → simple-solo/dna.json} +0 -0
- data/examples/simple-solo/solo.rb +6 -0
- data/examples/simple-solo/spec/foo_spec.rb +9 -0
- data/examples/{test → simple-solo/test}/test_foo.rb +0 -0
- data/examples/spec_examples/attributes/default.rb +8 -0
- data/examples/spec_examples/files/default/tests/minitest/example_test.rb +232 -0
- data/examples/spec_examples/metadata.rb +5 -0
- data/examples/spec_examples/recipes/default.rb +68 -0
- data/examples/spec_examples/templates/default/foo.erb +3 -0
- data/lib/minitest-chef-handler.rb +8 -23
- data/lib/minitest-chef-handler/assertions.rb +129 -0
- data/lib/minitest-chef-handler/context.rb +16 -0
- data/lib/minitest-chef-handler/infections.rb +37 -0
- data/lib/minitest-chef-handler/resources.rb +54 -0
- data/lib/minitest-chef-handler/runner.rb +12 -0
- data/lib/minitest-chef-handler/spec.rb +19 -0
- data/lib/minitest-chef-handler/unit.rb +11 -0
- data/minitest-chef-handler.gemspec +3 -1
- data/spec/minitest-chef-handler/assertions_spec.rb +293 -0
- data/spec/minitest-chef-handler/infections_spec.rb +31 -0
- data/spec/minitest-chef-handler/resources_spec.rb +51 -0
- data/spec/spec_helper.rb +16 -0
- metadata +65 -22
- data/examples/solo.rb +0 -4
data/History.txt
CHANGED
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -17,7 +17,7 @@ $ gem install minitest-chef-handler
|
|
17
17
|
|
18
18
|
## Usage
|
19
19
|
|
20
|
-
|
20
|
+
Add the report handler to your client.rb or solo.rb file:
|
21
21
|
|
22
22
|
```ruby
|
23
23
|
require 'minitest-chef-handler'
|
@@ -25,7 +25,9 @@ require 'minitest-chef-handler'
|
|
25
25
|
report_handlers << MiniTest::Chef::Handler.new
|
26
26
|
```
|
27
27
|
|
28
|
-
|
28
|
+
### Test cases
|
29
|
+
|
30
|
+
Write your tests as normal MiniTest cases extending from MiniTest::Chef::TestCase:
|
29
31
|
|
30
32
|
```ruby
|
31
33
|
class TestNginx < MiniTest::Chef::TestCase
|
@@ -45,6 +47,60 @@ class TestNginx < MiniTest::Chef::TestCase
|
|
45
47
|
end
|
46
48
|
```
|
47
49
|
|
50
|
+
### Spec cases
|
51
|
+
|
52
|
+
Wrap your descriptions with a class extending from MiniTest::Chef::Spec:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
class NginxSpec < MiniTest::Chef::Spec
|
56
|
+
describe 'configuration' do
|
57
|
+
it 'creates nginx.conf'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
Use the prefix `recipe::` in your descriptions:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
describe "recipe::nginx::configuration" do
|
66
|
+
it 'creates nginx.conf'
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
Or use `describe_recipe` to define your specs:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
describe_recipe "nginx::configuration" do
|
74
|
+
it 'creates nginx.conf'
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
You still have access to Chef's `run_status`, `node` and `run_context` from your specs:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
describe_recipe 'nginx:configuration' do
|
82
|
+
it 'installs version 1.0.15' do
|
83
|
+
node[:nginx][:version].should == '1.0.15'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
### Custom assertions
|
89
|
+
|
90
|
+
By including `MiniTest::Chef::Resources` and `MiniTest::Chef::Assertions` you
|
91
|
+
can also make assertions like these:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
file("/etc/fstab").must_have(:mode, "644")
|
95
|
+
package("less").must_be_installed
|
96
|
+
service("chef-client").must_be_running
|
97
|
+
```
|
98
|
+
|
99
|
+
The resources supported are: `cron`, `directory`, `file`, `group`, `ifconfig`,
|
100
|
+
`link`, `mount`, `package`, `service` and `user`.
|
101
|
+
|
102
|
+
For example usage see the tests under the `examples/spec_examples` directory.
|
103
|
+
|
48
104
|
## Further configuration
|
49
105
|
|
50
106
|
These are the options the handler accepts:
|
data/Rakefile
CHANGED
@@ -1,2 +1,17 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
require "bundler/gem_tasks"
|
3
|
+
require "rake/testtask"
|
4
|
+
|
5
|
+
desc 'Verify that nothing is broken against the simple solo example'
|
6
|
+
task :test do
|
7
|
+
Dir.chdir('./examples/simple-solo') do
|
8
|
+
sh 'ruby -I../../lib -S chef-solo -c solo.rb -j dna.json'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Rake::TestTask.new do |t|
|
13
|
+
t.name = 'spec'
|
14
|
+
t.pattern = 'spec/**/*_spec.rb'
|
15
|
+
end
|
16
|
+
|
17
|
+
task :default => [:spec, :test]
|
File without changes
|
data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/attributes/default.rb
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/providers/default.rb
RENAMED
File without changes
|
File without changes
|
data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/recipes/json_file.rb
RENAMED
File without changes
|
data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/recipes/minitest.rb
RENAMED
File without changes
|
data/examples/{cookbooks → chef-handler-cookbook/cookbooks}/chef_handler/resources/default.rb
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,232 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
#
|
3
|
+
# Cookbook Name:: spec_examples
|
4
|
+
# Spec:: default
|
5
|
+
#
|
6
|
+
# Copyright 2012, Opscode, Inc.
|
7
|
+
#
|
8
|
+
describe_recipe 'spec_examples::default' do
|
9
|
+
|
10
|
+
# It's often convenient to load these includes in a separate helper along with
|
11
|
+
# your own helper methods, but here we just include them directly:
|
12
|
+
include MiniTest::Chef::Assertions
|
13
|
+
include MiniTest::Chef::Context
|
14
|
+
include MiniTest::Chef::Resources
|
15
|
+
|
16
|
+
describe "files" do
|
17
|
+
|
18
|
+
# = Testing that a file exists =
|
19
|
+
#
|
20
|
+
# The simplest assertion is that a file exists following the Chef run:
|
21
|
+
it "creates the config file" do
|
22
|
+
file("/etc/fstab").must_exist
|
23
|
+
end
|
24
|
+
|
25
|
+
# All of the matchers starting with 'must_' also have a negative 'wont_'.
|
26
|
+
# So conversely we can also check that a file does not exist:
|
27
|
+
it "ensures that the foobar file is removed if present" do
|
28
|
+
file("/etc/foobar").wont_exist
|
29
|
+
end
|
30
|
+
|
31
|
+
# = Testing for behaviour =
|
32
|
+
#
|
33
|
+
# Chef has multiple resource types that create files. We use file to test
|
34
|
+
# all of them.
|
35
|
+
#
|
36
|
+
# This is not valid:
|
37
|
+
#
|
38
|
+
# cookbook_file(file_path).must_exist
|
39
|
+
#
|
40
|
+
# Testing the behaviour - is the file is created with the right content?
|
41
|
+
# - is preferable to testing how the file gets there. This way if your
|
42
|
+
# recipe switches from using a cookbook_file resource to a template resource
|
43
|
+
# your tests should still work.
|
44
|
+
|
45
|
+
# Let's go beyond just checking for existence to testing the other file
|
46
|
+
# attributes.
|
47
|
+
|
48
|
+
# = Other file attributes =
|
49
|
+
|
50
|
+
# Use .with on a resource and specify the attribute and expected value:
|
51
|
+
it "has the expected ownership and permissions" do
|
52
|
+
file("/etc/fstab").must_exist.with(:owner, "root")
|
53
|
+
end
|
54
|
+
|
55
|
+
# You can also use .must_have:
|
56
|
+
it "only root can modify the config file" do
|
57
|
+
file("/etc/fstab").must_have(:mode, "644")
|
58
|
+
end
|
59
|
+
|
60
|
+
# And you can chain attributes together if you are asserting several.
|
61
|
+
# You don't want to get too carried away doing this but it can be useful.
|
62
|
+
it "only root can modify the config file" do
|
63
|
+
file("/etc/fstab").must_have(:mode, "644").with(:owner, "root").and(:group, "root")
|
64
|
+
end
|
65
|
+
|
66
|
+
# Alternatively you could express it like this so each assertion is nicely
|
67
|
+
# self-contained:
|
68
|
+
describe "only root can modify the config file - alternate syntax" do
|
69
|
+
let(:config) { file("/etc/fstab") }
|
70
|
+
it { config.must_have(:mode, "644") }
|
71
|
+
it { config.must_have(:owner, "root") }
|
72
|
+
it { config.must_have(:group, "root") }
|
73
|
+
end
|
74
|
+
|
75
|
+
# = Checking file content =
|
76
|
+
|
77
|
+
# You can check if a config file contains a string:
|
78
|
+
it "sets phasers to stun" do
|
79
|
+
file('/tmp/foo').must_include 'phaser_setting=stun'
|
80
|
+
end
|
81
|
+
|
82
|
+
# Or if a file matches a regular expression:
|
83
|
+
it "sets phasers to off or stun" do
|
84
|
+
file('/tmp/foo').must_match /^phaser_setting=(off|stun)$/
|
85
|
+
file('/tmp/foo').wont_match /^phaser_setting=kill$/
|
86
|
+
end
|
87
|
+
|
88
|
+
# = Checking file timestamps =
|
89
|
+
it "touches the config to force a reload" do
|
90
|
+
file("/tmp/foo").must_be_modified_after(run_status.start_time)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "leaves the hosts file alone" do
|
94
|
+
file("/etc/hosts").wont_be_modified_after(run_status.start_time)
|
95
|
+
end
|
96
|
+
|
97
|
+
# = Directories =
|
98
|
+
|
99
|
+
# The file existence and permissions matchers are also valid for
|
100
|
+
# directories:
|
101
|
+
it { directory("/etc/").must_exist.with(:owner, "root") }
|
102
|
+
|
103
|
+
# = Links =
|
104
|
+
|
105
|
+
it "symlinks the foo in" do
|
106
|
+
link("/tmp/foo-symbolic").must_exist.with(
|
107
|
+
:link_type, :symbolic).and(:to, "/tmp/foo")
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "packages" do
|
113
|
+
|
114
|
+
# = Checking for package install =
|
115
|
+
it "installs my favorite pager" do
|
116
|
+
package("less").must_be_installed
|
117
|
+
end
|
118
|
+
|
119
|
+
it "doesn't install emacs" do
|
120
|
+
package("emacs").wont_be_installed
|
121
|
+
end
|
122
|
+
|
123
|
+
# = Package names =
|
124
|
+
#
|
125
|
+
# When writing cookbooks intended for use on multiple different operating
|
126
|
+
# systems or flavors you will often need to supply a different package
|
127
|
+
# name based on the node.platform.
|
128
|
+
#
|
129
|
+
# If you choose to test for package installation in your tests then you
|
130
|
+
# will also need to provide the right package name, which can lead to
|
131
|
+
# duplication.
|
132
|
+
|
133
|
+
it "installs my favorite pager" do
|
134
|
+
package(node['spec_examples']['pager']).must_be_installed
|
135
|
+
end
|
136
|
+
|
137
|
+
# = Package versions =
|
138
|
+
it "installs the package with the right version" do
|
139
|
+
package("less").must_be_installed.with(:version, "436-1")
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "services" do
|
144
|
+
|
145
|
+
# You can assert that a service must be running following the converge:
|
146
|
+
it "runs as a daemon" do
|
147
|
+
service("chef-client").must_be_running
|
148
|
+
end
|
149
|
+
|
150
|
+
# And that it will start when the server boots:
|
151
|
+
it "boots on startup" do
|
152
|
+
service("chef-client").must_be_enabled
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "users and groups" do
|
158
|
+
|
159
|
+
# = Users =
|
160
|
+
|
161
|
+
# Check if a user has been created:
|
162
|
+
it "creates a user for the daemon to run as" do
|
163
|
+
user("sshd").must_exist
|
164
|
+
end
|
165
|
+
|
166
|
+
# You can also use .with here to test attributes:
|
167
|
+
it "creates the user with the expected properties" do
|
168
|
+
user("sshd").must_exist.with(:home, '/var/run/sshd')
|
169
|
+
end
|
170
|
+
|
171
|
+
it "has an informative comment against the user" do
|
172
|
+
user("list").must_have(:comment, 'Mailing List Manager')
|
173
|
+
end
|
174
|
+
|
175
|
+
it "has the expected uid" do
|
176
|
+
user("root").must_have(:uid, 0)
|
177
|
+
end
|
178
|
+
|
179
|
+
# = Groups =
|
180
|
+
|
181
|
+
it "creates the users group" do
|
182
|
+
group("chipmunks").must_exist
|
183
|
+
end
|
184
|
+
|
185
|
+
# Check for group membership, you can pass a single user or an array of
|
186
|
+
# users:
|
187
|
+
it "grants group membership to the expected users" do
|
188
|
+
group("chipmunks").must_include('alvin')
|
189
|
+
group("chipmunks").must_include(['alvin', 'simon'])
|
190
|
+
group("chipmunks").wont_include('michelangelo')
|
191
|
+
end
|
192
|
+
|
193
|
+
# Alternatively rather than checking if the group includes specific users
|
194
|
+
# you can test that the group is made up of exactly the users you specify:
|
195
|
+
it "grants group membership only to specific users" do
|
196
|
+
group("chipmunks").must_have(:members, ['alvin', 'simon', 'theodore'])
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe "cron entries" do
|
201
|
+
|
202
|
+
it "creates a crontab entry" do
|
203
|
+
cron("noop").must_exist.with(:hour, "5").and(:minute, "0").and(:day, "*")
|
204
|
+
end
|
205
|
+
|
206
|
+
it "removes the self-destruct countdown" do
|
207
|
+
cron("self-destruct").wont_exist
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
# Note that the syntax for testing the mount resource is slightly different.
|
213
|
+
# You need to specify the device in the call to mount.
|
214
|
+
describe "mount" do
|
215
|
+
it { mount("/mnt", :device => "/dev/null").must_be_mounted }
|
216
|
+
it { mount("/mnt", :device => "/dev/null").must_be_enabled.with(:fstype, "tmpfs") }
|
217
|
+
it { mount("/mnt", :device => "/dev/olympus").wont_be_mounted }
|
218
|
+
end
|
219
|
+
|
220
|
+
describe "networking" do
|
221
|
+
|
222
|
+
# = Test network interface settings =
|
223
|
+
describe "ifconfig" do
|
224
|
+
it "has the expected network interfaces configured" do
|
225
|
+
ifconfig(node['ipaddress'], :device => "eth0").must_exist
|
226
|
+
ifconfig(node['ipaddress'], :device => "eth1").wont_exist
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#
|
2
|
+
# Cookbook Name:: spec_examples
|
3
|
+
# Recipe:: default
|
4
|
+
#
|
5
|
+
# Copyright 2012, Opscode, Inc.
|
6
|
+
#
|
7
|
+
|
8
|
+
template "/tmp/foo" do
|
9
|
+
variables({
|
10
|
+
'phasers' => 'stun'
|
11
|
+
})
|
12
|
+
action :create
|
13
|
+
end
|
14
|
+
|
15
|
+
file "/tmp/foo" do
|
16
|
+
action :touch
|
17
|
+
end
|
18
|
+
|
19
|
+
package "less" do
|
20
|
+
action :install
|
21
|
+
end
|
22
|
+
|
23
|
+
package node['spec_examples']['pager'] do
|
24
|
+
action :install
|
25
|
+
end
|
26
|
+
|
27
|
+
chipmunks = %w{alvin simon theodore}
|
28
|
+
chipmunks.each do |chipmunk|
|
29
|
+
user chipmunk do
|
30
|
+
action :create
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
group "chipmunks" do
|
35
|
+
members chipmunks
|
36
|
+
action :create
|
37
|
+
end
|
38
|
+
|
39
|
+
%w{hard symbolic}.each do |link_type|
|
40
|
+
link "/tmp/foo-#{link_type}" do
|
41
|
+
to "/tmp/foo"
|
42
|
+
link_type link_type
|
43
|
+
action :create
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
package "cron" do
|
48
|
+
action :install
|
49
|
+
end
|
50
|
+
|
51
|
+
cron "noop" do
|
52
|
+
hour "5"
|
53
|
+
minute "0"
|
54
|
+
command "/bin/true"
|
55
|
+
end
|
56
|
+
|
57
|
+
mount "/mnt" do
|
58
|
+
pass 0
|
59
|
+
fstype "tmpfs"
|
60
|
+
device "/dev/null"
|
61
|
+
options "nr_inodes=999k,mode=755,size=500m"
|
62
|
+
action [:mount, :enable]
|
63
|
+
end
|
64
|
+
|
65
|
+
ifconfig "192.168.20.2" do
|
66
|
+
device "eth0"
|
67
|
+
action :add
|
68
|
+
end
|