Files
ModernAdminStudio/Tests/RunAllTests.ps1
T

248 lines
9.9 KiB
PowerShell
Raw Normal View History

# 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 }
}