Tests/TestHelpers/CommonTestHelper.psm1
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] param () $errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' <# Cache the AppVeyor Administrator credential so that we do not reset the password multiple times if retrieved the credential is requested multiple times. #> $script:appVeyorAdministratorCredential = $null <# .SYNOPSIS Tests that the Get-TargetResource method of a DSC Resource is not null, can be converted to a hashtable, and has the correct properties. Uses Pester. .PARAMETER GetTargetResourceResult The result of the Get-TargetResource method. .PARAMETER GetTargetResourceResultProperties The properties that the result of Get-TargetResource should have. #> function Test-GetTargetResourceResult { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNull()] [Hashtable] $GetTargetResourceResult, [String[]] $GetTargetResourceResultProperties ) foreach ($property in $GetTargetResourceResultProperties) { $GetTargetResourceResult[$property] | Should Not Be $null } } <# .SYNOPSIS Tests if a scope represents the current machine. .PARAMETER Scope The scope to test. #> function Test-IsLocalMachine { [OutputType([Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $Scope ) if ($scope -eq '.') { return $true } if ($scope -eq $env:COMPUTERNAME) { return $true } if ($scope -eq 'localhost') { return $true } if ($scope.Contains('.')) { if ($scope -eq '127.0.0.1') { return $true } <# Determine if we have an ip address that matches an ip address on one of the network adapters. NOTE: This is likely overkill; consider removing it. #> $networkAdapters = @(Get-CimInstance Win32_NetworkAdapterConfiguration) foreach ($networkAdapter in $networkAdapters) { if ($null -ne $networkAdapter.IPAddress) { foreach ($address in $networkAdapter.IPAddress) { if ($address -eq $scope) { return $true } } } } } return $false } <# .SYNOPSIS Waits a certain amount of time for a script block to return true. Return $true if completed successfully in the given amount of time, $false otherwise. .PARAMETER ScriptBlock The ScriptBlock to wait for. .PARAMETER TimeoutSeconds The number of seconds to wait for the ScriptBlock to return $true. Default value is 5. #> function Wait-ScriptBlockReturnTrue { [OutputType([Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ScriptBlock] $ScriptBlock, [Int] $TimeoutSeconds = 5 ) $startTime = [DateTime]::Now $invokeScriptBlockResult = $false while (-not $invokeScriptBlockResult -and (([DateTime]::Now - $startTime).TotalSeconds -lt $TimeoutSeconds)) { $invokeScriptBlockResult = $ScriptBlock.Invoke() Start-Sleep -Seconds 1 } return $invokeScriptBlockResult } <# .SYNOPSIS Tests if a file is currently locked. .PARAMETER Path The path to the file to test. #> function Test-IsFileLocked { [OutputType([Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $Path ) if (-not (Test-Path $Path)) { return $false } try { $content = Get-Content -Path $Path return $false } catch { return $true } } <# .SYNOPSIS Tests that calling the Set-TargetResource cmdlet with the WhatIf parameter specified produces output that contains all the given expected output. Uses Pester. .PARAMETER Parameters The parameters to pass to Set-TargetResource. These parameters do not need to contain the WhatIf parameter, but if they do, this function will run Set-TargetResource with WhatIf = $true no matter what is in the Parameters Hashtable. .PARAMETER ExpectedOutput The output expected to be in the output from running WhatIf with the Set-TargetResource cmdlet. If this parameter is empty or null, this cmdlet will check that there was no output from Set-TargetResource with WhatIf specified. #> function Test-SetTargetResourceWithWhatIf { [OutputType([Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [Hashtable] $Parameters, [String[]] $ExpectedOutput ) $transcriptPath = Join-Path -Path (Get-Location) -ChildPath 'WhatIfTestTranscript.txt' if (Test-Path -Path $transcriptPath) { Wait-ScriptBlockReturnTrue -ScriptBlock {-not (Test-IsFileLocked -Path $transcriptPath)} -TimeoutSeconds 10 Remove-Item -Path $transcriptPath -Force } $Parameters['WhatIf'] = $true try { Wait-ScriptBlockReturnTrue -ScriptBlock {-not (Test-IsFileLocked -Path $transcriptPath)} Start-Transcript -Path $transcriptPath Set-TargetResource @Parameters Stop-Transcript Wait-ScriptBlockReturnTrue -ScriptBlock {-not (Test-IsFileLocked -Path $transcriptPath)} $transcriptContent = Get-Content -Path $transcriptPath -Raw $transcriptContent | Should Not Be $null $regexString = '\*+[^\*]*\*+' # Removing transcript diagnostic logging at top and bottom of file $selectedString = Select-String -InputObject $transcriptContent ` -Pattern $regexString ` -AllMatches foreach ($match in $selectedString.Matches) { $transcriptContent = $transcriptContent.Replace($match.Captures, '') } $transcriptContent = $transcriptContent.Replace("`r`n", '').Replace("`n", '') if ($null -eq $ExpectedOutput -or $ExpectedOutput.Count -eq 0) { [String]::IsNullOrEmpty($transcriptContent) | Should Be $true } else { foreach ($expectedOutputPiece in $ExpectedOutput) { $transcriptContent.Contains($expectedOutputPiece) | Should Be $true } } } finally { if (Test-Path -Path $transcriptPath) { Wait-ScriptBlockReturnTrue -ScriptBlock {-not (Test-IsFileLocked -Path $transcriptPath)} ` -TimeoutSeconds 10 Remove-Item -Path $transcriptPath -Force } } } <# .SYNOPSIS Retrieves the administrator credential on an AppVeyor machine. The password will be reset so that we know what the password is. .NOTES The AppVeyor credential will be cached after the first call to this function so that the password is not reset if this function is called again. #> function Get-AppVeyorAdministratorCredential { [OutputType([System.Management.Automation.PSCredential])] [CmdletBinding()] param () if ($null -eq $script:appVeyorAdministratorCredential) { $password = '' $randomGenerator = New-Object -TypeName 'System.Random' $passwordLength = Get-Random -Minimum 15 -Maximum 126 while ($password.Length -lt $passwordLength) { $password = $password + [Char]$randomGenerator.Next(45, 126) } # Change password $appVeyorAdministratorUsername = 'appveyor' $appVeyorAdministratorUser = [ADSI]("WinNT://$($env:computerName)/$appVeyorAdministratorUsername") $null = $appVeyorAdministratorUser.SetPassword($password) [Microsoft.Win32.Registry]::SetValue('HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon', 'DefaultPassword', $password) $securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force $script:appVeyorAdministratorCredential = New-Object -TypeName 'System.Management.Automation.PSCredential' -ArgumentList @( "$($env:computerName)\$appVeyorAdministratorUsername", $securePassword ) } return $script:appVeyorAdministratorCredential } <# .SYNOPSIS Enters a DSC Resource test environment. .PARAMETER DscResourceModuleName The name of the module that contains the DSC Resource to test. .PARAMETER DscResourceName The name of the DSC resource to test. .PARAMETER TestType Specifies whether the test environment will run a Unit test or an Integration test. #> function Enter-DscResourceTestEnvironment { [OutputType([Hashtable])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $DscResourceModuleName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $DscResourceName, [Parameter(Mandatory = $true)] [ValidateSet('Unit', 'Integration')] [String] $TestType ) $testsFolderPath = Split-Path -Path $PSScriptRoot -Parent $moduleRootPath = Split-Path -Path $testsFolderPath -Parent $dscResourceTestsPath = Join-Path -Path $moduleRootPath -ChildPath 'DSCResource.Tests' $testHelperFilePath = Join-Path -Path $dscResourceTestsPath -ChildPath 'TestHelper.psm1' if (-not (Test-Path -Path $dscResourceTestsPath)) { Push-Location $moduleRootPath git clone 'https://github.com/PowerShell/DscResource.Tests' --quiet Pop-Location } else { $gitInstalled = $null -ne (Get-Command -Name 'git' -ErrorAction 'SilentlyContinue') if ($gitInstalled) { Push-Location $dscResourceTestsPath git pull origin dev --quiet Pop-Location } else { Write-Verbose -Message 'Git not installed. Leaving current DSCResource.Tests as is.' } } Import-Module -Name $testHelperFilePath return Initialize-TestEnvironment ` -DSCModuleName $DscResourceModuleName ` -DSCResourceName $DscResourceName ` -TestType $TestType } <# .SYNOPSIS Exits the specified DSC Resource test environment. .PARAMETER TestEnvironment The test environment to exit. #> function Exit-DscResourceTestEnvironment { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [Hashtable] $TestEnvironment ) $testsFolderPath = Split-Path -Path $PSScriptRoot -Parent $moduleRootPath = Split-Path -Path $testsFolderPath -Parent $dscResourceTestsPath = Join-Path -Path $moduleRootPath -ChildPath 'DSCResource.Tests' $testHelperFilePath = Join-Path -Path $dscResourceTestsPath -ChildPath 'TestHelper.psm1' Import-Module -Name $testHelperFilePath Restore-TestEnvironment -TestEnvironment $TestEnvironment } Export-ModuleMember -Function @( 'Test-GetTargetResourceResult', ` 'Wait-ScriptBlockReturnTrue', ` 'Test-IsFileLocked', ` 'Test-SetTargetResourceWithWhatIf', ` 'Get-AppVeyorAdministratorCredential', ` 'Enter-DscResourceTestEnvironment', ` 'Exit-DscResourceTestEnvironment' ) |