Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Prosecutor and Public Defender use real fleet telemetry only; Judge dispatches L4 commands and emits full transcripts via seer_events and emberwake_court_debate when ai_control_enabled.
176 lines
5.2 KiB
Go
176 lines
5.2 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
fleetai "crypto-miner-server/internal/ai"
|
|
"crypto-miner-server/internal/atlas"
|
|
"crypto-miner-server/internal/clearance"
|
|
"crypto-miner-server/internal/db"
|
|
"crypto-miner-server/internal/models"
|
|
)
|
|
|
|
func TestHubCourtChamberEvidenceRealData(t *testing.T) {
|
|
database, err := db.New(t.TempDir())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer database.Close()
|
|
|
|
agentID := "chamber-evidence-agent"
|
|
if err := database.UpsertAgent(&models.Agent{
|
|
ID: agentID, Name: "Patient", IP: "192.168.50.10", Platform: "windows", Status: "online",
|
|
ChainExhausted: true,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for i := 0; i < 3; i++ {
|
|
_, _ = database.RecordSubnetSpreadFailure("192.168.50")
|
|
}
|
|
|
|
hub := NewWSHub(database)
|
|
hub.SetServerPolicy(ServerPolicy{ErasureLanesEnabled: true, AIControlEnabled: true})
|
|
hub.mu.Lock()
|
|
hub.subnetGossipWhispers = map[string][]atlas.GossipHint{
|
|
"192.168.50": {{Tier: "docker", Condition: "no_docker", Reason: "lan gossip"}},
|
|
}
|
|
hub.mu.Unlock()
|
|
|
|
adapter := NewHubCourtChamberAdapter(hub, database)
|
|
snap := fleetai.AgentSnapshot{
|
|
AgentID: agentID, Name: "Patient", GOOS: "windows",
|
|
MiningHashrate: 0, Stuck: true, ChainExhausted: true,
|
|
LOTLAttempts: []fleetai.TierAttempt{{Tier: "docker", OK: false, Error: "denied"}},
|
|
}
|
|
ev := adapter.ChamberEvidence(agentID, snap)
|
|
if !strings.Contains(ev.SubnetImmune, "192.168.50") || !strings.Contains(ev.SubnetImmune, "fail_count=3") {
|
|
t.Fatalf("subnet immune=%q", ev.SubnetImmune)
|
|
}
|
|
if !strings.Contains(ev.ErasureRecovery, "lanes_enabled=true") {
|
|
t.Fatalf("erasure=%q", ev.ErasureRecovery)
|
|
}
|
|
if !strings.Contains(ev.GossipWhispers, "docker|no_docker") {
|
|
t.Fatalf("gossip=%q", ev.GossipWhispers)
|
|
}
|
|
}
|
|
|
|
func TestIntegrationCourtChamberEmitsSeerDebate(t *testing.T) {
|
|
var llmBody string
|
|
var llmMu sync.Mutex
|
|
llmSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost || !strings.HasSuffix(r.URL.Path, "/chat/completions") {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
raw, _ := io.ReadAll(r.Body)
|
|
llmMu.Lock()
|
|
llmBody = string(raw)
|
|
llmMu.Unlock()
|
|
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"choices": []map[string]interface{}{
|
|
{"message": map[string]string{
|
|
"content": "Verdict: restart mining after adversarial chamber.\n" +
|
|
`{"commands":[{"type":"restart_mining","args":{}}]}`,
|
|
}},
|
|
},
|
|
})
|
|
}))
|
|
t.Cleanup(llmSrv.Close)
|
|
|
|
aiCfg := FleetAIConfigView{
|
|
AIControlEnabled: true, AIEndpoint: llmSrv.URL + "/v1",
|
|
AIModel: "test-model", AIDecisionIntervalSec: 1, AIAutoElevateClearance: true,
|
|
}
|
|
hub, database, _ := newFleetIntelligenceHub(t, aiCfg)
|
|
hub.SetServerPolicy(ServerPolicy{ErasureLanesEnabled: true, AIControlEnabled: true})
|
|
courtChamber := NewHubCourtChamberAdapter(hub, database)
|
|
seerEmitter := &HubSeerEmitter{Hub: hub, DB: database}
|
|
|
|
sched := fleetai.NewScheduler(
|
|
&ConfigAIAdapter{Src: &mutableFleetAIConfig{view: aiCfg}},
|
|
&WSHubSnapshotAdapter{Hub: hub},
|
|
&ClearanceGuardExecutor{Inner: &FleetAIExecutor{Hub: hub}, Clearance: hub.ClearanceManager()},
|
|
&DatabaseAIDecisionStore{DB: database},
|
|
courtChamber,
|
|
hub.ClearanceManager(),
|
|
)
|
|
sched.SetCourtDeps(fleetai.CourtDeps{Chamber: courtChamber, Seer: seerEmitter})
|
|
|
|
old := fleetai.DecideFunc
|
|
fleetai.DecideFunc = func(ctx context.Context, endpoint, model, systemPrompt, userPrompt string) (string, error) {
|
|
return fleetai.Decide(ctx, endpoint, model, systemPrompt, userPrompt)
|
|
}
|
|
t.Cleanup(func() { fleetai.DecideFunc = old })
|
|
|
|
agentID := "court-chamber-agent"
|
|
conn := connectIntelAgent(t, hub, agentID, map[string]interface{}{
|
|
"agent_id": agentID, "hostname": "chamber-host", "platform": "windows", "version": "1.0",
|
|
})
|
|
pushStuckAgentTelemetry(t, conn)
|
|
seedStuckAgentDB(t, database, agentID)
|
|
waitForAgentTelemetry(t, hub, agentID, "stuck", "lotl_attempts")
|
|
|
|
dashConn := connectTestDashboard(t, hub)
|
|
seerCh := make(chan map[string]interface{}, 1)
|
|
go func() {
|
|
for {
|
|
var msg Message
|
|
if err := dashConn.ReadJSON(&msg); err != nil {
|
|
return
|
|
}
|
|
if msg.Type != "seer_events" {
|
|
continue
|
|
}
|
|
var body map[string]interface{}
|
|
if json.Unmarshal(msg.Payload, &body) != nil {
|
|
continue
|
|
}
|
|
if body["event_type"] != "court_debate" {
|
|
continue
|
|
}
|
|
seerCh <- body
|
|
return
|
|
}
|
|
}()
|
|
|
|
sched.ResetLastRunForTest(agentID, 2*time.Minute)
|
|
sched.Tick()
|
|
|
|
llmMu.Lock()
|
|
body := llmBody
|
|
llmMu.Unlock()
|
|
if !strings.Contains(body, "PUBLIC DEFENDER") {
|
|
t.Fatalf("expected adversarial chamber judge prompt, got: %s", body)
|
|
}
|
|
|
|
select {
|
|
case ev := <-seerCh:
|
|
payload, ok := ev["payload"].(map[string]interface{})
|
|
if !ok {
|
|
// payload may be json.RawMessage nested
|
|
if raw, ok2 := ev["payload"]; ok2 {
|
|
b, _ := json.Marshal(raw)
|
|
_ = json.Unmarshal(b, &payload)
|
|
}
|
|
}
|
|
if ev["event_type"] != "court_debate" {
|
|
t.Fatalf("event_type=%v", ev["event_type"])
|
|
}
|
|
case <-time.After(5 * time.Second):
|
|
events, _ := database.ListSeerEvents(5)
|
|
t.Fatalf("timed out waiting for court_debate seer_events; db events=%+v", events)
|
|
}
|
|
|
|
if level := hub.ClearanceManager().Level(agentID); level < clearance.L1 {
|
|
t.Fatalf("unexpected clearance %d", level)
|
|
}
|
|
}
|