Files

161 lines
6.7 KiB
PowerShell
Raw Permalink Normal View History

[CmdletBinding()]
param(
[string]$PgRoot = 'D:\pgsql17'
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
$repoRoot = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
$pgRootResolved = (Resolve-Path -LiteralPath $PgRoot).Path
$bin = Join-Path $pgRootResolved 'bin'
$initdb = Join-Path $bin 'initdb.exe'
$pgCtl = Join-Path $bin 'pg_ctl.exe'
$psql = Join-Path $bin 'psql.exe'
$createdb = Join-Path $bin 'createdb.exe'
foreach ($required in @($initdb, $pgCtl, $psql, $createdb)) {
if (-not (Test-Path -LiteralPath $required -PathType Leaf)) {
throw "PostgreSQL executable is missing under the selected PgRoot."
}
}
$postgresExe = Join-Path $bin 'postgres.exe'
$version = (& $postgresExe --version 2>&1 | Out-String).Trim()
if ($LASTEXITCODE -ne 0 -or $version -notmatch 'PostgreSQL\) 17\.10$') {
throw 'PostgreSQL integration requires the frozen PostgreSQL 17.10 binaries.'
}
$existing5432 = @(
Get-NetTCPConnection -State Listen -LocalPort 5432 -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty OwningProcess -Unique |
Sort-Object
)
$listener = [System.Net.Sockets.TcpListener]::new(
[System.Net.IPAddress]::Loopback,
0
)
$listener.Start()
$port = ([System.Net.IPEndPoint]$listener.LocalEndpoint).Port
$listener.Stop()
$systemTemp = [IO.Path]::GetFullPath([IO.Path]::GetTempPath())
$clusterName = 'yovision-t012-pg-' + [guid]::NewGuid().ToString('N')
$dataDir = Join-Path $systemTemp $clusterName
$logPath = Join-Path $systemTemp ($clusterName + '.log')
$started = $false
$stopped = $false
$hadSenseDSN = Test-Path Env:YOVISION_TEST_POSTGRES_DSN
$previousSenseDSN = if ($hadSenseDSN) { (Get-Item Env:YOVISION_TEST_POSTGRES_DSN).Value } else { $null }
$hadAdminDSN = Test-Path Env:YOVISION_TEST_POSTGRES_ADMIN_DSN
$previousAdminDSN = if ($hadAdminDSN) { (Get-Item Env:YOVISION_TEST_POSTGRES_ADMIN_DSN).Value } else { $null }
$hadBellDSN = Test-Path Env:YOVISION_TEST_BELL_POSTGRES_DSN
$previousBellDSN = if ($hadBellDSN) { (Get-Item Env:YOVISION_TEST_BELL_POSTGRES_DSN).Value } else { $null }
function Invoke-Checked {
param(
[Parameter(Mandatory)] [string]$FilePath,
[Parameter(ValueFromRemainingArguments)] [string[]]$Arguments
)
& $FilePath @Arguments
if ($LASTEXITCODE -ne 0) {
throw "Command failed with exit code $LASTEXITCODE."
}
}
try {
New-Item -ItemType Directory -Path $dataDir | Out-Null
Invoke-Checked $initdb '-D' $dataDir '-U' 'postgres' '-A' 'trust' '--encoding=UTF8' '--no-locale' '--no-sync'
$serverOptions = "-h 127.0.0.1 -p $port -c listen_addresses=127.0.0.1"
Invoke-Checked $pgCtl '-D' $dataDir '-l' $logPath '-o' $serverOptions '-w' 'start'
$started = $true
$adminRootDSN = "postgres://postgres@127.0.0.1:$port/postgres?sslmode=disable"
$databaseName = 'yovision_t012'
$adminDatabaseDSN = "postgres://postgres@127.0.0.1:$port/${databaseName}?sslmode=disable"
$senseDSN = "postgres://yovision_t012_sense@127.0.0.1:$port/${databaseName}?sslmode=disable"
Invoke-Checked $psql '-X' '-v' 'ON_ERROR_STOP=1' '-d' $adminRootDSN '-f' (Join-Path $repoRoot 'deploy\postgres\001_roles.sql')
Invoke-Checked $createdb '-h' '127.0.0.1' '-p' ([string]$port) '-U' 'postgres' $databaseName
foreach ($pass in 1..2) {
foreach ($name in @(
'002_bell.sql',
'003_sense.sql',
'004_privileges.sql',
'005_area_policy.sql',
'006_device_operation_outbox.sql',
'007_privileges_area_audit.sql',
'008_control_api.sql',
'009_privileges_control_api.sql',
'010_reconcile_safety.sql',
'011_privileges_reconcile_safety.sql',
'012_bell_events.sql',
2026-08-11 00:24:32 +08:00
'013_privileges_bell_events.sql',
'014_audit_relay.sql',
'015_privileges_audit_relay.sql'
)) {
Invoke-Checked $psql '-X' '-v' 'ON_ERROR_STOP=1' '-d' $adminDatabaseDSN '-f' (Join-Path $repoRoot "deploy\postgres\$name")
}
}
Invoke-Checked $psql '-X' '-v' 'ON_ERROR_STOP=1' '-d' $adminRootDSN '-c' 'CREATE ROLE yovision_t012_sense LOGIN IN ROLE sense_app'
Invoke-Checked $psql '-X' '-v' 'ON_ERROR_STOP=1' '-d' $adminRootDSN '-c' 'CREATE ROLE yovision_t015_bell LOGIN IN ROLE bell_runtime'
Invoke-Checked $psql '-X' '-v' 'ON_ERROR_STOP=1' '-d' $adminDatabaseDSN '-f' (Join-Path $repoRoot 'deploy\postgres\tests\assertions.sql')
$env:YOVISION_TEST_POSTGRES_DSN = $senseDSN
$env:YOVISION_TEST_POSTGRES_ADMIN_DSN = $adminDatabaseDSN
$bellDSN = "postgres://yovision_t015_bell@127.0.0.1:$port/${databaseName}?sslmode=disable"
$env:YOVISION_TEST_BELL_POSTGRES_DSN = $bellDSN
Invoke-Checked 'go' '-C' (Join-Path $repoRoot 'Sense') 'test' './internal/store' '-run' '^TestPostgres' '-count=1'
Invoke-Checked 'go' '-C' (Join-Path $repoRoot 'Bell') 'test' './internal/store' '-run' '^TestPostgres' '-count=1'
}
finally {
if ($started) {
& $pgCtl '-D' $dataDir '-m' 'fast' '-w' 'stop'
$stopped = $LASTEXITCODE -eq 0
}
if (-not $started -or $stopped) {
$resolvedData = [IO.Path]::GetFullPath($dataDir)
if (-not $resolvedData.StartsWith($systemTemp, [StringComparison]::OrdinalIgnoreCase) -or
[IO.Path]::GetFileName($resolvedData) -notlike 'yovision-t012-pg-*') {
throw "Refusing to clean unexpected temporary path."
}
if (Test-Path -LiteralPath $resolvedData) {
Remove-Item -LiteralPath $resolvedData -Recurse -Force
}
if (Test-Path -LiteralPath $logPath) {
Remove-Item -LiteralPath $logPath -Force
}
}
else {
Write-Warning 'Temporary PostgreSQL did not stop; its data directory was retained for manual recovery.'
}
if ($hadSenseDSN) {
$env:YOVISION_TEST_POSTGRES_DSN = $previousSenseDSN
}
else {
Remove-Item Env:YOVISION_TEST_POSTGRES_DSN -ErrorAction SilentlyContinue
}
if ($hadAdminDSN) {
$env:YOVISION_TEST_POSTGRES_ADMIN_DSN = $previousAdminDSN
}
else {
Remove-Item Env:YOVISION_TEST_POSTGRES_ADMIN_DSN -ErrorAction SilentlyContinue
}
if ($hadBellDSN) {
$env:YOVISION_TEST_BELL_POSTGRES_DSN = $previousBellDSN
}
else {
Remove-Item Env:YOVISION_TEST_BELL_POSTGRES_DSN -ErrorAction SilentlyContinue
}
$after5432 = @(
Get-NetTCPConnection -State Listen -LocalPort 5432 -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty OwningProcess -Unique |
Sort-Object
)
if (($existing5432 -join ',') -ne ($after5432 -join ',')) {
throw 'The existing PostgreSQL listener on port 5432 changed during the isolated test.'
}
}
Write-Output "PostgreSQL $version isolated integration passed; temporary port $port was cleaned."