[CmdletBinding()] param( [string]$PostgresBin = 'D:\pgsql17\bin', [string]$Python = '', [switch]$SkipIndependentProductE2E, [switch]$KeepTemporary ) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' $repositoryRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..')) $temporaryRoot = [IO.Path]::GetFullPath((Join-Path ([IO.Path]::GetTempPath()) ('yovision-coordination-e2e-' + [guid]::NewGuid().ToString('N')))) $postgresData = Join-Path $temporaryRoot 'postgres' $postgresLog = Join-Path $temporaryRoot 'postgres.log' $postgresStarted = $false $savedEnvironment = @{} $sensitiveValues = [Collections.Generic.List[string]]::new() function Get-FreeTcpPort { $listener = [Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, 0) try { $listener.Start(); return ([Net.IPEndPoint]$listener.LocalEndpoint).Port } finally { $listener.Stop() } } function Wait-Tcp([int]$Port, [bool]$Open, [int]$Attempts = 120) { for ($attempt = 0; $attempt -lt $Attempts; $attempt++) { $client = [Net.Sockets.TcpClient]::new() try { $connected = $client.ConnectAsync('127.0.0.1', $Port).Wait(250) -and $client.Connected } catch { $connected = $false } finally { $client.Dispose() } if ($connected -eq $Open) { return } Start-Sleep -Milliseconds 250 } throw "TCP port $Port did not reach open=$Open" } function New-RandomName([string]$Prefix) { return $Prefix + '_' + [guid]::NewGuid().ToString('N').Substring(0, 12) } function New-RandomSecret { $buffer = New-Object byte[] 48 $generator = [Security.Cryptography.RandomNumberGenerator]::Create() try { $generator.GetBytes($buffer) } finally { $generator.Dispose() } return [Convert]::ToBase64String($buffer).Replace('+', 'A').Replace('/', 'B') } function Set-TestEnvironment([string]$Name, [string]$Value, [bool]$Sensitive = $false) { if (-not $script:savedEnvironment.ContainsKey($Name)) { $script:savedEnvironment[$Name] = [Environment]::GetEnvironmentVariable($Name, 'Process') } [Environment]::SetEnvironmentVariable($Name, $Value, 'Process') if ($Sensitive) { $script:sensitiveValues.Add($Value) } } function Invoke-Checked { param([string]$Name, [string]$WorkingDirectory, [scriptblock]$Command) Write-Host "[coordination-e2e] $Name" Push-Location $WorkingDirectory try { & $Command if ($LASTEXITCODE -ne 0) { throw "$Name failed with exit code $LASTEXITCODE" } } finally { Pop-Location } } function Assert-NoSecretInLogs { $logs = @(Get-ChildItem -LiteralPath $temporaryRoot -File -Recurse -ErrorAction SilentlyContinue) foreach ($log in $logs) { $stream = [IO.File]::Open($log.FullName, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::ReadWrite -bor [IO.FileShare]::Delete) try { $reader = [IO.StreamReader]::new($stream, [Text.Encoding]::UTF8, $true) try { $content = $reader.ReadToEnd() } finally { $reader.Dispose() } } finally { $stream.Dispose() } foreach ($secret in $sensitiveValues) { if ($secret.Length -ge 8 -and $content.Contains($secret)) { throw "Temporary log exposed a generated E2E secret: $($log.Name)" } } } } New-Item -ItemType Directory -Path $temporaryRoot | Out-Null try { foreach ($tool in @('initdb.exe', 'pg_ctl.exe', 'createdb.exe', 'psql.exe')) { $path = Join-Path $PostgresBin $tool if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { throw "Required PostgreSQL tool not found: $path" } } if ([string]::IsNullOrWhiteSpace($Python)) { $candidate = Join-Path $repositoryRoot 'Brain\.venv\Scripts\python.exe' $Python = if (Test-Path -LiteralPath $candidate -PathType Leaf) { $candidate } else { 'python.exe' } } $contractEnvironment = Join-Path $temporaryRoot 'contract-venv' & $Python -m venv $contractEnvironment if ($LASTEXITCODE -ne 0) { throw 'Could not create the isolated contract-test environment.' } $contractPython = Join-Path $contractEnvironment 'Scripts\python.exe' $env:PIP_DISABLE_PIP_VERSION_CHECK = '1' & $contractPython -m pip install --quiet -r (Join-Path $repositoryRoot 'contracts\tests\source-config-v1\requirements.txt') 'cryptography==50.0.1' if ($LASTEXITCODE -ne 0) { throw 'Could not install the pinned contract-test dependencies.' } $postgresPort = Get-FreeTcpPort if ($postgresPort -eq 5432) { throw 'Coordination E2E refuses the default PostgreSQL port.' } $clusterUser = New-RandomName 'yvcoord' $senseRole = New-RandomName 'sense_owner' $bellRole = New-RandomName 'bell_owner' $senseDatabase = New-RandomName 'sense_e2e' $bellDatabase = New-RandomName 'bell_e2e' & (Join-Path $PostgresBin 'initdb.exe') -D $postgresData -U $clusterUser -A trust --encoding=UTF8 --no-locale | Out-Null if ($LASTEXITCODE -ne 0) { throw 'Isolated PostgreSQL initdb failed.' } $startArguments = "-D `"$postgresData`" -l `"$postgresLog`" -o `"-p $postgresPort -h 127.0.0.1`" start" Start-Process -FilePath (Join-Path $PostgresBin 'pg_ctl.exe') -ArgumentList $startArguments -RedirectStandardOutput (Join-Path $temporaryRoot 'pg-ctl.out.log') -RedirectStandardError (Join-Path $temporaryRoot 'pg-ctl.err.log') -WindowStyle Hidden | Out-Null Wait-Tcp -Port $postgresPort -Open $true $postgresStarted = $true $psql = Join-Path $PostgresBin 'psql.exe' foreach ($role in @($senseRole, $bellRole)) { & $psql -X -h 127.0.0.1 -p $postgresPort -U $clusterUser -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE $role LOGIN;" | Out-Null if ($LASTEXITCODE -ne 0) { throw "Could not create isolated role $role" } } & (Join-Path $PostgresBin 'createdb.exe') -h 127.0.0.1 -p $postgresPort -U $clusterUser -O $senseRole $senseDatabase if ($LASTEXITCODE -ne 0) { throw 'Could not create isolated Sense database.' } & (Join-Path $PostgresBin 'createdb.exe') -h 127.0.0.1 -p $postgresPort -U $clusterUser -O $bellRole $bellDatabase if ($LASTEXITCODE -ne 0) { throw 'Could not create isolated Bell database.' } $senseDsn = "host=127.0.0.1 port=$postgresPort user=$senseRole dbname=$senseDatabase sslmode=disable" $bellDsn = "host=127.0.0.1 port=$postgresPort user=$bellRole dbname=$bellDatabase sslmode=disable" if ($senseDsn -eq $bellDsn -or $senseRole -eq $bellRole -or $senseDatabase -eq $bellDatabase) { throw 'Sense and Bell isolation invariant failed.' } Set-TestEnvironment 'GOTOOLCHAIN' 'go1.26.5' Set-TestEnvironment 'PYTHONDONTWRITEBYTECODE' '1' Set-TestEnvironment 'SENSE_OUTBOX_TEST_DATABASE_URL' $senseDsn Set-TestEnvironment 'BELL_DATABASE_URL' $bellDsn Set-TestEnvironment 'BELL_EVENT_INGRESS_TEST_DATABASE_URL' $bellDsn Set-TestEnvironment 'BELL_RULE_ALERT_TEST_DATABASE_URL' $bellDsn Set-TestEnvironment 'BELL_ALERT_LIFECYCLE_TEST_DATABASE_URL' $bellDsn Set-TestEnvironment 'BELL_JWT_SECRET' (New-RandomSecret) $true Set-TestEnvironment 'BELL_BOOTSTRAP_USERNAME' (New-RandomName 'coord_admin') Set-TestEnvironment 'BELL_BOOTSTRAP_PASSWORD' (New-RandomSecret) $true Set-TestEnvironment 'BELL_RULE_ALERT_OPERATOR_PASSWORD' (New-RandomSecret) $true Set-TestEnvironment 'BELL_HOST' '127.0.0.1' Set-TestEnvironment 'BELL_PORT' (Get-FreeTcpPort).ToString() Invoke-Checked 'Bell formal migrations' (Join-Path $repositoryRoot 'Bell\server') { go run . migrate -c config/settings.demo.yml *> (Join-Path $temporaryRoot 'bell-migrate.log') } Invoke-Checked 'source-config v1 contract' $repositoryRoot { & $contractPython -m unittest discover -s contracts/tests/source-config-v1 -p 'test_*.py' -v } Invoke-Checked 'runtime-status v1 contract' $repositoryRoot { & $contractPython contracts/tests/runtime-status-v1/test_contract.py } Invoke-Checked 'machine-identity v1 cross-language contract' $repositoryRoot { & $contractPython contracts/tests/machine-identity-v1/test_contract.py } Invoke-Checked 'events v1 contract' $repositoryRoot { & $contractPython contracts/tests/events-v1/test_contract.py } Invoke-Checked 'evidence v1 contract' $repositoryRoot { & $contractPython contracts/tests/evidence-v1/test_contract.py } Invoke-Checked 'Sense source/status integration' (Join-Path $repositoryRoot 'Sense\tests\integration\brain_control') { go test . -count=1 -v } Invoke-Checked 'Sense Brain-event/evidence/Outbox integration' (Join-Path $repositoryRoot 'Sense\tests\integration\bell_connector') { go test . -count=1 -v } Invoke-Checked 'Sense PostgreSQL Outbox recovery' (Join-Path $repositoryRoot 'Sense\server') { go test ./app/sense/outbox -count=1 -v } Invoke-Checked 'Brain source/status connector and anonymous event export' $repositoryRoot { & $Python -m pytest Brain/tests/integration/sense_control Brain/tests/integration/event_export -q } Invoke-Checked 'Bell ingress and evidence degradation' (Join-Path $repositoryRoot 'Bell\server') { go test ./tests/integration/event_ingress -count=1 -v } Invoke-Checked 'Bell rule and alert projection' (Join-Path $repositoryRoot 'Bell\server') { go test ./tests/bell_rule_alert -count=1 -v } Invoke-Checked 'Bell alert lifecycle' (Join-Path $repositoryRoot 'Bell\server') { go test ./tests/bell_alert_lifecycle -count=1 -v } $identityFacts = (& $psql -X -h 127.0.0.1 -p $postgresPort -U $clusterUser -d postgres -tAc "select datname||':'||pg_get_userbyid(datdba) from pg_database where datname in ('$senseDatabase','$bellDatabase') order by datname;") if (@($identityFacts).Count -ne 2 -or ($identityFacts -join '|') -notmatch [regex]::Escape($senseRole) -or ($identityFacts -join '|') -notmatch [regex]::Escape($bellRole)) { throw 'PostgreSQL ownership isolation evidence is incomplete.' } if (-not $SkipIndependentProductE2E) { Invoke-Checked 'Sense independent isolated E2E regression' $repositoryRoot { & (Join-Path $repositoryRoot 'Sense\tests\e2e\run-isolated-e2e.ps1') -PostgresBin $PostgresBin } Invoke-Checked 'Brain independent test regression' $repositoryRoot { & $Python -m pytest Brain/tests -q } Invoke-Checked 'Bell independent isolated E2E regression' $repositoryRoot { & (Join-Path $repositoryRoot 'Bell\tests\e2e\run-isolated-e2e.ps1') -PostgresBin $PostgresBin } } Assert-NoSecretInLogs Write-Host "COORDINATION_E2E passed: versioned contracts, source/status, anonymous event, durable Outbox recovery, Bell Receipt/Event/Alert lifecycle, identity/replay/conflict/evidence faults, separate Sense/Bell databases. postgres_port=$postgresPort" } finally { if ($postgresStarted) { Start-Process -FilePath (Join-Path $PostgresBin 'pg_ctl.exe') -ArgumentList "-D `"$postgresData`" -m fast stop" -RedirectStandardOutput (Join-Path $temporaryRoot 'pg-stop.out.log') -RedirectStandardError (Join-Path $temporaryRoot 'pg-stop.err.log') -WindowStyle Hidden -Wait | Out-Null try { Wait-Tcp -Port $postgresPort -Open $false -Attempts 40 } catch {} } foreach ($entry in $savedEnvironment.GetEnumerator()) { [Environment]::SetEnvironmentVariable($entry.Key, $entry.Value, 'Process') } if ($KeepTemporary) { Write-Host "Kept coordination E2E directory: $temporaryRoot" } elseif (Test-Path -LiteralPath $temporaryRoot) { $resolved = [IO.Path]::GetFullPath($temporaryRoot) $tempPrefix = [IO.Path]::GetFullPath([IO.Path]::GetTempPath()) if (-not $resolved.StartsWith($tempPrefix, [StringComparison]::OrdinalIgnoreCase) -or -not ([IO.Path]::GetFileName($resolved)).StartsWith('yovision-coordination-e2e-')) { throw "Refusing unsafe temporary cleanup: $resolved" } Remove-Item -LiteralPath $resolved -Recurse -Force } }