Files
story/scripts/check-functional.ps1
T

102 lines
3.6 KiB
PowerShell
Raw Permalink Normal View History

2026-08-26 06:41:07 +08:00
[CmdletBinding()]
param(
[ValidateRange(1, 60)]
[int]$LogWindowMinutes = 5
)
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
$projectRoot = Split-Path -Parent $PSScriptRoot
Set-Location -LiteralPath $projectRoot
function Assert-HttpStatus {
param(
[Parameter(Mandatory)]
[string]$Name,
[Parameter(Mandatory)]
[string]$Uri
)
try {
$response = Invoke-WebRequest -UseBasicParsing -Uri $Uri -TimeoutSec 5
} catch {
throw "$Name is unavailable at $Uri. $($_.Exception.Message)"
}
if ($response.StatusCode -ne 200) {
throw "$Name returned HTTP $($response.StatusCode) at $Uri."
}
Write-Host "[PASS] $Name returned HTTP 200."
}
function Assert-DependencyMetrics {
param(
[Parameter(Mandatory)]
[string]$Service,
[Parameter(Mandatory)]
[string]$Uri
)
$content = (Invoke-WebRequest -UseBasicParsing -Uri $Uri -TimeoutSec 5).Content
$dependencyLines = @($content -split "`n" | Where-Object {
$_ -match '^voice_platform_dependency_up\{'
})
if ($dependencyLines.Count -eq 0) {
throw "$Service did not expose dependency health metrics."
}
$down = @($dependencyLines | Where-Object { $_ -match '\}\s+0(?:\.0+)?\s*$' })
if ($down.Count -gt 0) {
throw "$Service has unavailable dependencies:`n$($down -join "`n")"
}
Write-Host "[PASS] $Service dependencies are available ($($dependencyLines.Count) checked)."
}
$requiredServices = @(
'api',
'callbacks',
'worker',
'web',
'mock-provider',
'mock-alert-receiver',
'postgres',
'redis',
'minio',
'keycloak'
)
$runningServices = @(docker compose ps --services --status running)
$missingServices = @($requiredServices | Where-Object { $_ -notin $runningServices })
if ($missingServices.Count -gt 0) {
throw "Required services are not running: $($missingServices -join ', ')."
}
Write-Host "[PASS] All required local services are running."
Assert-HttpStatus -Name 'API readiness' -Uri 'http://127.0.0.1:58080/health/ready'
Assert-HttpStatus -Name 'Callbacks readiness' -Uri 'http://127.0.0.1:58081/health/ready'
Assert-HttpStatus -Name 'Worker readiness' -Uri 'http://127.0.0.1:58082/health/ready'
Assert-HttpStatus -Name 'Web readiness' -Uri 'http://127.0.0.1:3100/health/ready'
Assert-HttpStatus -Name 'Mock provider liveness' -Uri 'http://127.0.0.1:59200/health/live'
Assert-HttpStatus -Name 'Mock alert receiver liveness' -Uri 'http://127.0.0.1:59210/health/live'
Assert-DependencyMetrics -Service 'API' -Uri 'http://127.0.0.1:58080/metrics'
Assert-DependencyMetrics -Service 'Worker' -Uri 'http://127.0.0.1:58082/metrics'
$outboxCounts = docker compose exec -T postgres psql -U voice -d voice_platform -tAc `
"SELECT count(*) FILTER (WHERE status IN ('pending', 'processing')), count(*) FILTER (WHERE status = 'dead') FROM outbox_event;"
$outboxCounts = $outboxCounts.Trim()
if ($outboxCounts -ne '0|0') {
throw "Outbox has open/dead work: $outboxCounts."
}
Write-Host '[PASS] Outbox has no pending, processing, or dead events.'
$recentLogs = docker compose logs --since "${LogWindowMinutes}m" api callbacks worker mock-provider mock-alert-receiver 2>&1
$errorLogs = @($recentLogs | Select-String -Pattern 'panic|fatal|level[=:]error|"level":"error"' -CaseSensitive:$false)
if ($errorLogs.Count -gt 0) {
throw "Recent service logs contain errors:`n$($errorLogs -join "`n")"
}
Write-Host "[PASS] No fatal or error-level logs in the last $LogWindowMinutes minute(s)."
Write-Host 'Lightweight functional readiness check passed.'