Files
yovision/Bell/server/tests/bell_alert_lifecycle/run-postgres.ps1
T

29 lines
5.7 KiB
PowerShell

[CmdletBinding()] param([string]$PostgresBin='D:\pgsql17\bin')
Set-StrictMode -Version 3.0
$ErrorActionPreference='Stop'; $started=$false; $server=$null
$root=Join-Path ([IO.Path]::GetTempPath()) ('yovision-bell-133-'+[guid]::NewGuid().ToString('N'))
$data=Join-Path $root 'postgres'; $log=Join-Path $root 'postgres.log'; $serverRoot=(Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path; $serverExe=Join-Path $root 'bell.exe'
function FreePort { $l=[Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback,0); try{$l.Start();return ([Net.IPEndPoint]$l.LocalEndpoint).Port}finally{$l.Stop()} }
function WaitPort([int]$port){for($i=0;$i -lt 120;$i++){try{$c=[Net.Sockets.TcpClient]::new();$ok=$c.ConnectAsync('127.0.0.1',$port).Wait(250)-and$c.Connected;$c.Dispose();if($ok){return}}catch{};Start-Sleep -Milliseconds 250};throw 'PostgreSQL did not start'}
function Login([string]$base,[string]$username,[string]$password){$body=@{username=$username;password=$password;code='0';uuid='0'}|ConvertTo-Json -Compress; $result=Invoke-RestMethod -Method Post -Uri "$base/api/v1/login" -ContentType 'application/json' -Body $body -NoProxy; if([int]$result.code-ne 200){throw "login failed: $username"}; return @{Authorization="Bearer $($result.token)"}}
New-Item -ItemType Directory -Path $root|Out-Null; $port=FreePort
try {
foreach($name in @('initdb.exe','pg_ctl.exe','createdb.exe','psql.exe')){if(-not(Test-Path (Join-Path $PostgresBin $name))){throw "Missing $name"}}
& (Join-Path $PostgresBin 'initdb.exe') -D $data -U postgres -A trust --encoding=UTF8 --no-locale|Out-Null; if($LASTEXITCODE-ne 0){throw 'initdb failed'}
$args="-D `"$data`" -l `"$log`" -o `"-p $port -h 127.0.0.1`" start"; Start-Process (Join-Path $PostgresBin 'pg_ctl.exe') -ArgumentList $args -WindowStyle Hidden|Out-Null; WaitPort $port; $started=$true
& (Join-Path $PostgresBin 'createdb.exe') -h 127.0.0.1 -p $port -U postgres bell_133; if($LASTEXITCODE-ne 0){throw 'createdb failed'}
$bellPort=FreePort; $base="http://127.0.0.1:$bellPort"; $env:GOTOOLCHAIN='go1.26.5'; $env:BELL_DATABASE_URL="host=127.0.0.1 port=$port user=postgres dbname=bell_133 sslmode=disable"; $env:BELL_ALERT_LIFECYCLE_TEST_DATABASE_URL=$env:BELL_DATABASE_URL; $env:BELL_JWT_SECRET=[guid]::NewGuid().ToString('N')+[guid]::NewGuid().ToString('N'); $env:BELL_BOOTSTRAP_USERNAME='bell_133_admin'; $env:BELL_BOOTSTRAP_PASSWORD=[guid]::NewGuid().ToString('N'); $env:BELL_HOST='127.0.0.1'; $env:BELL_PORT=$bellPort.ToString()
Push-Location $serverRoot; try { go run . migrate -c config/settings.demo.yml *> (Join-Path $root 'migrate.log'); if($LASTEXITCODE-ne 0){throw "migration failed: $root"}; go test ./tests/bell_alert_lifecycle -count=1 -v; if($LASTEXITCODE-ne 0){throw 'lifecycle test failed'}; go build -o $serverExe . } finally { Pop-Location }
$server=Start-Process $serverExe -ArgumentList @('server','-c','config/settings.demo.yml') -WorkingDirectory $serverRoot -RedirectStandardOutput (Join-Path $root 'server.out') -RedirectStandardError (Join-Path $root 'server.err') -WindowStyle Hidden -PassThru; WaitPort $bellPort
$a=Login $base 'bell_133_operator_a' 'test-password-133'; $b=Login $base 'bell_133_operator_b' 'test-password-133'; $list=Invoke-RestMethod -Uri "$base/api/v1/bell/alerts?status=open" -Headers $a -NoProxy; $id=[string]$list.data.list[0].id; if([string]::IsNullOrWhiteSpace($id)){throw 'open alert missing'}
$ack=Invoke-RestMethod -Method Post -Uri "$base/api/v1/bell/alerts/$id/ack" -Headers $a -ContentType 'application/json' -Body '{}' -NoProxy; $late=Invoke-RestMethod -Method Post -Uri "$base/api/v1/bell/alerts/$id/ack" -Headers $b -ContentType 'application/json' -Body '{}' -NoProxy; if([int]$ack.code-ne 200-or[int]$late.code-ne 409){throw 'ack API semantics failed'}
$forbidden=Invoke-RestMethod -Method Post -Uri "$base/api/v1/bell/alerts/$id/close" -Headers $b -ContentType 'application/json' -Body '{"outcome":"site_normal"}' -NoProxy; $missing=Invoke-RestMethod -Method Post -Uri "$base/api/v1/bell/alerts/$id/close" -Headers $a -ContentType 'application/json' -Body '{}' -NoProxy; if([int]$forbidden.code-ne 403-or[int]$missing.code-ne 400){throw 'close rejection semantics failed'}
$body='{"outcome":"site_normal","note":"现场正常"}'; $closed=Invoke-RestMethod -Method Post -Uri "$base/api/v1/bell/alerts/$id/close" -Headers $a -ContentType 'application/json; charset=utf-8' -Body $body -NoProxy; $replay=Invoke-RestMethod -Method Post -Uri "$base/api/v1/bell/alerts/$id/close" -Headers $a -ContentType 'application/json; charset=utf-8' -Body $body -NoProxy; $timeline=Invoke-RestMethod -Uri "$base/api/v1/bell/alerts/$id/lifecycle" -Headers $a -NoProxy; if([int]$closed.code-ne 200-or-not$replay.data.idempotent-or$timeline.data.detail.timeline.Count-ne 2){throw 'close/timeline API semantics failed'}
$leaks=& (Join-Path $PostgresBin 'psql.exe') -h 127.0.0.1 -p $port -U postgres -d bell_133 -Atc "select count(*) from sys_opera_log where oper_url like '%/bell/alerts/%/close' and ((oper_param <> '' and oper_param not like '%redacted%') or json_result not like '%redacted%');"; if($LASTEXITCODE-ne 0-or[int]$leaks-ne 0){throw 'lifecycle note leaked into GoAdmin operation log'}
Write-Output 'BELL_133_HTTP ack=200 late_ack=409 forbidden_close=403 missing_outcome=400 close=200 replay=true timeline=2'
} finally {
if($null-ne$server-and-not$server.HasExited){Stop-Process -Id $server.Id -Force; $server.WaitForExit(5000)|Out-Null}
if($started){& (Join-Path $PostgresBin 'pg_ctl.exe') -D $data -m fast stop *> (Join-Path $root 'stop.log')}
foreach($name in @('BELL_DATABASE_URL','BELL_ALERT_LIFECYCLE_TEST_DATABASE_URL','BELL_JWT_SECRET','BELL_BOOTSTRAP_USERNAME','BELL_BOOTSTRAP_PASSWORD','BELL_HOST','BELL_PORT')){Remove-Item "Env:$name" -ErrorAction SilentlyContinue}
}