Files
jiapuapp/tests/g03-bootstrap-client-release-gate.ps1
T
2026-07-23 08:24:07 +08:00

155 lines
5.7 KiB
PowerShell

$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
$issues = New-Object System.Collections.Generic.List[string]
function Add-Issue {
param([string]$Message)
$script:issues.Add($Message)
}
function Read-RequiredFile {
param([string]$RelativePath)
$path = Join-Path $root $RelativePath
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) {
Add-Issue "missing required client owner: $RelativePath"
return ''
}
return Get-Content -Raw -Encoding UTF8 -LiteralPath $path
}
function Assert-Contains {
param([string]$Content, [string]$Label, [string]$Expected)
if (-not $Content.Contains($Expected)) {
Add-Issue "$Label missing production ownership marker: $Expected"
}
}
function Assert-DoesNotContain {
param([string]$Content, [string]$Label, [string]$Forbidden)
if ($Content.Contains($Forbidden)) {
Add-Issue "$Label retains retired preview/legacy owner: $Forbidden"
}
}
$page = Read-RequiredFile 'pages/genealogy/g03-create-genealogy.vue'
$api = Read-RequiredFile 'utils/api.js'
$coordinator = Read-RequiredFile 'utils/genealogy-bootstrap.js'
$accessContract = Read-RequiredFile 'utils/genealogy-contracts.js'
$mock = Read-RequiredFile 'data/mock.js'
$staticContract = Read-RequiredFile 'tests/g03-create-flow-contract.ps1'
$legacyRuntimeSmoke = Read-RequiredFile 'tests/g03-create-flow-runtime-smoke.js'
$runtimePath = Join-Path $root 'tests/g03-bootstrap-runtime-smoke.js'
# Keep only durable public ownership checks here. Internal helper names, cache strategy,
# debounce timers, and test titles are intentionally left to executable behavior tests.
Assert-Contains $page 'G03 page' '@/utils/genealogy-bootstrap.js'
Assert-Contains $page 'G03 page' 'createGenealogyBootstrapCoordinator'
Assert-Contains $api 'utils/api.js' '/genealogy/app/genealogies'
Assert-Contains $api 'utils/api.js' '/genealogy/app/genealogy-bootstrap-operations/'
Assert-Contains $api 'utils/api.js' '/genealogy/app/region/search'
Assert-Contains $api 'utils/api.js' 'Idempotency-Key'
Assert-Contains $coordinator 'utils/genealogy-bootstrap.js' 'createGenealogyBootstrapCoordinator'
foreach ($entry in @(
[pscustomobject]@{ Content = $page; Label = 'G03 page' },
[pscustomobject]@{ Content = $mock; Label = 'data/mock.js' },
[pscustomobject]@{ Content = $staticContract; Label = 'G03 static flow contract' },
[pscustomobject]@{ Content = $legacyRuntimeSmoke; Label = 'G03 legacy runtime smoke' }
)) {
foreach ($retiredOwner in @(
'createLocalGenealogyPreview',
'updateLocalGenealogyPreview',
'updateLocalGenealogyPreviewAncestor',
'removeLocalGenealogyPreview',
'local-created-'
)) {
Assert-DoesNotContain $entry.Content $entry.Label $retiredOwner
}
}
foreach ($retiredApiOwner in @('async createGenealogy(', 'genealogies.unshift(created)')) {
Assert-DoesNotContain $api 'utils/api.js' $retiredApiOwner
}
foreach ($retiredAccessOwner in @(
'visibility:',
'joinMode:',
'fromApiGenealogyAccess',
'toApiGenealogyAccess'
)) {
Assert-DoesNotContain $accessContract 'utils/genealogy-contracts.js' $retiredAccessOwner
}
# The release gate executes the dependency-injected state machine suite. The suite must
# deep-compare the exact marker, use request/storage/cache/context/navigation spies, and
# publish a machine-readable case ledger only after all assertions pass.
$expectedCases = @(
'ACCOUNT_EPOCH_ISOLATION',
'CONTEXT_FAILURE_LOCAL_RETRY',
'FAILED_CLEAR',
'FATAL_EXPLICIT_ABANDON_CLEAR',
'KEY_REUSED_QUARANTINE',
'KNOWN_NO_COMMIT',
'LOCAL_ZERO_DISPATCH',
'MARKER_EXACT',
'NAVIGATION_FAILURE_LOCAL_RETRY',
'NO_MOCK_MUTATION',
'NO_NEW_KEY',
'NO_PII',
'PENDING_RETRY_AFTER',
'POST_408_UNKNOWN',
'POST_CANCEL_UNKNOWN',
'POST_UNEXPECTED_STATUS_UNKNOWN',
'POST_UNKNOWN',
'PRECLAIM_400',
'PRECLAIM_401',
'PRECLAIM_403',
'PROCESS_RECOVERY_STATUS_ONLY',
'REQUEST_BUILD_ZERO_DISPATCH',
'RETRY_429',
'STATUS_400_CLEAR',
'STATUS_401_CLEAR_SESSION',
'STATUS_404_KEEP',
'STATUS_429_KEEP',
'STATUS_500_KEEP',
'STATUS_CANCEL_KEEP',
'STATUS_MALFORMED_KEEP',
'STATUS_NETWORK_KEEP',
'STATUS_UNEXPECTED_STATUS_KEEP',
'SUCCEEDED_ORDER'
) | Sort-Object
if (-not (Test-Path -LiteralPath $runtimePath -PathType Leaf)) {
Add-Issue 'missing executable state-machine suite: tests/g03-bootstrap-runtime-smoke.js'
} else {
$runtimeOutput = @(& node $runtimePath 2>&1)
$runtimeExit = $LASTEXITCODE
if ($runtimeExit -ne 0) {
Add-Issue "G03 runtime state-machine suite failed: $($runtimeOutput -join ' | ')"
} else {
if ('G03-BOOTSTRAP-RUNTIME PASS' -notin $runtimeOutput) {
Add-Issue 'G03 runtime state-machine suite did not emit its PASS marker'
}
$coverageLine = @($runtimeOutput | Where-Object { $_ -like 'G03-BOOTSTRAP-RUNTIME-COVERAGE *' })
if ($coverageLine.Count -ne 1) {
Add-Issue 'G03 runtime state-machine suite must emit exactly one coverage ledger'
} else {
$actualCases = @(($coverageLine[0] -replace '^G03-BOOTSTRAP-RUNTIME-COVERAGE\s+', '').Split(',') | Where-Object { $_ } | Sort-Object)
if (($actualCases -join ',') -ne ($expectedCases -join ',')) {
Add-Issue "G03 runtime coverage ledger drifted: $($actualCases -join ',')"
}
}
}
}
if ($issues.Count -gt 0) {
$lines = New-Object System.Collections.Generic.List[string]
$lines.Add('G03-BOOTSTRAP-CLIENT-RELEASE BLOCKED')
foreach ($issue in $issues) { $lines.Add("- $issue") }
$lines.Add('- Keep the honest local preview until backend, workspace, client, and MuMu release gates are green.')
$lines.Add('- Migrate the coordinator, strict transport, marker/status recovery, context, fixtures, focused tests, and docs as one owner change.')
throw ($lines -join [Environment]::NewLine)
}
Write-Output 'G03-BOOTSTRAP-CLIENT-RELEASE PASS'