mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 03:20:11 +00:00
New test files with expanded coverage: API tests: - ai_handler_test.go: AI handler unit tests with mocking - agent_profiles_tools_test.go: Profile management tests - alerts_endpoints_test.go: Alert API endpoint tests - alerts_test.go: Updated for interface changes - audit_handlers_test.go: Audit handler tests - frontend_embed_test.go: Frontend embedding tests - metadata_handlers_test.go, metadata_provider_test.go: Metadata tests - notifications_test.go: Updated for interface changes - profile_suggestions_test.go: Profile suggestion tests - saml_service_test.go: SAML authentication tests - sensor_proxy_gate_test.go: Sensor proxy tests - updates_test.go: Updated for interface changes Agent tests: - dockeragent/signature_test.go: Docker agent signature tests - hostagent/agent_metrics_test.go: Host agent metrics tests - hostagent/commands_test.go: Command execution tests - hostagent/network_helpers_test.go: Network helper tests - hostagent/proxmox_setup_test.go: Updated setup tests - kubernetesagent/*_test.go: Kubernetes agent tests Core package tests: - monitoring/kubernetes_agents_test.go, reload_test.go - remoteconfig/client_test.go, signature_test.go - sensors/collector_test.go - updates/adapter_installsh_*_test.go: Install adapter tests - updates/manager_*_test.go: Update manager tests - websocket/hub_*_test.go: WebSocket hub tests Library tests: - pkg/audit/export_test.go: Audit export tests - pkg/metrics/store_test.go: Metrics store tests - pkg/proxmox/*_test.go: Proxmox client tests - pkg/reporting/reporting_test.go: Reporting tests - pkg/server/*_test.go: Server tests - pkg/tlsutil/extra_test.go: TLS utility tests Total: ~8000 lines of new test code
254 lines
7.1 KiB
Go
254 lines
7.1 KiB
Go
package api_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/alerts"
|
|
)
|
|
|
|
func TestAlertsEndpoints(t *testing.T) {
|
|
srv := newIntegrationServer(t)
|
|
|
|
// 1. Get initial alert config
|
|
t.Run("GetAlertConfig", func(t *testing.T) {
|
|
res, err := http.Get(srv.server.URL + "/api/alerts/config")
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res.StatusCode, http.StatusOK)
|
|
}
|
|
|
|
var config alerts.AlertConfig
|
|
if err := json.NewDecoder(res.Body).Decode(&config); err != nil {
|
|
t.Fatalf("decode failed: %v", err)
|
|
}
|
|
})
|
|
|
|
// 2. Update alert config
|
|
t.Run("UpdateAlertConfig", func(t *testing.T) {
|
|
newConfig := alerts.AlertConfig{
|
|
Schedule: alerts.ScheduleConfig{
|
|
Cooldown: 300,
|
|
},
|
|
}
|
|
body, _ := json.Marshal(newConfig)
|
|
// HandleAlerts expects PUT for config updates
|
|
req, err := http.NewRequest(http.MethodPut, srv.server.URL+"/api/alerts/config", bytes.NewBuffer(body))
|
|
if err != nil {
|
|
t.Fatalf("create request failed: %v", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res.StatusCode, http.StatusOK)
|
|
}
|
|
|
|
// Verify update persistence
|
|
resVerify, err := http.Get(srv.server.URL + "/api/alerts/config")
|
|
if err != nil {
|
|
t.Fatalf("verify request failed: %v", err)
|
|
}
|
|
defer resVerify.Body.Close()
|
|
|
|
var updatedConfig alerts.AlertConfig
|
|
if err := json.NewDecoder(resVerify.Body).Decode(&updatedConfig); err != nil {
|
|
t.Fatalf("decode failed: %v", err)
|
|
}
|
|
|
|
if updatedConfig.Schedule.Cooldown != 300 {
|
|
t.Errorf("expected cooldown 300, got %d", updatedConfig.Schedule.Cooldown)
|
|
}
|
|
})
|
|
|
|
// 3. Activate alerts
|
|
t.Run("ActivateAlerts", func(t *testing.T) {
|
|
req, err := http.NewRequest(http.MethodPost, srv.server.URL+"/api/alerts/activate", nil)
|
|
if err != nil {
|
|
t.Fatalf("create request failed: %v", err)
|
|
}
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res.StatusCode, http.StatusOK)
|
|
}
|
|
|
|
// Activate again (should be idempotent)
|
|
res2, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatalf("retry request failed: %v", err)
|
|
}
|
|
defer res2.Body.Close()
|
|
if res2.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res2.StatusCode, http.StatusOK)
|
|
}
|
|
})
|
|
|
|
// 4. Get active alerts
|
|
t.Run("GetActiveAlerts", func(t *testing.T) {
|
|
res, err := http.Get(srv.server.URL + "/api/alerts/active")
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res.StatusCode, http.StatusOK)
|
|
}
|
|
})
|
|
|
|
// 5. Get alert history
|
|
t.Run("GetAlertHistory", func(t *testing.T) {
|
|
res, err := http.Get(srv.server.URL + "/api/alerts/history")
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res.StatusCode, http.StatusOK)
|
|
}
|
|
|
|
// Test filters
|
|
resFilter, err := http.Get(srv.server.URL + "/api/alerts/history?limit=10&severity=critical")
|
|
if err != nil {
|
|
t.Fatalf("filter request failed: %v", err)
|
|
}
|
|
defer resFilter.Body.Close()
|
|
if resFilter.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", resFilter.StatusCode, http.StatusOK)
|
|
}
|
|
})
|
|
|
|
// 6. Clear alert history
|
|
t.Run("ClearAlertHistory", func(t *testing.T) {
|
|
// HandleAlerts expects DELETE on /history
|
|
req, err := http.NewRequest(http.MethodDelete, srv.server.URL+"/api/alerts/history", nil)
|
|
if err != nil {
|
|
t.Fatalf("create request failed: %v", err)
|
|
}
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res.StatusCode, http.StatusOK)
|
|
}
|
|
})
|
|
|
|
// 7. Acknowledge Alert (Single)
|
|
t.Run("AcknowledgeAlert", func(t *testing.T) {
|
|
body := map[string]string{"id": "test-alert-id"}
|
|
jsonBody, _ := json.Marshal(body)
|
|
|
|
req, err := http.NewRequest(http.MethodPost, srv.server.URL+"/api/alerts/acknowledge", bytes.NewBuffer(jsonBody))
|
|
if err != nil {
|
|
t.Fatalf("create request failed: %v", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
// Should be 404 because alert doesn't exist, but that proves the handler code ran
|
|
if res.StatusCode != http.StatusNotFound && res.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want 404 or 200", res.StatusCode)
|
|
}
|
|
})
|
|
|
|
// 8. Bulk Acknowledge
|
|
t.Run("BulkAcknowledge", func(t *testing.T) {
|
|
body := map[string]interface{}{
|
|
"alertIds": []string{"alert-1", "alert-2"},
|
|
}
|
|
jsonBody, _ := json.Marshal(body)
|
|
|
|
req, err := http.NewRequest(http.MethodPost, srv.server.URL+"/api/alerts/bulk/acknowledge", bytes.NewBuffer(jsonBody))
|
|
if err != nil {
|
|
t.Fatalf("create request failed: %v", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res.StatusCode, http.StatusOK)
|
|
}
|
|
})
|
|
|
|
// 9. Bulk Clear
|
|
t.Run("BulkClear", func(t *testing.T) {
|
|
body := map[string]interface{}{
|
|
"alertIds": []string{"alert-1", "alert-2"},
|
|
}
|
|
jsonBody, _ := json.Marshal(body)
|
|
|
|
req, err := http.NewRequest(http.MethodPost, srv.server.URL+"/api/alerts/bulk/clear", bytes.NewBuffer(jsonBody))
|
|
if err != nil {
|
|
t.Fatalf("create request failed: %v", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res.StatusCode, http.StatusOK)
|
|
}
|
|
})
|
|
|
|
// 10. Incident Timeline
|
|
t.Run("GetIncidentTimeline", func(t *testing.T) {
|
|
// Test timeline list by resource
|
|
res, err := http.Get(srv.server.URL + "/api/alerts/incidents?resource_id=test-node")
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res.StatusCode, http.StatusOK)
|
|
}
|
|
|
|
// Test specific alert timeline
|
|
res2, err := http.Get(srv.server.URL + "/api/alerts/incidents?alert_id=test-alert")
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
defer res2.Body.Close()
|
|
|
|
// 200 OK (empty/null) or 404 depending on impl. Implementation returns null/empty usually if not found but status 200.
|
|
if res2.StatusCode != http.StatusOK {
|
|
t.Errorf("status code = %d, want %d", res2.StatusCode, http.StatusOK)
|
|
}
|
|
})
|
|
}
|