mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 11:30:15 +00:00
Major new AI capabilities for infrastructure monitoring: Investigation System: - Autonomous finding investigation with configurable autonomy levels - Investigation orchestrator with rate limiting and guardrails - Safety checks for read-only mode enforcement - Chat-based investigation with approval workflows Forecasting & Remediation: - Trend forecasting for resource capacity planning - Remediation engine for generating fix proposals - Circuit breaker for AI operation protection Unified Findings: - Unified store bridging alerts and AI findings - Correlation and root cause analysis - Incident coordinator with metrics recording New Frontend: - AI Intelligence page with patrol controls - Investigation drawer for finding details - Unified findings panel with actions Supporting Infrastructure: - Learning store for user preference tracking - Proxmox event ingestion and correlation - Enhanced patrol with investigation triggers
124 lines
3.2 KiB
Go
124 lines
3.2 KiB
Go
package ai
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestIsDemoMode(t *testing.T) {
|
|
t.Setenv("PULSE_MOCK_MODE", "true")
|
|
if !IsDemoMode() {
|
|
t.Fatal("expected demo mode true when PULSE_MOCK_MODE=true")
|
|
}
|
|
|
|
t.Setenv("PULSE_MOCK_MODE", "false")
|
|
if IsDemoMode() {
|
|
t.Fatal("expected demo mode false when PULSE_MOCK_MODE=false")
|
|
}
|
|
}
|
|
|
|
func TestIsMockResource(t *testing.T) {
|
|
t.Setenv("PULSE_MOCK_MODE", "false")
|
|
if !IsMockResource("node/pve1", "", "") {
|
|
t.Fatal("expected mock resource to be detected for pattern match")
|
|
}
|
|
if IsMockResource("", "", "") {
|
|
t.Fatal("expected empty resource values to be treated as non-mock")
|
|
}
|
|
if IsMockResource("node/prod1", "production", "node1") {
|
|
t.Fatal("expected non-mock resource to return false")
|
|
}
|
|
|
|
t.Setenv("PULSE_MOCK_MODE", "true")
|
|
if IsMockResource("node/pve1", "", "") {
|
|
t.Fatal("expected demo mode to bypass mock filtering")
|
|
}
|
|
}
|
|
|
|
func TestPatrolService_InjectDemoFindings(t *testing.T) {
|
|
service := NewPatrolService(nil, nil)
|
|
if service.findings == nil || service.runHistoryStore == nil {
|
|
t.Fatal("expected findings and run history to be initialized")
|
|
}
|
|
|
|
service.InjectDemoFindings()
|
|
|
|
findings := service.findings.GetAll(nil)
|
|
if len(findings) != 5 {
|
|
t.Fatalf("expected 5 demo findings, got %d", len(findings))
|
|
}
|
|
if service.runHistoryStore.Count() != 13 {
|
|
t.Fatalf("expected 13 demo run history entries, got %d", service.runHistoryStore.Count())
|
|
}
|
|
}
|
|
|
|
func TestPatrolService_InjectDemoFindings_NoStore(t *testing.T) {
|
|
service := &PatrolService{}
|
|
service.InjectDemoFindings()
|
|
}
|
|
|
|
func TestPatrolService_InjectDemoRunHistory_NoStore(t *testing.T) {
|
|
service := &PatrolService{}
|
|
service.injectDemoRunHistory()
|
|
}
|
|
|
|
func TestGenerateDemoAIResponse(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
prompt string
|
|
expected string
|
|
}{
|
|
{"patrol", "Analyze the infrastructure for issues", "ZFS pool 'local-zfs' is 94% full"},
|
|
{"disk", "disk is full", "Disk Usage Analysis"},
|
|
{"memory", "memory pressure", "Memory Analysis"},
|
|
{"backup", "pbs backup status", "Backup Status Review"},
|
|
{"cpu", "cpu load is high", "CPU/Performance Analysis"},
|
|
{"hello", "hello there", "Pulse Assistant"},
|
|
{"default", "status report", "This Demo Shows"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
resp := GenerateDemoAIResponse(tt.prompt)
|
|
if resp == nil {
|
|
t.Fatal("expected response")
|
|
}
|
|
if !strings.Contains(resp.Content, tt.expected) {
|
|
t.Fatalf("expected response to contain %q, got %q", tt.expected, resp.Content)
|
|
}
|
|
if resp.Model == "" {
|
|
t.Fatal("expected model to be set")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateDemoAIStream(t *testing.T) {
|
|
var content strings.Builder
|
|
done := false
|
|
|
|
resp, err := GenerateDemoAIStream("disk usage", func(event StreamEvent) {
|
|
switch event.Type {
|
|
case "content":
|
|
chunk, ok := event.Data.(string)
|
|
if !ok {
|
|
t.Fatalf("expected string content chunk, got %T", event.Data)
|
|
}
|
|
content.WriteString(chunk)
|
|
case "done":
|
|
done = true
|
|
}
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("GenerateDemoAIStream failed: %v", err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected response")
|
|
}
|
|
if !done {
|
|
t.Fatal("expected done event")
|
|
}
|
|
if content.String() != resp.Content {
|
|
t.Fatal("expected streamed content to match response content")
|
|
}
|
|
}
|