Adding Mullvad to DNS (#4927)

* Update dns.json

* Fix Mullvad DNS review issues

* Add Mullvad secondary resolvers

* Register DoH before changing adapter DNS

* Report DNS failures to tweak workflow

* Handle non-terminating DNS assignment failures

* Preserve DNS fallback and document Mullvad options

---------

Co-authored-by: Chris Titus <contact@christitus.com>
This commit is contained in:
Graham David
2026-08-09 19:43:19 -05:00
committed by GitHub
co-authored by Chris Titus
parent afc3e1eec2
commit 0aa4ab3a40
8 changed files with 262 additions and 29 deletions
+54
View File
@@ -54,5 +54,59 @@
"Primary6": "2a10:50c0::bad1:ff", "Primary6": "2a10:50c0::bad1:ff",
"Secondary6": "2a10:50c0::bad2:ff", "Secondary6": "2a10:50c0::bad2:ff",
"DohTemplate": "https://family.adguard-dns.com/dns-query" "DohTemplate": "https://family.adguard-dns.com/dns-query"
},
"Mullvad":{
"Primary": "194.242.2.2",
"Secondary": "194.242.2.3",
"Primary6": "2a07:e340::2",
"Secondary6": "2a07:e340::3",
"DohOnly": true,
"DohTemplate": "https://dns.mullvad.net/dns-query",
"SecondaryDohTemplate": "https://adblock.dns.mullvad.net/dns-query"
},
"Mullvad_Ads_Trackers":{
"Primary": "194.242.2.3",
"Secondary": "194.242.2.2",
"Primary6": "2a07:e340::3",
"Secondary6": "2a07:e340::2",
"DohOnly": true,
"DohTemplate": "https://adblock.dns.mullvad.net/dns-query",
"SecondaryDohTemplate": "https://dns.mullvad.net/dns-query"
},
"Mullvad_Ads_Trackers_Malware":{
"Primary": "194.242.2.4",
"Secondary": "194.242.2.3",
"Primary6": "2a07:e340::4",
"Secondary6": "2a07:e340::3",
"DohOnly": true,
"DohTemplate": "https://base.dns.mullvad.net/dns-query",
"SecondaryDohTemplate": "https://adblock.dns.mullvad.net/dns-query"
},
"Mullvad_Ads_Trackers_Malware_Social":{
"Primary": "194.242.2.5",
"Secondary": "194.242.2.4",
"Primary6": "2a07:e340::5",
"Secondary6": "2a07:e340::4",
"DohOnly": true,
"DohTemplate": "https://extended.dns.mullvad.net/dns-query",
"SecondaryDohTemplate": "https://base.dns.mullvad.net/dns-query"
},
"Mullvad_Ads_Trackers_Malware_Adult_Gambling":{
"Primary": "194.242.2.6",
"Secondary": "194.242.2.5",
"Primary6": "2a07:e340::6",
"Secondary6": "2a07:e340::5",
"DohOnly": true,
"DohTemplate": "https://family.dns.mullvad.net/dns-query",
"SecondaryDohTemplate": "https://extended.dns.mullvad.net/dns-query"
},
"Mullvad_Ads_Trackers_Malware_Adult_Gambling_Social":{
"Primary": "194.242.2.9",
"Secondary": "194.242.2.6",
"Primary6": "2a07:e340::9",
"Secondary6": "2a07:e340::6",
"DohOnly": true,
"DohTemplate": "https://all.dns.mullvad.net/dns-query",
"SecondaryDohTemplate": "https://family.dns.mullvad.net/dns-query"
} }
} }
+1 -1
View File
@@ -1864,7 +1864,7 @@
"category": "z__Advanced Tweaks - CAUTION", "category": "z__Advanced Tweaks - CAUTION",
"panel": "1", "panel": "1",
"Type": "Combobox", "Type": "Combobox",
"ComboItems": "Default DHCP Google Cloudflare Cloudflare_Malware Cloudflare_Malware_Adult Open_DNS Quad9 AdGuard_Ads_Trackers AdGuard_Ads_Trackers_Malware_Adult", "ComboItems": "Default DHCP Google Cloudflare Cloudflare_Malware Cloudflare_Malware_Adult Open_DNS Quad9 AdGuard_Ads_Trackers AdGuard_Ads_Trackers_Malware_Adult Mullvad Mullvad_Ads_Trackers Mullvad_Ads_Trackers_Malware Mullvad_Ads_Trackers_Malware_Social Mullvad_Ads_Trackers_Malware_Adult_Gambling Mullvad_Ads_Trackers_Malware_Adult_Gambling_Social",
"link": "https://winutil.christitus.com/code-reference/tweaks/z--advanced-tweaks---caution/changedns" "link": "https://winutil.christitus.com/code-reference/tweaks/z--advanced-tweaks---caution/changedns"
}, },
"WPFAddUltPerf": { "WPFAddUltPerf": {
+8
View File
@@ -73,6 +73,14 @@ Use the DNS section to switch both IPv4 and IPv6 DNS providers without editing a
* [**Quad9**](https://quad9.net/): Focuses on security by blocking known malicious domains. * [**Quad9**](https://quad9.net/): Focuses on security by blocking known malicious domains.
* [**AdGuard_Ads_Trackers**](https://adguard-dns.io/en/welcome.html): AdGuard DNS blocks ads, trackers, and other unwanted DNS requests. Visit the website and sign in for a dashboard, statistics, and additional server-side customization. * [**AdGuard_Ads_Trackers**](https://adguard-dns.io/en/welcome.html): AdGuard DNS blocks ads, trackers, and other unwanted DNS requests. Visit the website and sign in for a dashboard, statistics, and additional server-side customization.
* [**AdGuard_Ads_Trackers_Malware_Adult**](https://adguard-dns.io/en/welcome.html): AdGuard DNS blocks ads, trackers, malware, and adult content, and enables Safe Search and Safe Mode where possible. * [**AdGuard_Ads_Trackers_Malware_Adult**](https://adguard-dns.io/en/welcome.html): AdGuard DNS blocks ads, trackers, malware, and adult content, and enables Safe Search and Safe Mode where possible.
* [**Mullvad**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Mullvad DNS without content blocking.
* [**Mullvad_Ads_Trackers**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Blocks ads and trackers.
* [**Mullvad_Ads_Trackers_Malware**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Blocks ads, trackers, and malware.
* [**Mullvad_Ads_Trackers_Malware_Social**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Blocks ads, trackers, malware, and social media.
* [**Mullvad_Ads_Trackers_Malware_Adult_Gambling**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Blocks ads, trackers, malware, adult content, and gambling.
* [**Mullvad_Ads_Trackers_Malware_Adult_Gambling_Social**](https://mullvad.net/en/help/dns-over-https-and-dns-over-tls): Applies all available Mullvad filters.
Mullvad profiles require DNS over HTTPS support in Windows. If the selected primary resolver is unavailable, WinUtil uses the closest Mullvad secondary resolver to preserve connectivity; that fallback may use a different filtering level and can be less restrictive.
### Customize Preferences ### Customize Preferences
+46 -21
View File
@@ -15,7 +15,7 @@ function Set-WinUtilDNS {
if($DNSProvider -eq "Default") { if($DNSProvider -eq "Default") {
Write-WinUtilLog -Component "DNS" -Message "DNS provider is Default; no DNS changes applied." Write-WinUtilLog -Component "DNS" -Message "DNS provider is Default; no DNS changes applied."
return return $true
} }
try { try {
@@ -29,11 +29,17 @@ function Set-WinUtilDNS {
if($null -eq $dns) { if($null -eq $dns) {
Write-Warning "DNS provider $DNSProvider was not found in configuration." Write-Warning "DNS provider $DNSProvider was not found in configuration."
Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "DNS provider $DNSProvider was not found in configuration." Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "DNS provider $DNSProvider was not found in configuration."
return return $false
} }
} }
$dohSupported = [bool](Get-Command Add-DnsClientDohServerAddress -ErrorAction SilentlyContinue) $dohSupported = [bool](Get-Command Add-DnsClientDohServerAddress -ErrorAction SilentlyContinue)
if ($DNSProvider -ne "DHCP" -and $dns.DohOnly -and -not $dohSupported) {
Write-Warning "DNS provider $DNSProvider requires DNS over HTTPS, which is not supported on this system."
Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "DNS provider $DNSProvider requires DNS over HTTPS, which is not supported on this system."
return $false
}
$dnscacheBase = "HKLM:\System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters" $dnscacheBase = "HKLM:\System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters"
Foreach ($Adapter in $Adapters) { Foreach ($Adapter in $Adapters) {
@@ -64,40 +70,59 @@ function Set-WinUtilDNS {
Remove-Item -Path $dohInterfaceSettings -Recurse -Force -ErrorAction SilentlyContinue Remove-Item -Path $dohInterfaceSettings -Recurse -Force -ErrorAction SilentlyContinue
} }
} else { } else {
Write-WinUtilLog -Component "DNS" -Message "Setting IPv4 DNS on adapter $($Adapter.Name) (ifIndex: $($Adapter.ifIndex)) to $($dns.Primary), $($dns.Secondary)." $ipv4Addresses = @(@($dns.Primary, $dns.Secondary) | Where-Object { $_ })
Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ServerAddresses ($dns.Primary, $dns.Secondary) $ipv6Addresses = @(@($dns.Primary6, $dns.Secondary6) | Where-Object { $_ })
Write-WinUtilLog -Component "DNS" -Message "Setting IPv6 DNS on adapter $($Adapter.Name) (ifIndex: $($Adapter.ifIndex)) to $($dns.Primary6), $($dns.Secondary6)."
Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ServerAddresses ($dns.Primary6, $dns.Secondary6)
if ($dohSupported -and $dns.DohTemplate) { if ($dohSupported -and $dns.DohTemplate) {
$ips = @($dns.Primary, $dns.Secondary, $dns.Primary6, $dns.Secondary6) | Where-Object { $_ } try {
foreach ($ip in $ips) { $ips = @($dns.Primary, $dns.Secondary, $dns.Primary6, $dns.Secondary6) | Where-Object { $_ }
$existing = Get-DnsClientDohServerAddress -ServerAddress $ip -ErrorAction SilentlyContinue foreach ($ip in $ips) {
if ($existing) { $dohTemplate = if ($dns.SecondaryDohTemplate -and @($dns.Secondary, $dns.Secondary6) -contains $ip) {
Set-DnsClientDohServerAddress -ServerAddress $ip -DohTemplate $dns.DohTemplate -AllowFallbackToUdp $false -AutoUpgrade $true -ErrorAction Stop $dns.SecondaryDohTemplate
} else { } else {
Write-WinUtilLog -Component "DNS" -Message "Registering DoH template for $ip." $dns.DohTemplate
Add-DnsClientDohServerAddress -ServerAddress $ip -DohTemplate $dns.DohTemplate -AllowFallbackToUdp $false -AutoUpgrade $true -ErrorAction Stop }
$existing = Get-DnsClientDohServerAddress -ServerAddress $ip -ErrorAction SilentlyContinue
if ($existing) {
Set-DnsClientDohServerAddress -ServerAddress $ip -DohTemplate $dohTemplate -AllowFallbackToUdp $false -AutoUpgrade $true -ErrorAction Stop
} else {
Write-WinUtilLog -Component "DNS" -Message "Registering DoH template for $ip."
Add-DnsClientDohServerAddress -ServerAddress $ip -DohTemplate $dohTemplate -AllowFallbackToUdp $false -AutoUpgrade $true -ErrorAction Stop
}
$leaf = if ($ip.Contains(':')) { 'Doh6' } else { 'Doh' }
$regPath = "$interfaceParams\DohInterfaceSettings\$leaf\$ip"
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force -ErrorAction Stop | Out-Null
}
New-ItemProperty -Path $regPath -Name "DohFlags" -Value 1 -PropertyType QWord -Force -ErrorAction Stop | Out-Null
} }
} catch {
$leaf = if ($ip.Contains(':')) { 'Doh6' } else { 'Doh' } if ($dns.DohOnly) {
$regPath = "$interfaceParams\DohInterfaceSettings\$leaf\$ip" throw
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force -ErrorAction Stop | Out-Null
} }
New-ItemProperty -Path $regPath -Name "DohFlags" -Value 1 -PropertyType QWord -Force -ErrorAction Stop | Out-Null
Write-Warning "DNS over HTTPS setup for provider $DNSProvider failed; continuing with plain DNS."
Write-WinUtilLog -Level "WARN" -Component "DNS" -Message "DNS over HTTPS setup for provider $DNSProvider failed; continuing with plain DNS: $($psitem.Exception.Message)"
} }
} }
Write-WinUtilLog -Component "DNS" -Message "Setting IPv4 DNS on adapter $($Adapter.Name) (ifIndex: $($Adapter.ifIndex)) to $($dns.Primary), $($dns.Secondary)."
Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ServerAddresses $ipv4Addresses -ErrorAction Stop
Write-WinUtilLog -Component "DNS" -Message "Setting IPv6 DNS on adapter $($Adapter.Name) (ifIndex: $($Adapter.ifIndex)) to $($dns.Primary6), $($dns.Secondary6)."
Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ServerAddresses $ipv6Addresses -ErrorAction Stop
} }
} }
if ($DNSProvider -ne "DHCP" -and $dohSupported -and $dns.DohTemplate) { if ($DNSProvider -ne "DHCP" -and $dohSupported -and $dns.DohTemplate) {
Clear-DnsClientCache Clear-DnsClientCache
} }
Write-WinUtilLog -Component "DNS" -Message "DNS provider change completed: $DNSProvider" Write-WinUtilLog -Component "DNS" -Message "DNS provider change completed: $DNSProvider"
return $true
} catch { } catch {
Write-Warning "DNS provider $DNSProvider was not completed because an error occurred." Write-Warning "DNS provider $DNSProvider was not completed because an error occurred."
Write-Warning $psitem.Exception.Message Write-Warning $psitem.Exception.Message
Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "DNS provider $DNSProvider was not completed: $($psitem.Exception.Message)" Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "DNS provider $DNSProvider was not completed: $($psitem.Exception.Message)"
return $false
} }
} }
+8 -1
View File
@@ -71,7 +71,14 @@ function Invoke-WPFtweaksbutton {
} }
if ($dnsProvider -ne "Default") { if ($dnsProvider -ne "Default") {
Set-WinUtilDNS -DNSProvider $dnsProvider $dnsResult = @(Set-WinUtilDNS -DNSProvider $dnsProvider)
if ($dnsResult[-1] -ne $true) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "DNS change failed" -Percent 100
$sync.ProcessRunning = $false
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
Write-WinUtilLog -Level "ERROR" -Component "Tweaks" -Message "Tweaks workflow stopped because the DNS change failed."
return
}
} }
for ($i = 0; $i -lt $tweaks.Count; $i++) { for ($i = 0; $i -lt $tweaks.Count; $i++) {
+11
View File
@@ -374,6 +374,17 @@ Describe "UI-rendered config entries" {
} }
} }
It "exposes every configured DNS provider in the DNS combobox" {
$dns = Get-WinUtilConfigObject -Name "dns"
$tweaks = Get-WinUtilConfigObject -Name "tweaks"
$comboItems = @($tweaks.WPFchangedns.ComboItems -split " ")
$missingProviders = @($dns.PSObject.Properties.Name | Where-Object { $comboItems -notcontains $_ })
if ($missingProviders.Count -gt 0) {
throw "WPFchangedns missing providers: $($missingProviders -join ', ')"
}
}
It "contains required feature fields and valid configured functions" { It "contains required feature fields and valid configured functions" {
$feature = Get-WinUtilConfigObject -Name "feature" $feature = Get-WinUtilConfigObject -Name "feature"
$functionNames = Get-WinUtilTopLevelFunctionNames $functionNames = Get-WinUtilTopLevelFunctionNames
+105 -5
View File
@@ -10,7 +10,8 @@ BeforeAll {
param( param(
$InterfaceIndex, $InterfaceIndex,
$ServerAddresses, $ServerAddresses,
[switch]$ResetServerAddresses [switch]$ResetServerAddresses,
$ErrorAction
) )
} }
function netsh { function netsh {
@@ -52,6 +53,23 @@ Describe "Set-WinUtilDNS" {
Secondary6 = "2606:4700:4700::1001" Secondary6 = "2606:4700:4700::1001"
DohTemplate = "https://cloudflare-dns.com/dns-query" DohTemplate = "https://cloudflare-dns.com/dns-query"
} }
Mullvad = [pscustomobject]@{
Primary = "194.242.2.2"
Secondary = "194.242.2.3"
Primary6 = "2a07:e340::2"
Secondary6 = "2a07:e340::3"
DohOnly = $true
DohTemplate = "https://dns.mullvad.net/dns-query"
SecondaryDohTemplate = "https://adblock.dns.mullvad.net/dns-query"
}
MullvadNoSecondary = [pscustomobject]@{
Primary = "194.242.2.2"
Secondary = ""
Primary6 = "2a07:e340::2"
Secondary6 = ""
DohOnly = $true
DohTemplate = "https://dns.mullvad.net/dns-query"
}
} }
} }
}) })
@@ -88,8 +106,9 @@ Describe "Set-WinUtilDNS" {
} }
It "sets IPv4 and IPv6 DNS server addresses separately and applies DoH templates" { It "sets IPv4 and IPv6 DNS server addresses separately and applies DoH templates" {
Set-WinUtilDNS -DNSProvider "Cloudflare" $result = Set-WinUtilDNS -DNSProvider "Cloudflare"
$result | Should -BeTrue
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Set-DnsClientServerAddress -Times 1 -Exactly -ParameterFilter {
$InterfaceIndex -eq 7 -and $InterfaceIndex -eq 7 -and
$ServerAddresses.Count -eq 2 -and $ServerAddresses.Count -eq 2 -and
@@ -131,6 +150,79 @@ Describe "Set-WinUtilDNS" {
Should -Invoke -CommandName Add-DnsClientDohServerAddress -Times 3 -Exactly Should -Invoke -CommandName Add-DnsClientDohServerAddress -Times 3 -Exactly
} }
It "filters empty DNS server addresses" {
Set-WinUtilDNS -DNSProvider "MullvadNoSecondary"
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 1 -Exactly -ParameterFilter {
$InterfaceIndex -eq 7 -and
$ServerAddresses.Count -eq 1 -and
$ServerAddresses[0] -eq "194.242.2.2"
}
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 1 -Exactly -ParameterFilter {
$InterfaceIndex -eq 7 -and
$ServerAddresses.Count -eq 1 -and
$ServerAddresses[0] -eq "2a07:e340::2"
}
Should -Invoke -CommandName Add-DnsClientDohServerAddress -Times 2 -Exactly
}
It "applies the matching DoH template to secondary resolvers" {
Set-WinUtilDNS -DNSProvider "Mullvad"
Should -Invoke -CommandName Add-DnsClientDohServerAddress -Times 2 -Exactly -ParameterFilter {
$ServerAddress -in @("194.242.2.2", "2a07:e340::2") -and
$DohTemplate -eq "https://dns.mullvad.net/dns-query"
}
Should -Invoke -CommandName Add-DnsClientDohServerAddress -Times 2 -Exactly -ParameterFilter {
$ServerAddress -in @("194.242.2.3", "2a07:e340::3") -and
$DohTemplate -eq "https://adblock.dns.mullvad.net/dns-query"
}
}
It "does not apply a DoH-only provider when DoH is unsupported" {
Mock Get-Command { return $null } -ParameterFilter { $Name -eq "Add-DnsClientDohServerAddress" }
$result = Set-WinUtilDNS -DNSProvider "Mullvad"
$result | Should -BeFalse
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 0 -Exactly
Should -Invoke -CommandName Add-DnsClientDohServerAddress -Times 0 -Exactly
Should -Invoke -CommandName Write-Warning -Times 1 -Exactly -ParameterFilter {
$Message -eq "DNS provider Mullvad requires DNS over HTTPS, which is not supported on this system."
}
}
It "does not change adapter DNS when DoH registration fails" {
Mock Add-DnsClientDohServerAddress { throw "DoH registration failed" }
$result = Set-WinUtilDNS -DNSProvider "Mullvad"
$result | Should -BeFalse
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 0 -Exactly
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Level -eq "ERROR" -and
$Component -eq "DNS" -and
$Message -like "DNS provider Mullvad was not completed: *"
}
}
It "falls back to plain DNS when optional DoH registration fails" {
Mock Add-DnsClientDohServerAddress { throw "DoH registration failed" }
$result = Set-WinUtilDNS -DNSProvider "Cloudflare"
$result | Should -BeTrue
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 2 -Exactly
Should -Invoke -CommandName Write-Warning -Times 1 -Exactly -ParameterFilter {
$Message -eq "DNS over HTTPS setup for provider Cloudflare failed; continuing with plain DNS."
}
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Level -eq "WARN" -and
$Component -eq "DNS" -and
$Message -like "DNS over HTTPS setup for provider Cloudflare failed; continuing with plain DNS: *"
}
}
It "resets DNS to DHCP and removes the applied DoH configuration" { It "resets DNS to DHCP and removes the applied DoH configuration" {
Mock Test-Path { return $true } -ParameterFilter { $Path -like "*DohInterfaceSettings*" } Mock Test-Path { return $true } -ParameterFilter { $Path -like "*DohInterfaceSettings*" }
Mock Get-ChildItem { Mock Get-ChildItem {
@@ -178,15 +270,23 @@ Describe "Set-WinUtilDNS" {
Should -Invoke -CommandName Remove-DnsClientDohServerAddress -Times 4 -Exactly Should -Invoke -CommandName Remove-DnsClientDohServerAddress -Times 4 -Exactly
} }
It "catches DNS setter failures so the tweak runspace can continue" { It "catches non-terminating DNS setter failures so the tweak runspace can continue" {
Mock Set-DnsClientServerAddress { throw "DNS failed" } Mock Set-DnsClientServerAddress { Write-Error "DNS failed" -ErrorAction $ErrorAction }
{ Set-WinUtilDNS -DNSProvider "Cloudflare" } | Should -Not -Throw $result = Set-WinUtilDNS -DNSProvider "Cloudflare"
$result | Should -BeFalse
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Level -eq "ERROR" -and $Level -eq "ERROR" -and
$Component -eq "DNS" -and $Component -eq "DNS" -and
$Message -like "DNS provider Cloudflare was not completed: *" $Message -like "DNS provider Cloudflare was not completed: *"
} }
} }
It "returns failure for an unknown DNS provider" {
$result = Set-WinUtilDNS -DNSProvider "Unknown"
$result | Should -BeFalse
Should -Invoke -CommandName Set-DnsClientServerAddress -Times 0 -Exactly
}
} }
+29 -1
View File
@@ -181,9 +181,14 @@ Describe "Invoke-WPFtweaksbutton" {
text = "Cloudflare" text = "Cloudflare"
} }
}) })
$script:capturedTweaksScriptBlock = $null
Mock Invoke-WPFRunspace { [pscustomobject]@{ MockHandle = $true } } Mock Invoke-WPFRunspace {
$script:capturedTweaksScriptBlock = $ScriptBlock
[pscustomobject]@{ MockHandle = $true }
}
Mock Invoke-WinUtilTweaks { } Mock Invoke-WinUtilTweaks { }
Mock Set-WinUtilTweaksProgressIndicator { }
Mock Invoke-WPFUIThread { } Mock Invoke-WPFUIThread { }
Mock Write-WinUtilLog { } Mock Write-WinUtilLog { }
Mock Write-Host { } Mock Write-Host { }
@@ -191,6 +196,7 @@ Describe "Invoke-WPFtweaksbutton" {
AfterEach { AfterEach {
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name capturedTweaksScriptBlock -Scope Script -ErrorAction SilentlyContinue
} }
It "passes selected tweaks, DNS provider, and progress counters to the tweak runspace" { It "passes selected tweaks, DNS provider, and progress counters to the tweak runspace" {
@@ -236,4 +242,26 @@ Describe "Invoke-WPFtweaksbutton" {
$ParameterList[3][1] -eq 2 $ParameterList[3][1] -eq 2
} }
} }
It "stops the tweak workflow when the DNS change fails" {
$script:sync.selectedTweaks.Add("WPFTweaksTelemetry")
Mock Set-WinUtilDNS { return $false }
Invoke-WPFtweaksbutton
& $script:capturedTweaksScriptBlock -tweaks @("WPFTweaksTelemetry") -dnsProvider "Mullvad" -completedSteps 0 -totalSteps 1
Should -Invoke -CommandName Invoke-WinUtilTweaks -Times 0 -Exactly
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "DNS change failed" -and $Percent -eq 100
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
}
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Level -eq "ERROR" -and
$Component -eq "Tweaks" -and
$Message -eq "Tweaks workflow stopped because the DNS change failed."
}
$script:sync.ProcessRunning | Should -BeFalse
}
} }