Refactor feature management scripts to improve error handling

This commit is contained in:
Jeffrey
2026-08-16 21:21:05 +02:00
parent 1a26934499
commit 492a374f5c
18 changed files with 870 additions and 317 deletions
+95 -2
View File
@@ -11,6 +11,7 @@ BeforeAll {
function Resolve-UserProfileContext { param($UserName) $null }
. (Join-Path $PSScriptRoot '..\Scripts\AppRemoval\Remove-SelectedApps.ps1')
. (Join-Path $PSScriptRoot '..\Scripts\AppRemoval\Invoke-ForceRemoveEdge.ps1')
}
Describe 'Remove-SelectedApps' {
@@ -71,10 +72,23 @@ Describe 'Remove-SelectedApps' {
It 'counts a failed WinGet removal' {
Mock Get-AppRemovalMethod { 'WinGet' }
Mock Remove-WinGetApp { $false }
Mock Test-AppInWingetList { $true }
Mock Write-Host {}
Remove-SelectedApps -appsList @('One.App')
$script:AppRemovalFailures | Should -Be 1
Should -Invoke Write-Host -Times 1 -Exactly -ParameterFilter { $Object -eq 'Unable to uninstall One.App via WinGet' -and $ForegroundColor -eq 'Red' }
}
It 'does not count a non-zero WinGet command when the app is absent after verification' {
Mock Get-AppRemovalMethod { 'WinGet' }
Mock Remove-WinGetApp { $false }
Mock Test-AppInWingetList { $false }
Remove-SelectedApps -appsList @('One.App') | Should -BeTrue
$script:AppRemovalFailures | Should -Be 0
}
It 'counts a WinGet removal that remains installed after a successful command' {
@@ -132,7 +146,7 @@ Describe 'Remove-WinGetApp' {
BeforeEach {
$script:Params = @{}
$script:WingetInstalled = $true
Mock Invoke-NonBlocking { $true }
Mock Invoke-NonBlocking { [PSCustomObject]@{ Success = $true; ExitCode = 0; Output = @() } }
Mock Set-RunOnceWingetTask { $true }
Mock Get-UserName { 'Alice' }
Mock Write-Host {}
@@ -174,14 +188,93 @@ Describe 'Remove-WinGetApp' {
It 'reports a timed-out winget uninstall and continues' {
$script:Params = @{ User = 'Alice' }
Mock Invoke-NonBlocking { throw 'Operation timed out after 120 seconds' }
Mock Write-Verbose {}
{ Remove-WinGetApp -app 'One.App' } | Should -Not -Throw
Should -Invoke Set-RunOnceWingetTask -Times 1 -Exactly
Should -Invoke Write-Error -Times 1 -Exactly -ParameterFilter {
Should -Invoke Write-Verbose -Times 1 -Exactly -ParameterFilter {
$Message -like '*did not complete within 120 seconds*'
}
}
It 'returns false when winget exits unsuccessfully' {
Mock Invoke-NonBlocking { [PSCustomObject]@{ Success = $false; ExitCode = 1; Output = @('failure') } }
Mock Write-Verbose {}
Remove-WinGetApp -app 'One.App' | Should -BeFalse
Should -Invoke Write-Verbose -Times 1 -Exactly -ParameterFilter { $Message -match 'exit code 1' }
}
It 'returns false for a non-zero WinGet exit code without writing an error record' {
Mock Invoke-NonBlocking { [PSCustomObject]@{ Success = $false; ExitCode = -1978335212; Output = @('No installed package found matching input criteria.') } }
Mock Write-Verbose {}
Remove-WinGetApp -app 'One.App' | Should -BeFalse
Should -Invoke Write-Verbose -Times 1 -Exactly -ParameterFilter { $Message -match 'post-removal inventory check' }
Should -Invoke Write-Verbose -Times 1 -Exactly -ParameterFilter { $Message -match 'No installed package found' }
}
It 'writes captured winget output to the verbose stream before returning success' {
Mock Invoke-NonBlocking { [PSCustomObject]@{ Success = $true; ExitCode = 0; Output = @('Successfully uninstalled One.App') } }
Mock Write-Verbose {}
Remove-WinGetApp -app 'One.App' | Should -BeTrue
Should -Invoke Write-Verbose -Times 1 -Exactly -ParameterFilter { $Message -eq 'Successfully uninstalled One.App' }
}
It 'writes captured winget diagnostics to the verbose stream when winget fails' {
Mock Invoke-NonBlocking { [PSCustomObject]@{ Success = $false; ExitCode = 1; Output = @('Package was not found') } }
Mock Write-Verbose {}
Remove-WinGetApp -app 'One.App' | Should -BeFalse
Should -Invoke Write-Verbose -Times 1 -Exactly -ParameterFilter { $Message -eq 'Package was not found' }
}
}
Describe 'Remove-EdgeAutostartValue' {
BeforeEach {
Mock Write-Warning {}
}
It 'treats a missing value as already cleaned up' {
Mock Get-ItemProperty { [PSCustomObject]@{} }
Mock Remove-ItemProperty {}
Remove-EdgeAutostartValue -Path 'HKCU:\Software\Example' -Name 'Microsoft Edge Update' | Should -BeTrue
Should -Invoke Remove-ItemProperty -Times 0 -Exactly
}
It 'removes an existing value' {
Mock Get-ItemProperty { [PSCustomObject]@{ 'Microsoft Edge Update' = 'enabled' } }
Mock Remove-ItemProperty {}
Remove-EdgeAutostartValue -Path 'HKCU:\Software\Example' -Name 'Microsoft Edge Update' | Should -BeTrue
Should -Invoke Remove-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq 'HKCU:\Software\Example' -and $Name -eq 'Microsoft Edge Update' }
}
It 'returns false when an existing value cannot be removed' {
Mock Get-ItemProperty { [PSCustomObject]@{ 'Microsoft Edge Update' = 'enabled' } }
Mock Remove-ItemProperty { throw 'access denied' }
Remove-EdgeAutostartValue -Path 'HKCU:\Software\Example' -Name 'Microsoft Edge Update' | Should -BeFalse
Should -Invoke Write-Warning -Times 1 -Exactly -ParameterFilter { $Message -match 'access denied' }
}
It 'returns false when the registry key cannot be inspected' {
Mock Get-ItemProperty { throw 'access denied' }
Remove-EdgeAutostartValue -Path 'HKCU:\Software\Example' -Name 'Microsoft Edge Update' | Should -BeFalse
Should -Invoke Write-Warning -Times 1 -Exactly -ParameterFilter { $Message -match 'access denied' }
}
}
Describe 'Remove-AppxApp' {