70 lines
5.2 KiB
PowerShell
70 lines
5.2 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|
$PSNativeCommandUseErrorActionPreference = $true
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
Set-Location -LiteralPath $root
|
|
|
|
$api = 'http://localhost:58080'
|
|
$tokenUrl = 'http://localhost:58180/realms/voice-local/protocol/openid-connect/token'
|
|
$tenant = '10000000-0000-0000-0000-000000000010'
|
|
$key = 'acceptance-snapshot-smoke-' + [Guid]::NewGuid().ToString('N')
|
|
$requestID = 'acceptance-snapshot-smoke-' + [Guid]::NewGuid().ToString('N')
|
|
$snapshot = $null
|
|
|
|
function Get-Token([string]$name) {
|
|
(Invoke-RestMethod -Method Post -Uri $tokenUrl -ContentType 'application/x-www-form-urlencoded' -Body @{
|
|
grant_type = 'password'; client_id = 'voice-platform-web'; username = $name; password = 'LocalOnly123!'; scope = 'openid'
|
|
}).access_token
|
|
}
|
|
|
|
try {
|
|
$owner = Get-Token 'owner'
|
|
$outsider = Get-Token 'outsider'
|
|
$imageDigest = (docker image inspect data-terminal-cloud-api --format '{{.Id}}').Trim()
|
|
if ($imageDigest -notmatch '^sha256:[a-f0-9]{64}$') { throw '无法取得API候选镜像摘要' }
|
|
$body = @{
|
|
candidate_version = '0.51.0-rc.local'
|
|
build_digest = $imageDigest
|
|
environment = 'LAB'
|
|
run_type = 'REAL_DEVICE_PROVIDER'
|
|
scope = '本地真实OIDC和平台技术预检'
|
|
required_dependencies = @('oidc_jwks')
|
|
reason = '验证发布候选验收快照的真实登录、门禁聚合和归档输出'
|
|
} | ConvertTo-Json -Compress
|
|
$headers = @{Authorization = "Bearer $owner"; 'X-Tenant-Id' = $tenant; 'Idempotency-Key' = $key; 'X-Request-Id' = $requestID}
|
|
|
|
$createdResponse = Invoke-WebRequest -UseBasicParsing -Method Post -Uri "$api/api/v1/ops/diagnostics/acceptance-snapshots" -Headers $headers -ContentType 'application/json' -Body $body
|
|
$snapshot = ($createdResponse.Content | ConvertFrom-Json).data
|
|
if ($createdResponse.StatusCode -ne 201 -or $snapshot.redaction_policy -ne 'ops-acceptance-minimal-v1') { throw '验收快照首次生成结果不正确' }
|
|
if ($snapshot.database.migration_version -lt 36 -or $snapshot.database.dirty) { throw '验收快照未正确冻结数据库迁移状态' }
|
|
if (@($snapshot.external_dependencies).Count -ne 1 -or $snapshot.external_dependencies[0].dependency -ne 'oidc_jwks' -or -not $snapshot.external_dependencies[0].gate_satisfied) { throw '验收快照未正确冻结OIDC外部依赖状态' }
|
|
if (@($snapshot.gates).Count -ne 8) { throw '验收快照门禁数量不正确' }
|
|
$productionGate = @($snapshot.gates | Where-Object { $_.code -eq 'production_infrastructure_ready' })
|
|
if ($productionGate.Count -ne 1 -or -not $productionGate[0].satisfied -or $snapshot.production_infrastructure.applicable -or -not $snapshot.production_infrastructure.gate_satisfied) { throw 'LAB快照不应被生产基础设施门禁阻断' }
|
|
$failedGates = @($snapshot.gates | Where-Object { -not $_.satisfied })
|
|
if ($snapshot.overall_gate_satisfied -ne ($failedGates.Count -eq 0)) { throw '验收快照总结果与八项门禁不一致' }
|
|
$blockingItems = @($snapshot.alerts.blocking_items)
|
|
$expectedItems = [Math]::Min([int]$snapshot.alerts.blocking, 100)
|
|
if ($blockingItems.Count -ne $expectedItems -or $snapshot.alerts.blocking_items_truncated -ne ($snapshot.alerts.blocking -gt 100)) { throw '阻断告警定位数量或截断标识不正确' }
|
|
foreach ($item in $blockingItems) {
|
|
if ($item.id -notmatch '^[0-9a-fA-F-]{36}$' -or -not $item.rule_code -or @('PRESENT','MISSING','AGGREGATE','UNKNOWN') -notcontains $item.resource_state) { throw '阻断告警定位字段不正确' }
|
|
}
|
|
if (($createdResponse.Content -match 'https?://') -or ($createdResponse.Content -match '"endpoint"')) { throw '验收快照泄露了外部探针端点' }
|
|
|
|
Start-Sleep -Milliseconds 550
|
|
$replayResponse = Invoke-WebRequest -UseBasicParsing -Method Post -Uri "$api/api/v1/ops/diagnostics/acceptance-snapshots" -Headers $headers -ContentType 'application/json' -Body $body
|
|
if ($replayResponse.StatusCode -ne 200 -or $replayResponse.Headers.'Idempotency-Replayed' -ne 'true' -or ($replayResponse.Content | ConvertFrom-Json).data.id -ne $snapshot.id) { throw '验收快照幂等重放不正确' }
|
|
|
|
Start-Sleep -Milliseconds 550
|
|
try {
|
|
Invoke-WebRequest -UseBasicParsing -Method Post -Uri "$api/api/v1/ops/diagnostics/acceptance-snapshots" -Headers @{Authorization = "Bearer $outsider"; 'X-Tenant-Id' = $tenant; 'Idempotency-Key' = ('outsider-' + $key)} -ContentType 'application/json' -Body $body | Out-Null
|
|
throw '跨客户组织生成验收快照不应成功'
|
|
} catch {
|
|
if ($null -eq $_.Exception.Response -or [int]$_.Exception.Response.StatusCode -ne 403) { throw }
|
|
}
|
|
$failedCodes = if ($failedGates.Count) { ($failedGates.code -join ',') } else { 'none' }
|
|
Write-Host "发布候选验收快照烟测通过:真实OIDC、迁移冻结、外部依赖、八项门禁、生产条件按环境适用、阻断告警定位、幂等重放、租户隔离和端点脱敏均正确;当前阻断项=$failedCodes。"
|
|
} finally {
|
|
docker compose exec -T postgres psql -U voice -d voice_platform -v ON_ERROR_STOP=1 -c "BEGIN; DELETE FROM audit_event WHERE request_id='$requestID'; DELETE FROM idempotency_record WHERE idempotency_key='$key'; COMMIT;" | Out-Null
|
|
Write-Host '已清理验收快照烟测审计和幂等记录;快照本身不在服务端留存。'
|
|
}
|