LibreDevOpsHelpers.Pester/LibreDevOpsHelpers.Pester.psm1
|
Set-StrictMode -Version Latest function Get-LdoCommandResult { # Internal. Runs a command string and returns its captured output and exit code. [CmdletBinding()] [OutputType([pscustomobject])] param([Parameter(Mandatory)][string]$Command) $global:LASTEXITCODE = 0 $output = & ([scriptblock]::Create($Command)) 2>&1 return [pscustomobject]@{ Output = @($output | ForEach-Object { $_.ToString() }) ExitCode = $LASTEXITCODE } } function Test-LdoZeroExitCode { <# .SYNOPSIS Pester operator backing test: asserts a command returns exit code zero. .DESCRIPTION Runs the command and reports success when its exit code is zero. Returns the Succeeded/FailureMessage object expected by Pester custom Should operators. Register it with Register-LdoPesterAssertion to use it as 'Should -ReturnZeroExitCode'. .PARAMETER ActualValue The command string to run. .PARAMETER Negate When set, inverts the result. .PARAMETER Because Reason text, kept for Pester signature parity. .EXAMPLE Test-LdoZeroExitCode -ActualValue 'terraform version' .OUTPUTS PSCustomObject with Succeeded and FailureMessage. #> [Diagnostics.CodeAnalysis.SuppressMessage('PSReviewUnusedParameter', 'Because', Justification = 'Required for Pester Should operator signature parity.')] [CmdletBinding()] [OutputType([pscustomobject])] param( [Parameter(Mandatory)][string]$ActualValue, [switch]$Negate, [string]$Because ) $failureMessage = $null try { $result = Get-LdoCommandResult -Command $ActualValue $succeeded = ($result.ExitCode -eq 0) if ($Negate) { $succeeded = -not $succeeded } if (-not $succeeded) { $indented = $result.Output | ForEach-Object { " $_" } | Out-String $failureMessage = "Command '$ActualValue' returned exit code $($result.ExitCode). Output:`n$indented" } } catch { $succeeded = $false $failureMessage = "Exception thrown while executing '$ActualValue': $($_.Exception.Message)" } [pscustomobject]@{ Succeeded = $succeeded FailureMessage = $failureMessage } } function Test-LdoCommandOutputMatch { <# .SYNOPSIS Pester operator backing test: asserts a command's output matches a regex. .DESCRIPTION Runs the command and reports success when its combined output matches the supplied regular expression (case sensitive). Returns the Succeeded/FailureMessage object expected by Pester custom Should operators. Register it with Register-LdoPesterAssertion to use it as 'Should -MatchCommandOutput'. .PARAMETER ActualValue The command string to run. .PARAMETER RegularExpression Regular expression to match against the command output. .PARAMETER Negate When set, inverts the result. .EXAMPLE Test-LdoCommandOutputMatch -ActualValue 'terraform version' -RegularExpression 'Terraform v' .OUTPUTS PSCustomObject with Succeeded and FailureMessage. #> [CmdletBinding()] [OutputType([pscustomobject])] param( [Parameter(Mandatory)][string]$ActualValue, [Parameter(Mandatory)][string]$RegularExpression, [switch]$Negate ) $failureMessage = $null try { $output = (Get-LdoCommandResult -Command $ActualValue).Output -join "`n" $succeeded = ($output -cmatch $RegularExpression) if ($Negate) { $succeeded = -not $succeeded } if (-not $succeeded) { $notText = if ($Negate) { 'not ' } else { '' } $failureMessage = "Expected '$ActualValue' output to ${notText}match regex '$RegularExpression'." } } catch { $succeeded = $false $failureMessage = "Exception thrown while executing '$ActualValue': $($_.Exception.Message)" } [pscustomobject]@{ Succeeded = $succeeded FailureMessage = $failureMessage } } function Register-LdoPesterAssertion { <# .SYNOPSIS Registers the Libre DevOps custom Pester Should operators. .DESCRIPTION Adds two custom Should operators backed by this module: -ReturnZeroExitCode and -MatchCommandOutput. Existing operators with the same name are left in place. .EXAMPLE Register-LdoPesterAssertion .OUTPUTS None #> [CmdletBinding()] [OutputType([void])] param() if (-not (Get-Module Pester)) { Import-Module Pester -ErrorAction Stop } $existing = Get-ShouldOperator | Select-Object -ExpandProperty Name if ($existing -notcontains 'ReturnZeroExitCode') { Add-ShouldOperator -Name ReturnZeroExitCode -InternalName Test-LdoZeroExitCode -Test ${function:Test-LdoZeroExitCode} Write-LdoLog -Level INFO -Message 'Registered Should operator: ReturnZeroExitCode.' } if ($existing -notcontains 'MatchCommandOutput') { Add-ShouldOperator -Name MatchCommandOutput -InternalName Test-LdoCommandOutputMatch -Test ${function:Test-LdoCommandOutputMatch} Write-LdoLog -Level INFO -Message 'Registered Should operator: MatchCommandOutput.' } } function Invoke-LdoPesterTest { <# .SYNOPSIS Runs a single Pester test file and throws when any test fails. .DESCRIPTION Resolves <TestRoot>/<TestFile>.Tests.ps1, runs it with Pester, and throws when no tests run or any test fails. An optional test name filter can be supplied. .PARAMETER TestFile Test file base name, without the .Tests.ps1 suffix. .PARAMETER TestName Optional full test name filter. .PARAMETER TestRoot Folder containing the test files. Defaults to ./tests under the current directory. .EXAMPLE Invoke-LdoPesterTest -TestFile Logger .OUTPUTS None #> [CmdletBinding()] [OutputType([void])] param( [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$TestFile, [string]$TestName, [string]$TestRoot = (Join-Path (Get-Location).Path 'tests') ) $testPath = Join-Path $TestRoot "$TestFile.Tests.ps1" if (-not (Test-Path $testPath)) { throw "Unable to find test file '$TestFile' at '$testPath'." } Write-LdoLog -Level INFO -Message "Running Pester tests in $testPath" if (-not (Get-Module Pester)) { Import-Module Pester -ErrorAction Stop } $configuration = New-PesterConfiguration $configuration.Run.Path = $testPath $configuration.Run.PassThru = $true $configuration.Output.Verbosity = 'Normal' if ($TestName) { $configuration.Filter.FullName = $TestName } $results = Invoke-Pester -Configuration $configuration if (-not ($results.FailedCount -eq 0 -and $results.TotalCount -gt 0)) { throw 'Test run has failed.' } Write-LdoLog -Level SUCCESS -Message "All $($results.PassedCount) tests passed." } Export-ModuleMember -Function ` Test-LdoZeroExitCode, ` Test-LdoCommandOutputMatch, ` Register-LdoPesterAssertion, ` Invoke-LdoPesterTest |