kitchen-pester 0.12.2 → 1.0.0

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 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
- }