package main import ( "encoding/json" "strings" "testing" "time" ) func TestAgentActionAllowlistAndLifecycle(t *testing.T) { store, err := openStore(":memory:") if err != nil { t.Fatal(err) } defer store.Close() report, _ := json.Marshal(AgentReport{Services: []AgentService{{Name: "gitea.service"}}, Containers: []AgentContainer{{ID: "abc123", Name: "gitea", State: "running"}, {ID: "old123", Name: "old", State: "exited"}}}) _, err = store.db.Exec(`INSERT INTO agents(id,name,secret_hash,enrolled_at,last_seen,report_json) VALUES('a','agent','x',?,?,?)`, time.Now().Unix(), time.Now().Unix(), string(report)) if err != nil { t.Fatal(err) } if _, err = store.QueueAgentAction("a", "systemd-restart", "../../bad.service"); err == nil { t.Fatal("unsafe unit accepted") } if _, err = store.QueueAgentAction("a", "docker-update", "gitea"); err != nil { t.Fatalf("known Docker container rejected: %v", err) } if _, err = store.QueueAgentAction("a", "system-update", ""); err != nil { t.Fatalf("system update rejected: %v", err) } if _, err = store.QueueAgentAction("a", "docker-remove", "gitea"); err == nil { t.Fatal("running/unknown-state container accepted for removal") } action, err := store.QueueAgentAction("a", "docker-remove", "old") if err != nil { t.Fatalf("stopped container rejected for removal: %v", err) } commands, err := store.DispatchAgentActions("a") if err != nil || len(commands) != 3 || commands[2].ID != action.ID { t.Fatalf("commands=%#v err=%v", commands, err) } store.CompleteAgentActions("a", []AgentActionResult{{ID: action.ID, Success: true, Message: "ok"}}) items, err := store.AgentActions(10) if err != nil || len(items) != 3 || items[0].Status != "completed" { t.Fatalf("items=%#v err=%v", items, err) } if _, err = store.QueueAgentAction("a", "system-reboot", ""); err != nil { t.Fatalf("system reboot rejected: %v", err) } if _, err = store.QueueAgentAction("a", "system-reboot", "now"); err == nil { t.Fatal("system reboot with unexpected target accepted") } } func TestManualServiceLinkGroupsOfflineAlerts(t *testing.T) { metrics := dashboardMetrics{Agents: []ManagedAgent{{ID: "a", Name: "app", VMID: 105, Online: false}}, ServiceLinks: []ServiceLink{{ServiceID: 7, AgentID: "a", VMID: 105}}, Services: ServicesMetrics{Services: []ServiceStatus{{MonitoredService: MonitoredService{ID: 7, Name: "Gitea"}, Endpoints: []ServiceEndpointStatus{{Kind: "local", Up: false}}}}}} alerts := []Alert{{ID: "agent-offline-a-105"}, {ID: "service-7-local"}, {ID: "other"}} grouped := groupLinkedAlerts(metrics, alerts) if len(grouped) != 2 { t.Fatalf("grouped=%#v", grouped) } found := false for _, alert := range grouped { if alert.ID == "group-agent-offline-a-105" { found = true } } if !found { t.Fatalf("group alert missing: %#v", grouped) } } func TestExplainReadOnlySystemUpdateError(t *testing.T) { message := explainSystemUpdateError("ERROR: apt failed: Read-only file system") if !strings.Contains(message, "v0.19.2") { t.Fatalf("read-only failure is not actionable: %s", message) } }