Tests/QA/ExampleFiles.common.v5.Tests.ps1
<#
.NOTES To run manually: $pathToHQRMTests = Join-Path -Path (Get-Module DscResource.Test).ModuleBase -ChildPath 'Tests\QA' $container = New-PesterContainer -Path "$pathToHQRMTests/ExampleFiles.common.*.Tests.ps1" -Data @{ SourcePath = './source' # ExcludeSourceFile = @('MyExample.ps1') } Invoke-Pester -Container $container -Output Detailed #> param ( [Parameter()] [System.String] $SourcePath, [Parameter()] [System.String[]] $ExcludeSourceFile, [Parameter(ValueFromRemainingArguments = $true)] $Args ) # This test _must_ be outside the BeforeDiscovery-block since Pester 4 does not recognizes it. $isPester5 = (Get-Module -Name Pester).Version -ge '5.1.0' # Only run if Pester 5.1. if (-not $isPester5) { Write-Verbose -Message 'Repository is using old Pester version, new HQRM tests for Pester 5 are skipped.' -Verbose return } BeforeDiscovery { # Re-imports the private (and public) functions. Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '../../DscResource.Test.psm1') -Force if (-not $SourcePath) { return } $examplesPath = Join-Path -Path $SourcePath -ChildPath 'Examples' # If there are no Examples folder, exit. if (-not (Test-Path -Path $examplesPath)) { return } $exampleFiles = @(Get-ChildItem -Path $examplesPath -Filter '*.ps1' -Recurse | WhereSourceFileNotExcluded -ExcludeSourceFile $ExcludeSourceFile) $exampleToTest = @() foreach ($exampleFile in $exampleFiles) { $exampleToTest += @{ ExampleFile = $exampleFile ExampleDescriptiveName = Join-Path -Path (Split-Path $exampleFile.Directory -Leaf) -ChildPath (Split-Path $exampleFile -Leaf) } } } BeforeAll { # Re-imports the private (and public) functions. Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '../../DscResource.Test.psm1') -Force } AfterAll { # Re-import just the public functions. Import-Module -Name 'DscResource.Test' -Force } Describe 'Common Tests - Validate Example Files' -Tag 'Common Tests - Validate Example Files' { Context 'When the example ''<ExampleDescriptiveName>'' exist' -ForEach $exampleToTest { It 'Should compile the MOF schema for the example correctly' { { $mockPassword = ConvertTo-SecureString '&iPm%M5q3K$Hhq=wcEK' -AsPlainText -Force $mockCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList @('username', $mockPassword) $mockConfigurationData = @{ AllNodes = @( @{ NodeName = 'localhost' PsDscAllowPlainTextPassword = $true } ) } <# Set this first because it is used in the final block, and must be set otherwise it fails on not being assigned. #> $existingCommandName = $null try { . $ExampleFile.FullName <# Test for either a configuration named 'Example', or parse the name from the filename and try that as the configuration name (requirement for Azure Automation). #> $commandName = @( 'Example', (Get-PublishFileName -Path $ExampleFile.FullName) ) # Get the first one that matches. $existingCommand = Get-ChildItem -Path 'function:' | Where-Object { $_.Name -in $commandName } | Select-Object -First 1 if ($existingCommand) { $existingCommandName = $existingCommand.Name $exampleCommand = Get-Command -Name $existingCommandName -ErrorAction 'SilentlyContinue' if ($exampleCommand) { $exampleParameters = @{} # Remove any common parameters that are available. $commandParameters = $exampleCommand.Parameters.Keys | Where-Object -FilterScript { ($_ -notin [System.Management.Automation.PSCmdlet]::CommonParameters) -and ` ($_ -notin [System.Management.Automation.PSCmdlet]::OptionalCommonParameters) } foreach ($parameterName in $commandParameters) { $parameterType = $exampleCommand.Parameters[$parameterName].ParameterType.FullName <# Each credential parameter in the Example function is assigned the mocked credential. 'PsDscRunAsCredential' is not assigned because that breaks the example. #> if ($parameterName -ne 'PsDscRunAsCredential' ` -and $parameterType -eq 'System.Management.Automation.PSCredential') { $exampleParameters.Add($parameterName, $mockCredential) } else { <# Check for mandatory parameters. Assume the parameters are all in the 'all' parameter set. #> $isParameterMandatory = $exampleCommand.Parameters[$parameterName].ParameterSets['__AllParameterSets'].IsMandatory if ($isParameterMandatory) { <# Convert '1' to the type that the parameter expects. Using '1' since it can be converted to String, Numeric and Boolean. #> $exampleParameters.Add($parameterName, ('1' -as $parameterType)) } } } <# If there is a $ConfigurationData variable that was dot-sourced then use that as the configuration data instead of the mocked configuration data. #> if (Get-Item -Path variable:ConfigurationData -ErrorAction 'SilentlyContinue') { $mockConfigurationData = $ConfigurationData } & $exampleCommand.Name @exampleParameters -ConfigurationData $mockConfigurationData -OutputPath 'TestDrive:\' -ErrorAction 'Continue' -WarningAction 'SilentlyContinue' | Out-Null } } else { throw ('The example ''{0}'' does not contain a configuration named ''{1}''.' -f $exampleDescriptiveName, ($commandName -join "', or '")) } } finally { <# Remove the function we dot-sourced so next example file doesn't use the previous Example-function. Using recurse since it saw child functions when copied in helper functions during debugging, it resulted in an interactive prompt. #> Remove-Item -Path "function:$existingCommandName" -ErrorAction 'SilentlyContinue' -Recurse -Force <# Remove the variable $ConfigurationData if it existed in the file we dot-sourced so next example file doesn't use the previous examples configuration. #> Remove-Item -Path 'variable:ConfigurationData' -ErrorAction 'SilentlyContinue' } } | Should -Not -Throw } } } |