Pulse/internal/ai/alert_provider_test.go

167 lines
4.7 KiB
Go

package ai
import (
"strings"
"testing"
"time"
)
type stubAlertProvider struct {
active []AlertInfo
resolved []ResolvedAlertInfo
}
func (s *stubAlertProvider) GetActiveAlerts() []AlertInfo { return s.active }
func (s *stubAlertProvider) GetRecentlyResolved(minutes int) []ResolvedAlertInfo { return s.resolved }
func (s *stubAlertProvider) GetAlertsByResource(resourceID string) []AlertInfo {
out := make([]AlertInfo, 0)
for _, a := range s.active {
if a.ResourceID == resourceID {
out = append(out, a)
}
}
return out
}
func (s *stubAlertProvider) GetAlertHistory(resourceID string, limit int) []ResolvedAlertInfo {
out := make([]ResolvedAlertInfo, 0)
for _, a := range s.resolved {
if a.ResourceID == resourceID {
out = append(out, a)
if len(out) >= limit {
break
}
}
}
return out
}
func TestService_buildAlertContext(t *testing.T) {
now := time.Now()
s := &Service{}
s.SetAlertProvider(&stubAlertProvider{
active: []AlertInfo{
{
ID: "a1",
Type: "cpu",
Level: "critical",
ResourceID: "node:pve1",
ResourceName: "pve1",
ResourceType: "node",
Node: "pve1",
Message: "cpu high",
Value: 95,
Threshold: 80,
StartTime: now.Add(-5 * time.Minute),
Duration: "5 mins",
Acknowledged: true,
},
{
ID: "a2",
Type: "memory",
Level: "warning",
ResourceID: "guest:100",
ResourceName: "vm-100",
ResourceType: "guest",
Message: "mem high",
Value: 80,
Threshold: 75,
StartTime: now.Add(-2 * time.Minute),
Duration: "2 mins",
},
},
resolved: []ResolvedAlertInfo{
{
AlertInfo: AlertInfo{
ID: "r1",
Type: "disk",
Level: "warning",
ResourceID: "storage:local",
ResourceName: "local",
Message: "disk ok",
Duration: "10 mins",
},
ResolvedTime: now.Add(-2 * time.Minute),
Duration: "10 mins",
},
},
})
ctx := s.buildAlertContext()
if !strings.Contains(ctx, "## Alert Status") {
t.Fatalf("expected alert status header, got: %s", ctx)
}
if !strings.Contains(ctx, "### Active Alerts") || !strings.Contains(ctx, "**Critical:**") || !strings.Contains(ctx, "**Warning:**") {
t.Fatalf("expected active alert sections, got: %s", ctx)
}
if !strings.Contains(ctx, "[ACKNOWLEDGED]") || !strings.Contains(ctx, "on node pve1") {
t.Fatalf("expected acknowledged/node formatting, got: %s", ctx)
}
if !strings.Contains(ctx, "### Recently Resolved") {
t.Fatalf("expected recently resolved section, got: %s", ctx)
}
}
func TestService_buildAlertContext_Empty(t *testing.T) {
s := &Service{}
if got := s.buildAlertContext(); got != "" {
t.Fatalf("expected empty string, got: %q", got)
}
s.SetAlertProvider(&stubAlertProvider{})
if got := s.buildAlertContext(); got != "" {
t.Fatalf("expected empty string when no alerts, got: %q", got)
}
}
func TestService_buildTargetAlertContext(t *testing.T) {
s := &Service{}
s.SetAlertProvider(&stubAlertProvider{
active: []AlertInfo{
{ID: "a1", Level: "critical", Type: "cpu", ResourceID: "node:pve1", ResourceName: "pve1", Duration: "1 min", Value: 90, Threshold: 80},
{ID: "a2", Level: "warning", Type: "memory", ResourceID: "node:pve2", ResourceName: "pve2", Duration: "1 min", Value: 80, Threshold: 70},
},
})
got := s.buildTargetAlertContext("node:pve1")
if !strings.Contains(got, "Active Alerts for This Resource") || !strings.Contains(got, "pve1") {
t.Fatalf("unexpected context: %s", got)
}
if strings.Contains(got, "pve2") {
t.Fatalf("unexpected extra resource: %s", got)
}
}
func TestFormatTimeAgo(t *testing.T) {
now := time.Now()
if got := formatTimeAgo(now.Add(-10 * time.Second)); got != "just now" {
t.Fatalf("formatTimeAgo(<1m) = %q", got)
}
if got := formatTimeAgo(now.Add(-2 * time.Minute)); got != "2 minutes" {
t.Fatalf("formatTimeAgo(2m) = %q", got)
}
if got := formatTimeAgo(now.Add(-2 * time.Hour)); got != "2 hours" {
t.Fatalf("formatTimeAgo(2h) = %q", got)
}
if got := formatTimeAgo(now.Add(-48 * time.Hour)); got != "2 days" {
t.Fatalf("formatTimeAgo(2d) = %q", got)
}
}
func TestGenerateAlertInvestigationPrompt(t *testing.T) {
out := GenerateAlertInvestigationPrompt(AlertInvestigationRequest{
Level: "critical",
ResourceName: "pve1",
ResourceType: "node",
AlertType: "cpu",
Value: 95,
Threshold: 80,
Duration: "5 mins",
Node: "pve1",
})
if !strings.Contains(out, "Investigate this CRITICAL alert") ||
!strings.Contains(out, "**Resource:** pve1 (node)") ||
!strings.Contains(out, "**Node:** pve1") {
t.Fatalf("unexpected prompt: %s", out)
}
}