58 lines
3.3 KiB
PowerShell
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
|
|
}
|