mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-10-08 11:39:10 +11:00
feat(dns): Add real-time DNS speed benchmark and 'Fastest' auto-selection option (#4935)
* feat(dns): Add real-time DNS speed benchmark and 'Fastest' auto-selection option * fix(dns): Address CodeRabbit & Codex PR review feedback * fix(ci): Remove .biomeignore to keep repository root clean * Fix Fastest DNS eligibility and failed probe handling --------- Co-authored-by: Chris Titus <contact@christitus.com>
This commit is contained in:
co-authored by
Chris Titus
parent
f65d2631a0
commit
8e3998d9fd
@@ -0,0 +1,79 @@
|
||||
BeforeAll {
|
||||
. "$PSScriptRoot/../functions/private/Get-WinUtilDNSBenchmark.ps1"
|
||||
function Write-WinUtilLog { param($Message, $Level, $Component) }
|
||||
}
|
||||
|
||||
Describe 'Get-WinUtilDNSBenchmark' {
|
||||
BeforeEach {
|
||||
$script:sync = @{ configs = @{ dns = Get-Content "$PSScriptRoot/../config/dns.json" -Raw | ConvertFrom-Json } }
|
||||
$script:probeCompleted = $true
|
||||
$script:connectFails = $false
|
||||
$script:disposed = 0
|
||||
$script:ended = 0
|
||||
$script:probed = @()
|
||||
Mock Write-WinUtilLog { }
|
||||
Mock New-Object {
|
||||
$client = [pscustomobject]@{}
|
||||
$client | Add-Member ScriptMethod BeginConnect {
|
||||
param($Address, $Port, $Callback, $State)
|
||||
$script:probed += $Address
|
||||
$handle = [pscustomobject]@{}
|
||||
$handle | Add-Member ScriptMethod WaitOne { param($Timeout, $ExitContext) return $script:probeCompleted }
|
||||
return [pscustomobject]@{ AsyncWaitHandle = $handle }
|
||||
}
|
||||
$client | Add-Member ScriptMethod EndConnect {
|
||||
param($Result)
|
||||
$script:ended++
|
||||
if ($script:connectFails) { throw 'Connection refused' }
|
||||
}
|
||||
$client | Add-Member ScriptMethod Dispose { $script:disposed++ }
|
||||
return $client
|
||||
} -ParameterFilter { $TypeName -eq 'System.Net.Sockets.TcpClient' }
|
||||
}
|
||||
|
||||
AfterEach {
|
||||
Remove-Variable sync -Scope Script
|
||||
}
|
||||
|
||||
It 'only probes explicitly eligible unfiltered providers, including when an unknown provider is added' {
|
||||
$script:sync.configs.dns | Add-Member NoteProperty UnknownProvider ([pscustomobject]@{ Primary = '192.0.2.1' })
|
||||
$results = @(Get-WinUtilDNSBenchmark)
|
||||
$results.Count | Should -Be 2
|
||||
$results.Provider | Should -Contain 'Google'
|
||||
$results.Provider | Should -Contain 'Cloudflare'
|
||||
$script:probed.Count | Should -Be 2
|
||||
$script:probed | Should -Contain '8.8.8.8'
|
||||
$script:probed | Should -Contain '1.1.1.1'
|
||||
@($results | Where-Object LatencyMs -ge 9999).Count | Should -Be 0
|
||||
$script:ended | Should -Be 2
|
||||
$script:disposed | Should -Be 2
|
||||
}
|
||||
|
||||
It 'disposes timed out probes and marks them unavailable' {
|
||||
$script:probeCompleted = $false
|
||||
$results = @(Get-WinUtilDNSBenchmark)
|
||||
@($results | Where-Object LatencyMs -eq 9999).Count | Should -Be 2
|
||||
$script:disposed | Should -Be 2
|
||||
$script:ended | Should -Be 0
|
||||
}
|
||||
|
||||
It 'does not treat EndConnect failures as successful latency measurements' {
|
||||
$script:connectFails = $true
|
||||
$results = @(Get-WinUtilDNSBenchmark)
|
||||
@($results | Where-Object LatencyMs -eq 9999).Count | Should -Be 2
|
||||
$script:disposed | Should -Be 2
|
||||
$script:ended | Should -Be 2
|
||||
}
|
||||
|
||||
It 'returns no results when no providers are eligible' {
|
||||
$script:sync.configs.dns.Google.BenchmarkEligible = $false
|
||||
$script:sync.configs.dns.Cloudflare.BenchmarkEligible = $false
|
||||
@(Get-WinUtilDNSBenchmark).Count | Should -Be 0
|
||||
Should -Invoke New-Object -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It 'rejects infinite or out-of-range timeouts' {
|
||||
{ Get-WinUtilDNSBenchmark -TimeoutMs -1 } | Should -Throw
|
||||
{ Get-WinUtilDNSBenchmark -TimeoutMs 10000 } | Should -Throw
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ BeforeAll {
|
||||
function Clear-DnsClientCache { }
|
||||
|
||||
. (Join-Path $script:repoRoot "functions\private\Set-WinUtilDNS.ps1")
|
||||
. (Join-Path $script:repoRoot "functions\private\Get-WinUtilDNSBenchmark.ps1")
|
||||
}
|
||||
|
||||
Describe "Set-WinUtilDNS" {
|
||||
@@ -131,6 +132,35 @@ Describe "Set-WinUtilDNS" {
|
||||
Should -Invoke -CommandName Clear-DnsClientCache -Times 1 -Exactly
|
||||
}
|
||||
|
||||
It "preserves DNS when Fastest has no successful probes" {
|
||||
Mock Get-WinUtilDNSBenchmark {
|
||||
[pscustomobject]@{ Provider = 'Cloudflare'; LatencyMs = 9999 }
|
||||
}
|
||||
Set-WinUtilDNS -DNSProvider 'Fastest' | Should -BeFalse
|
||||
Should -Invoke Get-NetAdapter -Times 0 -Exactly
|
||||
Should -Invoke Set-DnsClientServerAddress -Times 0 -Exactly
|
||||
Should -Invoke New-ItemProperty -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It "preserves DNS when Fastest returns no results" {
|
||||
Mock Get-WinUtilDNSBenchmark { }
|
||||
Set-WinUtilDNS -DNSProvider 'Fastest' | Should -BeFalse
|
||||
Should -Invoke Get-NetAdapter -Times 0 -Exactly
|
||||
Should -Invoke Set-DnsClientServerAddress -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It "applies the successful Fastest provider through the existing DNS and DoH path" {
|
||||
Mock Get-WinUtilDNSBenchmark {
|
||||
[pscustomobject]@{ Provider = 'Cloudflare'; LatencyMs = 12 }
|
||||
[pscustomobject]@{ Provider = 'Google'; LatencyMs = 9999 }
|
||||
}
|
||||
Set-WinUtilDNS -DNSProvider 'Fastest' | Should -BeTrue
|
||||
Should -Invoke Set-DnsClientServerAddress -Times 1 -Exactly -ParameterFilter {
|
||||
$ServerAddresses[0] -eq '1.1.1.1' -and $ServerAddresses[1] -eq '1.0.0.1'
|
||||
}
|
||||
Should -Invoke Add-DnsClientDohServerAddress -Times 4 -Exactly
|
||||
}
|
||||
|
||||
It "updates an existing DoH entry with the selected provider settings" {
|
||||
Mock Get-DnsClientDohServerAddress {
|
||||
if ($ServerAddress -eq "1.1.1.1") {
|
||||
|
||||
Reference in New Issue
Block a user