kitchen-pester 0.12.2 → 1.0.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
  SHA256:
3
- metadata.gz: 138cd15e107c0e0331ed15ad42b8f58771c03cf14c4af44a861aa01a54957198
4
- data.tar.gz: 4c57297d38ba62710e58e246573a603b37dde2b076026f023afe2bd36edc7137
3
+ metadata.gz: 3d308bdccbb4f9795482dcdac1abbe373935c4cb164e0bb1bca3d16c8d7185fe
4
+ data.tar.gz: 798da4d69f83306722efd7311104e8797611cec975af00d88168c535a2056cc7
5
5
  SHA512:
6
- metadata.gz: 0262276aa0ad1d0402de1449383286096e66599f98e7004059d404a9309da9491e9677038fad917e517e8d5e40fd94f2cfc8b224598960e1e2eee05e72efa901
7
- data.tar.gz: 866e065c5caa9472fcad981cdecaa3a5815bbb8c2a7b28ce1a804df7ab08a7834b0175fecbc6712f57165fae982b6a86312761bc97460578cb47d766e68acc88
6
+ metadata.gz: 5bebf6a71183b7c02b3dcce3c8f1769b039dd30266a4da75da95f5c9fefd7da00f42421950e543fa529cfdf3cdbf2eb41756569d13e0a91bce68af881aed3cc3
7
+ data.tar.gz: 408ac94d07a5b4a544892f325b7c70b85dbf4e9dcc9193d4505ca3307fcbda5b5e0f5a49c1a3f80cda8b8f084490cf52d7ac5169b372419c6a2075abbb21467a
data/Rakefile CHANGED
@@ -1,5 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
1
  require "bundler/gem_tasks"
4
2
 
5
3
  require "rake/testtask"
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
 
@@ -1,5 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
1
  # Author:: Steven Murawski (<steven.murawski@gmail.com>)
4
2
  #
5
3
  # Copyright (C) 2015, Steven Murawski
@@ -16,9 +14,12 @@
16
14
  # See the License for the specific language governing permissions and
17
15
  # limitations under the License.
18
16
 
17
+ require "fileutils"
19
18
  require "pathname"
19
+ require "kitchen/util"
20
20
  require "kitchen/verifier/base"
21
21
  require "kitchen/version"
22
+ require "base64"
22
23
  require_relative "pester_version"
23
24
 
24
25
  module Kitchen
@@ -32,9 +33,24 @@ module Kitchen
32
33
  plugin_version Kitchen::Verifier::PESTER_VERSION
33
34
 
34
35
  default_config :restart_winrm, false
35
- default_config :test_folder
36
- default_config :use_local_pester_module, false
36
+ default_config :test_folder, "tests"
37
+ default_config :remove_builtin_powershellget, true
38
+ default_config :remove_builtin_pester, true
39
+ default_config :skip_pester_install, false
40
+ default_config :bootstrap, {
41
+ repository_url: "https://www.powershellgallery.com/api/v2",
42
+ modules: [],
43
+ }
44
+ default_config :register_repository, []
45
+ default_config :pester_install, {
46
+ SkipPublisherCheck: true,
47
+ Force: true,
48
+ ErrorAction: "Stop",
49
+ }
50
+ default_config :install_modules, []
37
51
  default_config :downloads, ["./PesterTestResults.xml"] => "./testresults"
52
+ default_config :copy_folders, []
53
+ default_config :sudo, false
38
54
 
39
55
  # Creates a new Verifier object using the provided configuration data
40
56
  # which will be merged with any default configuration.
@@ -64,21 +80,56 @@ module Kitchen
64
80
  # end
65
81
  def create_sandbox
66
82
  super
67
- prepare_powershell_modules
83
+ prepare_supporting_psmodules
84
+ prepare_copy_folders
68
85
  prepare_pester_tests
69
86
  prepare_helpers
87
+
88
+ debug("\n\n")
89
+ debug("Sandbox content:\n")
90
+ list_files(sandbox_path).each do |f|
91
+ debug(" #{f}")
92
+ end
70
93
  end
71
94
 
72
95
  # Generates a command string which will install and configure the
73
96
  # verifier software on an instance. If no work is required, then `nil`
74
97
  # will be returned.
98
+ # PowerShellGet & Pester Bootstrap are done in prepare_command (after sandbox is transferred)
99
+ # so that we can use the PesterUtil.psm1
75
100
  #
76
101
  # @return [String] a command string
77
102
  def install_command
78
- return if local_suite_files.empty?
79
- return if config[:use_local_pester_module]
103
+ # the sandbox has not yet been copied to the SUT.
104
+ install_command_string = <<-PS1
105
+ Write-Verbose 'Running Install Command...'
106
+ $modulesToRemove = @(
107
+ if ($#{config[:remove_builtin_powershellget]}) {
108
+ Get-module -ListAvailable -FullyQualifiedName @{ModuleName = 'PackageManagement'; RequiredVersion = '1.0.0.1'}
109
+ Get-module -ListAvailable -FullyQualifiedName @{ModuleName = 'PowerShellGet'; RequiredVersion = '1.0.0.1'}
110
+ }
80
111
 
81
- really_wrap_shell_code(install_command_script)
112
+ if ($#{config[:remove_builtin_pester]}) {
113
+ Get-module -ListAvailable -FullyQualifiedName @{ModuleName = 'Pester'; RequiredVersion = '3.4.0'}
114
+ }
115
+ )
116
+
117
+ if ($modulesToRemove.ModuleBase.Count -eq 0) {
118
+ # for PS7 on linux
119
+ return
120
+ }
121
+
122
+ $modulesToRemove.ModuleBase | Foreach-Object {
123
+ $ModuleBaseLeaf = Split-Path -Path $_ -Leaf
124
+ if ($ModuleBaseLeaf -as [System.version]) {
125
+ Remove-Item -force -Recurse (Split-Path -Parent -Path $_) -ErrorAction SilentlyContinue
126
+ }
127
+ else {
128
+ Remove-Item -force -Recurse $_ -ErrorAction SilentlyContinue
129
+ }
130
+ }
131
+ PS1
132
+ really_wrap_shell_code(Util.outdent!(install_command_string))
82
133
  end
83
134
 
84
135
  # Generates a command string which will perform any data initialization
@@ -97,7 +148,10 @@ module Kitchen
97
148
  # required, then `nil` will be returned.
98
149
  #
99
150
  # @return [String] a command string
100
- def prepare_command; end
151
+ def prepare_command
152
+ info("Preparing the SUT and Pester dependencies...")
153
+ really_wrap_shell_code(install_command_script)
154
+ end
101
155
 
102
156
  # Generates a command string which will invoke the main verifier
103
157
  # command on the prepared instance. If no work is required, then `nil`
@@ -105,8 +159,6 @@ module Kitchen
105
159
  #
106
160
  # @return [String] a command string
107
161
  def run_command
108
- return if local_suite_files.empty?
109
-
110
162
  really_wrap_shell_code(run_command_script)
111
163
  end
112
164
 
@@ -116,7 +168,7 @@ module Kitchen
116
168
  def call(state)
117
169
  super
118
170
  ensure
119
- download_test_files(state)
171
+ download_test_files(state) unless config[:download].nil?
120
172
  end
121
173
  else
122
174
  def call(state)
@@ -124,7 +176,7 @@ module Kitchen
124
176
  rescue
125
177
  # If the verifier reports failure, we need to download the files ourselves.
126
178
  # Test Kitchen's base verifier doesn't have the download in an `ensure` block.
127
- download_test_files(state)
179
+ download_test_files(state) unless config[:download].nil?
128
180
 
129
181
  # Rethrow original exception, we still want to register the failure.
130
182
  raise
@@ -133,8 +185,8 @@ module Kitchen
133
185
 
134
186
  # private
135
187
  def run_command_script
136
- <<-CMD
137
- Import-Module -Name Pester -Force
188
+ <<-PS1
189
+ Import-Module -Name Pester -Force -ErrorAction Stop
138
190
 
139
191
  $TestPath = Join-Path "#{config[:root_path]}" -ChildPath "suites"
140
192
  $OutputFilePath = Join-Path "#{config[:root_path]}" -ChildPath 'PesterTestResults.xml'
@@ -148,148 +200,184 @@ module Kitchen
148
200
  $host.SetShouldExit($LASTEXITCODE)
149
201
 
150
202
  exit $LASTEXITCODE
151
- CMD
203
+ PS1
204
+ end
205
+
206
+ def get_powershell_modules_from_nugetapi
207
+ # don't return anything is the modules subkey or bootstrap is null
208
+ return if config.dig(:bootstrap, :modules).nil?
209
+
210
+ bootstrap = config[:bootstrap]
211
+ # if the repository url is set, use that as parameter to Install-ModuleFromNuget. Default is the PSGallery url
212
+ gallery_url_param = bootstrap[:repository_url] ? "-GalleryUrl '#{bootstrap[:repository_url]}'" : ""
213
+
214
+ info("Bootstrapping environment without PowerShellGet Provider...")
215
+ Array(bootstrap[:modules]).map do |powershell_module|
216
+ if powershell_module.is_a? Hash
217
+ <<-PS1
218
+ ${#{powershell_module[:Name]}} = #{ps_hash(powershell_module)}
219
+ Install-ModuleFromNuget -Module ${#{powershell_module[:Name]}} #{gallery_url_param}
220
+ PS1
221
+ else
222
+ <<-PS1
223
+ Install-ModuleFromNuget -Module @{Name = '#{powershell_module}'} #{gallery_url_param}
224
+ PS1
225
+ end
226
+ end
227
+ end
228
+
229
+ # Returns the string command to set a PS Repository
230
+ # for each PSRepo configured.
231
+ #
232
+ # @return [Array<String>] array of suite files
233
+ # @api private
234
+ def register_psrepository
235
+ return if config[:register_repository].nil?
236
+
237
+ info("Registering a new PowerShellGet Repository")
238
+ Array(config[:register_repository]).map do |psrepo|
239
+ # Using Set-PSRepo from ../../*/*/*/PesterUtil.psm1
240
+ debug("Command to set PSRepo #{psrepo[:Name]}.")
241
+ <<-PS1
242
+ Write-Host 'Registering psrepo #{psrepo[:Name]}...'
243
+ ${#{psrepo[:Name]}} = #{ps_hash(psrepo)}
244
+ Set-PSRepo -Repository ${#{psrepo[:Name]}}
245
+ PS1
246
+ end
247
+ end
248
+
249
+ # Returns the string command set the PSGallery as trusted, and
250
+ # Install Pester from gallery based on the params from Pester_install_params config
251
+ #
252
+ # @return <String> command to install Pester Module
253
+ # @api private
254
+ def install_pester
255
+ return if config[:skip_pester_install]
256
+
257
+ pester_install_params = config[:pester_install] || {}
258
+ <<-PS1
259
+ if ((Get-PSRepository -Name PSGallery).InstallationPolicy -ne 'Trusted') {
260
+ Write-Host -Object "Trusting the PSGallery to install Pester without -Force"
261
+ Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -ErrorAction SilentlyContinue
262
+ }
263
+
264
+ Write-Host "Installing Pester..."
265
+ $installPesterParams = #{ps_hash(pester_install_params)}
266
+ $installPesterParams['Name'] = 'Pester'
267
+ Install-module @installPesterParams
268
+ Write-Host 'Pester Installed.'
269
+ PS1
270
+ end
271
+
272
+ # returns a piece of PS scriptblock for each Module to install
273
+ # from gallery that has been sepcified in install_modules config.
274
+ #
275
+ # @return [Array<String>] array of PS commands.
276
+ # @api private
277
+ def install_modules_from_gallery
278
+ return if config[:install_modules].nil?
279
+
280
+ Array(config[:install_modules]).map do |powershell_module|
281
+ if powershell_module.is_a? Hash
282
+ # Sanitize variable name so that $powershell-yaml becomes $powershell_yaml
283
+ module_name = powershell_module[:Name].gsub(/[\W]/, "_")
284
+ # so we can splat that variable to install module
285
+ <<-PS1
286
+ $#{module_name} = #{ps_hash(powershell_module)}
287
+ Write-Host -NoNewline 'Installing #{module_name}'
288
+ Install-Module @#{module_name}
289
+ Write-host '... done.'
290
+ PS1
291
+ else
292
+ <<-PS1
293
+ Write-host -NoNewline 'Installing #{powershell_module} ...'
294
+ Install-Module -Name '#{powershell_module}'
295
+ Write-host '... done.'
296
+ PS1
297
+ end
298
+ end
152
299
  end
153
300
 
154
301
  def really_wrap_shell_code(code)
302
+ windows_os? ? really_wrap_windows_shell_code(code) : really_wrap_posix_shell_code(code)
303
+ end
304
+
305
+ def really_wrap_windows_shell_code(code)
155
306
  wrap_shell_code(Util.outdent!(use_local_powershell_modules(code)))
156
307
  end
157
308
 
309
+ # Writing the command to a ps1 file, adding the pwsh shebang
310
+ # invoke the file
311
+ def really_wrap_posix_shell_code(code)
312
+ if config[:sudo]
313
+ pwsh_cmd = "sudo pwsh"
314
+ else
315
+ pwsh_cmd = "pwsh"
316
+ end
317
+
318
+ my_command = <<-BASH
319
+ echo "Running as '$(whoami)'"
320
+ # Send the bash heredoc 'EOF' to the file current.ps1 using the tool cat
321
+ cat << 'EOF' > current.ps1
322
+ #!/usr/bin/env pwsh
323
+ #{Util.outdent!(use_local_powershell_modules(code))}
324
+ EOF
325
+ # create the modules folder, making sure it's done as current user (not root)
326
+ mkdir -p foo #{config[:root_path]}/modules
327
+ # Invoke the created current.ps1 file using pwsh
328
+ #{pwsh_cmd} -f current.ps1
329
+ BASH
330
+
331
+ debug(Util.outdent!(my_command))
332
+ Util.outdent!(my_command)
333
+ end
334
+
158
335
  def use_local_powershell_modules(script)
159
- <<-EOH
336
+ <<-PS1
160
337
  try {
338
+ if (!$IsLinux -and !$IsMacOs) {
161
339
  Set-ExecutionPolicy Unrestricted -force
340
+ }
162
341
  }
163
342
  catch {
164
343
  $_ | Out-String | Write-Warning
165
344
  }
166
345
 
167
346
  $global:ProgressPreference = 'SilentlyContinue'
168
- $env:PSModulePath = "$(Join-Path "#{config[:root_path]}" -ChildPath 'modules');$env:PSModulePath"
347
+ $PSModPathToPrepend = Join-Path "#{config[:root_path]}" -ChildPath 'modules'
348
+ Write-Verbose "Adding '$PSModPathToPrepend' to `$Env:PSModulePath."
349
+ if (!$isLinux -and -not (Test-Path -Path $PSModPathToPrepend)) {
350
+ # if you create this folder now un Linux, it will run as root (via sudo).
351
+ $null = New-Item -Path $PSModPathToPrepend -Force -ItemType Directory
352
+ }
353
+
354
+ if ($Env:PSModulePath.Split([io.path]::PathSeparator) -notcontains $PSModPathToPrepend) {
355
+ $env:PSModulePath = @($PSModPathToPrepend, $env:PSModulePath) -Join [io.path]::PathSeparator
356
+ }
169
357
 
170
358
  #{script}
171
- EOH
359
+ PS1
172
360
  end
173
361
 
174
362
  def install_command_script
175
- <<-EOH
176
- [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
177
-
178
- function Confirm-Directory {
179
- [CmdletBinding()]
180
- param($Path)
181
-
182
- $Item = if (Test-Path $Path) {
183
- Get-Item -Path $Path
184
- }
185
- else {
186
- New-Item -Path $Path -ItemType Directory
187
- }
188
-
189
- $Item.FullName
190
- }
191
-
192
- function Test-Module {
193
- [CmdletBinding()]
194
- param($Name)
195
-
196
- @(Get-Module -Name $Name -ListAvailable -ErrorAction SilentlyContinue).Count -gt 0
197
- }
363
+ <<-PS1
364
+ $PSModPathToPrepend = "#{config[:root_path]}"
198
365
 
199
- $VerifierModulePath = Confirm-Directory -Path (Join-Path #{config[:root_path]} -ChildPath 'modules')
200
- $VerifierDownloadPath = Confirm-Directory -Path (Join-Path #{config[:root_path]} -ChildPath 'pester')
366
+ Import-Module -ErrorAction Stop PesterUtil
201
367
 
202
- $env:PSModulePath = "$VerifierModulePath;$PSModulePath"
368
+ #{get_powershell_modules_from_nugetapi.join("\n") unless config.dig(:bootstrap, :modules).nil?}
203
369
 
204
- if (-not (Test-Module -Name Pester)) {
205
- if (Test-Module -Name PowerShellGet) {
206
- Import-Module PowerShellGet -Force
207
- Import-Module PackageManagement -Force
370
+ #{register_psrepository.join("\n") unless config[:register_repository].nil?}
208
371
 
209
- Get-PackageProvider -Name NuGet -Force > $null
372
+ #{install_pester}
210
373
 
211
- Install-Module Pester -Force
212
- }
213
- else {
214
- if (-not (Test-Module -Name PsGet)){
215
- $webClient = New-Object -TypeName System.Net.WebClient
216
-
217
- if ($env:HTTP_PROXY){
218
- if ($env:NO_PROXY){
219
- Write-Host "Creating WebProxy with 'HTTP_PROXY' and 'NO_PROXY' environment variables.
220
- $webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY, $true, $env:NO_PROXY
221
- }
222
- else {
223
- Write-Host "Creating WebProxy with 'HTTP_PROXY' environment variable.
224
- $webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY
225
- }
226
-
227
- $webClient.Proxy = $webproxy
228
- }
229
-
230
- Invoke-Expression -Command $webClient.DownloadString('http://bit.ly/GetPsGet')
231
- }
232
-
233
- try {
234
- # If the module isn't already loaded, ensure we can import it.
235
- if (-not (Get-Module -Name PsGet -ErrorAction SilentlyContinue)) {
236
- Import-Module -Name PsGet -Force -ErrorAction Stop
237
- }
238
-
239
- Install-Module -Name Pester -Force
240
- }
241
- catch {
242
- Write-Host "Installing from Github"
243
-
244
- $zipFile = Join-Path (Get-Item -Path $VerifierDownloadPath).FullName -ChildPath "pester.zip"
245
-
246
- if (-not (Test-Path $zipfile)) {
247
- $source = 'https://github.com/pester/Pester/archive/4.10.1.zip'
248
- $webClient = New-Object -TypeName Net.WebClient
249
-
250
- if ($env:HTTP_PROXY) {
251
- if ($env:NO_PROXY) {
252
- Write-Host "Creating WebProxy with 'HTTP_PROXY' and 'NO_PROXY' environment variables."
253
- $webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY, $true, $env:NO_PROXY
254
- }
255
- else {
256
- Write-Host "Creating WebProxy with 'HTTP_PROXY' environment variable."
257
- $webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY
258
- }
259
-
260
- $webClient.Proxy = $webproxy
261
- }
262
-
263
- [IO.File]::WriteAllBytes($zipfile, $webClient.DownloadData($source))
264
-
265
- [GC]::Collect()
266
- Write-Host "Downloaded Pester.zip"
267
- }
268
-
269
- Write-Host "Creating Shell.Application COM object"
270
- $shellcom = New-Object -ComObject Shell.Application
271
-
272
- Write-Host "Creating COM object for zip file."
273
- $zipcomobject = $shellcom.Namespace($zipfile)
274
-
275
- Write-Host "Creating COM object for module destination."
276
- $destination = $shellcom.Namespace($VerifierModulePath)
277
-
278
- Write-Host "Unpacking zip file."
279
- $destination.CopyHere($zipcomobject.Items(), 0x610)
280
-
281
- Rename-Item -Path (Join-Path $VerifierModulePath -ChildPath "Pester-4.10.1") -NewName 'Pester' -Force
282
- }
283
- }
284
- }
285
-
286
- if (-not (Test-Module Pester)) {
287
- throw "Unable to install Pester. Please include Pester in your base image or install during your converge."
288
- }
289
- EOH
374
+ #{install_modules_from_gallery.join("\n") unless config[:install_modules].nil?}
375
+ PS1
290
376
  end
291
377
 
292
378
  def restart_winrm_service
379
+ return unless verifier.windows_os?
380
+
293
381
  cmd = "schtasks /Create /TN restart_winrm /TR " \
294
382
  '"powershell -Command Restart-Service winrm" ' \
295
383
  "/SC ONCE /ST 00:00 "
@@ -301,8 +389,9 @@ module Kitchen
301
389
  end
302
390
 
303
391
  def download_test_files(state)
304
- info("Downloading test result files from #{instance.to_str}")
392
+ return if config[:downloads].nil?
305
393
 
394
+ info("Downloading test result files from #{instance.to_str}")
306
395
  instance.transport.connection(state) do |conn|
307
396
  config[:downloads].to_h.each do |remotes, local|
308
397
  debug("Downloading #{Array(remotes).join(", ")} to #{local}")
@@ -319,29 +408,25 @@ module Kitchen
319
408
  #
320
409
  # @return [Array<String>] array of suite files
321
410
  # @api private
322
-
323
411
  def suite_test_folder
324
412
  @suite_test_folder ||= File.join(test_folder, config[:suite_name])
325
413
  end
326
414
 
327
- def suite_level_glob
328
- Dir.glob(File.join(suite_test_folder, "*"))
329
- end
330
-
331
- def suite_verifier_level_glob
332
- Dir.glob(File.join(suite_test_folder, "*/**/*"))
333
- end
334
-
335
- def local_suite_files
336
- suite = suite_level_glob
337
- suite_verifier = suite_verifier_level_glob
338
- (suite << suite_verifier).flatten!.reject do |f|
339
- File.directory?(f)
340
- end
415
+ # Returns the current file's parent folder's full path.
416
+ #
417
+ # @return [string]
418
+ # @api private
419
+ def script_root
420
+ @script_root ||= File.dirname(__FILE__)
341
421
  end
342
422
 
343
- def sandboxify_path(path)
344
- File.join(sandbox_path, "suites", path.sub(%r{#{suite_test_folder}/}i, ""))
423
+ # Returns the absolute path of the Supporting PS module to
424
+ # be copied to the SUT via the Sandbox.
425
+ #
426
+ # @return [string]
427
+ # @api private
428
+ def support_psmodule_folder
429
+ @support_psmodule_folder ||= Pathname.new(File.join(script_root, "../../support/modules/PesterUtil")).cleanpath
345
430
  end
346
431
 
347
432
  # Returns an Array of common helper filenames currently residing on the
@@ -369,50 +454,128 @@ module Kitchen
369
454
  end
370
455
  end
371
456
 
372
- # Copies all test suite files into the suites directory in the sandbox.
457
+ # Creates a PowerShell hashtable from a ruby map.
458
+ # The only types supported for now are hash, array, string and Boolean.
373
459
  #
374
460
  # @api private
375
- def prepare_pester_tests
376
- info("Preparing to copy files from #{suite_test_folder} to the SUT.")
461
+ def ps_hash(obj, depth = 0)
462
+ if [true, false].include? obj
463
+ %{$#{obj}} # Return $true or $false when value is a bool
464
+ elsif obj.is_a?(Hash)
465
+ obj.map do |k, v|
466
+ # Format "Key = Value" enabling recursion
467
+ %{#{pad(depth + 2)}#{ps_hash(k)} = #{ps_hash(v, depth + 2)}}
468
+ end
469
+ .join("\n") # append \n to the key/value definitions
470
+ .insert(0, "@{\n") # prepend @{\n
471
+ .insert(-1, "\n#{pad(depth)}}\n") # append \n}\n
472
+
473
+ elsif obj.is_a?(Array)
474
+ array_string = obj.map { |v| ps_hash(v, depth + 4) }.join(",")
475
+ "#{pad(depth)}@(\n#{array_string}\n)"
476
+ else
477
+ # When the object is not a string nor a hash or array, it will be quoted as a string.
478
+ # In most cases, PS is smart enough to convert back to the type it needs.
479
+ "'" + obj.to_s + "'"
480
+ end
481
+ end
377
482
 
378
- local_suite_files.each do |src|
379
- dest = sandboxify_path(src)
380
- debug("Copying #{src} to #{dest}")
381
- FileUtils.mkdir_p(File.dirname(dest))
382
- FileUtils.cp(src, dest, preserve: true)
483
+ # returns the path of the modules subfolder
484
+ # in the sandbox, where PS Modules and folders will be copied to.
485
+ #
486
+ # @api private
487
+ def sandbox_module_path
488
+ File.join(sandbox_path, "modules")
489
+ end
490
+
491
+ # copy files into the 'modules' folder of the sandbox,
492
+ # so that copied folders can be discovered with the updated $Env:PSModulePath.
493
+ #
494
+ # @api private
495
+ def prepare_copy_folders
496
+ return if config[:copy_folders].nil?
497
+
498
+ info("Preparing to copy specified folders to #{sandbox_module_path}.")
499
+ kitchen_root_path = config[:kitchen_root]
500
+ config[:copy_folders].each do |folder|
501
+ debug("copying #{folder}")
502
+ folder_to_copy = File.join(kitchen_root_path, folder)
503
+ copy_if_src_exists(folder_to_copy, sandbox_module_path)
383
504
  end
384
505
  end
385
506
 
386
- def prepare_powershell_module(name)
387
- FileUtils.mkdir_p(File.join(sandbox_path, "modules/#{name}"))
388
- FileUtils.cp(
389
- File.join(File.dirname(__FILE__), "../../support/powershell/#{name}/#{name}.psm1"),
390
- File.join(sandbox_path, "modules/#{name}/#{name}.psm1"),
391
- preserve: true
392
- )
507
+ # returns an array of string
508
+ # Creates a flat list of files contained in a folder.
509
+ # This is useful when trying to debug what has been copied to
510
+ # the sandbox.
511
+ #
512
+ # @return [Array<String>] array of files in a folder
513
+ # @api private
514
+ def list_files(path)
515
+ base_directory_content = Dir.glob(File.join(path, "*"))
516
+ nested_directory_content = Dir.glob(File.join(path, "*/**/*"))
517
+ [base_directory_content, nested_directory_content].flatten
518
+ end
519
+
520
+ # Copies all test suite files into the suites directory in the sandbox.
521
+ #
522
+ # @api private
523
+ def prepare_pester_tests
524
+ info("Preparing to copy files from '#{suite_test_folder}' to the SUT.")
525
+ sandboxed_suites_path = File.join(sandbox_path, "suites")
526
+ copy_if_src_exists(suite_test_folder, sandboxed_suites_path)
527
+ end
528
+
529
+ def prepare_supporting_psmodules
530
+ debug("Preparing to copy files from '#{support_psmodule_folder}' to the SUT.")
531
+ sandbox_module_path = File.join(sandbox_path, "modules")
532
+ copy_if_src_exists(support_psmodule_folder, sandbox_module_path)
393
533
  end
394
534
 
395
- def prepare_powershell_modules
396
- info("Preparing to copy supporting powershell modules.")
397
- %w{PesterUtil}.each do |module_name|
398
- prepare_powershell_module module_name
535
+ # Copies a folder recursively preserving its layers,
536
+ # mostly used to copy to the sandbox.
537
+ #
538
+ # @api private
539
+ def copy_if_src_exists(src_to_validate, destination)
540
+ unless Dir.exist?(src_to_validate)
541
+ info("The path #{src_to_validate} was not found. Not copying to #{destination}.")
542
+ return
399
543
  end
544
+
545
+ debug("Moving #{src_to_validate} to #{destination}")
546
+ unless Dir.exist?(destination)
547
+ FileUtils.mkdir_p(destination)
548
+ debug("Folder '#{destination}' created.")
549
+ end
550
+ FileUtils.mkdir_p(File.join(destination, "__bugfix"))
551
+ FileUtils.cp_r(src_to_validate, destination, preserve: true)
400
552
  end
401
553
 
554
+ # returns the absolute path of the folders containing the
555
+ # test suites, use default if not set.
556
+ #
557
+ # @api private
402
558
  def test_folder
403
- return config[:test_base_path] if config[:test_folder].nil?
404
-
405
- absolute_test_folder
559
+ config[:test_folder].nil? ? config[:test_base_path] : absolute_test_folder
406
560
  end
407
561
 
562
+ # returns the absolute path of the relative folders containing the
563
+ # test suites, use default i not set.
564
+ #
565
+ # @api private
408
566
  def absolute_test_folder
409
567
  path = (Pathname.new config[:test_folder]).realpath
410
568
  integration_path = File.join(path, "integration")
411
- return path unless Dir.exist?(integration_path)
412
-
413
- integration_path
569
+ Dir.exist?(integration_path) ? integration_path : path
414
570
  end
415
571
 
572
+ # returns a string of space of the specified depth.
573
+ # This is used to pad messages or when building PS hashtables.
574
+ #
575
+ # @api private
576
+ def pad(depth = 0)
577
+ " " * depth
578
+ end
416
579
  end
417
580
  end
418
581
  end
@@ -1,5 +1,5 @@
1
1
  module Kitchen
2
2
  module Verifier
3
- PESTER_VERSION = "0.12.2".freeze
3
+ PESTER_VERSION = "1.0.0".freeze
4
4
  end
5
5
  end
@@ -0,0 +1,157 @@
1
+ [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
2
+
3
+ function Install-ModuleFromNuget {
4
+ [CmdletBinding()]
5
+ param (
6
+ [hashtable]
7
+ $Module,
8
+
9
+ [string]
10
+ $GalleryUrl = 'https://www.powershellgallery.com/api/v2'
11
+ )
12
+
13
+ $tempPath = [System.IO.Path]::GetTempPath()
14
+ $zipFileName = "{0}.{1}.{2}" -f $Module.Name, $Module.Version, 'zip'
15
+ $downloadedZip = Join-Path -Path $tempPath $zipFileName
16
+ $ModulePath = Join-Path -Path $PSHome -ChildPath 'Modules'
17
+ $ModuleFolder = Join-Path -Path $ModulePath -ChildPath $Module.Name
18
+
19
+ if ((Test-Path $ModuleFolder) -and ($PSVersionTable.PSVersion.Major -lt 5 -or $module.Force)) {
20
+ # Check if available version is correct
21
+ $ModuleManifest = Join-Path -Path $ModuleFolder -ChildPath "$($Module.Name).psd1"
22
+ if ((Test-Path -Path $ModuleManifest) -and -not $Module.Force) {
23
+ # Import-PowerShellDataFile only exists since 5.1
24
+ $ManifestInfo = Import-LocalizedData -BaseDirectory (Split-Path -Parent -Path $ModuleManifest) -FileName $Module.Name
25
+ $ModuleVersionNoPreRelease = $Module.Version -replace '-.*$'
26
+ # Compare the version in manifest with version required without Pre-release
27
+ if ($ManifestInfo.ModuleVersion -eq $ModuleVersionNoPreRelease) {
28
+ Write-Host "Module $($Module.Name) already installed, skipping."
29
+ return
30
+ }
31
+ else {
32
+ Write-Host "Module $($Module.Name) found with version '$($ManifestInfo.ModuleVersion)', expecting '$ModuleVersionNoPreRelease'."
33
+ }
34
+ }
35
+ else {
36
+ # if incorrect, remove it before install
37
+ Remove-Item -Recurse -Force -Path $ModuleFolder
38
+ }
39
+ }
40
+ elseif ($PSVersionTable.PSVersion.Major -gt 5) {
41
+ # skip if the version already exists or if force is enabled
42
+ $ModuleVersionNoPreRelease = $Module.Version -replace '-.*$'
43
+ $ModuleFolder = Join-Path -Path $ModuleFolder -ChildPath $ModuleVersionNoPreRelease
44
+ if (-not $Module.Force -and (Test-Path -Path $ModuleFolder)) {
45
+ Write-Verbose -Message "Module already installed."
46
+ return
47
+ }
48
+ }
49
+
50
+ if (-not (Test-Path $ModuleFolder)) {
51
+ $null = New-Item -Path $ModuleFolder -force -ItemType Directory
52
+ }
53
+
54
+ $urlSuffix = "/package/$($Module.Name)/$($Module.Version)".TrimEnd('/')
55
+ $nupkgUrl = $GalleryUrl.TrimEnd('/') + '/' + $urlSuffix.Trim('/')
56
+ $webclient = New-Object 'system.net.webclient'
57
+
58
+ if ($env:HTTP_PROXY){
59
+ if ($env:NO_PROXY){
60
+ Write-Host "Creating WebProxy with 'HTTP_PROXY' and 'NO_PROXY' environment variables."
61
+ $webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY, $true, $env:NO_PROXY
62
+ }
63
+ else {
64
+ Write-Host "Creating WebProxy with 'HTTP_PROXY' environment variable."
65
+ $webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY
66
+ }
67
+
68
+ $webclient.Proxy = $webproxy
69
+ }
70
+
71
+ Write-Verbose -Message "Downloading Package from $nupkgUrl"
72
+ try {
73
+ if (Test-Path $downloadedZip) {
74
+ Remove-Item -Force -ErrorAction SilentlyContinue -Path $downloadedZip
75
+ }
76
+ $webclient.DownloadFile($nupkgUrl, $downloadedZip)
77
+ }
78
+ catch {
79
+ Write-Error "Error trying to download nupkg '$nupkgUrl' to '$downloadedZip'."
80
+ throw $_
81
+ }
82
+
83
+ if (-not (Test-Path -Path $downloadedZip)) {
84
+ throw "Error trying to download nupkg '$nupkgUrl' to '$downloadedZip'."
85
+ }
86
+
87
+ # Test to see if Expand-Archive is available first
88
+ if (Get-Command Expand-Archive) {
89
+ Expand-Archive -Path $downloadedZip -DestinationPath $ModuleFolder -Force
90
+ }
91
+ else {
92
+ # Fall back to COM object for Shell.Application zip extraction
93
+ Write-Host "Creating COM object for zip file '$downloadedZip'."
94
+ $shellcom = New-Object -ComObject Shell.Application
95
+ $zipcomobject = $shellcom.Namespace($downloadedZip)
96
+ $destination = $shellcom.Namespace($ModuleFolder)
97
+ $destination.CopyHere($zipcomobject.Items(), 0x610)
98
+ Write-Host "Nupkg installed at $ModuleFolder"
99
+ }
100
+
101
+ [GC]::Collect()
102
+ }
103
+
104
+ function Set-PSRepo {
105
+ param(
106
+ [Parameter(Mandatory)]
107
+ $Repository
108
+ )
109
+ if (-not (Get-Command Get-PSRepository) -and (Get-Command Get-PackageSource)) {
110
+ # Old version of PSGet do not have a *-PSrepository but have *-PackageSource instead.
111
+ if (Get-PackageSource -Name $Repository.Name) {
112
+ Set-PackageSource @Repository
113
+ }
114
+ else {
115
+ Register-PackageSource @Repository
116
+ }
117
+ }
118
+ elseif (Get-Command Get-PSRepository) {
119
+ if (Get-PSRepository -Name $Repository.Name -ErrorAction SilentlyContinue) {
120
+ # The repo exists, we should use Set-PSRepository and splat parameters
121
+ Set-PSRepository @Repository
122
+ }
123
+ else {
124
+ # The repo does not exist, use Register-PSRepository and splat
125
+ Register-PSRepository @Repository
126
+ }
127
+ }
128
+ else {
129
+ throw "Cannot Set PS Repository, command Set or Register for PSRepository or PackageSource not found."
130
+ }
131
+ }
132
+
133
+ function ConvertFrom-PesterOutputObject {
134
+ param (
135
+ [parameter(ValueFromPipeline=$true)]
136
+ [object]
137
+ $InputObject
138
+ )
139
+ begin {
140
+ $PesterModule = Import-Module Pester -Passthru
141
+ }
142
+ process {
143
+ $DescribeGroup = $InputObject.testresult | Group-Object Describe
144
+ foreach ($DescribeBlock in $DescribeGroup) {
145
+ $PesterModule.Invoke({Write-Screen $args[0]}, "Describing $($DescribeBlock.Name)")
146
+ $ContextGroup = $DescribeBlock.group | Group-Object Context
147
+ foreach ($ContextBlock in $ContextGroup) {
148
+ $PesterModule.Invoke({Write-Screen $args[0]}, "`tContext $($subheader.name)")
149
+ foreach ($TestResult in $ContextBlock.group) {
150
+ $PesterModule.Invoke({Write-PesterResult $args[0]}, $TestResult)
151
+ }
152
+ }
153
+ }
154
+
155
+ $PesterModule.Invoke({Write-PesterReport $args[0]}, $InputObject)
156
+ }
157
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-pester
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Murawski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-13 00:00:00.000000000 Z
11
+ date: 2020-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -105,7 +105,7 @@ files:
105
105
  - kitchen-pester.gemspec
106
106
  - lib/kitchen/verifier/pester.rb
107
107
  - lib/kitchen/verifier/pester_version.rb
108
- - lib/support/powershell/PesterUtil/PesterUtil.psm1
108
+ - lib/support/modules/PesterUtil/PesterUtil.psm1
109
109
  homepage: https://github.com/test-kitchen/kitchen-pester
110
110
  licenses:
111
111
  - Apache-2.0
@@ -1,24 +0,0 @@
1
- function ConvertFrom-PesterOutputObject {
2
- param (
3
- [parameter(ValueFromPipeline=$true)]
4
- [object]
5
- $InputObject
6
- )
7
- begin {
8
- $PesterModule = Import-Module Pester -Passthru
9
- }
10
- process {
11
- $DescribeGroup = $InputObject.testresult | Group-Object Describe
12
- foreach ($DescribeBlock in $DescribeGroup) {
13
- $PesterModule.Invoke({Write-Screen $args[0]}, "Describing $($DescribeBlock.Name)")
14
- $ContextGroup = $DescribeBlock.group | Group-Object Context
15
- foreach ($ContextBlock in $ContextGroup) {
16
- $PesterModule.Invoke({Write-Screen $args[0]}, "`tContext $($subheader.name)")
17
- foreach ($TestResult in $ContextBlock.group) {
18
- $PesterModule.Invoke({Write-PesterResult $args[0]}, $TestResult)
19
- }
20
- }
21
- }
22
- $PesterModule.Invoke({Write-PesterReport $args[0]}, $InputObject)
23
- }
24
- }