Public/Repair-TestEnvironment.ps1
|
function Repair-TestEnvironment { <# .EXTERNALHELP TestEnvironment-Help.xml .SYNOPSIS Puts back what verification found missing, by re-running only the seed steps that own it #> [CmdletBinding(SupportsShouldProcess = $true)] [OutputType([PSCustomObject])] param( [Parameter()] [switch]$SkipMembership ) begin { if (-not $script:ActiveProvider) { Write-Error 'Not connected. Run Connect-TestEnvironment -Provider <name> first.' -ErrorAction Stop return } } process { $provider = $script:ActiveProvider $verify = 'Test-{0}Environment' -f $provider $seed = 'New-{0}Environment' -f $provider foreach ($target in $verify, $seed) { if (-not (Get-Command -Name $target -ErrorAction SilentlyContinue)) { Write-Error "The $provider provider does not implement $target." -ErrorAction Stop return } } $map = Get-Variable -Name ('{0}RepairStep' -f $provider) -Scope Script -ValueOnly -ErrorAction SilentlyContinue if (-not $map) { Write-Error "The $provider provider does not say which seed step owns which check, so it cannot be repaired." -ErrorAction Stop return } Write-TestMessage -Message "Repairing the $provider seed" -Type Header $before = & $verify -Quiet -SkipMembership:$SkipMembership $failed = @($before.Checks | Where-Object { $_.Passed -eq $false }) $result = [ordered]@{ PSTypeName = 'TestEnvironmentRepair' Provider = $provider Before = $before StepsRun = @() SeedResult = $null After = $null Repaired = $null } if ($failed.Count -eq 0) { Write-TestMessage -Message 'Nothing to repair: every check passed.' -Type Success $result.Repaired = $true return [PSCustomObject]$result } # The steps that own what failed, plus what every repair needs around them. $steps = New-Object System.Collections.Generic.List[string] $unowned = New-Object System.Collections.Generic.List[string] foreach ($check in $failed) { $name = [string]$check.Name if ($map.Step.ContainsKey($name)) { if (-not $steps.Contains($map.Step[$name])) { $steps.Add($map.Step[$name]) } } else { $unowned.Add($name) } if (@($check.Unexpected).Count -gt 0) { Write-TestMessage -Message ("{0}: {1} object(s) the data does not describe are left alone; teardown removes them ({2})" -f $name, @($check.Unexpected).Count, (Format-TestEnvironmentSample -Item $check.Unexpected -Limit 3)) -Type Warning } } foreach ($name in $unowned) { Write-Warning "No seed step owns the check '$name', so it cannot be repaired here." } if ($steps.Count -eq 0) { Write-TestMessage -Message 'Nothing a seed step can put back.' -Type Warning $result.Repaired = $false return [PSCustomObject]$result } foreach ($always in @($map.Always)) { if (-not $steps.Contains($always)) { $steps.Add($always) } } # The orchestrator takes what to leave out; the rest of its steps are the ones to run. $skipParameter = (Get-Command -Name $seed).Parameters['Skip'] $allSteps = @(@($skipParameter.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] })[0].ValidValues) $ordered = @($allSteps | Where-Object { $steps.Contains($_) }) $skip = @($allSteps | Where-Object { -not $steps.Contains($_) }) $result.StepsRun = $ordered Write-TestMessage -Message ("Failed: {0}. Re-running {1}." -f (@($failed | ForEach-Object { $_.Name }) -join ', '), ($ordered -join ', ')) -Type Info if (-not $PSCmdlet.ShouldProcess($provider, "Re-run the $($ordered -join ', ') step(s) of $seed")) { return [PSCustomObject]$result } $seedArguments = @{ PassThru = $true } if ($skip.Count -gt 0) { $seedArguments['Skip'] = $skip } $result.SeedResult = & $seed @seedArguments $after = & $verify -Quiet -SkipMembership:$SkipMembership $result.After = $after $result.Repaired = [bool]$after.Passed $stillFailed = @($after.Checks | Where-Object { $_.Passed -eq $false }) if ($after.Passed) { Write-TestMessage -Message 'Repaired: every check passes.' -Type Success } else { Write-TestMessage -Message ("Not repaired: {0} check(s) still fail ({1})." -f $stillFailed.Count, (@($stillFailed | ForEach-Object { $_.Name }) -join ', ')) -Type Warning } return [PSCustomObject]$result } } |