From 0aa4ab3a40367ad4a6eabae77842cac7951795a5 Mon Sep 17 00:00:00 2001 From: Graham David <125028396+voyager2saturn@users.noreply.github.com> Date: Mon, 10 Aug 2026 06:13:19 +0530 Subject: [PATCH] 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 --- config/dns.json | 54 ++++++++++ config/tweaks.json | 2 +- docs/src/content/docs/guides/tweaks.mdx | 8 ++ functions/private/Set-WinUtilDNS.ps1 | 67 ++++++++---- functions/public/Invoke-WPFtweaksbutton.ps1 | 9 +- pester/configs.Tests.ps1 | 11 ++ pester/dns.Tests.ps1 | 110 +++++++++++++++++++- pester/tweaks.Tests.ps1 | 30 +++++- 8 files changed, 262 insertions(+), 29 deletions(-) diff --git a/config/dns.json b/config/dns.json index 02499902..5c36a5f6 100644 --- a/config/dns.json +++ b/config/dns.json @@ -54,5 +54,59 @@ "Primary6": "2a10:50c0::bad1:ff", "Secondary6": "2a10:50c0::bad2:ff", "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" } } diff --git a/config/tweaks.json b/config/tweaks.json index 75b5c6ff..cd2d35c8 100644 --- a/config/tweaks.json +++ b/config/tweaks.json @@ -1864,7 +1864,7 @@ "category": "z__Advanced Tweaks - CAUTION", "panel": "1", "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" }, "WPFAddUltPerf": { diff --git a/docs/src/content/docs/guides/tweaks.mdx b/docs/src/content/docs/guides/tweaks.mdx index 1c1465e9..87d1f551 100644 --- a/docs/src/content/docs/guides/tweaks.mdx +++ b/docs/src/content/docs/guides/tweaks.mdx @@ -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. * [**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. +* [**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 diff --git a/functions/private/Set-WinUtilDNS.ps1 b/functions/private/Set-WinUtilDNS.ps1 index b7e5f86c..44afd9d8 100644 --- a/functions/private/Set-WinUtilDNS.ps1 +++ b/functions/private/Set-WinUtilDNS.ps1 @@ -15,7 +15,7 @@ function Set-WinUtilDNS { if($DNSProvider -eq "Default") { Write-WinUtilLog -Component "DNS" -Message "DNS provider is Default; no DNS changes applied." - return + return $true } try { @@ -29,11 +29,17 @@ function Set-WinUtilDNS { if($null -eq $dns) { 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." - return + return $false } } $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" Foreach ($Adapter in $Adapters) { @@ -64,40 +70,59 @@ function Set-WinUtilDNS { Remove-Item -Path $dohInterfaceSettings -Recurse -Force -ErrorAction SilentlyContinue } } else { - 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 ($dns.Primary, $dns.Secondary) - 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) + $ipv4Addresses = @(@($dns.Primary, $dns.Secondary) | Where-Object { $_ }) + $ipv6Addresses = @(@($dns.Primary6, $dns.Secondary6) | Where-Object { $_ }) if ($dohSupported -and $dns.DohTemplate) { - $ips = @($dns.Primary, $dns.Secondary, $dns.Primary6, $dns.Secondary6) | Where-Object { $_ } - foreach ($ip in $ips) { - $existing = Get-DnsClientDohServerAddress -ServerAddress $ip -ErrorAction SilentlyContinue - if ($existing) { - Set-DnsClientDohServerAddress -ServerAddress $ip -DohTemplate $dns.DohTemplate -AllowFallbackToUdp $false -AutoUpgrade $true -ErrorAction Stop - } else { - Write-WinUtilLog -Component "DNS" -Message "Registering DoH template for $ip." - Add-DnsClientDohServerAddress -ServerAddress $ip -DohTemplate $dns.DohTemplate -AllowFallbackToUdp $false -AutoUpgrade $true -ErrorAction Stop + try { + $ips = @($dns.Primary, $dns.Secondary, $dns.Primary6, $dns.Secondary6) | Where-Object { $_ } + foreach ($ip in $ips) { + $dohTemplate = if ($dns.SecondaryDohTemplate -and @($dns.Secondary, $dns.Secondary6) -contains $ip) { + $dns.SecondaryDohTemplate + } else { + $dns.DohTemplate + } + $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 } - - $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 + } catch { + if ($dns.DohOnly) { + throw } - 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) { Clear-DnsClientCache } Write-WinUtilLog -Component "DNS" -Message "DNS provider change completed: $DNSProvider" + return $true } catch { Write-Warning "DNS provider $DNSProvider was not completed because an error occurred." Write-Warning $psitem.Exception.Message Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "DNS provider $DNSProvider was not completed: $($psitem.Exception.Message)" + return $false } } diff --git a/functions/public/Invoke-WPFtweaksbutton.ps1 b/functions/public/Invoke-WPFtweaksbutton.ps1 index 838fde83..bca2080f 100644 --- a/functions/public/Invoke-WPFtweaksbutton.ps1 +++ b/functions/public/Invoke-WPFtweaksbutton.ps1 @@ -71,7 +71,14 @@ function Invoke-WPFtweaksbutton { } 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++) { diff --git a/pester/configs.Tests.ps1 b/pester/configs.Tests.ps1 index cc1d7897..cf7e8880 100644 --- a/pester/configs.Tests.ps1 +++ b/pester/configs.Tests.ps1 @@ -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" { $feature = Get-WinUtilConfigObject -Name "feature" $functionNames = Get-WinUtilTopLevelFunctionNames diff --git a/pester/dns.Tests.ps1 b/pester/dns.Tests.ps1 index c7ab0896..e1376c6e 100644 --- a/pester/dns.Tests.ps1 +++ b/pester/dns.Tests.ps1 @@ -10,7 +10,8 @@ BeforeAll { param( $InterfaceIndex, $ServerAddresses, - [switch]$ResetServerAddresses + [switch]$ResetServerAddresses, + $ErrorAction ) } function netsh { @@ -52,6 +53,23 @@ Describe "Set-WinUtilDNS" { Secondary6 = "2606:4700:4700::1001" 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" { - Set-WinUtilDNS -DNSProvider "Cloudflare" + $result = Set-WinUtilDNS -DNSProvider "Cloudflare" + $result | Should -BeTrue Should -Invoke -CommandName Set-DnsClientServerAddress -Times 1 -Exactly -ParameterFilter { $InterfaceIndex -eq 7 -and $ServerAddresses.Count -eq 2 -and @@ -131,6 +150,79 @@ Describe "Set-WinUtilDNS" { 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" { Mock Test-Path { return $true } -ParameterFilter { $Path -like "*DohInterfaceSettings*" } Mock Get-ChildItem { @@ -178,15 +270,23 @@ Describe "Set-WinUtilDNS" { Should -Invoke -CommandName Remove-DnsClientDohServerAddress -Times 4 -Exactly } - It "catches DNS setter failures so the tweak runspace can continue" { - Mock Set-DnsClientServerAddress { throw "DNS failed" } + It "catches non-terminating DNS setter failures so the tweak runspace can continue" { + 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 { $Level -eq "ERROR" -and $Component -eq "DNS" -and $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 + } } diff --git a/pester/tweaks.Tests.ps1 b/pester/tweaks.Tests.ps1 index c502749c..1645a0d8 100644 --- a/pester/tweaks.Tests.ps1 +++ b/pester/tweaks.Tests.ps1 @@ -181,9 +181,14 @@ Describe "Invoke-WPFtweaksbutton" { text = "Cloudflare" } }) + $script:capturedTweaksScriptBlock = $null - Mock Invoke-WPFRunspace { [pscustomobject]@{ MockHandle = $true } } + Mock Invoke-WPFRunspace { + $script:capturedTweaksScriptBlock = $ScriptBlock + [pscustomobject]@{ MockHandle = $true } + } Mock Invoke-WinUtilTweaks { } + Mock Set-WinUtilTweaksProgressIndicator { } Mock Invoke-WPFUIThread { } Mock Write-WinUtilLog { } Mock Write-Host { } @@ -191,6 +196,7 @@ Describe "Invoke-WPFtweaksbutton" { AfterEach { 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" { @@ -236,4 +242,26 @@ Describe "Invoke-WPFtweaksbutton" { $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 + } }