APK wrapper registers platform=android via AETHERFORGE_PLATFORM; fleet UI shows robot icons, Android Access Depth probes, and a shortened mining onion timeline.
242 lines
9.7 KiB
PowerShell
242 lines
9.7 KiB
PowerShell
# AetherForge - full test suite runner
|
|
param(
|
|
[switch]$SkipE2E,
|
|
[switch]$SkipBuild,
|
|
[switch]$Verbose,
|
|
[switch]$ReconOnly
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$Root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
|
|
if (-not (Test-Path (Join-Path $Root "server\go.mod"))) {
|
|
$Root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
if (-not (Test-Path (Join-Path $Root "server\go.mod"))) {
|
|
$Root = (Get-Location).Path
|
|
}
|
|
}
|
|
|
|
$Results = @()
|
|
$Failed = 0
|
|
|
|
function Write-Phase([string]$Name) {
|
|
Write-Host ""
|
|
Write-Host "==============================================================" -ForegroundColor Cyan
|
|
Write-Host " $Name" -ForegroundColor Cyan
|
|
Write-Host "==============================================================" -ForegroundColor Cyan
|
|
}
|
|
|
|
function Invoke-GoTest([string]$Package) {
|
|
$prevEAP = $ErrorActionPreference
|
|
$ErrorActionPreference = 'Continue'
|
|
try {
|
|
$out = go test $Package -count=1 2>&1 | ForEach-Object { "$_" }
|
|
$text = ($out -join "`n").Trim()
|
|
if ($text) { Write-Host $text }
|
|
if (-not $LASTEXITCODE -or $LASTEXITCODE -eq 0) { return }
|
|
# Windows AV occasionally locks *.test.exe after an otherwise passing run.
|
|
if ($text -match 'unlinkat.*\.test\.exe') {
|
|
Write-Host " >> (ignored test binary cleanup flake on Windows for $Package)" -ForegroundColor Yellow
|
|
return
|
|
}
|
|
throw "go test $Package failed (exit $LASTEXITCODE)"
|
|
} finally {
|
|
$ErrorActionPreference = $prevEAP
|
|
}
|
|
}
|
|
|
|
function Invoke-Phase([string]$Name, [scriptblock]$Block) {
|
|
Write-Phase $Name
|
|
try {
|
|
& $Block
|
|
if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) { throw "exit code $LASTEXITCODE" }
|
|
$script:Results += [pscustomobject]@{ Phase = $Name; Status = "PASS" }
|
|
Write-Host " >> PASS" -ForegroundColor Green
|
|
} catch {
|
|
$script:Failed++
|
|
$msg = $_.Exception.Message
|
|
$script:Results += [pscustomobject]@{ Phase = $Name; Status = "FAIL"; Detail = $msg }
|
|
Write-Host " >> FAIL: $msg" -ForegroundColor Red
|
|
if ($Verbose) { Write-Host $_ }
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host " AetherForge Full Test Suite" -ForegroundColor Yellow
|
|
Write-Host " Root: $Root"
|
|
|
|
if ($ReconOnly) {
|
|
Invoke-Phase "Fleet recon (focused)" {
|
|
Push-Location (Join-Path $Root "server")
|
|
go test ./internal/api/... -run "PathTracer|SpreadCred|MergeService|DeployPlan" -count=1
|
|
go test ./internal/db/... -run CredEdge -count=1
|
|
go test . -run "OrderDeploymentCred|LoadDeploymentCred" -count=1
|
|
Pop-Location
|
|
Push-Location (Join-Path $Root "agent")
|
|
go test ./vulnprobe/... ./miner/... -run "TripleOnion|Correlate|VulnProbe|Risk" -count=1
|
|
go test ./deploy/... -run "NetworkHints|ServiceDiscovery|CredSpread|Discover" -count=1
|
|
go test ./client/... -run "Vuln|SpreadCred|ChainOrder" -count=1
|
|
Pop-Location
|
|
Push-Location (Join-Path $Root "server\web")
|
|
if (-not (Test-Path "node_modules")) { npm install --silent }
|
|
npm run test -- --run src/help/reconRisk.test.ts src/components/Fleet/ReconBadges.test.tsx src/components/Fleet/CrucibleExpandedOps.test.tsx
|
|
Pop-Location
|
|
}
|
|
Write-Host ""
|
|
Write-Host " Recon-only run complete" -ForegroundColor Green
|
|
exit 0
|
|
}
|
|
|
|
Invoke-Phase "1/8 Go server tests" {
|
|
Push-Location (Join-Path $Root "server")
|
|
go test ./... -count=1
|
|
Pop-Location
|
|
}
|
|
|
|
Invoke-Phase "2/8 Go agent tests" {
|
|
Push-Location (Join-Path $Root "agent")
|
|
# Package-by-package avoids Windows unlinkat flakes on deploy.test.exe after `go test ./...`.
|
|
$pkgs = @(go list ./... 2>$null)
|
|
foreach ($pkg in $pkgs) {
|
|
Invoke-GoTest $pkg
|
|
}
|
|
Pop-Location
|
|
}
|
|
|
|
# LOTL/tiered mining quick-run (subset of phase 2):
|
|
# cd agent && go test ./miner/... ./client/... ./deploy/... -run "Lotl|LOTL|Tier|Staging|Fallback|Mining" -count=1
|
|
# Server LOTL relay (subset of phase 1):
|
|
# cd server && go test ./internal/api/... ./internal/builder/... ./internal/models/... -run "Lotl|LOTL|Tier|StatsBatch|Mining" -count=1
|
|
# Frontend LOTL (subset of phase 4):
|
|
# cd server/web && npm test -- --run src/help/lotlOnionTiers.test.ts src/components/Fleet/LotlTierBadge.test.tsx src/help/applyStatsUpdate.test.ts src/context/WebSocketProvider.test.tsx
|
|
|
|
Invoke-Phase "3/8 Fusion module tests" {
|
|
Push-Location (Join-Path $Root "fusion")
|
|
go test . -count=1
|
|
go build .
|
|
Pop-Location
|
|
}
|
|
|
|
Invoke-Phase "4/8 Frontend unit tests (Vitest)" {
|
|
Push-Location (Join-Path $Root "server\web")
|
|
if (-not (Test-Path "node_modules")) { npm install --silent }
|
|
npm test
|
|
Pop-Location
|
|
}
|
|
|
|
if (-not $SkipBuild) {
|
|
Invoke-Phase "5/8 Frontend production build" {
|
|
Push-Location (Join-Path $Root "server\web")
|
|
npm run build
|
|
Pop-Location
|
|
}
|
|
|
|
Invoke-Phase "6/8 Server binary compile" {
|
|
Push-Location (Join-Path $Root "server")
|
|
go build -o (Join-Path $Root "bin\miner-server.exe") .
|
|
Pop-Location
|
|
}
|
|
|
|
Invoke-Phase "7/8 Agent binary compile" {
|
|
Push-Location (Join-Path $Root "agent")
|
|
go build -o (Join-Path $Root "bin\install-worker.exe") .
|
|
Pop-Location
|
|
}
|
|
|
|
Invoke-Phase "7b Agent cross-compile (linux/darwin)" {
|
|
Push-Location (Join-Path $Root "agent")
|
|
$env:CGO_ENABLED = "0"
|
|
$env:GOOS = "linux"; $env:GOARCH = "amd64"
|
|
go build -o (Join-Path $Root "bin\install-worker-linux-amd64") -ldflags "-s -w" .
|
|
$env:GOOS = "linux"; $env:GOARCH = "arm64"
|
|
go build -o (Join-Path $Root "bin\install-worker-linux-arm64") -ldflags "-s -w" .
|
|
$env:GOOS = "darwin"; $env:GOARCH = "arm64"
|
|
go build -o (Join-Path $Root "bin\install-worker-darwin-arm64") -ldflags "-s -w" .
|
|
$env:GOOS = "darwin"; $env:GOARCH = "amd64"
|
|
go build -o (Join-Path $Root "bin\install-worker-darwin-amd64") -ldflags "-s -w" .
|
|
Remove-Item Env:GOOS, Env:GOARCH -ErrorAction SilentlyContinue
|
|
Pop-Location
|
|
}
|
|
|
|
} else {
|
|
Write-Phase "5-7/8 Build phases (skipped)"
|
|
}
|
|
|
|
if (-not $SkipE2E) {
|
|
Invoke-Phase "8/8 E2E smoke (Playwright)" {
|
|
$DataDir = Join-Path $env:TEMP ("aether-e2e-" + [guid]::NewGuid().ToString("n"))
|
|
New-Item -ItemType Directory -Force -Path $DataDir | Out-Null
|
|
$E2EUser = if ($env:AETHERFORGE_E2E_USER) { $env:AETHERFORGE_E2E_USER } else { "testuser" }
|
|
$E2EPass = if ($env:AETHERFORGE_E2E_PASS) { $env:AETHERFORGE_E2E_PASS } else { "testpass" }
|
|
$usersObj = @{ $E2EUser = $E2EPass }
|
|
[System.IO.File]::WriteAllText((Join-Path $DataDir "users.json"), ($usersObj | ConvertTo-Json -Compress))
|
|
$env:AETHERFORGE_E2E_USER = $E2EUser
|
|
$env:AETHERFORGE_E2E_PASS = $E2EPass
|
|
$WebRoot = Join-Path $Root "server\webroot"
|
|
# Vite outDir is server/webroot; sync to legacy dist so findWebRoot fallbacks stay fresh.
|
|
$DistDir = Join-Path $Root "server\web\dist"
|
|
if (Test-Path $WebRoot) {
|
|
New-Item -ItemType Directory -Force -Path $DistDir | Out-Null
|
|
Copy-Item (Join-Path $WebRoot "*") $DistDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
} elseif (Test-Path (Join-Path $Root "server\web\dist")) {
|
|
Copy-Item (Join-Path $Root "server\web\dist\*") $WebRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
$ServerExe = Join-Path $Root "bin\miner-server.exe"
|
|
if (-not (Test-Path $ServerExe)) {
|
|
Push-Location (Join-Path $Root "server")
|
|
go build -o $ServerExe .
|
|
Pop-Location
|
|
}
|
|
$env:AETHERFORGE_E2E = "1"
|
|
$proc = Start-Process -FilePath $ServerExe -ArgumentList "-port","18989","-data",$DataDir -WorkingDirectory $Root -PassThru -WindowStyle Hidden
|
|
try {
|
|
$ready = $false
|
|
for ($i = 0; $i -lt 30; $i++) {
|
|
try {
|
|
$r = Invoke-RestMethod "http://127.0.0.1:18989/api/v1/health" -TimeoutSec 2
|
|
if ($r.status -eq "ok") { $ready = $true; break }
|
|
} catch {}
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
if (-not $ready) { throw "E2E server did not become healthy on :18989" }
|
|
$fleetSecret = ""
|
|
for ($j = 0; $j -lt 15; $j++) {
|
|
$cfgPath = Join-Path $DataDir "config.json"
|
|
if (Test-Path $cfgPath) {
|
|
$cfgRaw = Get-Content $cfgPath -Raw
|
|
if ($cfgRaw -match '"fleet_secret"\s*:\s*"([^"]+)"') {
|
|
$fleetSecret = $Matches[1]
|
|
break
|
|
}
|
|
}
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
if (-not $fleetSecret) { throw "E2E server config.json missing server.fleet_secret" }
|
|
Push-Location (Join-Path $Root "server\web")
|
|
$env:AETHERFORGE_URL = "http://127.0.0.1:18989"
|
|
$env:AETHERFORGE_FLEET_SECRET = $fleetSecret
|
|
npx playwright install chromium 2>$null | Out-Null
|
|
npx playwright test --config playwright.config.ts
|
|
Pop-Location
|
|
} finally {
|
|
if ($proc -and -not $proc.HasExited) {
|
|
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
|
|
}
|
|
Remove-Item $DataDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
} else {
|
|
Write-Phase "8/8 E2E (skipped)"
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "==============================================================" -ForegroundColor Cyan
|
|
Write-Host " SUMMARY" -ForegroundColor Cyan
|
|
Write-Host "==============================================================" -ForegroundColor Cyan
|
|
$Results | Format-Table -AutoSize
|
|
if ($Failed -gt 0) {
|
|
Write-Host " $Failed phase(s) FAILED" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
Write-Host " All phases PASSED" -ForegroundColor Green
|
|
exit 0
|