Tests/E2E/PesterE2EHelper.ps1
|
# Shared Pester E2E test helper — ensures PSU is running and provides Run-OnPSU functions. # # Usage in E2E test BeforeAll: # $projectRoot = Resolve-Path (Join-Path $PSScriptRoot '..path..') # . (Join-Path $projectRoot 'psu-app' 'Tests' 'E2E' 'PesterE2EHelper.ps1') # Initialize-PesterE2E -ProjectRoot $projectRoot function script:Initialize-PesterE2E { param( [Parameter(Mandatory)][string]$ProjectRoot, [int]$TimeoutSeconds = 120 ) Import-Module (Join-Path $ProjectRoot 'Devolutions.CIEM.Admin' 'Devolutions.CIEM.Admin.psd1') -Force $psuUrl = 'http://localhost:5001' $healthUrl = "$psuUrl/api/v1/alive" $isReady = $false try { $response = Invoke-RestMethod -Uri $healthUrl -TimeoutSec 5 -ErrorAction Stop $isReady = $response.loading -eq $false } catch { $isReady = $false } if (-not $isReady) { Write-Host '[pester-e2e] PSU not ready, starting...' $setupScript = Join-Path $ProjectRoot 'scripts' 'setup-local-psu.sh' & bash $setupScript start --no-wait $startTime = Get-Date $deadline = $startTime.AddSeconds($TimeoutSeconds) while ((Get-Date) -lt $deadline) { try { $response = Invoke-RestMethod -Uri $healthUrl -TimeoutSec 5 -ErrorAction Stop if ($response.loading -eq $false) { $elapsed = [math]::Round(((Get-Date) - $startTime).TotalSeconds) Write-Host "[pester-e2e] PSU is ready (took ${elapsed}s)" $isReady = $true break } } catch {} Start-Sleep -Seconds 2 } if (-not $isReady) { throw "PSU server did not become ready within ${TimeoutSeconds}s" } } else { Write-Host '[pester-e2e] PSU is already running.' } Connect-PSU -Local | Out-Null } function script:Run-OnPSU { param( [Parameter(Mandatory)][string]$Command, [int]$TimeoutSeconds = 60 ) $wrappedCommand = @" `$ErrorActionPreference = 'Continue' `$__result = & { $Command } if (`$null -ne `$__result) { `$__result | ConvertTo-Json -Depth 5 -Compress } else { '___NULL___' } "@ $allOutput = @(Invoke-TestCommand -ScriptBlock ([scriptblock]::Create($wrappedCommand)) -TimeoutSeconds $TimeoutSeconds) $jobResult = $allOutput | Where-Object { $_.PSObject.Properties.Name -contains 'JobId' } | Select-Object -Last 1 if (-not $jobResult) { throw "PSU command returned no job result." } if ($jobResult.Status -eq 'Failed') { $errMsgs = @($jobResult.Output) | Where-Object { $_.type -eq 4 } | ForEach-Object { $_.message } throw "PSU command failed: $($errMsgs -join '; ')" } if ($jobResult.Status -notin @('Completed', 'Warning')) { throw "PSU job $($jobResult.JobId) did not complete. Status: $($jobResult.Status)" } $pipelineItems = @($jobResult.PipelineOutput) if ($pipelineItems.Count -eq 0) { return $null } $lastItem = $pipelineItems[-1] $jsonDataStr = $lastItem.jsonData if (-not $jsonDataStr) { return $null } $jsonEntries = $jsonDataStr | ConvertFrom-Json $rawValue = ($jsonEntries | Select-Object -Last 1).value if ($rawValue -eq '___NULL___') { return $null } try { $rawValue | ConvertFrom-Json } catch { $rawValue } } function script:Run-OnPSU-LongRunning { param( [Parameter(Mandatory)][string]$Command, [int]$TimeoutSeconds = 600 ) $wrappedCommand = @" `$ErrorActionPreference = 'Continue' `$__result = & { $Command } if (`$null -ne `$__result) { `$__result | ConvertTo-Json -Depth 5 -Compress } else { '___NULL___' } "@ # Start the job with a short timeout — we just need the job ID $allOutput = @(Invoke-TestCommand -ScriptBlock ([scriptblock]::Create($wrappedCommand)) -TimeoutSeconds 5) $jobResult = $allOutput | Where-Object { $_.PSObject.Properties.Name -contains 'JobId' } | Select-Object -Last 1 if (-not $jobResult -or -not $jobResult.JobId) { throw "PSU command returned no job result." } $jobId = $jobResult.JobId Write-Host "Started PSU job $jobId, waiting up to ${TimeoutSeconds}s..." # Wait for completion using PSU's gRPC-based Wait-PSUJob Wait-PSUJob -JobId $jobId -Timeout $TimeoutSeconds | Out-Null # Get final status (Status is PowerShellUniversal.JobStatus enum — string comparison works) $finalJob = Get-PSUJob -Id $jobId $statusName = "$($finalJob.Status)" if ($statusName -eq 'Failed') { $errOutput = Get-PSUJobOutput -JobId $jobId $errMsgs = @($errOutput) | Where-Object { $_.Type -eq 4 } | ForEach-Object { $_.Message } throw "PSU job $jobId failed: $($errMsgs -join '; ')" } if ($statusName -notin @('Completed', 'Warning')) { throw "PSU job $jobId did not complete within ${TimeoutSeconds}s. Status: $statusName" } Write-Host "PSU job $jobId completed with status: $statusName" # Get-PSUJobPipelineOutput returns raw strings (the JSON our wrapper emitted) $pipelineOutput = Get-PSUJobPipelineOutput -JobId $jobId if (-not $pipelineOutput) { return $null } $rawValue = @($pipelineOutput)[-1] if ($rawValue -eq '___NULL___') { return $null } try { $rawValue | ConvertFrom-Json } catch { $rawValue } } |