tests/Stubs/QAOps.PowerShell.Stubs.psm1
|
# QAOps.PowerShell stub module for Pester tests. # Provides in-memory stand-ins for the cmdlets the QAFramework library depends on. # The shapes mirror QAOps.PowerShell (QaOpsBridge, QaOpsBridgeExecution, ...) so the library # can be exercised without a cluster. $script:Bridges = [System.Collections.Generic.List[object]]::new() $script:Executions = [System.Collections.Generic.List[object]]::new() $script:TestResults = [System.Collections.Generic.List[object]]::new() $script:Cluster = $null $script:TestRunContext = $null $script:FailoverSwitches = [System.Collections.Generic.List[object]]::new() $script:Outcomes = @{} $script:DefaultOutcome = $null $script:AutoComplete = $true function Reset-QAOpsStubs { [CmdletBinding()] param() $script:Bridges.Clear() $script:Executions.Clear() $script:TestResults.Clear() $script:Cluster = $null $script:TestRunContext = $null $script:FailoverSwitches.Clear() $script:Outcomes = @{} $script:DefaultOutcome = $null $script:AutoComplete = $true } function Add-QAOpsStubBridge { [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$Id, [string]$DisplayName = $Id, [string]$HostName = 'localhost', [bool]$IsOrchestrator = $false, [bool]$IsSelf = $false, [string]$TestPackageContentPath = 'C:\QAOps\Content', [Nullable[int]]$DmaId = $null, [bool]$HasDataMiner = $true, [bool]$IsFailover = $false, [string]$FailoverPartnerBridgeId = $null, [hashtable]$Labels = @{} ) $bridge = [pscustomobject]@{ Id = $Id DisplayName = $DisplayName HostName = $HostName IsOrchestrator = $IsOrchestrator IsSelf = $IsSelf TestPackageContentPath = $TestPackageContentPath DmaId = $DmaId HasDataMiner = $HasDataMiner IsFailover = $IsFailover FailoverPartnerBridgeId = $FailoverPartnerBridgeId Labels = $Labels } $script:Bridges.Add($bridge) return $bridge } function Set-QAOpsStubCluster { [CmdletBinding()] param( [string]$Name = 'TestCluster', [string]$DataMinerVersion = '10.5.0.0', [string]$DbmsType = 'Cassandra', [bool]$IsCentralized = $true, [bool]$IsRedGreen = $false, [hashtable]$Labels = @{}, [hashtable]$SolutionVersions = @{} ) $script:Cluster = [pscustomobject]@{ Name = $Name DataMinerVersion = $DataMinerVersion DbmsType = $DbmsType IsCentralized = $IsCentralized IsRedGreen = $IsRedGreen Labels = $Labels SolutionVersions = $SolutionVersions } } function Set-QAOpsStubTestRunContext { [CmdletBinding()] param( [string]$TestRunId = 'run-1', [bool]$IsOrchestrator = $true, [hashtable]$Labels = @{}, [string]$SupplementaryFilesPath = $null ) $script:TestRunContext = [pscustomobject]@{ TestRunId = $TestRunId IsOrchestrator = $IsOrchestrator Labels = $Labels SupplementaryFilesPath = $SupplementaryFilesPath } } function Set-QAOpsStubExecutionOutcome { <# Controls how an execution ends. Matching is done on a substring of the arguments, which for the QAFramework library is the automation script name. #> [CmdletBinding()] param( [Parameter(Mandatory = $false)][string]$ArgumentsLike, [string]$State = 'Completed', [int]$ExitCode = 0, [string]$StandardOutput = '', [string]$StandardError = '', [string]$ErrorMessage = '', [switch]$StayRunning ) $outcome = [pscustomobject]@{ State = $State ExitCode = $ExitCode StandardOutput = $StandardOutput StandardError = $StandardError ErrorMessage = $ErrorMessage StayRunning = [bool]$StayRunning } if ([string]::IsNullOrEmpty($ArgumentsLike)) { $script:DefaultOutcome = $outcome } else { $script:Outcomes[$ArgumentsLike] = $outcome } } function Get-QAOpsStubExecution { [CmdletBinding()] param() return $script:Executions | ForEach-Object { $_ } } function Get-QAOpsStubTestResult { [CmdletBinding()] param() return $script:TestResults | ForEach-Object { $_ } } function Get-QAOpsStubFailoverSwitch { [CmdletBinding()] param() return $script:FailoverSwitches | ForEach-Object { $_ } } function Complete-QAOpsStubExecution { <# Completes executions that were configured to stay running, so a test can simulate a scheduler tick where nothing finished yet. #> [CmdletBinding()] param([string]$Id) foreach ($execution in $script:Executions) { if ($Id -and $execution.Id -ne $Id) { continue } if ($execution.IsFinished) { continue } $execution.State = 'Completed' $execution.IsFinished = $true $execution.EndTime = [DateTime]::UtcNow } } function Get-QAOpsBridge { [CmdletBinding()] param( [Parameter(Position = 0, Mandatory = $false)][string]$Id, [Parameter(Mandatory = $false)][switch]$ExcludeSelf, [Parameter(Mandatory = $false)][string]$ApiUrl ) $result = $script:Bridges | ForEach-Object { $_ } if ($PSBoundParameters.ContainsKey('Id')) { $result = $result | Where-Object { $_.Id -eq $Id } } if ($ExcludeSelf) { $result = $result | Where-Object { -not $_.IsSelf } } return $result } function Start-QAOpsBridgeExecution { [CmdletBinding()] param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]$Bridge, [Parameter(Position = 1, Mandatory = $true)][string]$Executable, [Parameter(Position = 2, Mandatory = $false)][string[]]$Arguments = @(), [Parameter(Mandatory = $false)][string]$WorkingDirectory, [Parameter(Mandatory = $false)][hashtable]$EnvironmentVariables, [Parameter(Mandatory = $false)][int]$TimeoutSeconds, [Parameter(Mandatory = $false)][string]$ApiUrl ) $bridgeId = if ($Bridge -is [string]) { $Bridge } else { $Bridge.Id } $argumentText = ($Arguments -join ' ') $outcome = $script:DefaultOutcome foreach ($key in $script:Outcomes.Keys) { if ($argumentText -like "*$key*") { $outcome = $script:Outcomes[$key]; break } } $execution = [pscustomobject]@{ Id = "exec-$($script:Executions.Count + 1)" BridgeId = $bridgeId BridgeArgumentType = if ($Bridge -is [string]) { 'String' } else { $Bridge.GetType().FullName } BridgeDisplayName = $bridgeId Executable = $Executable Arguments = [string[]]$Arguments WorkingDirectory = $WorkingDirectory EnvironmentVariables = $EnvironmentVariables TimeoutSeconds = $TimeoutSeconds State = 'Running' ExitCode = $null StandardOutput = '' StandardError = '' ErrorMessage = '' IsFinished = $false StartTime = [DateTime]::UtcNow EndTime = $null StubOutcome = $outcome GetArgumentType = $null WaitArgumentType = $null StopArgumentType = $null } $script:Executions.Add($execution) return $execution } function Get-QAOpsBridgeExecution { [CmdletBinding()] param( [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)]$Execution, [Parameter(Mandatory = $false)][string]$ApiUrl ) if (-not $Execution) { return $script:Executions | ForEach-Object { $_ } } $id = if ($Execution -is [string]) { $Execution } else { $Execution.Id } $stored = $script:Executions | Where-Object { $_.Id -eq $id } | Select-Object -First 1 if ($stored) { $stored.GetArgumentType = if ($Execution -is [string]) { 'String' } else { $Execution.GetType().FullName } } return $stored } function Wait-QAOpsBridgeExecution { [CmdletBinding()] param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)][object[]]$Execution, [Parameter(Mandatory = $false)][switch]$Any, [Parameter(Mandatory = $false)][int]$TimeoutSeconds = 7200, [Parameter(Mandatory = $false)][int]$PollingIntervalSeconds = 5, [Parameter(Mandatory = $false)][string]$ApiUrl ) foreach ($item in $Execution) { $itemId = if ($item -is [string]) { [string]$item } else { [string]$item.Id } $stored = $script:Executions | Where-Object { $_.Id -eq $itemId } | Select-Object -First 1 if ($stored) { $stored.WaitArgumentType = if ($item -is [string]) { 'String' } else { $item.GetType().FullName } } $stored = Get-QAOpsBridgeExecution -Execution $item if (-not $stored -or $stored.IsFinished) { continue } $outcome = $stored.StubOutcome if ($outcome -and $outcome.StayRunning) { continue } $stored.State = if ($outcome) { $outcome.State } else { 'Completed' } $stored.ExitCode = if ($outcome) { $outcome.ExitCode } else { 0 } $stored.StandardOutput = if ($outcome) { $outcome.StandardOutput } else { '' } $stored.StandardError = if ($outcome) { $outcome.StandardError } else { '' } $stored.ErrorMessage = if ($outcome) { $outcome.ErrorMessage } else { '' } $stored.IsFinished = $stored.State -in @('Completed', 'Failed', 'Cancelled', 'Rejected') $stored.EndTime = [DateTime]::UtcNow } return $Execution | ForEach-Object { Get-QAOpsBridgeExecution -Execution $_ } } function Stop-QAOpsBridgeExecution { [CmdletBinding()] param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]$Execution, [Parameter(Mandatory = $false)][switch]$Wait, [Parameter(Mandatory = $false)][string]$ApiUrl ) $stored = Get-QAOpsBridgeExecution -Execution $Execution if ($stored) { $stored.StopArgumentType = if ($Execution -is [string]) { 'String' } else { $Execution.GetType().FullName } $stored.State = 'Cancelled' $stored.IsFinished = $true $stored.EndTime = [DateTime]::UtcNow } return $stored } function Push-TestCaseResult { [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateSet('Ok', 'Fail', 'NotExecuted', 'NotApplicable')][string]$Outcome, [Parameter(Mandatory = $true)][string]$Name, [Parameter(Mandatory = $false)][TimeSpan]$Duration = [TimeSpan]::Zero, [Parameter(Mandatory = $false)][string]$Message = '', [Parameter(Mandatory = $false)][ValidateSet('Assertion', 'Execution')][string]$TestAspect = 'Assertion' ) $script:TestResults.Add([pscustomobject]@{ Outcome = $Outcome Name = $Name Duration = $Duration Message = $Message TestAspect = $TestAspect }) } function Get-QAOpsCluster { [CmdletBinding()] param([Parameter(Mandatory = $false)][string]$ApiUrl) return $script:Cluster } function Get-QAOpsTestRunContext { [CmdletBinding()] param([Parameter(Mandatory = $false)][string]$ApiUrl) return $script:TestRunContext } function Start-QAOpsFailoverSwitch { [CmdletBinding()] param( [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)]$Bridge, [Parameter(Mandatory = $false)][string]$PairId, [Parameter(Mandatory = $false)][string]$ApiUrl ) $switchOperation = [pscustomobject]@{ Id = "fos-$($script:FailoverSwitches.Count + 1)" PairId = $PairId BridgeId = if ($Bridge -is [string]) { $Bridge } elseif ($Bridge) { $Bridge.Id } else { $null } BridgeArgumentType = if ($Bridge -is [string]) { 'String' } else { $Bridge.GetType().FullName } State = 'Completed' IsFinished = $true ActiveBridgeIdBefore = if ($Bridge) { $Bridge.Id } else { $null } ActiveBridgeIdAfter = if ($Bridge) { $Bridge.FailoverPartnerBridgeId } else { $null } CurrentOnlineAgentName = if ($Bridge.FailoverPartnerHostName) { $Bridge.FailoverPartnerHostName } else { $Bridge.HostName } ErrorMessage = '' } $script:FailoverSwitches.Add($switchOperation) return $switchOperation } function Wait-QAOpsFailoverSwitch { [CmdletBinding()] param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]${Switch}, [Parameter(Mandatory = $false)][int]$TimeoutSeconds = 900, [Parameter(Mandatory = $false)][int]$PollingIntervalSeconds = 5, [Parameter(Mandatory = $false)][string]$ApiUrl ) return ${Switch} } Export-ModuleMember -Function @( 'Reset-QAOpsStubs', 'Add-QAOpsStubBridge', 'Set-QAOpsStubCluster', 'Set-QAOpsStubTestRunContext', 'Set-QAOpsStubExecutionOutcome', 'Get-QAOpsStubExecution', 'Get-QAOpsStubTestResult', 'Get-QAOpsStubFailoverSwitch', 'Complete-QAOpsStubExecution', 'Get-QAOpsBridge', 'Start-QAOpsBridgeExecution', 'Get-QAOpsBridgeExecution', 'Wait-QAOpsBridgeExecution', 'Stop-QAOpsBridgeExecution', 'Push-TestCaseResult', 'Get-QAOpsCluster', 'Get-QAOpsTestRunContext', 'Start-QAOpsFailoverSwitch', 'Wait-QAOpsFailoverSwitch' ) |