mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-27 08:31:28 +00:00
- Create Intelligence struct that aggregates all AI subsystems - Add /api/ai/intelligence endpoint for system-wide and per-resource insights - Wire Intelligence into PatrolService as a facade (not replacement) - Add TypeScript types and API client for frontend - Add unit tests for Intelligence orchestrator - Fix pre-existing test failures using diagnostic commands instead of actionable ones The Intelligence orchestrator provides: - System-wide health scoring (A-F grades) - Aggregated findings, predictions, correlations - Per-resource context generation for AI prompts - Learning progress tracking This unifies access to AI subsystems without replacing existing code paths.
170 lines
4.5 KiB
Go
170 lines
4.5 KiB
Go
package ai
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/ai/patterns"
|
|
)
|
|
|
|
func TestNewIntelligence(t *testing.T) {
|
|
intel := NewIntelligence(IntelligenceConfig{DataDir: "/tmp/test"})
|
|
if intel == nil {
|
|
t.Fatal("Expected non-nil Intelligence")
|
|
}
|
|
}
|
|
|
|
func TestIntelligence_GetSummary_NoSubsystems(t *testing.T) {
|
|
intel := NewIntelligence(IntelligenceConfig{})
|
|
|
|
summary := intel.GetSummary()
|
|
if summary == nil {
|
|
t.Fatal("Expected non-nil summary")
|
|
}
|
|
|
|
// Should have default healthy state
|
|
if summary.OverallHealth.Score != 100 {
|
|
t.Errorf("Expected health score 100, got %f", summary.OverallHealth.Score)
|
|
}
|
|
if summary.OverallHealth.Grade != HealthGradeA {
|
|
t.Errorf("Expected grade A, got %s", summary.OverallHealth.Grade)
|
|
}
|
|
if summary.Timestamp.IsZero() {
|
|
t.Error("Expected non-zero timestamp")
|
|
}
|
|
}
|
|
|
|
func TestIntelligence_GetResourceIntelligence_NoSubsystems(t *testing.T) {
|
|
intel := NewIntelligence(IntelligenceConfig{})
|
|
|
|
resourceIntel := intel.GetResourceIntelligence("test-resource")
|
|
if resourceIntel == nil {
|
|
t.Fatal("Expected non-nil resource intelligence")
|
|
}
|
|
|
|
if resourceIntel.ResourceID != "test-resource" {
|
|
t.Errorf("Expected resource ID 'test-resource', got %s", resourceIntel.ResourceID)
|
|
}
|
|
|
|
// Should have default healthy state
|
|
if resourceIntel.Health.Score != 100 {
|
|
t.Errorf("Expected health score 100, got %f", resourceIntel.Health.Score)
|
|
}
|
|
}
|
|
|
|
func TestIntelligence_FormatContext_NoSubsystems(t *testing.T) {
|
|
intel := NewIntelligence(IntelligenceConfig{})
|
|
|
|
// With no subsystems, context should be empty
|
|
ctx := intel.FormatContext("test-resource")
|
|
if ctx != "" {
|
|
t.Errorf("Expected empty context with no subsystems, got: %s", ctx)
|
|
}
|
|
}
|
|
|
|
func TestIntelligence_CreatePredictionFinding(t *testing.T) {
|
|
intel := NewIntelligence(IntelligenceConfig{})
|
|
|
|
pred := patterns.FailurePrediction{
|
|
ResourceID: "vm-100",
|
|
EventType: patterns.EventHighCPU, // Use the constant instead of cast
|
|
DaysUntil: 0.5, // Less than 1 day
|
|
Confidence: 0.85, // High confidence
|
|
Basis: "Pattern detected",
|
|
}
|
|
|
|
finding := intel.CreatePredictionFinding(pred)
|
|
if finding == nil {
|
|
t.Fatal("Expected non-nil finding")
|
|
}
|
|
|
|
// High confidence + < 1 day should be critical
|
|
if finding.Severity != FindingSeverityCritical {
|
|
t.Errorf("Expected critical severity for imminent high-confidence prediction, got %s", finding.Severity)
|
|
}
|
|
|
|
if finding.ResourceID != "vm-100" {
|
|
t.Errorf("Expected resource ID 'vm-100', got %s", finding.ResourceID)
|
|
}
|
|
}
|
|
|
|
func TestIntelligence_SetSubsystems(t *testing.T) {
|
|
intel := NewIntelligence(IntelligenceConfig{})
|
|
|
|
// Create a findings store
|
|
findings := NewFindingsStore()
|
|
|
|
// Add a finding
|
|
findings.Add(&Finding{
|
|
ID: "test-finding",
|
|
Key: "test:finding",
|
|
Severity: FindingSeverityWarning,
|
|
Category: FindingCategoryPerformance,
|
|
ResourceID: "vm-100",
|
|
ResourceName: "test-vm",
|
|
ResourceType: "vm",
|
|
Title: "Test Finding",
|
|
DetectedAt: time.Now(),
|
|
LastSeenAt: time.Now(),
|
|
Source: "test",
|
|
})
|
|
|
|
// Set subsystems with just findings
|
|
intel.SetSubsystems(findings, nil, nil, nil, nil, nil, nil, nil)
|
|
|
|
// Get summary
|
|
summary := intel.GetSummary()
|
|
|
|
// Should have 1 warning
|
|
if summary.FindingsCount.Warning != 1 {
|
|
t.Errorf("Expected 1 warning, got %d", summary.FindingsCount.Warning)
|
|
}
|
|
if summary.FindingsCount.Total != 1 {
|
|
t.Errorf("Expected 1 total finding, got %d", summary.FindingsCount.Total)
|
|
}
|
|
|
|
// Health should be reduced due to warning
|
|
if summary.OverallHealth.Score >= 100 {
|
|
t.Error("Expected health score < 100 due to warning finding")
|
|
}
|
|
}
|
|
|
|
func TestIntelligence_HealthGrades(t *testing.T) {
|
|
tests := []struct {
|
|
score float64
|
|
grade HealthGrade
|
|
}{
|
|
{100, HealthGradeA},
|
|
{95, HealthGradeA},
|
|
{90, HealthGradeA},
|
|
{85, HealthGradeB},
|
|
{75, HealthGradeB},
|
|
{70, HealthGradeC},
|
|
{60, HealthGradeC},
|
|
{55, HealthGradeD},
|
|
{40, HealthGradeD},
|
|
{30, HealthGradeF},
|
|
{0, HealthGradeF},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
grade := scoreToGrade(tt.score)
|
|
if grade != tt.grade {
|
|
t.Errorf("scoreToGrade(%f) = %s, expected %s", tt.score, grade, tt.grade)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIntelligence_CheckBaselinesForResource(t *testing.T) {
|
|
intel := NewIntelligence(IntelligenceConfig{})
|
|
|
|
// With no baseline store, should return nil
|
|
anomalies := intel.CheckBaselinesForResource("vm-100", map[string]float64{
|
|
"cpu": 85.0,
|
|
"memory": 90.0,
|
|
})
|
|
|
|
if anomalies != nil {
|
|
t.Error("Expected nil anomalies when baseline store not configured")
|
|
}
|
|
}
|