#=========================================================================== # Tests - Windows Update Profiles #=========================================================================== BeforeAll { $script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path . (Join-Path $script:repoRoot "functions\public\Invoke-WPFUpdatesdisable.ps1") . (Join-Path $script:repoRoot "functions\public\Invoke-WPFUpdatesdefault.ps1") . (Join-Path $script:repoRoot "functions\public\Invoke-WPFUpdatessecurity.ps1") function Write-WinUtilLog { param($Message, $Level, $Component) } function Show-WinUtilMessage { param($Message, $Title, $Button, $Icon) } function Get-ScheduledTask { param($TaskPath, $ErrorAction) } function Disable-ScheduledTask { param( [Parameter(ValueFromPipeline = $true)] $InputObject, $ErrorAction ) process { } } function Enable-ScheduledTask { param( [Parameter(ValueFromPipeline = $true)] $InputObject, $ErrorAction ) process { } } function secedit { param( [Parameter(ValueFromRemainingArguments = $true)] $Arguments ) } $script:updateTaskPaths = @( '\Microsoft\Windows\InstallService\*', '\Microsoft\Windows\UpdateOrchestrator\*', '\Microsoft\Windows\UpdateAssistant\*', '\Microsoft\Windows\WaaSMedic\*', '\Microsoft\Windows\WindowsUpdate\*', '\Microsoft\WindowsUpdate\*' ) } Describe "Invoke-WPFUpdatesdisable" { BeforeEach { Mock Write-Host { } Mock Write-WinUtilLog { } Mock Show-WinUtilMessage { "Yes" } Mock New-Item { } Mock Set-ItemProperty { } Mock Set-Service { } Mock Stop-Service { } Mock Remove-Item { } Mock Get-ScheduledTask { [pscustomobject]@{ TaskPath = $TaskPath } } Mock Disable-ScheduledTask { } } It "sets update disable registry policy values" { Invoke-WPFUpdatesdisable Should -Invoke -CommandName New-Item -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and $Force -eq $true } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and $Name -eq "NoAutoUpdate" -and $Type -eq "DWord" -and $Value -eq 1 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and $Name -eq "AUOptions" -and $Type -eq "DWord" -and $Value -eq 1 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -and $Name -eq "DODownloadMode" -and $Type -eq "DWord" -and $Value -eq 0 } } It "disables update services and clears the SoftwareDistribution folder" { Invoke-WPFUpdatesdisable foreach ($expectedServiceName in @("BITS", "wuauserv", "UsoSvc")) { $expected = $expectedServiceName Should -Invoke -CommandName Stop-Service -Times 1 -Exactly -ParameterFilter { $Name -eq $expected -and $Force -eq $true } } Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "BITS" -and $StartupType -eq "Disabled" } Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "wuauserv" -and $StartupType -eq "Disabled" } Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "UsoSvc" -and $StartupType -eq "Disabled" } Should -Invoke -CommandName Remove-Item -Times 1 -Exactly -ParameterFilter { $Path -eq "C:\Windows\SoftwareDistribution\*" -and $Recurse -eq $true -and $Force -eq $true } } It "disables update scheduled task paths" { Invoke-WPFUpdatesdisable foreach ($expectedTaskPath in $script:updateTaskPaths) { $expected = $expectedTaskPath Should -Invoke -CommandName Get-ScheduledTask -Times 1 -Exactly -ParameterFilter { $TaskPath -eq $expected } } Should -Invoke -CommandName Disable-ScheduledTask -Times $script:updateTaskPaths.Count -Exactly } It "requires confirmation before disabling updates" { Mock Show-WinUtilMessage { "No" } Invoke-WPFUpdatesdisable Should -Invoke Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter { $Title -eq "Disable Windows Update?" -and $Button -eq "YesNo" -and $Icon -eq "Warning" } Should -Not -Invoke Set-ItemProperty Should -Not -Invoke Set-Service Should -Not -Invoke Remove-Item } } Describe "Invoke-WPFUpdatesdefault" { BeforeEach { Mock Write-Host { } Mock Write-WinUtilLog { } Mock Remove-Item { } Mock Remove-ItemProperty { } Mock Get-ItemProperty { [pscustomobject]@{ SettingsPageVisibility = "hide:windowsupdate" } } Mock Set-Service { } Mock Start-Service { } Mock Get-ScheduledTask { [pscustomobject]@{ TaskPath = $TaskPath } } Mock Enable-ScheduledTask { } Mock secedit { } } It "removes only registry values managed by WinUtil" { Invoke-WPFUpdatesdefault $expectedRegistryValues = @( @("HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU", "NoAutoUpdate", "AUOptions", "NoAutoRebootWithLoggedOnUsers", "AUPowerManagement"), @("HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate", "ExcludeWUDriversInQualityUpdate", "DeferFeatureUpdates", "DeferFeatureUpdatesPeriodInDays", "DeferQualityUpdates", "DeferQualityUpdatesPeriodInDays"), @("HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings", "BranchReadinessLevel", "DeferFeatureUpdatesPeriodInDays", "DeferQualityUpdatesPeriodInDays"), @("HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata", "PreventDeviceMetadataFromNetwork"), @("HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching", "DontPromptForWindowsUpdate", "DontSearchWindowsUpdate", "DriverUpdateWizardWuSearchEnabled"), @("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config", "DODownloadMode") ) foreach ($expectedEntry in $expectedRegistryValues) { $expectedPath = $expectedEntry[0] foreach ($expectedName in $expectedEntry[1..($expectedEntry.Count - 1)]) { $valueName = $expectedName Should -Invoke -CommandName Remove-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq $expectedPath -and $Name -eq $valueName } } } Should -Not -Invoke Remove-Item Should -Invoke -CommandName Remove-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer" -and $Name -eq "SettingsPageVisibility" } } It "preserves unrelated Settings page visibility policy" { Mock Get-ItemProperty { [pscustomobject]@{ SettingsPageVisibility = "hide:privacy" } } Invoke-WPFUpdatesdefault Should -Not -Invoke Remove-ItemProperty -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer" -and $Name -eq "SettingsPageVisibility" } } It "restores update service startup types" { Invoke-WPFUpdatesdefault Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "BITS" -and $StartupType -eq "Manual" } Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "wuauserv" -and $StartupType -eq "Manual" } Should -Invoke -CommandName Start-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "UsoSvc" } Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "UsoSvc" -and $StartupType -eq "Automatic" } Should -Not -Invoke Set-Service -ParameterFilter { $Name -eq "WaaSMedicSvc" } } It "enables update scheduled task paths without resetting unrelated local security policy" { Invoke-WPFUpdatesdefault foreach ($expectedTaskPath in $script:updateTaskPaths) { $expected = $expectedTaskPath Should -Invoke -CommandName Get-ScheduledTask -Times 1 -Exactly -ParameterFilter { $TaskPath -eq $expected } } Should -Invoke -CommandName Enable-ScheduledTask -Times $script:updateTaskPaths.Count -Exactly Should -Not -Invoke secedit } } Describe "Invoke-WPFUpdatessecurity" { BeforeEach { Mock Write-Host { } Mock Write-WinUtilLog { } Mock New-Item { } Mock Set-ItemProperty { } Mock Remove-ItemProperty { } Mock Set-Service { } Mock Start-Service { } Mock Get-ScheduledTask { [pscustomobject]@{ TaskPath = $TaskPath } } Mock Enable-ScheduledTask { } } It "restores update availability before applying recommended settings" { Invoke-WPFUpdatessecurity Should -Invoke -CommandName Remove-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and $Name -eq "NoAutoUpdate" } Should -Invoke -CommandName Remove-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -and $Name -eq "DODownloadMode" } Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "BITS" -and $StartupType -eq "Manual" } Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "wuauserv" -and $StartupType -eq "Manual" } Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "UsoSvc" -and $StartupType -eq "Automatic" } Should -Invoke -CommandName Start-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "UsoSvc" } foreach ($expectedTaskPath in $script:updateTaskPaths) { $expected = $expectedTaskPath Should -Invoke -CommandName Get-ScheduledTask -Times 1 -Exactly -ParameterFilter { $TaskPath -eq $expected } } Should -Invoke -CommandName Enable-ScheduledTask -Times $script:updateTaskPaths.Count -Exactly } It "disables driver metadata and Windows Update driver search" { Invoke-WPFUpdatessecurity Should -Invoke -CommandName New-Item -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" -and $Force -eq $true } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" -and $Name -eq "PreventDeviceMetadataFromNetwork" -and $Type -eq "DWord" -and $Value -eq 1 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -and $Name -eq "DontPromptForWindowsUpdate" -and $Type -eq "DWord" -and $Value -eq 1 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -and $Name -eq "DontSearchWindowsUpdate" -and $Type -eq "DWord" -and $Value -eq 1 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -and $Name -eq "DriverUpdateWizardWuSearchEnabled" -and $Type -eq "DWord" -and $Value -eq 0 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -and $Name -eq "ExcludeWUDriversInQualityUpdate" -and $Type -eq "DWord" -and $Value -eq 1 } } It "sets recommended update deferral and auto-reboot policy values" { Invoke-WPFUpdatessecurity Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -and $Name -eq "DeferFeatureUpdates" -and $Type -eq "DWord" -and $Value -eq 1 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -and $Name -eq "DeferFeatureUpdatesPeriodInDays" -and $Type -eq "DWord" -and $Value -eq 365 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -and $Name -eq "DeferQualityUpdates" -and $Type -eq "DWord" -and $Value -eq 1 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -and $Name -eq "DeferQualityUpdatesPeriodInDays" -and $Type -eq "DWord" -and $Value -eq 4 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and $Name -eq "AUOptions" -and $Type -eq "DWord" -and $Value -eq 4 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and $Name -eq "NoAutoRebootWithLoggedOnUsers" -and $Type -eq "DWord" -and $Value -eq 1 } Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and $Name -eq "AUPowerManagement" -and $Type -eq "DWord" -and $Value -eq 0 } } It "removes legacy WinUtil deferral values from the unsupported UX settings path" { Invoke-WPFUpdatessecurity foreach ($expectedValueName in @("BranchReadinessLevel", "DeferFeatureUpdatesPeriodInDays", "DeferQualityUpdatesPeriodInDays")) { $expected = $expectedValueName Should -Invoke Remove-ItemProperty -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -and $Name -eq $expected } } } }