# AetherForge API smoke tests — B-01 through B-10 matrix param( [string]$BaseUrl = "http://localhost:8989", [string]$Username, [string]$Password, [string]$FleetSecret, [string]$DataDir ) if (-not $Username) { $Username = if ($env:AETHERFORGE_E2E_USER) { $env:AETHERFORGE_E2E_USER } else { "testuser" } } if (-not $Password) { $Password = if ($env:AETHERFORGE_E2E_PASS) { $env:AETHERFORGE_E2E_PASS } else { "testpass" } } if (-not $FleetSecret) { $FleetSecret = $env:AETHERFORGE_FLEET_SECRET } if (-not $FleetSecret -and $DataDir) { $cfgPath = Join-Path $DataDir "config.json" if (Test-Path $cfgPath) { $cfg = Get-Content $cfgPath -Raw | ConvertFrom-Json if ($cfg.server.fleet_secret) { $FleetSecret = $cfg.server.fleet_secret } } } $ErrorActionPreference = "Stop" $passed = 0 $failed = 0 $results = @() # Dashboard API routes require HTTP Basic Auth (see router.go). $cred = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${Username}:${Password}")) $authHeaders = @{ Authorization = "Basic $cred" } function Invoke-AgentRestMethod { param( [string]$Uri, [string]$Method = "Post", [string]$Body, [string]$ContentType = "application/json" ) if (-not $FleetSecret) { throw "fleet secret required for agent API; pass -FleetSecret, -DataDir, or set AETHERFORGE_FLEET_SECRET" } $headers = @{ "X-Fleet-Secret" = $FleetSecret } $params = @{ Uri = $Uri Method = $Method Headers = $headers } if ($Body) { $params.Body = $Body $params.ContentType = $ContentType } return Invoke-RestMethod @params } function Invoke-AuthRestMethod { param( [string]$Uri, [string]$Method = "Get", [string]$Body, [string]$ContentType = "application/json" ) $params = @{ Uri = $Uri Method = $Method Headers = $authHeaders } if ($Body) { $params.Body = $Body $params.ContentType = $ContentType } return Invoke-RestMethod @params } function Invoke-SmokeTest { param( [string]$TestId, [string]$TestName, [scriptblock]$Block ) try { & $Block $script:passed++ $script:results += [pscustomobject]@{ ID = $TestId; Name = $TestName; Status = "PASS" } Write-Host "[PASS] $TestId $TestName" -ForegroundColor Green } catch { $script:failed++ $msg = $_.Exception.Message $script:results += [pscustomobject]@{ ID = $TestId; Name = $TestName; Status = "FAIL"; Detail = $msg } Write-Host "[FAIL] $TestId $TestName - $msg" -ForegroundColor Red } } Invoke-SmokeTest "B-01" "GET /health" { $r = Invoke-RestMethod "$BaseUrl/api/v1/health" if ($r.status -ne "ok") { throw "unexpected status" } } Invoke-SmokeTest "B-02" "GET /server/info" { $r = Invoke-AuthRestMethod "$BaseUrl/api/v1/server/info" if (-not $r.local_ips) { throw "missing local_ips" } } Invoke-SmokeTest "B-03" "GET/PUT /config round-trip" { $cfg = Invoke-AuthRestMethod "$BaseUrl/api/v1/config" $sub = "smoke-test-$(Get-Date -Format 'HHmmss')" $cfg.server.dashboard_subtitle = $sub $body = $cfg | ConvertTo-Json -Depth 10 Invoke-AuthRestMethod "$BaseUrl/api/v1/config" -Method Put -Body $body | Out-Null $cfg2 = Invoke-AuthRestMethod "$BaseUrl/api/v1/config" if ($cfg2.server.dashboard_subtitle -ne $sub) { throw "subtitle not persisted" } } Invoke-SmokeTest "B-04" "GET /builds list" { $null = Invoke-AuthRestMethod "$BaseUrl/api/v1/builds" } Invoke-SmokeTest "B-05" "GET /alerts" { $null = Invoke-AuthRestMethod "$BaseUrl/api/v1/alerts" } Invoke-SmokeTest "B-06" "Blueprint CRUD" { $name = "smoke-blueprint-$(Get-Date -Format 'HHmmss')" $payload = @{ name = $name; data = @{ worker_name = "smoke"; wallet = "4test" } } | ConvertTo-Json -Depth 5 Invoke-AuthRestMethod "$BaseUrl/api/v1/blueprints" -Method Post -Body $payload | Out-Null $list = Invoke-AuthRestMethod "$BaseUrl/api/v1/blueprints" if (-not ($list | Where-Object { $_.name -eq $name })) { throw "blueprint not listed" } Invoke-AuthRestMethod "$BaseUrl/api/v1/blueprints?name=$name" -Method Delete | Out-Null } Invoke-SmokeTest "B-07" "POST /agent/decide" { $body = @{ agent_id = "smoke-agent" worker_name = "smoke" hostname = "smoke-pc" uptime_seconds = 60 is_running = $true cpu_cores = 4 cpu_usage_pct = 10 memory_gb = 8 memory_usage_pct = 20 hashrate_15m = 100 shares_total = 0 shares_good = 0 shares_bad = 0 } | ConvertTo-Json -Depth 5 try { Invoke-AgentRestMethod "$BaseUrl/api/v1/agent/decide" -Body $body | Out-Null } catch { $code = $_.Exception.Response.StatusCode.value__ if ($code -eq 403) { return } if ($code -ge 500) { throw $_ } throw $_ } } Invoke-SmokeTest "B-08" "POST /agent/report array" { $body = '[{"agent_id":"smoke-agent","tool":"sleep","success":true,"output":"ok"}]' $r = Invoke-AgentRestMethod "$BaseUrl/api/v1/agent/report" -Body $body if (-not $r.success) { throw "report not accepted" } } Invoke-SmokeTest "B-09" "GET /pools/status + /ai/activity" { $null = Invoke-AuthRestMethod "$BaseUrl/api/v1/pools/status" $null = Invoke-AuthRestMethod "$BaseUrl/api/v1/ai/activity" } Invoke-SmokeTest "B-10" "GET /agents + /dashboard/stats" { $null = Invoke-AuthRestMethod "$BaseUrl/api/v1/agents" $null = Invoke-AuthRestMethod "$BaseUrl/api/v1/dashboard/stats" } Write-Host "" Write-Host "Smoke summary: $passed passed, $failed failed" -ForegroundColor Cyan $results | Format-Table -AutoSize if ($failed -gt 0) { exit 1 }