Public/Invoke-QAFrameworkTestPackage.ps1
|
function Invoke-QAFrameworkTestPackage { <# .SYNOPSIS Runs a QAFramework test package through the standalone orchestrator. .DESCRIPTION Installs or updates the package-local QAFramework tool and invokes its run command. Agent preparation, selection, scheduling, Failover, retries, and result publication are owned by the orchestrator; a run always revalidates Agent setup. .PARAMETER TestPackageContentPath Test package content root. .PARAMETER Keywords Override the keyword selection. A value prefixed with ! is an exclusion for legacy PipelineLibrary configurations. .PARAMETER ExcludeKeywords Override keyword exclusions. .PARAMETER Squads Override the squad selection. A value prefixed with ! is an exclusion for legacy PipelineLibrary configurations. .PARAMETER ExcludeSquads Override squad exclusions. .PARAMETER Customers Override the customer selection. .PARAMETER ConfigPath Explicit unified or supported legacy QAFramework configuration file. .PARAMETER SupplementaryFilesPath Validated supplementary-files directory used for configuration overrides. .PARAMETER ResultsPath Optional path for the final JSON summary. Defaults to the QAFramework summary path in TestPackagePipeline. .PARAMETER SetupTimeoutSeconds Maximum duration allowed for Agent setup. .PARAMETER SkipPublish Do not publish attempt or overall results to QAOps. This is not a dry-run mode. .PARAMETER PassThru Return the parsed JSON run summary on success. .PARAMETER SkipAgentSetup Removed. The orchestrator always revalidates Agent setup before scheduling tests. .PARAMETER OverallResultName Only the standard pipeline_TestPackageExecution result name is supported. .EXAMPLE Invoke-QAFrameworkTestPackage -TestPackageContentPath (Resolve-Path "$PSScriptRoot\..") -PassThru .OUTPUTS The parsed JSON run summary when -PassThru is used. #> [CmdletBinding(SupportsShouldProcess = $true)] [OutputType([pscustomobject])] param( [Parameter(Mandatory = $true)] [string]$TestPackageContentPath, [Parameter()] [string[]]$Keywords, [Parameter()] [string[]]$ExcludeKeywords, [Parameter()] [string[]]$Squads, [Parameter()] [string[]]$ExcludeSquads, [Parameter()] [string[]]$Customers, [Parameter()] [string]$ConfigPath, [Parameter()] [string]$SupplementaryFilesPath, [Parameter()] [string]$ResultsPath, [Parameter()] [ValidateRange(1, [int]::MaxValue)] [int]$SetupTimeoutSeconds = 1800, [Parameter()] [switch]$SkipAgentSetup, [Parameter()] [switch]$SkipPublish, [Parameter()] [switch]$PassThru, [Parameter()] [string]$OverallResultName = 'pipeline_TestPackageExecution' ) if ($SkipAgentSetup) { throw '-SkipAgentSetup is no longer supported. The orchestrator always revalidates Agent setup before running tests.' } if ($OverallResultName -ne 'pipeline_TestPackageExecution') { throw 'Custom -OverallResultName values are no longer supported. The orchestrator publishes the standard pipeline_TestPackageExecution result.' } $paths = Resolve-QAFrameworkContentPath -Path $TestPackageContentPath if (-not $PSCmdlet.ShouldProcess($paths.ContentPath, 'Run QAFramework test package')) { return } $filter = @{} $keywordExclusions = [System.Collections.Generic.List[string]]::new() $squadExclusions = [System.Collections.Generic.List[string]]::new() if ($PSBoundParameters.ContainsKey('Keywords')) { $includedKeywords = [System.Collections.Generic.List[string]]::new() foreach ($keyword in @($Keywords)) { if ([string]::IsNullOrWhiteSpace($keyword)) { throw 'Keywords cannot contain empty values.' } if ($keyword.StartsWith('!', [System.StringComparison]::Ordinal) -and $keyword.Length -gt 1) { $keywordExclusions.Add($keyword.Substring(1)) } else { $includedKeywords.Add($keyword) } } $filter['keywords'] = [string[]]$includedKeywords } if ($PSBoundParameters.ContainsKey('ExcludeKeywords')) { if (@($ExcludeKeywords | Where-Object { [string]::IsNullOrWhiteSpace($_) }).Count -gt 0) { throw 'ExcludeKeywords cannot contain empty values.' } $keywordExclusions.AddRange([string[]]@($ExcludeKeywords)) } if ($keywordExclusions.Count -gt 0 -or $PSBoundParameters.ContainsKey('ExcludeKeywords')) { $filter['excludeKeywords'] = [string[]]$keywordExclusions } if ($PSBoundParameters.ContainsKey('Squads')) { $includedSquads = [System.Collections.Generic.List[string]]::new() foreach ($squad in @($Squads)) { if ([string]::IsNullOrWhiteSpace($squad)) { throw 'Squads cannot contain empty values.' } if ($squad.StartsWith('!', [System.StringComparison]::Ordinal) -and $squad.Length -gt 1) { $squadExclusions.Add($squad.Substring(1)) } else { $includedSquads.Add($squad) } } $filter['squads'] = [string[]]$includedSquads } if ($PSBoundParameters.ContainsKey('ExcludeSquads')) { if (@($ExcludeSquads | Where-Object { [string]::IsNullOrWhiteSpace($_) }).Count -gt 0) { throw 'ExcludeSquads cannot contain empty values.' } $squadExclusions.AddRange([string[]]@($ExcludeSquads)) } if ($squadExclusions.Count -gt 0 -or $PSBoundParameters.ContainsKey('ExcludeSquads')) { $filter['excludeSquads'] = [string[]]$squadExclusions } if ($PSBoundParameters.ContainsKey('Customers')) { if (@($Customers | Where-Object { [string]::IsNullOrWhiteSpace($_) }).Count -gt 0) { throw 'Customers cannot contain empty values.' } $filter['customers'] = [string[]]@($Customers) } $overrides = if ($filter.Count -gt 0) { @{ filter = $filter } } else { @{} } if ((-not $PSBoundParameters.ContainsKey('ConfigPath')) -and ($null -eq (Get-QAFrameworkPackageConfigInfo -ContentPath $paths.ContentPath))) { $overrides['selectionPolicy'] = 'LegacyPipeline' } $requestPath = $null $configPathResolved = $null if ($PSBoundParameters.ContainsKey('ConfigPath')) { $configPathResolved = Resolve-QAFrameworkPackageFilePath ` -Path $ConfigPath ` -ContentPath $paths.ContentPath ` -Description 'ConfigPath' } $supplementaryPathResolved = $null if ($PSBoundParameters.ContainsKey('SupplementaryFilesPath')) { $supplementaryPathResolved = (Resolve-Path -LiteralPath $SupplementaryFilesPath -ErrorAction Stop).ProviderPath if (-not (Test-Path -LiteralPath $supplementaryPathResolved -PathType Container)) { throw "SupplementaryFilesPath must be an existing directory: $SupplementaryFilesPath" } } if ($PSBoundParameters.ContainsKey('ResultsPath')) { if ([string]::IsNullOrWhiteSpace($ResultsPath)) { throw 'ResultsPath cannot be empty.' } $summaryPath = if ([System.IO.Path]::IsPathRooted($ResultsPath)) { [System.IO.Path]::GetFullPath($ResultsPath) } else { [System.IO.Path]::GetFullPath((Join-Path $paths.PipelinePath $ResultsPath)) } } else { $summaryPath = Join-Path $paths.PipelinePath 'qaops-qaframework-results.json' } try { $null = Install-QAFrameworkTool -PipelineDirectory $paths.PipelinePath $requestPath = New-QAFrameworkRequestFile -Overrides $overrides -Directory $paths.PipelinePath $arguments = @( 'run', '--content', $paths.ContentPath, '--setup-timeout-seconds', [string]$SetupTimeoutSeconds, '--results', $summaryPath ) if ($configPathResolved) { $arguments += @('--config', $configPathResolved) } if ($supplementaryPathResolved) { $arguments += @('--supplementary-path', $supplementaryPathResolved) } if ($requestPath) { $arguments += @('--request-file', $requestPath) } if ($SkipPublish) { $arguments += '--skip-publish' } try { $summary = Invoke-QAFrameworkToolJson -PipelineDirectory $paths.PipelinePath -Operation 'run' -Arguments $arguments } catch { throw [System.InvalidOperationException]::new( "$($_.Exception.Message) Summary: $summaryPath", $_.Exception) } if ($PassThru) { return $summary } Write-Host ("QAFramework run {0}: {1} attempt(s), {2} skipped, {3} error(s). Summary: {4}" -f ` $summary.overallOutcome, @($summary.attempts).Count, @($summary.skipped).Count, @($summary.errors).Count, $summaryPath) } finally { if ($requestPath -and (Test-Path -LiteralPath $requestPath -PathType Leaf)) { Remove-Item -LiteralPath $requestPath -Force } } } |