pdqtest 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +122 -2
- data/exe/pdqtest +19 -0
- data/lib/pdqtest/skeleton.rb +40 -10
- data/lib/pdqtest/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0ea0994090ec12f98d62f75eb95cdaa98aec982
|
4
|
+
data.tar.gz: 388fec4606165f561b92049ca7bd5a91d2322566
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e4098facbb04bdd3fb57f0f98200f80829d01fa5d5a166d3d0fb223aa9dcc6351d245cb0e855ba0ff29fdaa46e35a11df2ed62027721693581867e42cd390ca
|
7
|
+
data.tar.gz: 771f55dd4ab0d274f2cf2aa75e2280f9013d6a17cec4a34d705ff8c7029977cd8c9301f207216b425a6ee89e90d4e8575498210e92bce8103ebd5232217d2e60
|
data/README.md
CHANGED
@@ -6,7 +6,9 @@ PDQTest - Puppet Docker Quick-test - is the quickest and easiest way to test you
|
|
6
6
|
* Linting
|
7
7
|
* Syntax
|
8
8
|
* RSpec
|
9
|
-
* Acceptance (BATS)
|
9
|
+
* Acceptance ([BATS](https://github.com/sstephenson/bats))
|
10
|
+
|
11
|
+
And can generate code to retrofit testing to a new or existing module, along with skeleton RSpec and acceptance tests to get you started.
|
10
12
|
|
11
13
|
PDQTest runs linting, syntax and RSpec tests within the machine it is running from and then loads a docker container to perform acceptance testing.
|
12
14
|
|
@@ -26,7 +28,7 @@ gem 'pdqtest
|
|
26
28
|
* It's advisable to specify a version number to ensure repeatable builds
|
27
29
|
|
28
30
|
### Puppet modules
|
29
|
-
To add PDQTests to a puppet module, run the command:
|
31
|
+
To add PDQTests to a new or existing puppet module, run the command:
|
30
32
|
```
|
31
33
|
pdqtest init
|
32
34
|
```
|
@@ -94,6 +96,123 @@ pdqtest --keep-container all
|
|
94
96
|
* Keep the container Running
|
95
97
|
* After testing, the container used to run tests will be left running and a message will be printed showing how to enter the container used for testing. Your code is avaiable at `/cut`
|
96
98
|
|
99
|
+
## About acceptance tests
|
100
|
+
* Acceptance tests run within a Docker container managed by PDQTest
|
101
|
+
* The Docker container breaks all the rules of Docker and runs a full version of Systemd to allow complete testing of Puppet code in the most portable way (basically treat docker as a high speed VM)... This is deliberate - PDQTest exists to get results, not be a perfect Docker app.
|
102
|
+
* The test container uses a minimal version of Centos 7, there's no option to change this at present
|
103
|
+
* The container will only be started if at least one example file is present and contains the correct magic marker
|
104
|
+
* You can get a shell on the docker container if needed:
|
105
|
+
* A representitive system, identical to that which will be used to run the tests `pdqtest shell`
|
106
|
+
* The actual container that was just used on a test run `pdqtest --keep-container all` and run the command indicated on exit
|
107
|
+
|
108
|
+
### Test workflow
|
109
|
+
1. Scan for all example files under `/examples`. Files must end in `.pp` and contain the magic marker `#@PDQTest` to be processed
|
110
|
+
2. If example files are present, start the docker container
|
111
|
+
3. For each example file:
|
112
|
+
* Check for a file at `/spec/acceptance/EXAMPLE_NAME__setup.sh`, If it exists, run it inside the container. This is a useful place to install preqrequisites, edit files, install mock services, clean up after other tests, etc.
|
113
|
+
* Check for a file at `/spec/acceptance/EXAMPLE_NAME__before.bats`, If it exists run BATS against it. This is normally used to check the system state is clean or that any test prerequisites have been setup correctly
|
114
|
+
* Run `puppet apply` on the example file twice
|
115
|
+
* Check for a file at `/spec/acceptance/EXAMPLE_NAME.bats`, If it exists run BATS against it. This is normally used to check the state of the system after running puppet. You can do things like check services are running, check files edited correctly, or basically anything that you can write in bash!
|
116
|
+
4. Destroy the container unless we were asked to keep it with `--keep-container`
|
117
|
+
|
118
|
+
Note: `EXAMPLE_NAME` is the example filename minus the directory and `.pp`, eg the `EXAMPLE_NAME` for `examples/foo.pp` would just be `foo`.
|
119
|
+
|
120
|
+
### Example files
|
121
|
+
* Found inside `/examples`
|
122
|
+
* Regular puppet code
|
123
|
+
* Will be executed _twice_ (to check for idempotency) with `puppet apply`
|
124
|
+
* _MUST_ contain the magic marker `#@PDQTest` on a line at the top of the file to indicate that this test should be processed by PDQTest
|
125
|
+
* Basically the same as a regular puppet [smoke test](https://docs.puppet.com/puppet/latest/tests_smoke.html#module-smoke-testing) but run automatically and with system state tests
|
126
|
+
|
127
|
+
### BATS tests
|
128
|
+
If you've never seen a BATS test before and have previously been writing server spec code, you'll probably kick youself when you see how simple they are. Here's an example:
|
129
|
+
|
130
|
+
```BASH
|
131
|
+
# Tests are really easy! just the exit status of running a command...
|
132
|
+
@test "addition using bc" {
|
133
|
+
result="$(ls /)"
|
134
|
+
[ "$?" -eq 0 ]
|
135
|
+
}
|
136
|
+
```
|
137
|
+
|
138
|
+
Tests are all written in BASH and the testcase passes if the stanza returns zero. This means that basically any Linux/Unix sysadmin is now empowered to write Testcases and do TDD - score!
|
139
|
+
|
140
|
+
Since our tests are written in BASH, there are of course all the usual bash quirks such as failing on incorrect spacing, bizzare variable quoting etc, but given the simplicity gained its a worthwhile tradeoff.
|
141
|
+
|
142
|
+
Consider the following (real) test:
|
143
|
+
|
144
|
+
```BASH
|
145
|
+
@test "ClientAliveCountMax" {
|
146
|
+
grep "ClientAliveCountMax 1" /etc/ssh/sshd_config
|
147
|
+
}
|
148
|
+
```
|
149
|
+
|
150
|
+
Here, we have done exactly what it looks like we have done - checked for the value of a particular setting in a text file. Since our tests our plain old BASH, it means we can do things like test daemons are running with `systemctl`, test ports are open with `netstat` and do complete system tests `wget` and `curl`. Cool!
|
151
|
+
|
152
|
+
#### Worked Example
|
153
|
+
Lets say you have two examples, `foo.pp` and `init.pp`. In this case, the full range of files to create for acceptance testing would look like this:
|
154
|
+
```
|
155
|
+
mycoolmodule
|
156
|
+
├── examples
|
157
|
+
│ ├── foo.pp
|
158
|
+
│ └── init.pp
|
159
|
+
├── ...
|
160
|
+
└── spec
|
161
|
+
├── acceptance
|
162
|
+
│ ├── foo__before.bats
|
163
|
+
│ ├── foo__setup.sh
|
164
|
+
│ ├── foo.bats
|
165
|
+
│ ├── init__before.bats
|
166
|
+
│ ├── init__setup.sh
|
167
|
+
│ └── init.bats
|
168
|
+
├── ...
|
169
|
+
```
|
170
|
+
|
171
|
+
* You can put any puppet code you like (includeing an empty file...) in each of the files under `/examples` and it will executed with `puppet apply`
|
172
|
+
* If you need to test multiple different things (eg different parameters for a new type and provider), you need to create different testcase for each distinct thing to test. See below for help generating these
|
173
|
+
* You can skip setup or BATS testing before and/or after running `puppet apply` if desired by deleting the appropriate files
|
174
|
+
* If you delete all of the files under `spec/acceptance` for a given example, then PDQTest will just check that puppet runs idempotently for your example
|
175
|
+
* To disable tests temporarily for a specific example, remove the magic marker `#@PDQTest` from the desired example
|
176
|
+
* Nested examples (subdirectories) are not supported at this time
|
177
|
+
|
178
|
+
## Real world example
|
179
|
+
See https://github.com/GeoffWilliams/puppet-filemagic for a project with lots of acceptance tests.
|
180
|
+
|
181
|
+
## Test Generation
|
182
|
+
Creating a bunch of files manually is an error prone and tedious operation so PDQTest can generate files and boilerplate code for you so that your up and running in the quickest time possible.
|
183
|
+
|
184
|
+
### Skeleton
|
185
|
+
* `pdqtest init` will generate a basic set of tests to get you started (tests init.pp)
|
186
|
+
|
187
|
+
### RSpec tests
|
188
|
+
The skeleton tests created by `pdqtest init` only cover the `init.pp` file which is useful but your likely going to need to support more classes as your modules grow. PDQTest can generate basic RSpec testcases for each new puppet class that exists in the manifests directory for you:
|
189
|
+
|
190
|
+
```shell
|
191
|
+
pdqtest generate_rspec
|
192
|
+
```
|
193
|
+
|
194
|
+
* For every `.pp` file containing a puppet class under `/manifests`, RSpec will be generated to:
|
195
|
+
* Check the catalogue compiles
|
196
|
+
* Check the catalogue contains an instance of the class
|
197
|
+
* This gives developers an easy place to start writing additional RSpec tests for specific behaviour
|
198
|
+
* Its safe to run this command whenever you add a new class, it won't overwrite any existing RSpec testcases
|
199
|
+
|
200
|
+
### Acceptance tests
|
201
|
+
|
202
|
+
#### Generate boilerplate files for each example found (including those without a magic marker)
|
203
|
+
|
204
|
+
```shell
|
205
|
+
pdqtest generate_acceptance
|
206
|
+
```
|
207
|
+
|
208
|
+
#### Generate boilerplate files for one specific example
|
209
|
+
|
210
|
+
```shell
|
211
|
+
pdqtest generate_acceptance examples/mynewthing.pp
|
212
|
+
```
|
213
|
+
* Note: will also create examples/mynewthing.pp if you haven't created it yet
|
214
|
+
|
215
|
+
|
97
216
|
## Development
|
98
217
|
|
99
218
|
PRs welcome :) Please ensure suitable tests cover any new functionality and that all tests are passing before and after your development work:
|
@@ -110,6 +229,7 @@ You should use pdqtest if you find it increases your productivity and enriches y
|
|
110
229
|
* Don't forget to run `pdqtest setup` before your first `pdqtest` run to download/update the Docker image
|
111
230
|
* If you need to access private git repositories, make sure to use `fixtures.yml` not `.fixtures.yml`
|
112
231
|
* If you need a private key to access private repositories, set this up for your regular git command/ssh and `pdqtest` will reuse the settings
|
232
|
+
* Be sure to annotate the examples you wish to acceptance test with the magic marker comment `#@PDQTest`
|
113
233
|
|
114
234
|
## Support
|
115
235
|
This software is not supported by Puppet, Inc. Use at your own risk.
|
data/exe/pdqtest
CHANGED
@@ -97,6 +97,25 @@ Escort::App.create do |app|
|
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
|
+
app.command :generate_rspec do |command|
|
101
|
+
command.summary "Generate Acceptance test"
|
102
|
+
command.description "Create a testcase in /examples and the corresponding files for acceptance testing"
|
103
|
+
command.options do |opts|
|
104
|
+
opts.opt(:example,
|
105
|
+
'Generate only this example (eg --example examples/newtest.pp)',
|
106
|
+
:long => '--example',
|
107
|
+
:type => :string,
|
108
|
+
:default => nil,
|
109
|
+
)
|
110
|
+
end
|
111
|
+
command.action do |options, arguments|
|
112
|
+
PDQTest::Emoji.disable(options[:global][:options][:disable_emoji])
|
113
|
+
example = options[:global][:commands][:acceptance][:options][:example]
|
114
|
+
|
115
|
+
PDQTest::Skeleton.generate_acceptance(example)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
100
119
|
|
101
120
|
app.command :shell do |command|
|
102
121
|
command.summary "Shell"
|
data/lib/pdqtest/skeleton.rb
CHANGED
@@ -27,10 +27,13 @@ module PDQTest
|
|
27
27
|
|
28
28
|
def self.install_skeleton(target_file, skeleton, replace=true)
|
29
29
|
skeleton_file = Util::resource_path(File.join(SKELETON_DIR, skeleton))
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
install = false
|
31
|
+
if File.exists?(target_file)
|
32
|
+
if replace and should_replace_file(target_file, skeleton_file)
|
33
|
+
# move existing file out of the way
|
34
|
+
FileUtils.mv(target_file, target_file + BACKUP_EXT)
|
35
|
+
install = true
|
36
|
+
end
|
34
37
|
else
|
35
38
|
install = true
|
36
39
|
end
|
@@ -39,8 +42,8 @@ module PDQTest
|
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
42
|
-
def self.install_example
|
43
|
-
example_file = File.join(EXAMPLES_DIR,
|
45
|
+
def self.install_example(filename)
|
46
|
+
example_file = File.join(EXAMPLES_DIR, filename)
|
44
47
|
if ! File.exists?(example_file)
|
45
48
|
template = File.read(Util::resource_path(File.join('templates', 'examples_init.pp.erb')))
|
46
49
|
init_pp = ERB.new(template, nil, '-').result(binding)
|
@@ -81,18 +84,45 @@ module PDQTest
|
|
81
84
|
# skeleton files if required
|
82
85
|
install_skeleton('Rakefile', 'Rakefile')
|
83
86
|
install_skeleton(File.join('spec', 'spec_helper.rb'), 'spec_helper.rb')
|
84
|
-
install_skeleton(File.join('spec', 'acceptance', 'init.bats'), 'init.bats', false)
|
85
|
-
install_skeleton(File.join('spec', 'acceptance', 'init__before.bats'), 'init__before.bats', false)
|
86
|
-
install_skeleton(File.join('spec', 'acceptance', 'init__setup.sh'), 'init__setup.sh', false)
|
87
87
|
install_skeleton('.travis.yml', 'dot_travis.yml')
|
88
88
|
install_skeleton('.gitignore', 'dot_gitignore')
|
89
89
|
install_skeleton('.rspec', 'dot_rspec')
|
90
90
|
install_skeleton('Makefile', 'Makefile')
|
91
91
|
|
92
|
-
|
92
|
+
install_acceptance()
|
93
93
|
install_gemfile()
|
94
94
|
|
95
95
|
# Make sure there is a Gemfile and we are in it
|
96
96
|
end
|
97
|
+
|
98
|
+
def self.install_acceptance(example_file ="init.pp")
|
99
|
+
install_example(File.basename(example_file))
|
100
|
+
example_name = File.basename(example_file).gsub(/\.pp$/, '')
|
101
|
+
|
102
|
+
install_skeleton(File.join('spec', 'acceptance', "#{example_name}.bats"), 'init.bats', false)
|
103
|
+
install_skeleton(File.join('spec', 'acceptance', "#{example_name}__before.bats"), 'init__before.bats', false)
|
104
|
+
install_skeleton(File.join('spec', 'acceptance', "#{example_name}__setup.sh"), 'init__setup.sh', false)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Scan the examples directory and create a set of acceptance tests. If a
|
108
|
+
# specific file is given as `example` then only the listed example will be
|
109
|
+
# processed (and it will be created if missing). If files already exist, they
|
110
|
+
# will not be touched
|
111
|
+
def self.generate_acceptance(example=nil)
|
112
|
+
examples = []
|
113
|
+
if example
|
114
|
+
# specific file only
|
115
|
+
examples << example
|
116
|
+
else
|
117
|
+
# Each .pp file in /examples (don't worry about magic markers yet, user
|
118
|
+
# will be told on execution if no testscases are present as a reminder to
|
119
|
+
# add it
|
120
|
+
examples += Dir["examples/*.pp"]
|
121
|
+
end
|
122
|
+
|
123
|
+
examples.each { |e|
|
124
|
+
install_acceptance(e)
|
125
|
+
}
|
126
|
+
end
|
97
127
|
end
|
98
128
|
end
|
data/lib/pdqtest/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pdqtest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geoff Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-04-
|
11
|
+
date: 2017-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|