Files
QVPN/scripts/verify-recovery-installed.ps1

58 lines
3.3 KiB
PowerShell

[CmdletBinding()]
param()
$ErrorActionPreference='Stop'
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
$dll=Join-Path $repo 'tests/QVPN.Tests/bin/Release/net10.0-windows/QVPN.Tests.dll'
$root=Join-Path $env:ProgramFiles 'QVPN'
$report=Join-Path $repo 'research/installed-recovery-verification.json'
$checks=[Collections.Generic.List[object]]::new()
function Status {
$raw=& dotnet $dll --client status
if($raw -notmatch '^\{'){throw 'Status endpoint not available'}
$response=$raw|ConvertFrom-Json
if(!$response.Ok){throw 'Status endpoint returned failure'}
return $response.Status
}
function Wait-Recovered([int]$oldEngine,[int]$oldService=0) {
$timer=[Diagnostics.Stopwatch]::StartNew()
do {
Start-Sleep -Seconds 2
try {
$s=Status
$service=Get-CimInstance Win32_Service -Filter "Name='QVPNService'"
if($s.State -eq 'connected' -and $s.DesiredConnected -and $s.EnginePid -and $s.EnginePid -ne $oldEngine -and (!$oldService -or $service.ProcessId -ne $oldService)){return $s}
}catch{}
}while($timer.Elapsed.TotalSeconds -lt 60)
throw 'Automatic recovery did not complete within 60 seconds'
}
try {
$s=Status
if($s.State -ne 'connected' -or !$s.DesiredConnected){throw 'QVPN must already be connected for this test'}
$oldEngine=$s.EnginePid
$p=Get-Process -Id $oldEngine
if($p.Path -ne (Join-Path $root 'core/sing-box.exe')){throw 'Unexpected engine executable'}
$timer=[Diagnostics.Stopwatch]::StartNew()
Stop-Process -Id $oldEngine -Force
$s=Wait-Recovered $oldEngine
$checks.Add([pscustomobject]@{Scenario='Installed TUN engine crash recovery';Passed=$true;Seconds=[Math]::Round($timer.Elapsed.TotalSeconds,1);Restarts=$s.Restarts;KeyEnteredAgain=$false})
$oldEngine=$s.EnginePid
$service=Get-CimInstance Win32_Service -Filter "Name='QVPNService'"
$p=Get-Process -Id $service.ProcessId
if($p.Path -ne (Join-Path $root 'service/QVPN.Service.exe')){throw 'Unexpected service executable'}
$oldService=$p.Id
$timer.Restart()
Stop-Process -Id $oldService -Force
Start-Sleep -Seconds 2
$orphan=Get-Process -Id $oldEngine -ErrorAction SilentlyContinue
if($orphan -and $orphan.Path -eq (Join-Path $root 'core/sing-box.exe')){throw 'Orphan engine survived service crash'}
$s=Wait-Recovered $oldEngine $oldService
$checks.Add([pscustomobject]@{Scenario='Installed SCM crash recovery and persisted intent/key';Passed=$true;Seconds=[Math]::Round($timer.Elapsed.TotalSeconds,1);OrphanEngine=$false;Nodes=$s.Nodes.Count})
$http=& curl.exe --silent --show-error --noproxy '*' --max-time 15 --output NUL --write-out '%{http_code}' https://cp.cloudflare.com/generate_204
if($LASTEXITCODE -ne 0 -or $http -ne '204'){throw 'System HTTPS check failed after recovery'}
$checks.Add([pscustomobject]@{Scenario='System HTTPS after both crashes';Passed=$true;HttpCode=$http})
[pscustomobject]@{Completed=$true;At=[DateTimeOffset]::Now.ToString('o');Checks=$checks.ToArray()}|ConvertTo-Json -Depth 8|Set-Content -LiteralPath $report -Encoding utf8
}catch{
[pscustomobject]@{Completed=$false;At=[DateTimeOffset]::Now.ToString('o');Failure=$_.Exception.GetType().Name+': '+$_.Exception.Message;Checks=$checks.ToArray()}|ConvertTo-Json -Depth 8|Set-Content -LiteralPath $report -Encoding utf8
exit 1
}