Pulse/internal/api/ai_intelligence_handlers_more2_test.go
2026-02-02 23:01:29 +00:00

760 lines
26 KiB
Go

package api
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/ai"
"github.com/rcourtman/pulse-go-rewrite/internal/ai/forecast"
"github.com/rcourtman/pulse-go-rewrite/internal/ai/learning"
"github.com/rcourtman/pulse-go-rewrite/internal/ai/proxmox"
"github.com/rcourtman/pulse-go-rewrite/internal/ai/remediation"
"github.com/rcourtman/pulse-go-rewrite/internal/ai/unified"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
)
type stubForecastProvider struct {
points []forecast.MetricDataPoint
err error
}
func (s stubForecastProvider) GetMetricHistory(_ string, _ string, _, _ time.Time) ([]forecast.MetricDataPoint, error) {
if s.err != nil {
return nil, s.err
}
return s.points, nil
}
type stubForecastStateProvider struct {
state forecast.StateSnapshot
}
func (s stubForecastStateProvider) GetState() forecast.StateSnapshot {
return s.state
}
func makeForecastPoints(count int, start time.Time, startValue, step float64) []forecast.MetricDataPoint {
points := make([]forecast.MetricDataPoint, 0, count)
for i := 0; i < count; i++ {
points = append(points, forecast.MetricDataPoint{
Timestamp: start.Add(time.Duration(i) * time.Hour),
Value: startValue + float64(i)*step,
})
}
return points
}
func addBaseline(t *testing.T, store *ai.BaselineStore, resourceID, resourceType, metric string, value float64) {
t.Helper()
points := []ai.BaselineMetricPoint{{Value: value, Timestamp: time.Now()}}
if err := store.Learn(resourceID, resourceType, metric, points); err != nil {
t.Fatalf("baseline Learn error: %v", err)
}
}
func TestHandleGetAnomalies_NoStateProvider(t *testing.T) {
svc := newEnabledAIService(t)
store := ai.NewBaselineStore(ai.BaselineConfig{MinSamples: 1})
svc.SetBaselineStore(store)
handler := &AISettingsHandler{legacyAIService: svc}
req := httptest.NewRequest(http.MethodGet, "/api/ai/intelligence/anomalies", nil)
rec := httptest.NewRecorder()
handler.HandleGetAnomalies(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if payload["message"] != "State provider not available" {
t.Fatalf("unexpected message: %#v", payload["message"])
}
}
func TestHandleGetAnomalies_MixedResources(t *testing.T) {
svc := newEnabledAIService(t)
store := ai.NewBaselineStore(ai.BaselineConfig{MinSamples: 1})
addBaseline(t, store, "vm-1", "vm", "cpu", 10)
addBaseline(t, store, "vm-1", "vm", "memory", 10)
addBaseline(t, store, "vm-1", "vm", "disk", 10)
addBaseline(t, store, "ct-1", "container", "cpu", 10)
addBaseline(t, store, "ct-1", "container", "memory", 10)
addBaseline(t, store, "ct-1", "container", "disk", 10)
addBaseline(t, store, "node-1", "node", "cpu", 10)
addBaseline(t, store, "node-1", "node", "memory", 10)
svc.SetBaselineStore(store)
state := models.StateSnapshot{
VMs: []models.VM{
{ID: "vm-1", Name: "vm-one", Status: "running", CPU: 0.9, Memory: models.Memory{Usage: 85}, Disk: models.Disk{Usage: 90}},
{ID: "vm-template", Template: true, Status: "running", CPU: 0.9, Memory: models.Memory{Usage: 90}},
{ID: "vm-stopped", Status: "stopped", CPU: 0.9, Memory: models.Memory{Usage: 90}},
},
Containers: []models.Container{
{ID: "ct-1", Name: "ct-one", Status: "running", CPU: 0.9, Memory: models.Memory{Usage: 85}, Disk: models.Disk{Usage: 90}},
{ID: "ct-template", Template: true, Status: "running", CPU: 0.9, Memory: models.Memory{Usage: 90}},
{ID: "ct-stopped", Status: "stopped", CPU: 0.9, Memory: models.Memory{Usage: 90}},
},
Nodes: []models.Node{
{ID: "node-1", Name: "node-one", CPU: 0.9, Memory: models.Memory{Usage: 85}},
},
}
svc.SetStateProvider(snapshotStateProvider{state: state})
handler := &AISettingsHandler{legacyAIService: svc}
req := httptest.NewRequest(http.MethodGet, "/api/ai/intelligence/anomalies", nil)
rec := httptest.NewRecorder()
handler.HandleGetAnomalies(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
anomalies, ok := payload["anomalies"].([]interface{})
if !ok || len(anomalies) == 0 {
t.Fatalf("expected anomalies, got %#v", payload["anomalies"])
}
types := map[string]bool{}
for _, item := range anomalies {
row, _ := item.(map[string]interface{})
if rtype, ok := row["resource_type"].(string); ok {
types[rtype] = true
}
}
if !types["vm"] || !types["container"] || !types["node"] {
t.Fatalf("expected vm, container, node anomalies, got %#v", types)
}
}
func TestHandleGetLearningStatus_WaitingAndActive(t *testing.T) {
t.Run("waiting", func(t *testing.T) {
svc := newEnabledAIService(t)
store := ai.NewBaselineStore(ai.BaselineConfig{MinSamples: 1})
svc.SetBaselineStore(store)
handler := &AISettingsHandler{legacyAIService: svc}
req := httptest.NewRequest(http.MethodGet, "/api/ai/intelligence/learning", nil)
rec := httptest.NewRecorder()
handler.HandleGetLearningStatus(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if payload["status"] != "waiting" {
t.Fatalf("expected status waiting, got %#v", payload["status"])
}
})
t.Run("active", func(t *testing.T) {
svc := newEnabledAIService(t)
store := ai.NewBaselineStore(ai.BaselineConfig{MinSamples: 1})
for i := 0; i < 5; i++ {
id := fmt.Sprintf("res-%d", i)
addBaseline(t, store, id, "vm", "cpu", 10)
}
svc.SetBaselineStore(store)
handler := &AISettingsHandler{legacyAIService: svc}
req := httptest.NewRequest(http.MethodGet, "/api/ai/intelligence/learning", nil)
rec := httptest.NewRecorder()
handler.HandleGetLearningStatus(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if payload["status"] != "active" {
t.Fatalf("expected status active, got %#v", payload["status"])
}
})
}
func TestHandleGetLearningPreferences_Stats(t *testing.T) {
store := learning.NewLearningStore(learning.LearningStoreConfig{})
store.RecordFeedback(learning.FeedbackRecord{
FindingID: "finding-1",
ResourceID: "res-1",
Category: "performance",
Severity: "warning",
Action: learning.ActionAcknowledge,
})
handler := &AISettingsHandler{}
handler.SetLearningStore(store)
req := httptest.NewRequest(http.MethodGet, "/api/ai/learning/preferences", nil)
rec := httptest.NewRecorder()
handler.HandleGetLearningPreferences(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if _, ok := payload["statistics"]; !ok {
t.Fatalf("expected statistics in response")
}
}
func TestHandleGetUnifiedFindings_Statuses(t *testing.T) {
store := unified.NewUnifiedStore(unified.DefaultAlertToFindingConfig())
now := time.Now()
resolvedAt := now.Add(-2 * time.Hour)
snoozedUntil := now.Add(2 * time.Hour)
store.AddFromAI(&unified.UnifiedFinding{
ID: "finding-active",
Source: unified.SourceAIPatrol,
Severity: unified.SeverityWarning,
Category: unified.CategoryPerformance,
ResourceID: "res-1",
ResourceName: "res-1",
ResourceType: "vm",
Title: "Active",
Description: "active",
DetectedAt: now,
LastSeenAt: now,
})
store.AddFromAI(&unified.UnifiedFinding{
ID: "finding-resolved",
Source: unified.SourceThreshold,
Severity: unified.SeverityCritical,
Category: unified.CategoryCapacity,
ResourceID: "res-1",
ResourceName: "res-1",
ResourceType: "vm",
Title: "Resolved",
Description: "resolved",
ResolvedAt: &resolvedAt,
DetectedAt: now,
LastSeenAt: now,
})
store.AddFromAI(&unified.UnifiedFinding{
ID: "finding-snoozed",
Source: unified.SourceAIPatrol,
Severity: unified.SeverityWarning,
Category: unified.CategoryPerformance,
ResourceID: "res-1",
ResourceName: "res-1",
ResourceType: "vm",
Title: "Snoozed",
Description: "snoozed",
SnoozedUntil: &snoozedUntil,
DetectedAt: now,
LastSeenAt: now,
})
store.AddFromAI(&unified.UnifiedFinding{
ID: "finding-dismissed",
Source: unified.SourceAIPatrol,
Severity: unified.SeverityInfo,
Category: unified.CategoryGeneral,
ResourceID: "res-1",
ResourceName: "res-1",
ResourceType: "vm",
Title: "Dismissed",
Description: "dismissed",
DismissedReason: "noise",
Suppressed: true,
DetectedAt: now,
LastSeenAt: now,
})
handler := &AISettingsHandler{}
handler.SetUnifiedStore(store)
req := httptest.NewRequest(http.MethodGet, "/api/ai/unified/findings?resource_id=res-1&include_resolved=true", nil)
rec := httptest.NewRecorder()
handler.HandleGetUnifiedFindings(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if payload["count"] != float64(4) {
t.Fatalf("expected count 4, got %#v", payload["count"])
}
if payload["active_count"] != float64(1) {
t.Fatalf("expected active_count 1, got %#v", payload["active_count"])
}
}
func TestHandleGetForecast_MissingParams(t *testing.T) {
handler := &AISettingsHandler{forecastService: forecast.NewService(forecast.DefaultForecastConfig())}
req := httptest.NewRequest(http.MethodGet, "/api/ai/forecast?metric=cpu", nil)
rec := httptest.NewRecorder()
handler.HandleGetForecast(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
}
}
func TestHandleGetForecast_Success(t *testing.T) {
points := makeForecastPoints(60, time.Now().Add(-60*time.Hour), 50, 0.1)
svc := forecast.NewService(forecast.DefaultForecastConfig())
svc.SetDataProvider(stubForecastProvider{points: points})
handler := &AISettingsHandler{forecastService: svc}
req := httptest.NewRequest(http.MethodGet, "/api/ai/forecast?resource_id=vm-1&resource_name=vm-one&metric=cpu&horizon_hours=2&threshold=60", nil)
rec := httptest.NewRecorder()
handler.HandleGetForecast(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
forecastVal, ok := payload["forecast"].(map[string]interface{})
if !ok {
t.Fatalf("expected forecast object")
}
if forecastVal["resource_id"] != "vm-1" {
t.Fatalf("unexpected resource_id: %#v", forecastVal["resource_id"])
}
if forecastVal["metric"] != "cpu" {
t.Fatalf("unexpected metric: %#v", forecastVal["metric"])
}
}
func TestHandleGetForecastOverview_Success(t *testing.T) {
points := makeForecastPoints(60, time.Now().Add(-60*time.Hour), 50, 0.1)
svc := forecast.NewService(forecast.DefaultForecastConfig())
svc.SetDataProvider(stubForecastProvider{points: points})
svc.SetStateProvider(stubForecastStateProvider{state: forecast.StateSnapshot{
VMs: []forecast.VMInfo{{ID: "vm-1", Name: "vm-one"}},
Containers: []forecast.ContainerInfo{{ID: "ct-1", Name: "ct-one"}},
Nodes: []forecast.NodeInfo{{ID: "node-1", Name: "node-one"}},
}})
handler := &AISettingsHandler{forecastService: svc}
req := httptest.NewRequest(http.MethodGet, "/api/ai/forecasts/overview?metric=cpu&horizon_hours=24&threshold=60", nil)
rec := httptest.NewRecorder()
handler.HandleGetForecastOverview(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if payload["metric"] != "cpu" {
t.Fatalf("unexpected metric: %#v", payload["metric"])
}
if payload["threshold"] != float64(60) {
t.Fatalf("unexpected threshold: %#v", payload["threshold"])
}
forecasts, ok := payload["forecasts"].([]interface{})
if !ok || len(forecasts) == 0 {
t.Fatalf("expected forecasts, got %#v", payload["forecasts"])
}
}
func TestHandleGetProxmoxEvents_ResourceFilter(t *testing.T) {
correlator := proxmox.NewEventCorrelator(proxmox.EventCorrelatorConfig{})
correlator.RecordEvent(proxmox.ProxmoxEvent{
ID: "evt-1",
Type: proxmox.EventVMStart,
ResourceID: "vm-1",
})
handler := &AISettingsHandler{proxmoxCorrelator: correlator}
req := httptest.NewRequest(http.MethodGet, "/api/ai/proxmox/events?resource_id=vm-1&limit=1", nil)
rec := httptest.NewRecorder()
handler.HandleGetProxmoxEvents(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
events, ok := payload["events"].([]interface{})
if !ok || len(events) != 1 {
t.Fatalf("expected 1 event, got %#v", payload["events"])
}
}
func TestHandleGetProxmoxCorrelations_ResourceFilter(t *testing.T) {
correlator := proxmox.NewEventCorrelator(proxmox.EventCorrelatorConfig{})
corr := proxmox.EventCorrelation{
ID: "corr-1",
Event: proxmox.ProxmoxEvent{
ID: "evt-1",
Type: proxmox.EventVMStart,
ResourceID: "vm-1",
},
ImpactedResources: []string{"vm-1"},
CreatedAt: time.Now(),
}
setUnexportedField(t, correlator, "correlations", []proxmox.EventCorrelation{corr})
handler := &AISettingsHandler{proxmoxCorrelator: correlator}
req := httptest.NewRequest(http.MethodGet, "/api/ai/proxmox/correlations?resource_id=vm-1", nil)
rec := httptest.NewRecorder()
handler.HandleGetProxmoxCorrelations(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
correlations, ok := payload["correlations"].([]interface{})
if !ok || len(correlations) != 1 {
t.Fatalf("expected 1 correlation, got %#v", payload["correlations"])
}
}
func TestHandleExecuteRemediationPlan_Errors(t *testing.T) {
t.Run("no_engine", func(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/remediation/plans/plan-1/execute", nil)
rec := httptest.NewRecorder()
handler.HandleExecuteRemediationPlan(rec, req)
if rec.Code != http.StatusServiceUnavailable {
t.Fatalf("expected status %d, got %d", http.StatusServiceUnavailable, rec.Code)
}
})
t.Run("invalid_body", func(t *testing.T) {
handler := &AISettingsHandler{remediationEngine: remediation.NewEngine(remediation.EngineConfig{})}
req := httptest.NewRequest(http.MethodPost, "/api/ai/remediation/plans/plan-1/execute", bytes.NewBufferString("bad-json"))
rec := httptest.NewRecorder()
handler.HandleExecuteRemediationPlan(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
}
})
t.Run("missing_ids", func(t *testing.T) {
handler := &AISettingsHandler{remediationEngine: remediation.NewEngine(remediation.EngineConfig{})}
req := httptest.NewRequest(http.MethodPost, "/api/ai/remediation/plans/plan-1/execute", bytes.NewBufferString("{}"))
rec := httptest.NewRecorder()
handler.HandleExecuteRemediationPlan(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
}
})
t.Run("execution_error", func(t *testing.T) {
engine := remediation.NewEngine(remediation.EngineConfig{})
plan := &remediation.RemediationPlan{
ID: "plan-no-exec",
FindingID: "finding-1",
ResourceID: "res-1",
Title: "Restart",
Description: "Restart service",
Steps: []remediation.RemediationStep{
{
Order: 0,
Description: "Restart",
Command: "echo ok",
Target: "host-1",
},
},
}
if err := engine.CreatePlan(plan); err != nil {
t.Fatalf("CreatePlan: %v", err)
}
handler := &AISettingsHandler{remediationEngine: engine}
body := bytes.NewBufferString(`{"plan_id":"plan-no-exec"}`)
req := httptest.NewRequest(http.MethodPost, "/api/ai/remediation/plans/plan-no-exec/execute", body)
rec := httptest.NewRecorder()
handler.HandleExecuteRemediationPlan(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
}
})
}
func TestHandleRollbackRemediationPlan_Errors(t *testing.T) {
t.Run("no_engine", func(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/remediation/plans/plan-1/rollback", nil)
rec := httptest.NewRecorder()
handler.HandleRollbackRemediationPlan(rec, req)
if rec.Code != http.StatusServiceUnavailable {
t.Fatalf("expected status %d, got %d", http.StatusServiceUnavailable, rec.Code)
}
})
t.Run("invalid_body", func(t *testing.T) {
handler := &AISettingsHandler{remediationEngine: remediation.NewEngine(remediation.EngineConfig{})}
req := httptest.NewRequest(http.MethodPost, "/api/ai/remediation/plans/plan-1/rollback", bytes.NewBufferString("bad-json"))
rec := httptest.NewRecorder()
handler.HandleRollbackRemediationPlan(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
}
})
t.Run("missing_execution_id", func(t *testing.T) {
handler := &AISettingsHandler{remediationEngine: remediation.NewEngine(remediation.EngineConfig{})}
req := httptest.NewRequest(http.MethodPost, "/api/ai/remediation/plans/plan-1/rollback", bytes.NewBufferString("{}"))
rec := httptest.NewRecorder()
handler.HandleRollbackRemediationPlan(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
}
})
}
func TestHandleGetIncidentData_Errors(t *testing.T) {
t.Run("invalid_path", func(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodGet, "/api/ai/incident/abc", nil)
rec := httptest.NewRecorder()
handler.HandleGetIncidentData(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
}
})
t.Run("missing_resource", func(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodGet, "/api/ai/incidents/", nil)
rec := httptest.NewRecorder()
handler.HandleGetIncidentData(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
}
})
t.Run("no_service", func(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodGet, "/api/ai/incidents/res-1", nil)
rec := httptest.NewRecorder()
handler.HandleGetIncidentData(rec, req)
if rec.Code != http.StatusServiceUnavailable {
t.Fatalf("expected status %d, got %d", http.StatusServiceUnavailable, rec.Code)
}
})
}
func TestHandleGetRecentIncidents_NoService(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodGet, "/api/ai/incidents", nil)
rec := httptest.NewRecorder()
handler.HandleGetRecentIncidents(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if payload["message"] != "Pulse Patrol service not available" {
t.Fatalf("unexpected message: %#v", payload["message"])
}
}
func TestHandleGetCircuitBreakerStatus_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/circuit/status", nil)
rec := httptest.NewRecorder()
handler.HandleGetCircuitBreakerStatus(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleGetForecastOverview_NoStateProvider(t *testing.T) {
points := makeForecastPoints(40, time.Now().Add(-40*time.Hour), 20, 1)
svc := forecast.NewService(forecast.DefaultForecastConfig())
svc.SetDataProvider(stubForecastProvider{points: points})
handler := &AISettingsHandler{forecastService: svc}
req := httptest.NewRequest(http.MethodGet, "/api/ai/forecasts/overview?metric=cpu", nil)
rec := httptest.NewRecorder()
handler.HandleGetForecastOverview(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if payload["error"] == nil {
t.Fatalf("expected error in response")
}
}
func TestHandleGetForecast_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{forecastService: forecast.NewService(forecast.DefaultForecastConfig())}
req := httptest.NewRequest(http.MethodPost, "/api/ai/forecast", nil)
rec := httptest.NewRecorder()
handler.HandleGetForecast(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleGetIncidentData_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/incidents/res-1", nil)
rec := httptest.NewRecorder()
handler.HandleGetIncidentData(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleGetRecentIncidents_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/incidents", nil)
rec := httptest.NewRecorder()
handler.HandleGetRecentIncidents(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleGetAnomalies_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/intelligence/anomalies", nil)
rec := httptest.NewRecorder()
handler.HandleGetAnomalies(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleGetForecastOverview_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/forecasts/overview", nil)
rec := httptest.NewRecorder()
handler.HandleGetForecastOverview(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleGetLearningPreferences_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/learning/preferences", nil)
rec := httptest.NewRecorder()
handler.HandleGetLearningPreferences(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleGetUnifiedFindings_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/unified/findings", nil)
rec := httptest.NewRecorder()
handler.HandleGetUnifiedFindings(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleGetProxmoxEvents_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/proxmox/events", nil)
rec := httptest.NewRecorder()
handler.HandleGetProxmoxEvents(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleGetProxmoxCorrelations_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodPost, "/api/ai/proxmox/correlations", nil)
rec := httptest.NewRecorder()
handler.HandleGetProxmoxCorrelations(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleExecuteRemediationPlan_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodGet, "/api/ai/remediation/plans/plan-1/execute", nil)
rec := httptest.NewRecorder()
handler.HandleExecuteRemediationPlan(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}
func TestHandleRollbackRemediationPlan_MethodNotAllowed(t *testing.T) {
handler := &AISettingsHandler{}
req := httptest.NewRequest(http.MethodGet, "/api/ai/remediation/plans/plan-1/rollback", nil)
rec := httptest.NewRecorder()
handler.HandleRollbackRemediationPlan(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
}
}