Очередь job получила lease и fencing, телеметрия и прогресс deploy сохраняются, ревизия пишет manifest входов. Обновление префиксов не переписывает неизменённый снимок, большие наборы идут через COPY, а устаревшая ревизия не попадает в deploy. Бандл ноды кэшируется, prune и постановка job больше не держат горячий путь. Co-authored-by: Cursor <cursoragent@cursor.com>
47 lines
2.9 KiB
PowerShell
47 lines
2.9 KiB
PowerShell
# Offline contract test: no Docker daemon or production access needed.
|
|
$ErrorActionPreference = 'Stop'
|
|
$repoRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
|
|
$output = Join-Path $repoRoot 'artifacts/backend-baseline/offline-test.json'
|
|
$mockState = @{ Calls = [Collections.Generic.List[object]]::new(); EmptyStats = $false }
|
|
function docker {
|
|
param([Parameter(ValueFromRemainingArguments = $true)][string[]]$DockerArgs)
|
|
$mockState.Calls.Add($DockerArgs)
|
|
$global:LASTEXITCODE = 0
|
|
switch ($DockerArgs[0]) {
|
|
'compose' {
|
|
if ($DockerArgs -contains 'config') {
|
|
'{"services":{"api":{"image":"example/api:test","environment":{"PASSWORD":"MUST_NOT_LEAK"},"command":"MUST_NOT_LEAK","deploy":{"resources":{"limits":{"memory":"280M","cpus":"0.85"}}}}}}'
|
|
} else { 'aaaaaaaaaaaa'; 'bbbbbbbbbbbb' }
|
|
}
|
|
'info' { '{"server_version":"test","os_type":"linux","cpus":2,"memory_bytes":1073741824}' }
|
|
'inspect' {
|
|
'{"id":"' + $DockerArgs[-1] + '","image_id":"sha256:test","running":true,"memory_limit_bytes":293601280,"oom_killed":false,"restart_count":0}'
|
|
}
|
|
'stats' {
|
|
if ($mockState.EmptyStats) { return }
|
|
'{"id":"aaaaaaaaaaaa","cpu_percent":"1.0%","memory_usage":"20MiB / 280MiB"}'
|
|
'{"id":"bbbbbbbbbbbb","cpu_percent":"2.0%","memory_usage":"30MiB / 280MiB"}'
|
|
}
|
|
default { throw 'unexpected Docker operation' }
|
|
}
|
|
}
|
|
& (Join-Path $repoRoot 'scripts/collect-backend-baseline.ps1') -OutputPath $output -Samples 1
|
|
$raw = Get-Content -LiteralPath $output -Raw
|
|
$result = $raw | ConvertFrom-Json
|
|
if ($result.status -ne 'collected') { throw 'collector did not succeed' }
|
|
if ($result.containers.Count -ne 2 -or $result.samples[0].containers.Count -ne 2) { throw 'NDJSON multi-container stats were lost' }
|
|
if ($raw.Contains('MUST_NOT_LEAK') -or $raw.Contains('PASSWORD')) { throw 'raw Compose secrets leaked' }
|
|
if ($result.configured_services[0].memory_limit -ne '280M') { throw 'effective resource limit missing' }
|
|
if ($mockState.Calls.Count -eq 0) { throw 'Docker mock was not exercised' }
|
|
foreach ($call in $mockState.Calls) {
|
|
if ($call[0] -notin @('compose', 'info', 'inspect', 'stats')) { throw 'unexpected operation' }
|
|
if ($call[0] -eq 'compose' -and -not ($call -contains 'config' -or $call -contains 'ps')) { throw 'mutating Compose operation' }
|
|
}
|
|
$mockState.EmptyStats = $true
|
|
& (Join-Path $repoRoot 'scripts/collect-backend-baseline.ps1') -OutputPath $output -Samples 1
|
|
if ($LASTEXITCODE -ne 2) { throw 'missing stats must exit 2' }
|
|
$partial = Get-Content -LiteralPath $output -Raw | ConvertFrom-Json
|
|
if ($partial.status -ne 'partial' -or $partial.samples[0].complete -ne $false) { throw 'empty stats reported as complete' }
|
|
Write-Output 'PASS: multi-container snapshot, resource projection, secret exclusion, read-only operations, incomplete stats detection'
|
|
exit 0
|