You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
6.6 KiB
130 lines
6.6 KiB
$ErrorActionPreference = 'Continue'
|
|
# Self-contained regression suite for the lights-out skill.
|
|
# pwsh -NoProfile -File <skill>\scripts\test\e2e.ps1
|
|
# Uses a stubbed gnhf and a sandboxed state dir + vault, so it never touches the real
|
|
# registry at ~\.scout\lights-out or the Obsidian vault.
|
|
$sand = Join-Path $env:TEMP 'lights-out-e2e'
|
|
$stubSrc = Join-Path $PSScriptRoot 'stub-gnhf.ps1'
|
|
$stub = Join-Path $sand 'bin'
|
|
$env:LIGHTS_OUT_ROOT = "$sand\state"
|
|
$env:LIGHTS_OUT_VAULT_DIR = "$sand\vault"
|
|
$scriptsDir = Split-Path -Parent $PSScriptRoot
|
|
$S = Join-Path $scriptsDir 'start-run.ps1'
|
|
$C = Join-Path $scriptsDir 'check-run.ps1'
|
|
|
|
Remove-Item -Recurse -Force $sand -ErrorAction SilentlyContinue
|
|
New-Item -ItemType Directory -Force -Path "$sand\repo", $stub | Out-Null
|
|
# Get-Command resolves .ps1 from PATH, so the stub must be named exactly gnhf.ps1.
|
|
Copy-Item $stubSrc (Join-Path $stub 'gnhf.ps1') -Force
|
|
Push-Location "$sand\repo"
|
|
git init -q -b main
|
|
git config user.email 't@t.local'
|
|
git config user.name 'T'
|
|
'# scratch' | Set-Content README.md
|
|
'export const widget = (x) => { if (true) { return x + 1; } };' | Set-Content index.js
|
|
git add -A
|
|
git commit -q -m 'init'
|
|
Pop-Location
|
|
|
|
function Show($n) { Write-Host "`n### $n" -ForegroundColor Yellow }
|
|
$script:pass = 0
|
|
$script:fail = 0
|
|
function Check($label, $cond) {
|
|
if ($cond) { Write-Host " PASS $label" -ForegroundColor Green; $script:pass++ }
|
|
else { Write-Host " FAIL $label" -ForegroundColor Red; $script:fail++ }
|
|
}
|
|
# Load the shared lib so the suite can assert on resolution directly.
|
|
. (Join-Path $scriptsDir 'lib.ps1')
|
|
function Resolve-GnhfCommandForTest { Resolve-GnhfCommand }
|
|
|
|
Show 'TEST 0: vendored gnhf is self-contained'
|
|
$vendored = Join-Path (Split-Path -Parent $scriptsDir) 'vendor\node_modules\gnhf\dist\cli.mjs'
|
|
Check 'vendored gnhf present' (Test-Path $vendored)
|
|
$env:LIGHTS_OUT_GNHF = ''
|
|
$resolved = Resolve-GnhfCommand
|
|
Check 'resolves to vendored by default' ($resolved -and $resolved.Kind -eq 'vendored')
|
|
Check 'vendored version reported' ($resolved -and $resolved.Version)
|
|
if ($resolved) { Write-Host " -> $($resolved.Kind) v$($resolved.Version): $($resolved.Source)" }
|
|
|
|
Show 'TEST 1: empty prompt rejected'
|
|
try { & $S -RepoPath "$sand\repo" -Prompt ' ' | Out-Null; Check 'empty prompt rejected' $false }
|
|
catch { Check 'empty prompt rejected' $true }
|
|
|
|
Show 'TEST 2: dirty tree + missing verify tool (pnpm trap)'
|
|
'dirty' | Set-Content "$sand\repo\scratch.txt"
|
|
$r2 = (& $S -RepoPath "$sand\repo" -Prompt 'tidy things up' -VerifyCommand 'pnpm run test') | ConvertFrom-Json
|
|
Check 'preflight failed' (-not $r2.ok)
|
|
Check 'dirty tree reported' ([bool]($r2.problems -match 'dirty'))
|
|
Check 'missing pnpm reported' ([bool]($r2.problems -match 'pnpm'))
|
|
Remove-Item "$sand\repo\scratch.txt" -Force
|
|
|
|
Show 'TEST 3: already on a gnhf/* branch (non-TTY overwrite trap)'
|
|
Push-Location "$sand\repo"; git checkout -q -b 'gnhf/fake'; Pop-Location
|
|
$r3 = (& $S -RepoPath "$sand\repo" -Prompt 'tidy things up') | ConvertFrom-Json
|
|
Check 'gnhf-branch launch blocked' (-not $r3.ok)
|
|
Push-Location "$sand\repo"; git checkout -q main; git branch -q -D 'gnhf/fake'; Pop-Location
|
|
|
|
Show 'TEST 4: unknown agent CLI blocked'
|
|
$r4 = (& $S -RepoPath "$sand\repo" -Prompt 'tidy things up' -Agent 'notarealagent') | ConvertFrom-Json
|
|
Check 'missing agent CLI blocked' (-not $r4.ok)
|
|
|
|
Show 'TEST 5: happy path with stubbed gnhf'
|
|
$env:LIGHTS_OUT_GNHF = (Join-Path $stub 'gnhf.ps1')
|
|
Check 'stub gnhf resolves via override' ((Resolve-GnhfCommandForTest).Kind -eq 'override')
|
|
$out = & $S -RepoPath "$sand\repo" -Prompt 'reduce complexity in the widget helper' -Title 'Reduce Complexity' -StopWhen 'npm test passes and no unrelated files changed' -VerifyCommand 'git --version' -MaxIterations 5 -Mode companion -Agent 'copilot'
|
|
$res = $out | ConvertFrom-Json
|
|
Check 'launch ok' ($res.ok -eq $true)
|
|
Write-Host " run id: $($res.id) pid: $($res.workerPid)"
|
|
|
|
Show 'Waiting for detached worker...'
|
|
$final = $null
|
|
for ($i = 0; $i -lt 45; $i++) {
|
|
Start-Sleep -Seconds 2
|
|
$j = & $C -Id $res.id -Json
|
|
if ($j) { $final = ($j | ConvertFrom-Json)[0] }
|
|
if ($final -and $final.status -in @('completed', 'failed', 'orphaned')) { break }
|
|
}
|
|
Check 'worker finished' ($null -ne $final)
|
|
if ($final) {
|
|
Check 'status completed' ($final.status -eq 'completed')
|
|
Check 'two commits captured' ($final.commitCount -eq 2)
|
|
Check 'end-state parsed' ($final.endState.status -eq 'completed')
|
|
Check 'verify ran before and after' (($final.verifyExitCodeBefore -eq 0) -and ($final.verifyExitCodeAfter -eq 0))
|
|
Check 'run dir resolved' ($null -ne $final.runDir)
|
|
Check 'notes tail captured' ([bool]$final.notesTail)
|
|
Check 'gnhf provenance recorded' ($final.gnhfKind -eq 'override')
|
|
}
|
|
|
|
Show 'Stub received correct gnhf flags'
|
|
$stubArgs = Get-Content "$sand\state\stub-args.json" -Raw -ErrorAction SilentlyContinue | ConvertFrom-Json
|
|
if ($stubArgs) {
|
|
Write-Host (' ' + ($stubArgs -join ' '))
|
|
Check 'passed --stop-when' ([bool]($stubArgs -contains '--stop-when'))
|
|
Check 'passed --prevent-sleep' ([bool]($stubArgs -contains '--prevent-sleep'))
|
|
Check 'passed --max-iterations' ([bool]($stubArgs -contains '--max-iterations'))
|
|
Check 'prompt is last arg' ($stubArgs[-1] -eq 'reduce complexity in the widget helper')
|
|
}
|
|
else { Check 'stub was invoked' $false }
|
|
|
|
Show 'Vault output'
|
|
Get-ChildItem -Recurse "$sand\vault" -File -ErrorAction SilentlyContinue | ForEach-Object { ' ' + $_.FullName.Replace("$sand\vault\", '') }
|
|
$note = Get-ChildItem -Recurse -Filter 'summary.md' "$sand\vault" -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
Check 'summary.md written' ($null -ne $note)
|
|
if ($note) {
|
|
$body = Get-Content $note.FullName -Raw
|
|
Check 'has lights-out-run type' ($body -match 'type: lights-out-run')
|
|
Check 'has assessment stub' ($body -match '## Assessment')
|
|
Check 'records commits' ($body -match 'refactor: simplify widget helper')
|
|
Check 'diff.patch saved' (Test-Path (Join-Path $note.DirectoryName 'diff.patch'))
|
|
Check 'notes.md copied' (Test-Path (Join-Path $note.DirectoryName 'notes.md'))
|
|
$yamlOk = (python -c "import yaml,io,sys; t=io.open(sys.argv[1],encoding='utf-8').read(); yaml.safe_load(t.split('---')[1]); print('OK')" $note.FullName 2>&1) -join ''
|
|
Check 'frontmatter is valid YAML' ($yamlOk -match 'OK')
|
|
if ($yamlOk -notmatch 'OK') { Write-Host " $yamlOk" -ForegroundColor Red }
|
|
Check 'ISO started timestamp' ($body -match '\| Started \| \d{4}-\d{2}-\d{2}T[\d:.]+[+-]\d{2}:\d{2} \|')
|
|
}
|
|
|
|
Show "RESULT: $($script:pass) passed, $($script:fail) failed"
|
|
if ($note) { Write-Host "`n--- summary.md ---`n"; Get-Content $note.FullName }
|
|
$env:LIGHTS_OUT_GNHF = ''
|
|
Remove-Item -Recurse -Force $sand -ErrorAction SilentlyContinue
|
|
if ($script:fail -gt 0) { exit 1 }
|
|
|