kitchen-pester 1.1.1 → 1.2.1
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 +4 -4
- data/README.md +401 -0
- data/kitchen-pester.gemspec +14 -8
- data/lib/kitchen/verifier/pester.rb +251 -70
- data/lib/kitchen/verifier/pester_version.rb +31 -1
- data/lib/support/modules/PesterUtil/PesterUtil.psm1 +20 -0
- metadata +30 -25
- data/Gemfile +0 -29
- data/Rakefile +0 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2265777e3844674d88d2c9fee716b53e51931370db862b9ae1ab1e0e146a0d3f
|
|
4
|
+
data.tar.gz: ea5c52a19e982eb5f762d342a098160723120b4800734b611c1da782a0a49b62
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 521b9f823e6e4229dc55753e1a19c809056a70855276a5ab512ebcc4e41f9301396cd05deba5a8fe40528f1df25705f069c73401eeb8d67acd3501d9e7f3e502
|
|
7
|
+
data.tar.gz: 1294cc1198df01d440000612209d21b23d4904129d0b9fe2092b37a9186030819c87a145b88ea37526f544ce40e2ede8f616d2ea428f57bd878fc01d6b8902cc
|
data/README.md
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# kitchen-pester
|
|
2
|
+
|
|
3
|
+
[](http://badge.fury.io/rb/kitchen-pester)
|
|
4
|
+
|
|
5
|
+
Run [Pester](https://pester.dev/) tests against a machine that
|
|
6
|
+
[Test Kitchen](https://kitchen.ci/) built for you.
|
|
7
|
+
|
|
8
|
+
Test Kitchen creates the machine, your provisioner configures it, and
|
|
9
|
+
kitchen-pester copies your `*.Tests.ps1` files onto it, installs Pester, runs
|
|
10
|
+
the tests, and brings the results back. No Busser layer in between.
|
|
11
|
+
|
|
12
|
+
It works on Windows and on Linux or macOS instances with
|
|
13
|
+
[PowerShell](https://github.com/PowerShell/PowerShell) installed.
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
### 1. Install the gem
|
|
18
|
+
|
|
19
|
+
This verifier ships as part of [Cinc Workstation](https://cinc.sh/start/workstation/).
|
|
20
|
+
If you have Cinc Workstation installed, there is nothing else to install.
|
|
21
|
+
|
|
22
|
+
To install it into a standalone Ruby:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
gem install kitchen-pester
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
or add it to your `Gemfile`:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
gem "kitchen-pester"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The examples below use the `cinc` commands. Everything here works identically
|
|
35
|
+
with Chef Workstation — see [Using with Chef](#using-with-chef).
|
|
36
|
+
|
|
37
|
+
### 2. Point Test Kitchen at it
|
|
38
|
+
|
|
39
|
+
In `kitchen.yml`:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
verifier:
|
|
43
|
+
name: pester
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
You can set this at the top level, per platform, or per suite.
|
|
47
|
+
|
|
48
|
+
### 3. Put your tests where it will find them
|
|
49
|
+
|
|
50
|
+
For a suite named `default`, that is `tests/integration/default/`:
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
tests/
|
|
54
|
+
└── integration/
|
|
55
|
+
├── default/
|
|
56
|
+
│ └── myapp.Tests.ps1
|
|
57
|
+
└── helpers/ # optional, copied to every suite
|
|
58
|
+
└── Assertions.ps1
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 4. Write a test
|
|
62
|
+
|
|
63
|
+
An ordinary Pester file — nothing kitchen-specific:
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
Describe 'myapp' {
|
|
67
|
+
It 'installed the binary' {
|
|
68
|
+
'C:\Program Files\myapp\myapp.exe' | Should -Exist
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
It 'is listening on 8080' {
|
|
72
|
+
Get-NetTCPConnection -LocalPort 8080 | Should -Not -BeNullOrEmpty
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 5. Run it
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
cinc kitchen verify
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Results are written to `./testresults/PesterTestResults.xml` in NUnit format,
|
|
84
|
+
ready for a CI system to pick up. A failing Pester test fails `cinc kitchen verify`.
|
|
85
|
+
|
|
86
|
+
## How it works
|
|
87
|
+
|
|
88
|
+
Worth knowing when something goes wrong:
|
|
89
|
+
|
|
90
|
+
1. **Sandbox.** Your suite's tests, any `helpers/`, anything in `copy_folders`,
|
|
91
|
+
and kitchen-pester's own `PesterUtil` PowerShell module are staged into a
|
|
92
|
+
local temp directory.
|
|
93
|
+
2. **Transfer.** Test Kitchen ships that sandbox to the instance, under
|
|
94
|
+
`$env:TEMP/verifier` on Windows or `/tmp/verifier` elsewhere.
|
|
95
|
+
3. **Prepare.** kitchen-pester prepends the sandbox's `modules/` folder to
|
|
96
|
+
`$env:PSModulePath`, then installs Pester and anything in
|
|
97
|
+
`install_modules`.
|
|
98
|
+
4. **Run.** It writes a `kitchen_cmd.ps1` on the instance and invokes it. Your
|
|
99
|
+
tests run from the `suites/` folder inside the sandbox.
|
|
100
|
+
5. **Download.** Everything in `downloads` is copied back — including when the
|
|
101
|
+
run fails, so you always get the results file.
|
|
102
|
+
|
|
103
|
+
Every step is generated PowerShell. If a run misbehaves, `kitchen_cmd.ps1` on
|
|
104
|
+
the instance is the exact script that ran.
|
|
105
|
+
|
|
106
|
+
## Pester versions
|
|
107
|
+
|
|
108
|
+
kitchen-pester supports **Pester 5** (the default) and **Pester 4**.
|
|
109
|
+
|
|
110
|
+
It detects the installed version on the instance and adapts: Pester 5 gets a
|
|
111
|
+
`PesterConfiguration` object built from your `pester_configuration`, Pester 4
|
|
112
|
+
gets loose `Invoke-Pester` parameters. You do not need to tell it which one
|
|
113
|
+
you are on.
|
|
114
|
+
|
|
115
|
+
To stay on Pester 4, cap the install:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
verifier:
|
|
119
|
+
name: pester
|
|
120
|
+
pester_install:
|
|
121
|
+
MaximumVersion: '4.99.999'
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Configuration
|
|
125
|
+
|
|
126
|
+
All of these go under `verifier:` in `kitchen.yml`.
|
|
127
|
+
|
|
128
|
+
### Common
|
|
129
|
+
|
|
130
|
+
| Option | Type | Default | What it does |
|
|
131
|
+
| --- | --- | --- | --- |
|
|
132
|
+
| `test_folder` | string | `tests` | Where your tests live. See [Test discovery](#test-discovery). |
|
|
133
|
+
| `downloads` | map | `{"./PesterTestResults.xml" => "./testresults/"}` | Files to copy back from the instance. See [Downloads](#downloads). |
|
|
134
|
+
| `environment` | map | `{}` | Environment variables to set for your tests. |
|
|
135
|
+
| `copy_folders` | array | `[]` | Local folders to copy to the instance and put on `$env:PSModulePath`. |
|
|
136
|
+
| `pester_configuration` | map | see [below](#pester_configuration) | Passed through to Pester. |
|
|
137
|
+
| `root_path` | string | driver default | Directory on the SUT the sandbox is copied into. Relative `copy_folders` sources and the `suites` directory resolve against it, and `PesterTestResults.xml` is written there. |
|
|
138
|
+
| `suite_name` | string | the suite name | Name of the suite, used when locating its tests. |
|
|
139
|
+
|
|
140
|
+
### Installing Pester and its dependencies
|
|
141
|
+
|
|
142
|
+
| Option | Type | Default | What it does |
|
|
143
|
+
| --- | --- | --- | --- |
|
|
144
|
+
| `pester_install` | map | `{SkipPublisherCheck: true, Force: true, ErrorAction: "Stop"}` | Splatted to `Install-Module -Name Pester`. |
|
|
145
|
+
| `skip_pester_install` | bool | `false` | Use whatever Pester is already on the box. |
|
|
146
|
+
| `install_modules` | array | `[]` | Extra modules to install from a gallery. |
|
|
147
|
+
| `register_repository` | array | `[]` | PSRepositories to register first, for private feeds. |
|
|
148
|
+
| `bootstrap` | map | `{repository_url: "https://www.powershellgallery.com/api/v2", modules: []}` | Modules to fetch straight from a NuGet feed, before PowerShellGet is usable. |
|
|
149
|
+
| `remove_builtin_pester` | bool | `true` | Remove the Pester 3.4.0 that ships with Windows. |
|
|
150
|
+
| `remove_builtin_powershellget` | bool | `true` | Remove the PowerShellGet and PackageManagement 1.0.0.1 that ship with Windows. |
|
|
151
|
+
|
|
152
|
+
### Platform and shell
|
|
153
|
+
|
|
154
|
+
| Option | Type | Default | What it does |
|
|
155
|
+
| --- | --- | --- | --- |
|
|
156
|
+
| `shell` | string | `nil` | Shell binary to use. Defaults to `powershell` on Windows, `pwsh` elsewhere. |
|
|
157
|
+
| `sudo` | bool | `false` | Run PowerShell under sudo. Non-Windows only. |
|
|
158
|
+
| `restart_winrm` | bool | `false` | Restart WinRM via a scheduled task before verifying. Windows only. |
|
|
159
|
+
|
|
160
|
+
### Test discovery
|
|
161
|
+
|
|
162
|
+
`test_folder` is where kitchen-pester starts looking. It may be relative to
|
|
163
|
+
the directory you run `kitchen` from, or absolute, and it must exist.
|
|
164
|
+
|
|
165
|
+
If `<test_folder>/integration` exists, that becomes the root instead — which is
|
|
166
|
+
why the default `tests` finds `tests/integration`. Within that root:
|
|
167
|
+
|
|
168
|
+
- `<root>/<suite_name>/` is copied to the instance and is what Pester runs.
|
|
169
|
+
Nest files however you like; Pester recurses.
|
|
170
|
+
- `<root>/helpers/` is copied alongside **every** suite.
|
|
171
|
+
|
|
172
|
+
### Downloads
|
|
173
|
+
|
|
174
|
+
The key is the file on the instance, the value is where to put it locally.
|
|
175
|
+
|
|
176
|
+
The **source** may be relative to the verifier folder (`$env:TEMP/verifier` by
|
|
177
|
+
default) or absolute (`/var/tmp/file.zip`, `C:\Windows\Temp\file.zip`).
|
|
178
|
+
|
|
179
|
+
The **destination** may be relative to the current directory or absolute, may
|
|
180
|
+
end in `/` or `\` to mean "a directory, keep the filename", and may contain
|
|
181
|
+
`%{instance_name}` to keep results from different instances apart:
|
|
182
|
+
|
|
183
|
+
```yaml
|
|
184
|
+
verifier:
|
|
185
|
+
name: pester
|
|
186
|
+
downloads:
|
|
187
|
+
PesterTestResults.xml: "testresults/%{instance_name}/"
|
|
188
|
+
kitchen_cmd.ps1: "testresults/%{instance_name}/"
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Downloading `kitchen_cmd.ps1` like that is a useful debugging trick: it is the
|
|
192
|
+
generated script that actually ran.
|
|
193
|
+
|
|
194
|
+
### pester_configuration
|
|
195
|
+
|
|
196
|
+
Defaults to:
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
run:
|
|
200
|
+
path: "."
|
|
201
|
+
PassThru: true
|
|
202
|
+
TestResult:
|
|
203
|
+
Enabled: true
|
|
204
|
+
OutputPath: PesterTestResults.xml
|
|
205
|
+
TestSuiteName: ""
|
|
206
|
+
Output:
|
|
207
|
+
Verbosity: Detailed
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**On Pester 5**, this becomes a `PesterConfiguration` via
|
|
211
|
+
`New-PesterConfiguration -Hashtable`. Three keys are filled in for you if you
|
|
212
|
+
leave them unset:
|
|
213
|
+
|
|
214
|
+
| Key | Filled in with |
|
|
215
|
+
| --- | --- |
|
|
216
|
+
| `Run.Path` | `$env:TEMP/verifier/suites` |
|
|
217
|
+
| `TestResult.TestSuiteName` | `Pester - <kitchen instance name>` |
|
|
218
|
+
| `TestResult.OutputPath` | `$env:TEMP/verifier/PesterTestResults.xml` |
|
|
219
|
+
|
|
220
|
+
**On Pester 4**, keys matching a real `Invoke-Pester` parameter are used and
|
|
221
|
+
the rest are ignored. These defaults apply unless you set them:
|
|
222
|
+
|
|
223
|
+
| Parameter | Default |
|
|
224
|
+
| --- | --- |
|
|
225
|
+
| `Script` | `$env:TEMP/verifier/suites` |
|
|
226
|
+
| `OutputFile` | `$env:TEMP/verifier/PesterTestResults.xml` |
|
|
227
|
+
| `OutputFormat` | `NUnitXml` |
|
|
228
|
+
| `PassThru` | `true` |
|
|
229
|
+
| `PesterOption` | `New-PesterOption -TestSuiteName "Pester - <instance name>"` |
|
|
230
|
+
|
|
231
|
+
### register_repository
|
|
232
|
+
|
|
233
|
+
Each entry is splatted to `Register-PSRepository`, or `Set-PSRepository` if the
|
|
234
|
+
repository already exists:
|
|
235
|
+
|
|
236
|
+
```yaml
|
|
237
|
+
verifier:
|
|
238
|
+
name: pester
|
|
239
|
+
register_repository:
|
|
240
|
+
- Name: MyPrivateNuget
|
|
241
|
+
SourceLocation: https://mypsrepo.local/api/v2
|
|
242
|
+
InstallationPolicy: trusted
|
|
243
|
+
PackageManagementProvider: Nuget
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### install_modules
|
|
247
|
+
|
|
248
|
+
Plain names, or maps splatted to `Install-Module`:
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
verifier:
|
|
252
|
+
name: pester
|
|
253
|
+
install_modules:
|
|
254
|
+
- PSScriptAnalyzer
|
|
255
|
+
- Name: MyModule
|
|
256
|
+
Repository: MyPrivateRepo
|
|
257
|
+
SkipPublisherCheck: true
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### bootstrap
|
|
261
|
+
|
|
262
|
+
For machines where PowerShellGet is too old to install anything — notably a
|
|
263
|
+
stock Windows image. Modules are downloaded and unzipped straight from the
|
|
264
|
+
NuGet API, bypassing `Install-Module` entirely.
|
|
265
|
+
|
|
266
|
+
The feed must serve `$repository_url/package/<ModuleName>`, which some private
|
|
267
|
+
feed implementations do not.
|
|
268
|
+
|
|
269
|
+
```yaml
|
|
270
|
+
verifier:
|
|
271
|
+
name: pester
|
|
272
|
+
bootstrap:
|
|
273
|
+
repository_url: https://www.powershellgallery.com/api/v2
|
|
274
|
+
modules:
|
|
275
|
+
- PackageManagement
|
|
276
|
+
- PowerShellGet
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
> **Note:** this key is replaced wholesale, not merged. If you set `modules`,
|
|
280
|
+
> set `repository_url` too.
|
|
281
|
+
|
|
282
|
+
## Examples
|
|
283
|
+
|
|
284
|
+
### Testing a PowerShell module you just built
|
|
285
|
+
|
|
286
|
+
`copy_folders` puts your build output on the instance's `$env:PSModulePath`,
|
|
287
|
+
so your tests can `Import-Module MyModule` as if it were installed:
|
|
288
|
+
|
|
289
|
+
```yaml
|
|
290
|
+
verifier:
|
|
291
|
+
name: pester
|
|
292
|
+
copy_folders:
|
|
293
|
+
- output/MyModule
|
|
294
|
+
downloads:
|
|
295
|
+
PesterTestResults.xml: "testresults/%{instance_name}/"
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### A stock Windows image
|
|
299
|
+
|
|
300
|
+
Windows ships Pester 3.4.0 and PowerShellGet 1.0.0.1, neither of which can
|
|
301
|
+
install a modern Pester on its own. The defaults already remove both; bootstrap
|
|
302
|
+
replacements from NuGet so the install has something to work with:
|
|
303
|
+
|
|
304
|
+
```yaml
|
|
305
|
+
verifier:
|
|
306
|
+
name: pester
|
|
307
|
+
bootstrap:
|
|
308
|
+
repository_url: https://www.powershellgallery.com/api/v2
|
|
309
|
+
modules:
|
|
310
|
+
- PackageManagement
|
|
311
|
+
- PowerShellGet
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Linux, with PowerShell installed on the fly
|
|
315
|
+
|
|
316
|
+
Use Test Kitchen's lifecycle hooks to install `pwsh` after the machine comes
|
|
317
|
+
up. A recent `pwsh` ships a usable PowerShellGet, so no bootstrap is needed:
|
|
318
|
+
|
|
319
|
+
```yaml
|
|
320
|
+
provisioner:
|
|
321
|
+
name: shell
|
|
322
|
+
script: tests/integration/provisioning.ps1
|
|
323
|
+
|
|
324
|
+
verifier:
|
|
325
|
+
name: pester
|
|
326
|
+
|
|
327
|
+
platforms:
|
|
328
|
+
- name: ubuntu-22.04
|
|
329
|
+
lifecycle:
|
|
330
|
+
post_create:
|
|
331
|
+
- remote: sudo snap install powershell --classic
|
|
332
|
+
|
|
333
|
+
suites:
|
|
334
|
+
- name: default
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
If `pwsh` is only reachable through sudo — as with a snap install on some
|
|
338
|
+
systems — add `sudo: true` to the verifier.
|
|
339
|
+
|
|
340
|
+
### Passing secrets and settings to your tests
|
|
341
|
+
|
|
342
|
+
```yaml
|
|
343
|
+
verifier:
|
|
344
|
+
name: pester
|
|
345
|
+
environment:
|
|
346
|
+
API_KEY: <%= ENV['API_KEY'] %>
|
|
347
|
+
PUSH_URI: https://push.example.com
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
```powershell
|
|
351
|
+
Describe 'configuration' {
|
|
352
|
+
It 'received the API key' {
|
|
353
|
+
$env:API_KEY | Should -Not -BeNullOrEmpty
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Troubleshooting
|
|
359
|
+
|
|
360
|
+
**`cinc kitchen verify` fails but I get no results file.** You should still get one
|
|
361
|
+
— downloads run even when the verify fails. If the file is missing, the run
|
|
362
|
+
died before Pester started; check the `cinc kitchen verify` output for the install
|
|
363
|
+
step.
|
|
364
|
+
|
|
365
|
+
**I want to see the script that ran.** Add `kitchen_cmd.ps1` to `downloads`,
|
|
366
|
+
or look for it in `$env:TEMP/verifier` on the instance.
|
|
367
|
+
|
|
368
|
+
**`Install-Module` cannot find a repository.** The built-in PowerShellGet was
|
|
369
|
+
removed (the default) and nothing replaced it. Either `bootstrap` a newer
|
|
370
|
+
PowerShellGet, or set `remove_builtin_powershellget: false` if the machine's
|
|
371
|
+
own copy works.
|
|
372
|
+
|
|
373
|
+
**Tests are not found.** Check that your files are under
|
|
374
|
+
`<test_folder>/integration/<suite_name>/` and match Pester's discovery
|
|
375
|
+
pattern (`*.Tests.ps1`), and that the suite name in `kitchen.yml` matches the
|
|
376
|
+
folder name.
|
|
377
|
+
|
|
378
|
+
**PowerShell is not installed on a Linux instance.** kitchen-pester does not
|
|
379
|
+
install it. Use a lifecycle hook or your provisioner.
|
|
380
|
+
|
|
381
|
+
## Using with Chef
|
|
382
|
+
|
|
383
|
+
This verifier runs Pester and does not depend on Cinc or Chef being installed on
|
|
384
|
+
the system under test — it works with any Test Kitchen driver and provisioner.
|
|
385
|
+
|
|
386
|
+
The examples above use [Cinc Workstation](https://cinc.sh/start/workstation/) and
|
|
387
|
+
the `cinc kitchen` commands. With
|
|
388
|
+
[Chef Workstation](https://www.chef.io/downloads/tools/workstation) run `kitchen`
|
|
389
|
+
instead of `cinc kitchen`. No verifier configuration changes are needed.
|
|
390
|
+
|
|
391
|
+
## Contributing
|
|
392
|
+
|
|
393
|
+
Bug reports and pull requests are welcome on
|
|
394
|
+
[GitHub](https://github.com/test-kitchen/kitchen-pester). See
|
|
395
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) for development setup, how to run the unit
|
|
396
|
+
specs, the PowerShell module specs, and the integration suite, and how the
|
|
397
|
+
documentation is generated.
|
|
398
|
+
|
|
399
|
+
## License
|
|
400
|
+
|
|
401
|
+
MIT. See [LICENSE](LICENSE).
|
data/kitchen-pester.gemspec
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
lib = File.expand_path("lib", __dir__)
|
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
5
|
|
|
4
6
|
require "kitchen/verifier/pester_version"
|
|
5
7
|
|
|
6
8
|
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name
|
|
9
|
+
spec.name = "kitchen-pester"
|
|
10
|
+
spec.required_ruby_version = ">= 3.1"
|
|
8
11
|
spec.version = Kitchen::Verifier::PESTER_VERSION
|
|
9
12
|
spec.authors = ["Steven Murawski"]
|
|
10
13
|
spec.email = ["steven.murawski@gmail.com"]
|
|
11
14
|
spec.summary = "Test-Kitchen verifier for Pester."
|
|
12
15
|
spec.description = "Skip all that Busser stuff and jump right into Pester."
|
|
13
16
|
spec.homepage = "https://github.com/test-kitchen/kitchen-pester"
|
|
14
|
-
spec.license = "
|
|
17
|
+
spec.license = "MIT"
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
# README.md is what YARD picks up as the front page of the generated docs,
|
|
20
|
+
# which is how rubydoc.info renders this gem.
|
|
21
|
+
spec.files = %w{LICENSE README.md kitchen-pester.gemspec} + Dir.glob("lib/**/*")
|
|
17
22
|
spec.require_paths = ["lib"]
|
|
18
23
|
|
|
19
|
-
spec.add_development_dependency "bundler"
|
|
20
24
|
spec.add_development_dependency "rake"
|
|
21
|
-
spec.add_development_dependency "minitest", "
|
|
22
|
-
spec.add_development_dependency "mocha", "
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
spec.add_development_dependency "minitest", ">= 5.25", "< 7"
|
|
26
|
+
spec.add_development_dependency "mocha", ">= 2.0", "< 4"
|
|
27
|
+
spec.add_development_dependency "yard", "~> 0.9"
|
|
28
|
+
# 3.6.0 is the first release to require Ruby >= 3.1, which this gem also
|
|
29
|
+
# requires, so anything older cannot be installed alongside it in practice.
|
|
30
|
+
spec.add_dependency "test-kitchen", ">= 3.6", "< 5"
|
|
25
31
|
end
|
|
@@ -1,31 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Author:: Steven Murawski (<steven.murawski@gmail.com>)
|
|
2
4
|
#
|
|
3
|
-
# Copyright (
|
|
5
|
+
# Copyright (c) 2015 Steven Murawski
|
|
4
6
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
# furnished to do so, subject to the following conditions:
|
|
8
13
|
#
|
|
9
|
-
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
|
15
|
+
# all copies or substantial portions of the Software.
|
|
10
16
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
|
+
# THE SOFTWARE.
|
|
16
24
|
|
|
17
25
|
require "fileutils" unless defined?(FileUtils)
|
|
18
26
|
require "pathname" unless defined?(Pathname)
|
|
19
27
|
require "kitchen/util"
|
|
20
28
|
require "kitchen/verifier/base"
|
|
21
|
-
require "kitchen/version"
|
|
22
|
-
require "base64" unless defined?(Base64)
|
|
23
29
|
require_relative "pester_version"
|
|
24
30
|
|
|
25
31
|
module Kitchen
|
|
26
32
|
|
|
27
33
|
module Verifier
|
|
28
34
|
|
|
35
|
+
# A Test Kitchen verifier that runs Pester tests on the system under test.
|
|
36
|
+
#
|
|
37
|
+
# The verifier does almost all of its work by generating PowerShell source
|
|
38
|
+
# locally and handing it to the transport to execute remotely. Each command
|
|
39
|
+
# hook -- {#install_command}, {#init_command}, {#prepare_command} and
|
|
40
|
+
# {#run_command} -- returns a script string rather than performing the work
|
|
41
|
+
# itself.
|
|
42
|
+
#
|
|
43
|
+
# Test files, helper files and any folders named in `copy_folders` are
|
|
44
|
+
# staged into a sandbox by {#create_sandbox}, shipped to the instance, and
|
|
45
|
+
# discovered there through `$Env:PSModulePath`.
|
|
46
|
+
#
|
|
47
|
+
# @example configuring the verifier in kitchen.yml
|
|
48
|
+
#
|
|
49
|
+
# verifier:
|
|
50
|
+
# name: pester
|
|
51
|
+
# test_folder: tests
|
|
52
|
+
# install_modules:
|
|
53
|
+
# - PSScriptAnalyzer
|
|
54
|
+
# downloads:
|
|
55
|
+
# ./PesterTestResults.xml: ./testresults/
|
|
56
|
+
#
|
|
57
|
+
# @see https://pester.dev/ Pester
|
|
29
58
|
class Pester < Kitchen::Verifier::Base
|
|
30
59
|
|
|
31
60
|
kitchen_verifier_api_version 1
|
|
@@ -66,6 +95,7 @@ module Kitchen
|
|
|
66
95
|
default_config :copy_folders, []
|
|
67
96
|
default_config :sudo, false
|
|
68
97
|
default_config :shell, nil
|
|
98
|
+
default_config :environment, {}
|
|
69
99
|
|
|
70
100
|
# Creates a new Verifier object using the provided configuration data
|
|
71
101
|
# which will be merged with any default configuration.
|
|
@@ -73,6 +103,7 @@ module Kitchen
|
|
|
73
103
|
# @param config [Hash] provided verifier configuration
|
|
74
104
|
def initialize(config = {})
|
|
75
105
|
init_config(config)
|
|
106
|
+
raise ClientError.new "Environment Variables must be specified as a hash, not a #{config[:environment].class}" unless config[:environment].is_a?(Hash)
|
|
76
107
|
end
|
|
77
108
|
|
|
78
109
|
# Creates a temporary directory on the local workstation into which
|
|
@@ -93,6 +124,8 @@ module Kitchen
|
|
|
93
124
|
# # any further file copies, preparations, etc.
|
|
94
125
|
# end
|
|
95
126
|
# end
|
|
127
|
+
#
|
|
128
|
+
# @return [void]
|
|
96
129
|
def create_sandbox
|
|
97
130
|
super
|
|
98
131
|
prepare_supporting_psmodules
|
|
@@ -188,51 +221,56 @@ module Kitchen
|
|
|
188
221
|
config[:downloads] = config[:downloads]
|
|
189
222
|
.map do |source, destination|
|
|
190
223
|
source = source.to_s
|
|
224
|
+
destination = destination.gsub("%{instance_name}", instance.name)
|
|
191
225
|
info(" resolving remote source's absolute path.")
|
|
192
|
-
unless source.match?(
|
|
226
|
+
unless source.match?(%r{^/|^[a-zA-Z]:[\\/]}) # is Absolute?
|
|
193
227
|
info(" '#{source}' is a relative path, resolving to: #{File.join(config[:root_path], source)}")
|
|
194
228
|
source = File.join(config[:root_path], source.to_s).to_s
|
|
195
229
|
end
|
|
196
230
|
|
|
197
|
-
if destination.match?(
|
|
198
|
-
|
|
231
|
+
if destination.match?(%r{[\\/]$}) # is Folder (ends with / or \)
|
|
232
|
+
# Append to the separator the user already supplied. File.join
|
|
233
|
+
# would add a second one, of whichever flavour the workstation
|
|
234
|
+
# happens to use.
|
|
235
|
+
destination = "#{destination}#{remote_basename(source)}"
|
|
199
236
|
end
|
|
200
237
|
info(" Destination: #{destination}")
|
|
201
238
|
if !File.directory?(File.dirname(destination))
|
|
202
239
|
FileUtils.mkdir_p(File.dirname(destination))
|
|
203
240
|
else
|
|
204
|
-
info(" Directory #{File.dirname(destination)}
|
|
241
|
+
info(" Directory #{File.dirname(destination)} seems to exist.")
|
|
205
242
|
end
|
|
206
243
|
|
|
207
244
|
[ source, destination ]
|
|
208
245
|
end
|
|
246
|
+
.to_h # Hash#map yields pairs; keep :downloads the hash it started as
|
|
209
247
|
nil # make sure we do not return anything
|
|
210
248
|
end
|
|
211
249
|
|
|
212
|
-
#
|
|
213
|
-
#
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
rescue
|
|
226
|
-
# If the verifier reports failure, we need to download the files ourselves.
|
|
227
|
-
# Test Kitchen's base verifier doesn't have the download in an `ensure` block.
|
|
228
|
-
info("Rescue to download test files.")
|
|
229
|
-
download_test_files(state) unless config[:downloads].nil?
|
|
230
|
-
# Rethrow original exception, we still want to register the failure.
|
|
231
|
-
raise
|
|
232
|
-
end
|
|
250
|
+
# Runs the verifier on the instance, retrieving the test results even
|
|
251
|
+
# when the run fails.
|
|
252
|
+
#
|
|
253
|
+
# @param state [Hash] mutable instance state
|
|
254
|
+
# @raise [Kitchen::ActionFailed] if the verification failed
|
|
255
|
+
# @return [void]
|
|
256
|
+
def call(state)
|
|
257
|
+
super
|
|
258
|
+
rescue
|
|
259
|
+
info("Rescue to download test files.")
|
|
260
|
+
download_test_files(state) unless config[:downloads].nil?
|
|
261
|
+
# Rethrow the original exception; the failure still has to register.
|
|
262
|
+
raise
|
|
233
263
|
end
|
|
234
264
|
|
|
235
|
-
#
|
|
265
|
+
# Returns the PowerShell that imports Pester and invokes it.
|
|
266
|
+
#
|
|
267
|
+
# Two dialects are emitted behind a version check evaluated on the SUT:
|
|
268
|
+
# Pester 4 and earlier take loose parameters, Pester 5 and later take a
|
|
269
|
+
# `PesterConfiguration` object. The script exits with Pester's failed
|
|
270
|
+
# test count so the transport registers the failure.
|
|
271
|
+
#
|
|
272
|
+
# @return [String] a PowerShell script
|
|
273
|
+
# @api private
|
|
236
274
|
def invoke_pester_scriptblock
|
|
237
275
|
<<-PS1
|
|
238
276
|
$PesterModule = Import-Module -Name Pester -Force -ErrorAction Stop -PassThru
|
|
@@ -240,6 +278,7 @@ module Kitchen
|
|
|
240
278
|
$TestPath = Join-Path "#{config[:root_path]}" -ChildPath "suites"
|
|
241
279
|
$OutputFilePath = Join-Path "#{config[:root_path]}" -ChildPath 'PesterTestResults.xml'
|
|
242
280
|
|
|
281
|
+
#{ps_environment(config[:environment])}
|
|
243
282
|
if ($PesterModule.Version.Major -le 4)
|
|
244
283
|
{
|
|
245
284
|
Write-Host -Object "Invoke Pester with v$($PesterModule.Version) Options"
|
|
@@ -304,7 +343,7 @@ module Kitchen
|
|
|
304
343
|
|
|
305
344
|
$resultXmlPath = (Join-Path -Path $TestPath -ChildPath 'result.xml')
|
|
306
345
|
if (Test-Path -Path $resultXmlPath) {
|
|
307
|
-
$result | Export-CliXml -Path
|
|
346
|
+
$result | Export-CliXml -Path $resultXmlPath
|
|
308
347
|
}
|
|
309
348
|
|
|
310
349
|
$LASTEXITCODE = $result.FailedCount
|
|
@@ -314,6 +353,17 @@ module Kitchen
|
|
|
314
353
|
PS1
|
|
315
354
|
end
|
|
316
355
|
|
|
356
|
+
# Returns the commands that install the bootstrap modules straight from
|
|
357
|
+
# a NuGet feed.
|
|
358
|
+
#
|
|
359
|
+
# This runs before PowerShellGet is available, so it uses
|
|
360
|
+
# `Install-ModuleFromNuget` from PesterUtil.psm1 rather than
|
|
361
|
+
# `Install-Module`. Each entry of `bootstrap.modules` may be a plain
|
|
362
|
+
# module name or a hash of parameters.
|
|
363
|
+
#
|
|
364
|
+
# @return [Array<String>, nil] one PowerShell fragment per module, or nil
|
|
365
|
+
# when no bootstrap modules are configured
|
|
366
|
+
# @api private
|
|
317
367
|
def get_powershell_modules_from_nugetapi
|
|
318
368
|
# don't return anything is the modules subkey or bootstrap is null
|
|
319
369
|
return if config.dig(:bootstrap, :modules).nil?
|
|
@@ -361,7 +411,7 @@ module Kitchen
|
|
|
361
411
|
# Returns the string command set the PSGallery as trusted, and
|
|
362
412
|
# Install Pester from gallery based on the params from Pester_install_params config
|
|
363
413
|
#
|
|
364
|
-
# @return
|
|
414
|
+
# @return [String] command to install Pester Module
|
|
365
415
|
# @api private
|
|
366
416
|
def install_pester
|
|
367
417
|
return if config[:skip_pester_install]
|
|
@@ -382,7 +432,7 @@ module Kitchen
|
|
|
382
432
|
end
|
|
383
433
|
|
|
384
434
|
# returns a piece of PS scriptblock for each Module to install
|
|
385
|
-
# from gallery that has been
|
|
435
|
+
# from gallery that has been specified in install_modules config.
|
|
386
436
|
#
|
|
387
437
|
# @return [Array<String>] array of PS commands.
|
|
388
438
|
# @api private
|
|
@@ -410,13 +460,31 @@ module Kitchen
|
|
|
410
460
|
end
|
|
411
461
|
end
|
|
412
462
|
|
|
463
|
+
# Note for anyone adding to the script builders below: Kitchen::Util.outdent!
|
|
464
|
+
# mutates the string it is handed, and this file sets
|
|
465
|
+
# `frozen_string_literal: true`. Interpolated literals are not frozen, so
|
|
466
|
+
# every heredoc that reaches outdent! today is fine -- but a heredoc with
|
|
467
|
+
# no `#{}` in it would be frozen and would raise FrozenError at runtime.
|
|
468
|
+
# Use +dup+ on any such string before passing it along.
|
|
469
|
+
|
|
470
|
+
# Wraps generated PowerShell in the platform's shell invocation.
|
|
471
|
+
#
|
|
472
|
+
# @param code [String] the PowerShell to run on the instance
|
|
473
|
+
# @return [String] a shell command string
|
|
474
|
+
# @api private
|
|
413
475
|
def really_wrap_shell_code(code)
|
|
414
476
|
windows_os? ? really_wrap_windows_shell_code(code) : really_wrap_posix_shell_code(code)
|
|
415
477
|
end
|
|
416
478
|
|
|
417
|
-
#
|
|
418
|
-
#
|
|
419
|
-
#
|
|
479
|
+
# Returns the shell binary used to run the generated script.
|
|
480
|
+
#
|
|
481
|
+
# An explicit `shell` config wins, which allows pwsh-preview or a full
|
|
482
|
+
# path to a specific binary. Otherwise Windows uses powershell and every
|
|
483
|
+
# other platform uses pwsh. `sudo` is honoured everywhere except the
|
|
484
|
+
# Windows branch, where it is meaningless.
|
|
485
|
+
#
|
|
486
|
+
# @return [String] the shell command, prefixed with sudo when configured
|
|
487
|
+
# @api private
|
|
420
488
|
def shell_cmd
|
|
421
489
|
if !config[:shell].nil?
|
|
422
490
|
config[:sudo] ? "sudo #{config[:shell]}" : "#{config[:shell]}"
|
|
@@ -427,6 +495,15 @@ module Kitchen
|
|
|
427
495
|
end
|
|
428
496
|
end
|
|
429
497
|
|
|
498
|
+
# Wraps PowerShell for a Windows instance.
|
|
499
|
+
#
|
|
500
|
+
# The payload is written to kitchen_cmd.ps1 and invoked, rather than
|
|
501
|
+
# passed on the command line, so that quoting and length limits do not
|
|
502
|
+
# apply to it.
|
|
503
|
+
#
|
|
504
|
+
# @param code [String] the PowerShell to run on the instance
|
|
505
|
+
# @return [String] a shell command string
|
|
506
|
+
# @api private
|
|
430
507
|
def really_wrap_windows_shell_code(code)
|
|
431
508
|
my_command = <<-PWSH
|
|
432
509
|
echo "Running as '$(whoami)'..."
|
|
@@ -435,7 +512,9 @@ module Kitchen
|
|
|
435
512
|
# Send the pwsh here string to the file kitchen_cmd.ps1
|
|
436
513
|
@'
|
|
437
514
|
try {
|
|
438
|
-
|
|
515
|
+
if (@('Bypass', 'Unrestricted') -notcontains (Get-ExecutionPolicy)) {
|
|
516
|
+
Set-ExecutionPolicy Unrestricted -Force -Scope Process
|
|
517
|
+
}
|
|
439
518
|
}
|
|
440
519
|
catch {
|
|
441
520
|
$_ | Out-String | Write-Warning
|
|
@@ -450,8 +529,15 @@ module Kitchen
|
|
|
450
529
|
wrap_shell_code(Util.outdent!(my_command))
|
|
451
530
|
end
|
|
452
531
|
|
|
453
|
-
#
|
|
454
|
-
#
|
|
532
|
+
# Wraps PowerShell for a non-Windows instance.
|
|
533
|
+
#
|
|
534
|
+
# Writes the payload to kitchen_cmd.ps1 through a quoted heredoc, so the
|
|
535
|
+
# POSIX shell does not interpolate PowerShell variables, adds a pwsh
|
|
536
|
+
# shebang and invokes it.
|
|
537
|
+
#
|
|
538
|
+
# @param code [String] the PowerShell to run on the instance
|
|
539
|
+
# @return [String] a shell command string
|
|
540
|
+
# @api private
|
|
455
541
|
def really_wrap_posix_shell_code(code)
|
|
456
542
|
my_command = <<-BASH
|
|
457
543
|
echo "Running as '$(whoami)'"
|
|
@@ -472,6 +558,12 @@ module Kitchen
|
|
|
472
558
|
Util.outdent!(my_command)
|
|
473
559
|
end
|
|
474
560
|
|
|
561
|
+
# Prefixes a script with the preamble that makes the sandbox's modules
|
|
562
|
+
# folder importable.
|
|
563
|
+
#
|
|
564
|
+
# @param script [String] the PowerShell to run after the preamble
|
|
565
|
+
# @return [String] the script with the PSModulePath preamble prepended
|
|
566
|
+
# @api private
|
|
475
567
|
def use_local_powershell_modules(script)
|
|
476
568
|
<<-PS1
|
|
477
569
|
Write-Host -Object ("{0} - PowerShell {1}" -f $PSVersionTable.OS,$PSVersionTable.PSVersion)
|
|
@@ -491,6 +583,16 @@ module Kitchen
|
|
|
491
583
|
PS1
|
|
492
584
|
end
|
|
493
585
|
|
|
586
|
+
# Returns the PowerShell that prepares the SUT once the sandbox has been
|
|
587
|
+
# transferred.
|
|
588
|
+
#
|
|
589
|
+
# Runs after the transfer so that PesterUtil.psm1 is available to import.
|
|
590
|
+
# Composes, in order: the NuGet bootstrap, any PSRepository registration,
|
|
591
|
+
# the Pester install, and any gallery modules. Each section is omitted
|
|
592
|
+
# when its config is nil.
|
|
593
|
+
#
|
|
594
|
+
# @return [String] a PowerShell script
|
|
595
|
+
# @api private
|
|
494
596
|
def install_command_script
|
|
495
597
|
<<-PS1
|
|
496
598
|
$PSModPathToPrepend = "#{config[:root_path]}"
|
|
@@ -507,8 +609,16 @@ module Kitchen
|
|
|
507
609
|
PS1
|
|
508
610
|
end
|
|
509
611
|
|
|
612
|
+
# Returns the command that schedules and runs a WinRM restart.
|
|
613
|
+
#
|
|
614
|
+
# The restart is driven through a scheduled task so that it survives the
|
|
615
|
+
# WinRM session being torn down by the restart itself.
|
|
616
|
+
#
|
|
617
|
+
# @return [String, nil] a shell command string, or nil on a non-Windows
|
|
618
|
+
# instance
|
|
619
|
+
# @api private
|
|
510
620
|
def restart_winrm_service
|
|
511
|
-
return unless
|
|
621
|
+
return unless windows_os?
|
|
512
622
|
|
|
513
623
|
cmd = "schtasks /Create /TN restart_winrm /TR " \
|
|
514
624
|
'"powershell -Command Restart-Service winrm" ' \
|
|
@@ -520,6 +630,12 @@ module Kitchen
|
|
|
520
630
|
))
|
|
521
631
|
end
|
|
522
632
|
|
|
633
|
+
# Retrieves the configured result files from the instance.
|
|
634
|
+
#
|
|
635
|
+
# @param state [Hash] mutable instance state, used to open the transport
|
|
636
|
+
# connection
|
|
637
|
+
# @return [void]
|
|
638
|
+
# @api private
|
|
523
639
|
def download_test_files(state)
|
|
524
640
|
if config[:downloads].nil?
|
|
525
641
|
info("Skipped downloading test result file from #{instance.to_str}; 'downloads' hash is empty.")
|
|
@@ -575,8 +691,9 @@ module Kitchen
|
|
|
575
691
|
end
|
|
576
692
|
|
|
577
693
|
# Copies all common testing helper files into the suites directory in
|
|
578
|
-
# the sandbox.
|
|
694
|
+
# the sandbox, stripping the `helpers/` prefix from their paths.
|
|
579
695
|
#
|
|
696
|
+
# @return [void]
|
|
580
697
|
# @api private
|
|
581
698
|
def prepare_helpers
|
|
582
699
|
base = File.join(test_folder, "helpers")
|
|
@@ -589,9 +706,15 @@ module Kitchen
|
|
|
589
706
|
end
|
|
590
707
|
end
|
|
591
708
|
|
|
592
|
-
#
|
|
593
|
-
# The only types supported for now are hash, array, string and Boolean.
|
|
709
|
+
# Renders a Ruby value as PowerShell source.
|
|
594
710
|
#
|
|
711
|
+
# Hashes become hashtables, arrays become arrays, booleans become $true
|
|
712
|
+
# or $false, and everything else is quoted as a string -- PowerShell is
|
|
713
|
+
# generally able to coerce it back to the type it needs.
|
|
714
|
+
#
|
|
715
|
+
# @param obj [Object] the value to render
|
|
716
|
+
# @param depth [Integer] current nesting depth, used for indentation
|
|
717
|
+
# @return [String] PowerShell source for the value
|
|
595
718
|
# @api private
|
|
596
719
|
def ps_hash(obj, depth = 0)
|
|
597
720
|
if [true, false].include? obj
|
|
@@ -611,21 +734,51 @@ module Kitchen
|
|
|
611
734
|
else
|
|
612
735
|
# When the object is not a string nor a hash or array, it will be quoted as a string.
|
|
613
736
|
# In most cases, PS is smart enough to convert back to the type it needs.
|
|
614
|
-
|
|
737
|
+
ps_single_quote(obj)
|
|
615
738
|
end
|
|
616
739
|
end
|
|
617
740
|
|
|
618
|
-
#
|
|
619
|
-
# in the sandbox, where PS Modules and folders will be copied to.
|
|
741
|
+
# Creates environment variable assignments from a ruby map.
|
|
620
742
|
#
|
|
743
|
+
# @param obj [Hash] variable names mapped to their values
|
|
744
|
+
# @return [String] newline-separated `$env:NAME = 'value'` assignments
|
|
745
|
+
# @api private
|
|
746
|
+
def ps_environment(obj)
|
|
747
|
+
commands = obj.map do |k, v|
|
|
748
|
+
"$env:#{k} = #{ps_single_quote(v)}"
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
commands.join("\n")
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
# Renders a value as a single-quoted PowerShell string literal.
|
|
755
|
+
#
|
|
756
|
+
# PowerShell escapes a literal quote inside a single-quoted string by
|
|
757
|
+
# doubling it. Without this an apostrophe anywhere in the config -- a
|
|
758
|
+
# module name, an environment value, a password -- closes the string
|
|
759
|
+
# early and corrupts the rest of the generated script.
|
|
760
|
+
#
|
|
761
|
+
# @param value [Object] any value; #to_s is used
|
|
762
|
+
# @return [String] a quoted, escaped PowerShell string literal
|
|
763
|
+
# @api private
|
|
764
|
+
def ps_single_quote(value)
|
|
765
|
+
"'#{value.to_s.gsub("'", "''")}'"
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
# Returns the path of the modules subfolder in the sandbox, where PS
|
|
769
|
+
# modules and folders will be copied to.
|
|
770
|
+
#
|
|
771
|
+
# @return [String] absolute path to the sandbox's modules folder
|
|
621
772
|
# @api private
|
|
622
773
|
def sandbox_module_path
|
|
623
774
|
File.join(sandbox_path, "modules")
|
|
624
775
|
end
|
|
625
776
|
|
|
626
|
-
#
|
|
627
|
-
# so
|
|
777
|
+
# Copies the folders named in `copy_folders` into the sandbox's modules
|
|
778
|
+
# folder, so they can be discovered through the updated
|
|
779
|
+
# $Env:PSModulePath.
|
|
628
780
|
#
|
|
781
|
+
# @return [void]
|
|
629
782
|
# @api private
|
|
630
783
|
def prepare_copy_folders
|
|
631
784
|
return if config[:copy_folders].nil?
|
|
@@ -639,12 +792,13 @@ module Kitchen
|
|
|
639
792
|
end
|
|
640
793
|
end
|
|
641
794
|
|
|
642
|
-
#
|
|
643
|
-
#
|
|
644
|
-
#
|
|
645
|
-
# the sandbox.
|
|
795
|
+
# Creates a flat list of the files contained in a folder.
|
|
796
|
+
#
|
|
797
|
+
# Useful when debugging what has actually been copied to the sandbox.
|
|
646
798
|
#
|
|
647
|
-
# @
|
|
799
|
+
# @param path [String] the folder to list
|
|
800
|
+
# @return [Array<String>] paths of the entries at the top level and
|
|
801
|
+
# nested beneath it
|
|
648
802
|
# @api private
|
|
649
803
|
def list_files(path)
|
|
650
804
|
base_directory_content = Dir.glob(File.join(path, "*"))
|
|
@@ -654,6 +808,7 @@ module Kitchen
|
|
|
654
808
|
|
|
655
809
|
# Copies all test suite files into the suites directory in the sandbox.
|
|
656
810
|
#
|
|
811
|
+
# @return [void]
|
|
657
812
|
# @api private
|
|
658
813
|
def prepare_pester_tests
|
|
659
814
|
info("Preparing to copy files from '#{suite_test_folder}' to the SUT.")
|
|
@@ -661,15 +816,23 @@ module Kitchen
|
|
|
661
816
|
copy_if_src_exists(suite_test_folder, sandboxed_suites_path)
|
|
662
817
|
end
|
|
663
818
|
|
|
819
|
+
# Copies PesterUtil.psm1 into the sandbox's modules folder, where the
|
|
820
|
+
# updated $Env:PSModulePath will find it.
|
|
821
|
+
#
|
|
822
|
+
# @return [void]
|
|
823
|
+
# @api private
|
|
664
824
|
def prepare_supporting_psmodules
|
|
665
825
|
info("Preparing to copy files from '#{support_psmodule_folder}' to the SUT.")
|
|
666
826
|
sandbox_module_path = File.join(sandbox_path, "modules")
|
|
667
827
|
copy_if_src_exists(support_psmodule_folder, sandbox_module_path)
|
|
668
828
|
end
|
|
669
829
|
|
|
670
|
-
# Copies a folder recursively preserving its layers
|
|
671
|
-
#
|
|
830
|
+
# Copies a folder recursively, preserving its layers. Mostly used to
|
|
831
|
+
# copy into the sandbox. Does nothing when the source does not exist.
|
|
672
832
|
#
|
|
833
|
+
# @param src_to_validate [String] folder to copy
|
|
834
|
+
# @param destination [String] folder to copy into, created if missing
|
|
835
|
+
# @return [void]
|
|
673
836
|
# @api private
|
|
674
837
|
def copy_if_src_exists(src_to_validate, destination)
|
|
675
838
|
unless Dir.exist?(src_to_validate)
|
|
@@ -686,27 +849,45 @@ module Kitchen
|
|
|
686
849
|
FileUtils.cp_r(src_to_validate, destination, preserve: true)
|
|
687
850
|
end
|
|
688
851
|
|
|
689
|
-
#
|
|
690
|
-
#
|
|
852
|
+
# Returns the folder containing the test suites, falling back to
|
|
853
|
+
# `test_base_path` when `test_folder` is not set.
|
|
691
854
|
#
|
|
855
|
+
# @return [String] path to the folder holding the suites
|
|
692
856
|
# @api private
|
|
693
857
|
def test_folder
|
|
694
858
|
config[:test_folder].nil? ? config[:test_base_path] : absolute_test_folder
|
|
695
859
|
end
|
|
696
860
|
|
|
697
|
-
#
|
|
698
|
-
#
|
|
861
|
+
# Resolves `test_folder` to an absolute path, descending into an
|
|
862
|
+
# `integration` subfolder when one exists.
|
|
699
863
|
#
|
|
864
|
+
# @return [String] absolute path to the folder holding the suites
|
|
700
865
|
# @api private
|
|
701
866
|
def absolute_test_folder
|
|
702
867
|
path = (Pathname.new config[:test_folder]).realpath
|
|
703
868
|
integration_path = File.join(path, "integration")
|
|
704
|
-
Dir.exist?(integration_path) ? integration_path : path
|
|
869
|
+
Dir.exist?(integration_path) ? integration_path : path.to_s
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
# Returns the final segment of a path that lives on the SUT.
|
|
873
|
+
#
|
|
874
|
+
# File.basename applies the *workstation's* separator rules, so a Windows
|
|
875
|
+
# remote path such as 'C:\results\out.xml' comes back unchanged when
|
|
876
|
+
# kitchen runs on macOS or Linux -- the usual case for a Windows SUT.
|
|
877
|
+
# Split on either separator instead.
|
|
878
|
+
#
|
|
879
|
+
# @param path [String] a path as it exists on the instance
|
|
880
|
+
# @return [String] the last path segment
|
|
881
|
+
# @api private
|
|
882
|
+
def remote_basename(path)
|
|
883
|
+
path.to_s.split(%r{[\\/]}).last.to_s
|
|
705
884
|
end
|
|
706
885
|
|
|
707
|
-
#
|
|
708
|
-
#
|
|
886
|
+
# Returns a run of spaces of the given width, used to pad messages and
|
|
887
|
+
# indent generated PowerShell hashtables.
|
|
709
888
|
#
|
|
889
|
+
# @param depth [Integer] number of spaces
|
|
890
|
+
# @return [String] the padding
|
|
710
891
|
# @api private
|
|
711
892
|
def pad(depth = 0)
|
|
712
893
|
" " * depth
|
|
@@ -1,5 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2015 Steven Murawski
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
# THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
# Test Kitchen's top-level namespace.
|
|
1
24
|
module Kitchen
|
|
25
|
+
# Namespace for Test Kitchen verifier plugins.
|
|
2
26
|
module Verifier
|
|
3
|
-
|
|
27
|
+
# Version of the kitchen-pester gem.
|
|
28
|
+
#
|
|
29
|
+
# Kept in its own file so that the gemspec can read it without loading
|
|
30
|
+
# test-kitchen, which is not yet available when the gemspec is evaluated.
|
|
31
|
+
#
|
|
32
|
+
# @return [String] the gem version
|
|
33
|
+
PESTER_VERSION = "1.2.1"
|
|
4
34
|
end
|
|
5
35
|
end
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
# Copyright (c) 2015 Steven Murawski
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
|
11
|
+
# all copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
# THE SOFTWARE.
|
|
20
|
+
|
|
1
21
|
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
|
|
2
22
|
|
|
3
23
|
function Install-ModuleFromNuget {
|
metadata
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kitchen-pester
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steven Murawski
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-08-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: rake
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
@@ -25,73 +25,79 @@ dependencies:
|
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: minitest
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '5.25'
|
|
34
|
+
- - "<"
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: '7'
|
|
34
37
|
type: :development
|
|
35
38
|
prerelease: false
|
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
40
|
requirements:
|
|
38
41
|
- - ">="
|
|
39
42
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
43
|
+
version: '5.25'
|
|
44
|
+
- - "<"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '7'
|
|
41
47
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
48
|
+
name: mocha
|
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
|
44
50
|
requirements:
|
|
45
|
-
- - "
|
|
51
|
+
- - ">="
|
|
46
52
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
53
|
+
version: '2.0'
|
|
48
54
|
- - "<"
|
|
49
55
|
- !ruby/object:Gem::Version
|
|
50
|
-
version: '
|
|
56
|
+
version: '4'
|
|
51
57
|
type: :development
|
|
52
58
|
prerelease: false
|
|
53
59
|
version_requirements: !ruby/object:Gem::Requirement
|
|
54
60
|
requirements:
|
|
55
|
-
- - "
|
|
61
|
+
- - ">="
|
|
56
62
|
- !ruby/object:Gem::Version
|
|
57
|
-
version: '
|
|
63
|
+
version: '2.0'
|
|
58
64
|
- - "<"
|
|
59
65
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
66
|
+
version: '4'
|
|
61
67
|
- !ruby/object:Gem::Dependency
|
|
62
|
-
name:
|
|
68
|
+
name: yard
|
|
63
69
|
requirement: !ruby/object:Gem::Requirement
|
|
64
70
|
requirements:
|
|
65
71
|
- - "~>"
|
|
66
72
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
73
|
+
version: '0.9'
|
|
68
74
|
type: :development
|
|
69
75
|
prerelease: false
|
|
70
76
|
version_requirements: !ruby/object:Gem::Requirement
|
|
71
77
|
requirements:
|
|
72
78
|
- - "~>"
|
|
73
79
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '
|
|
80
|
+
version: '0.9'
|
|
75
81
|
- !ruby/object:Gem::Dependency
|
|
76
82
|
name: test-kitchen
|
|
77
83
|
requirement: !ruby/object:Gem::Requirement
|
|
78
84
|
requirements:
|
|
79
85
|
- - ">="
|
|
80
86
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '
|
|
87
|
+
version: '3.6'
|
|
82
88
|
- - "<"
|
|
83
89
|
- !ruby/object:Gem::Version
|
|
84
|
-
version: '
|
|
90
|
+
version: '5'
|
|
85
91
|
type: :runtime
|
|
86
92
|
prerelease: false
|
|
87
93
|
version_requirements: !ruby/object:Gem::Requirement
|
|
88
94
|
requirements:
|
|
89
95
|
- - ">="
|
|
90
96
|
- !ruby/object:Gem::Version
|
|
91
|
-
version: '
|
|
97
|
+
version: '3.6'
|
|
92
98
|
- - "<"
|
|
93
99
|
- !ruby/object:Gem::Version
|
|
94
|
-
version: '
|
|
100
|
+
version: '5'
|
|
95
101
|
description: Skip all that Busser stuff and jump right into Pester.
|
|
96
102
|
email:
|
|
97
103
|
- steven.murawski@gmail.com
|
|
@@ -99,16 +105,15 @@ executables: []
|
|
|
99
105
|
extensions: []
|
|
100
106
|
extra_rdoc_files: []
|
|
101
107
|
files:
|
|
102
|
-
- Gemfile
|
|
103
108
|
- LICENSE
|
|
104
|
-
-
|
|
109
|
+
- README.md
|
|
105
110
|
- kitchen-pester.gemspec
|
|
106
111
|
- lib/kitchen/verifier/pester.rb
|
|
107
112
|
- lib/kitchen/verifier/pester_version.rb
|
|
108
113
|
- lib/support/modules/PesterUtil/PesterUtil.psm1
|
|
109
114
|
homepage: https://github.com/test-kitchen/kitchen-pester
|
|
110
115
|
licenses:
|
|
111
|
-
-
|
|
116
|
+
- MIT
|
|
112
117
|
metadata: {}
|
|
113
118
|
post_install_message:
|
|
114
119
|
rdoc_options: []
|
|
@@ -118,14 +123,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
118
123
|
requirements:
|
|
119
124
|
- - ">="
|
|
120
125
|
- !ruby/object:Gem::Version
|
|
121
|
-
version: '
|
|
126
|
+
version: '3.1'
|
|
122
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
128
|
requirements:
|
|
124
129
|
- - ">="
|
|
125
130
|
- !ruby/object:Gem::Version
|
|
126
131
|
version: '0'
|
|
127
132
|
requirements: []
|
|
128
|
-
rubygems_version: 3.
|
|
133
|
+
rubygems_version: 3.5.9
|
|
129
134
|
signing_key:
|
|
130
135
|
specification_version: 4
|
|
131
136
|
summary: Test-Kitchen verifier for Pester.
|
data/Gemfile
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
source "https://rubygems.org"
|
|
2
|
-
|
|
3
|
-
# Specify your gem's dependencies in kitchen-pester.gemspec
|
|
4
|
-
gemspec
|
|
5
|
-
|
|
6
|
-
group :integration do
|
|
7
|
-
gem "berkshelf"
|
|
8
|
-
gem "kitchen-inspec"
|
|
9
|
-
gem "kitchen-azurerm"
|
|
10
|
-
gem "kitchen-chocolatey"
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
group :changelog do
|
|
14
|
-
gem "github_changelog_generator", "1.16.4"
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
group :debug do
|
|
18
|
-
gem "pry", "~>0.13.1"
|
|
19
|
-
gem "pry-byebug", "~>3.9.0"
|
|
20
|
-
gem "pry-stack_explorer"
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
group :chefstyle do
|
|
24
|
-
gem "chefstyle"
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
group :docs do
|
|
28
|
-
gem "yard"
|
|
29
|
-
end
|
data/Rakefile
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
require "bundler/gem_tasks"
|
|
2
|
-
|
|
3
|
-
require "rake/testtask"
|
|
4
|
-
Rake::TestTask.new(:unit) do |t|
|
|
5
|
-
t.libs.push "lib"
|
|
6
|
-
t.test_files = FileList["spec/**/*_spec.rb"]
|
|
7
|
-
t.verbose = true
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
task test: :unit
|
|
11
|
-
|
|
12
|
-
begin
|
|
13
|
-
require "chefstyle"
|
|
14
|
-
require "rubocop/rake_task"
|
|
15
|
-
RuboCop::RakeTask.new(:style) do |task|
|
|
16
|
-
task.options += ["--display-cop-names", "--no-color"]
|
|
17
|
-
end
|
|
18
|
-
rescue LoadError
|
|
19
|
-
puts "chefstyle is not available. (sudo) gem install chefstyle to do style checking."
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
desc "Run all quality tasks"
|
|
23
|
-
task quality: :style
|
|
24
|
-
|
|
25
|
-
begin
|
|
26
|
-
require "yard" unless defined?(YARD)
|
|
27
|
-
YARD::Rake::YardocTask.new
|
|
28
|
-
rescue LoadError
|
|
29
|
-
puts "yard is not available. (sudo) gem install yard to generate yard documentation."
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
task default: %i{test quality}
|
|
33
|
-
|
|
34
|
-
begin
|
|
35
|
-
require "github_changelog_generator/task"
|
|
36
|
-
require "kitchen/verifier/pester_version"
|
|
37
|
-
|
|
38
|
-
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
|
|
39
|
-
config.future_release = "v#{Kitchen::Verifier::PESTER_VERSION}"
|
|
40
|
-
config.issues = false
|
|
41
|
-
config.pulls = true
|
|
42
|
-
config.user = "test-kitchen"
|
|
43
|
-
config.project = "kitchen-pester"
|
|
44
|
-
end
|
|
45
|
-
rescue LoadError
|
|
46
|
-
puts "github_changelog_generator is not available." \
|
|
47
|
-
" (sudo) gem install github_changelog_generator to generate changelogs"
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
namespace :docs do
|
|
51
|
-
desc "Deploy docs"
|
|
52
|
-
task :deploy do
|
|
53
|
-
sh "cd docs && hugo"
|
|
54
|
-
sh "aws --profile chef-cd s3 sync docs/public s3://test-kitchen-legacy.cd.chef.co --delete --acl public-read"
|
|
55
|
-
sh "aws --profile chef-cd cloudfront create-invalidation --distribution-id EQD8MRW086SRT --paths '/*'"
|
|
56
|
-
end
|
|
57
|
-
end
|