package ai import ( "strings" "testing" "time" "crypto-miner-server/internal/strategy" ) func TestBuildCourtDebateIncludesRealEvidence(t *testing.T) { ev := CourtChamberEvidence{ AgentID: "a1", AgentName: "stuck", Hashrate: 0, Stuck: true, ChainExhausted: true, AtlasSummary: "docker 5/5 failed (100%)", SubnetImmune: "prefix=10.0.0 fail_count=5 paused_until=2026-06-08T00:00:00Z UTC", ErasureRecovery: "lanes_enabled=true stage_fetch_attempted=false (RS 4+2 shard recovery available via stage_fetch)", GossipWhispers: "docker|defender_on (lan gossip)", } snap := AgentSnapshot{ AgentID: "a1", Name: "stuck", Stuck: true, ChainExhausted: true, LOTLAttempts: stuckSpreadAttempts(), } transcript, bundle := BuildCourtDebate(snap, ev, nil, PersonaBalanced) if len(transcript.Transcript) != 2 { t.Fatalf("transcript=%+v", transcript.Transcript) } if transcript.Transcript[0].Role != "prosecutor" || transcript.Transcript[1].Role != "defender" { t.Fatalf("roles=%+v", transcript.Transcript) } pros := transcript.Transcript[0].Text for _, want := range []string{ "0.00 H/s", "Failure atlas: docker", "Subnet immune table:", "Erasure recovery:", "Atlas LAN gossip whispers:", "14/14 failed", } { if !strings.Contains(pros, want) { t.Fatalf("prosecutor missing %q: %s", want, pros) } } if !strings.Contains(bundle.SystemPrompt, "## PUBLIC DEFENDER") { t.Fatalf("judge prompt missing defender section") } if !strings.Contains(bundle.UserPrompt, "## JUDGE (L4)") { t.Fatalf("judge user prompt: %s", bundle.UserPrompt) } } func TestBuildCourtDebateDefenderPhenotype(t *testing.T) { phenotype := &strategy.FleetPhenotype{ Fingerprint: "win|1", SourceAgentName: "winner", ActiveTier: "container", PeakHashrate: 900, SpreadLane: "winrm", TierOrder: []string{"container", "wsl"}, } ev := CourtChamberEvidence{ErasureRecovery: "lanes_enabled=true stage_fetch_attempted=false"} _, bundle := BuildCourtDebate(AgentSnapshot{AgentID: "a1"}, ev, phenotype, PersonaBalanced) if !strings.Contains(bundle.DefenderSnippet, "Fleet phenotype from winner") { t.Fatalf("defender=%s", bundle.DefenderSnippet) } if !strings.Contains(bundle.DefenderSnippet, "Erasure RS 4+2 lanes") { t.Fatalf("defender erasure=%s", bundle.DefenderSnippet) } } func TestFinalizeCourtDebateAddsJudgeTurn(t *testing.T) { raw := "Verdict: restart mining.\n{\"commands\":[{\"type\":\"restart_mining\",\"args\":{}}]}" transcript := CourtDebateTranscript{ Transcript: []CourtDebateTurn{{Role: "prosecutor", Text: "charges"}}, } out := FinalizeCourtDebate(transcript, raw) if len(out.Transcript) != 2 || out.Transcript[1].Role != "judge" { t.Fatalf("transcript=%+v", out.Transcript) } if out.Verdict != "restart mining." { t.Fatalf("verdict=%q", out.Verdict) } if !strings.Contains(out.CommandsJSON, "restart_mining") { t.Fatalf("commands=%q", out.CommandsJSON) } } func TestFormatSubnetImmuneRowPaused(t *testing.T) { until := time.Now().UTC().Add(2 * time.Hour) line := FormatSubnetImmuneRow("10.0.0", 5, &until) if !strings.Contains(line, "fail_count=5") || !strings.Contains(line, "paused_until=") { t.Fatalf("line=%q", line) } } func TestFormatErasureRecovery(t *testing.T) { line := FormatErasureRecovery(true, true, false) if !strings.Contains(line, "lanes_enabled=true") || !strings.Contains(line, "stage_fetch_ok=false") { t.Fatalf("line=%q", line) } }