feat(store): add PostgreSQL foundation [T-009]
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
[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 'T-009 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-t009-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 }
|
||||
|
||||
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_t009'
|
||||
$adminDatabaseDSN = "postgres://postgres@127.0.0.1:$port/${databaseName}?sslmode=disable"
|
||||
$senseDSN = "postgres://yovision_t009_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')) {
|
||||
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_t009_sense LOGIN IN ROLE sense_app'
|
||||
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
|
||||
Invoke-Checked 'go' '-C' (Join-Path $repoRoot 'Sense') '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-t009-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
|
||||
}
|
||||
$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."
|
||||
Reference in New Issue
Block a user