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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 000ec1d235ba0b6ad374ef81ded2be06e0afd599
4
- data.tar.gz: be76b9944535f196fba9535bfa2adcf0bc04c4bf
3
+ metadata.gz: a0ea0994090ec12f98d62f75eb95cdaa98aec982
4
+ data.tar.gz: 388fec4606165f561b92049ca7bd5a91d2322566
5
5
  SHA512:
6
- metadata.gz: 60a7f9d3f0aeeac29618081754420221e27ada958726242e3eb55740157efc2347bcc8db7de4026c16fbf24ad567a3a429cb9085b0f5f43147f967de8fc760ee
7
- data.tar.gz: 358f66da0df4815fe27218c9907e850d7fa84a1c405c14970ef705099afc876fbd23ab1b9e433aa2d3bef28911723115ec76254fe6f0c66e834e3d5785ab466b
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"
@@ -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
- if File.exists?(target_file) and replace and should_replace_file(target_file, skeleton_file)
31
- # move existing file out of the way
32
- FileUtils.mv(target_file, target_file + BACKUP_EXT)
33
- install = true
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, 'init.pp')
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
- install_example()
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
@@ -1,3 +1,3 @@
1
1
  module PDQTest
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.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-04 00:00:00.000000000 Z
11
+ date: 2017-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler