# Tests\RunAllTests.ps1 # Enterprise Admin Studio - Comprehensive Test Runner Script # Dual-Compatible with both Windows PowerShell 5.1 and PowerShell 7 (Core) # Supports standard Pester v5+ and standalone runner fallback for environments without Pester v5. [CmdletBinding()] param( [string[]]$Path = @(), [string]$Tag = "", [string]$Filter = "", [string]$Output = "Detailed", [switch]$Live, [switch]$CI ) $ErrorActionPreference = 'Stop' $ScriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Path } $WorkspaceRoot = Split-Path -Parent $ScriptDir Write-Host "================================================================================" -ForegroundColor Cyan Write-Host " Enterprise Admin Studio - Comprehensive Test Suite Runner" -ForegroundColor Cyan Write-Host " PowerShell Version : $($PSVersionTable.PSVersion)" -ForegroundColor Cyan Write-Host " OS Platform : $(if ($PSVersionTable.OS) { $PSVersionTable.OS } else { [System.Environment]::OSVersion.VersionString })" -ForegroundColor Cyan Write-Host " Working Directory : $WorkspaceRoot" -ForegroundColor Cyan Write-Host " Mode : $(if ($Live) { 'Live External Infrastructure' } else { '100% Mock / Standalone Reproducible' })" -ForegroundColor Cyan Write-Host "================================================================================" -ForegroundColor Cyan # Determine test files to execute $testFiles = if ($Path -and $Path.Count -gt 0) { @($Path | ForEach-Object { if ([System.IO.Path]::IsPathRooted($_)) { $_ } else { Join-Path $ScriptDir $_ } }) } else { @( (Join-Path $ScriptDir "ConfigService.Tests.ps1"), (Join-Path $ScriptDir "UserResolverService.Tests.ps1"), (Join-Path $ScriptDir "SqlService.Tests.ps1"), (Join-Path $ScriptDir "ADService.Tests.ps1"), (Join-Path $ScriptDir "NetAppService.Tests.ps1"), (Join-Path $ScriptDir "BatchPipeline.Tests.ps1") ) } # Verify test files exist $missingFiles = @($testFiles | Where-Object { -not (Test-Path $_) }) if ($missingFiles.Count -gt 0) { Write-Error "Missing test files:`n$($missingFiles -join "`n")" exit 1 } # Try importing Pester 5 $pesterModule = Get-Module -Name Pester if (-not $pesterModule -or $pesterModule.Version.Major -lt 5) { $pester5 = Get-Module -ListAvailable -Name Pester | Where-Object { $_.Version.Major -ge 5 } | Select-Object -First 1 if ($pester5) { Import-Module $pester5.Path -Force -ErrorAction SilentlyContinue $pesterModule = Get-Module -Name Pester } else { $userModulePaths = @( "$([System.Environment]::GetFolderPath('MyDocuments'))\PowerShell\Modules\Pester\5.5.0\Pester.psd1", "$([System.Environment]::GetFolderPath('MyDocuments'))\WindowsPowerShell\Modules\Pester\5.5.0\Pester.psd1" ) foreach ($mp in $userModulePaths) { if (Test-Path $mp) { Import-Module $mp -Force -ErrorAction SilentlyContinue $pesterModule = Get-Module -Name Pester if ($pesterModule) { break } } } } } $usePester5 = ($pesterModule -and $pesterModule.Version.Major -ge 5) $startTime = [System.Diagnostics.Stopwatch]::StartNew() if ($usePester5) { Write-Host "Running via Pester v$($pesterModule.Version)...`n" -ForegroundColor Green $pesterConfig = [PesterConfiguration]::Default $pesterConfig.Run.Path = $testFiles $pesterConfig.Run.PassThru = $true $pesterConfig.Output.Verbosity = $Output if (-not [string]::IsNullOrWhiteSpace($Tag)) { $pesterConfig.Filter.Tag = @($Tag -split ",") } elseif ($Live) { $pesterConfig.Filter.Tag = @("Live") } if (-not [string]::IsNullOrWhiteSpace($Filter)) { $pesterConfig.Filter.FullName = "*$Filter*" } $testResult = Invoke-Pester -Configuration $pesterConfig $startTime.Stop() $passedCount = if ($testResult.PassedCount) { $testResult.PassedCount } else { 0 } $failedCount = if ($testResult.FailedCount) { $testResult.FailedCount } else { 0 } $skippedCount = if ($testResult.SkippedCount) { $testResult.SkippedCount } else { 0 } $totalCount = if ($testResult.TotalCount) { $testResult.TotalCount } else { ($passedCount + $failedCount + $skippedCount) } } else { Write-Host "Pester v5 not detected. Executing with embedded Standalone Test Engine...`n" -ForegroundColor Yellow $script:StandalonePassed = 0 $script:StandaloneFailed = 0 $script:StandaloneSkipped = 0 $script:FailedDetails = [System.Collections.Generic.List[string]]::new() $script:ActiveBeforeEach = $null $script:ActiveAfterEach = $null function script:Describe { param([string]$Name, [string[]]$Tag = @(), [scriptblock]$Fixture) Write-Host "Describing $Name" -ForegroundColor Cyan & $Fixture } function script:Context { param([string]$Name, [string[]]$Tag = @(), [scriptblock]$Fixture) if ((-not $Live) -and ($Tag -contains "Live")) { Write-Host " Skipping Context: $Name (Live tag disabled)" -ForegroundColor DarkGray $script:StandaloneSkipped++ return } Write-Host " Context $Name" -ForegroundColor Yellow & $Fixture } function script:It { param([string]$Name, [string[]]$Tag = @(), [scriptblock]$Test) if ((-not $Live) -and ($Tag -contains "Live")) { $script:StandaloneSkipped++ return } if ($Filter -and ($Name -notmatch $Filter)) { return } try { if ($script:ActiveBeforeEach) { & $script:ActiveBeforeEach } & $Test if ($script:ActiveAfterEach) { & $script:ActiveAfterEach } $script:StandalonePassed++ Write-Host " [+] $Name" -ForegroundColor Green } catch { $script:StandaloneFailed++ $err = "$Name failed: $($_.Exception.Message)" $script:FailedDetails.Add($err) Write-Host " [-] $err" -ForegroundColor Red } } function script:BeforeAll { param([scriptblock]$Script) & $Script } function script:AfterAll { param([scriptblock]$Script) & $Script } function script:BeforeEach { param([scriptblock]$Script) $script:ActiveBeforeEach = $Script } function script:AfterEach { param([scriptblock]$Script) $script:ActiveAfterEach = $Script } function script:Mock { param([string]$CommandName, [scriptblock]$MockWith) Set-Item "function:global:$CommandName" -Value $MockWith } filter script:Should { param( [string]$Operator, [object]$Expected, [switch]$Not, [switch]$BeTrue, [switch]$BeFalse, [switch]$BeNullOrEmpty ) $val = $_ $op = if ($Operator) { $Operator.TrimStart('-') } else { "" } if ($op -eq 'Not') { $Not = $true # subsequent argument } $pass = $false if ($BeTrue -or $op -eq 'BeTrue') { $pass = ($val -eq $true) } elseif ($BeFalse -or $op -eq 'BeFalse') { $pass = ($val -eq $false) } elseif ($BeNullOrEmpty -or $op -eq 'BeNullOrEmpty') { $pass = ($null -eq $val -or ($val -is [string] -and [string]::IsNullOrEmpty($val)) -or ($val.Count -eq 0)) } elseif ($op -eq 'Be') { $pass = ($val -eq $Expected) } elseif ($op -eq 'Match') { $pass = ($val -match $Expected) } elseif ($op -eq 'Contain') { $pass = ($val -contains $Expected) } elseif ($op -eq 'BeGreaterThan') { $pass = ($val -gt $Expected) } elseif ($op -eq 'BeGreaterOrEqual') { $pass = ($val -ge $Expected) } elseif ($op -eq 'Throw') { $threw = $false if ($val -is [scriptblock]) { try { & $val } catch { $threw = $true; if ($Expected) { $pass = ($_.Exception.Message -like $Expected) } else { $pass = $true } } } $pass = $threw } if ($Not) { $pass = -not $pass } if (-not $pass) { throw "Assertion failed. Actual: '$val', Operator: '$op', Expected: '$Expected', Not: $Not" } } foreach ($tf in $testFiles) { Write-Host "Running tests from '$tf'" -ForegroundColor Gray . $tf } $startTime.Stop() $passedCount = $script:StandalonePassed $failedCount = $script:StandaloneFailed $skippedCount = $script:StandaloneSkipped $totalCount = $passedCount + $failedCount + $skippedCount } # Generate Test Summary Write-Host "`n================================================================================" -ForegroundColor Cyan Write-Host " TEST EXECUTION SUMMARY" -ForegroundColor Cyan Write-Host "================================================================================" -ForegroundColor Cyan $summaryTable = [PSCustomObject]@{ TotalSuites = $testFiles.Count TotalTests = $totalCount Passed = $passedCount Failed = $failedCount Skipped = $skippedCount DurationSec = [Math]::Round($startTime.Elapsed.TotalSeconds, 2) SuccessRate = if ($totalCount -gt 0) { "$([Math]::Round(($passedCount / $totalCount) * 100, 1))%" } else { "0%" } } $summaryTable | Format-Table -AutoSize if ($failedCount -gt 0) { Write-Host "`n[FAIL] Test suite failed with $failedCount errors." -ForegroundColor Red if ($testResult -and $testResult.Failed) { Write-Host "`nFailed Test Details:" -ForegroundColor Red foreach ($f in $testResult.Failed) { Write-Host " - $($f.ExpandedPath): $($f.ErrorRecord.Exception.Message)" -ForegroundColor Red } } elseif ($script:FailedDetails) { foreach ($fd in $script:FailedDetails) { Write-Host " - $fd" -ForegroundColor Red } } if ($CI) { exit 1 } else { return $false } } else { Write-Host "`n[PASS] All $passedCount tests passed successfully without error!" -ForegroundColor Green if ($CI) { exit 0 } else { return $true } }